Skip to content

Instantly share code, notes, and snippets.

@tbbooher
Created September 5, 2011 13:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tbbooher/1195048 to your computer and use it in GitHub Desktop.
Save tbbooher/1195048 to your computer and use it in GitHub Desktop.
class PolcoGroup
include Mongoid::Document
field :name, :type => String
field :type, :type => Symbol, :default => :custom
field :description, :type => String
index :name
index :type
field :follower_count
field :member_count
belongs_to :owner, :class_name => "User", :inverse_of => :custom_groups
has_and_belongs_to_many :members, :class_name => "User", :inverse_of => :joined_groups
has_and_belongs_to_many :followers, :class_name => "User", :inverse_of => :followed_groups
# some validations
validates_uniqueness_of :name, :scope => :type
validates_inclusion_of :type, :in => [:custom, :state, :district, :common, :country], :message => 'Only valid groups are custom, state, district, common, country'
scope :states, where(type: :state)
scope :districts, where(type: :district)
scope :customs, where(type: :custom)
# time to create the ability to follow
#has_many :followers, :class_name => "User", :inverse_of =>
def followers_count
self.follower_ids.count
end
def members_count
self.member_ids.count
end
def the_rep
if self.type == :district
Legislator.all.select{|l| l.district_name == self.name}.first
else
"Only districts can have a representative"
end
end
end
# -*- encoding : utf-8 -*-
# This is a Device-User-Class extended with ROLES, Avatar-handling, and more
class User
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paperclip
include Geocoder::Model::Mongoid
geocoded_by :coordinates
# TODO ^^ is this still needed
cache
devise :database_authenticatable, :registerable, :confirmable,
:recoverable, :rememberable, :trackable, :validatable
field :name, :type => String
field :roles_mask, :type => Fixnum, :default => 0
field :use_gravatar, :type => Boolean, :default => true
field :invitation_id, :type => BSON::ObjectId
# tbb modifications for polco specific code
field :zip_code, :type => String
field :coordinates, :type => Array
field :us_state, :type => String # TODO enum for this?
field :district, :type => String
has_many :custom_groups, :class_name => "PolcoGroup", :inverse_of => :owner
has_many :votes
has_and_belongs_to_many :joined_groups, :class_name => "PolcoGroup"
has_and_belongs_to_many :followed_groups, :class_name => "PolcoGroup"
has_and_belongs_to_many :senators, :class_name => "Legislator", :inverse_of => :state_constituents
belongs_to :representative, :class_name => "Legislator", :inverse_of => :district_constituents
def invitation
@invitation ||= Invitation.criteria.for_ids(self.invitation_id).first
end
def invitation=(inv)
@invitation = nil
self.invitation_id = inv.id
end
def articles
[]
end
references_many :authentications, :dependent => :delete
references_many :postings, :dependent => :delete
references_many :invitations, :dependent => :delete
embeds_many :user_notifications
has_many :articles
validates_presence_of :name
validates_uniqueness_of :name, :case_sensitive => false
validates :email, :presence => true, :email => true
validates_uniqueness_of :email, :case_sensitive => false
attr_accessible :name, :email, :password, :password_confirmation, :roles_mask,
:remember_me, :authentication_token, :confirmation_token,
:avatar, :clear_avatar, :crop_x, :crop_y, :crop_w, :crop_h,
:time_zone, :language, :use_gravatar, :invitation_id
attr_accessor :clear_avatar
has_mongoid_attached_file :avatar,
:styles => {
:popup => "800x600=",
:medium => "300x300>",
:thumb => "100x100>",
:icon => "64x64"
},
:processors => [:cropper]
attr_accessor :crop_x, :crop_y, :crop_w, :crop_h
after_update :reprocess_avatar, :if => :cropping?
# Notifications
after_create :async_notify_on_creation
before_destroy :async_notify_on_cancellation
before_update :notify_if_confirmed
# Authentications
after_create :save_new_authentication
after_create :first_user_hook
# Roles - Do not change the order and do not remove roles if you
# already have productive data! Thou it's safe to append new roles
# at the end of the string. And it's safe to rename roles in place
ROLES = [:guest, :confirmed_user, :author, :moderator, :maintainer, :admin, :registered]
scope :with_role, lambda { |role| {:where => {:roles_mask.gte => ROLES.index(role)}} }
def registered?
(self.role == :registered || !self.zip_code.nil?)
end
def cropping?
!crop_x.blank? && !crop_y.blank? && !crop_w.blank? && !crop_h.blank?
end
def avatar_geometry(style = :original)
paperclip_geometry avatar, style
end
def new_avatar?
avatar.updated_at && ((Time::now() - Time::at(self.avatar.updated_at)) < 1.minute)
end
def admin?
User.all.any? ? (self == User.first || role?(:admin)) : true
end
def role=(role)
self.roles_mask = ROLES.index(role)
Rails.logger.warn("SET ROLES TO #{self.roles_mask} FOR #{self.inspect}")
end
# return user's role as symbol.
def role
ROLES[roles_mask].to_sym
end
# Ask if the user has at least a specific role.
# @user.role?('admin')
def role?(role)
self.roles_mask >= ROLES.index(role.to_sym)
end
# virtual attribute needed for the view but is false always.
def clear_avatar
false
end
# clear a previous uploaded avatar-image.
def clear_avatar=(new_value)
self.avatar = nil if new_value == '1'
end
# fetch attributes from the omniauth-record.
def apply_omniauth(omniauth)
self.email = omniauth['user_info']['email'] if email.blank?
apply_trusted_services(omniauth) if self.new_record?
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
end
# remove the password and password-confirmation attribute if not needed.
def update_with_password(params={})
if params[:password].blank?
params.delete(:password)
params.delete(:password_confirmation) if params[:password_confirmation].blank?
end
super
end
# Remove an URL of the local avatar or the gravatar
def avatar_url(mode)
if self.use_gravatar
"http://gravatar.com/avatar/#{gravatar_id}.png?cache=#{self.updated_at.strftime('%Y%m%d%H%M%S')}"
else
avatar.url(mode)
end
end
# Link to the gravatar profile
def gravatar_profile
if self.use_gravatar
"http://gravatar.com/#{gravatar_id}"
end
end
def vote_on(bill, value)
# test to make sure the user is a member of a group
my_groups = self.joined_groups
unless my_groups.empty?
unless bill.voted_on?(self)
my_groups.each do |g|
unless Vote.create(:value => value, :user => self, :polco_group => g, :bill => bill)
raise "vote not valid"
end
end
else
Rails.logger.warn ""
#raise "already voted on"
end
#bill.save!
else
raise "no joined_groups for this user"
end
end
# district fun
def get_district(coords)
lat, lon = coords # .first, coords.last
self.coordinates = [lat, lon]
feed_url = "#{GOVTRACK_URL}perl/district-lookup.cgi?lat=#{lat}&long=#{lon}"
feed = Feedzirra::Feed.fetch_raw(feed_url)
if feed == 0
raise "Failed to connect to #{feed_url}"
else
govtrack_data = Feedzirra::Parser::GovTrackDistrict.parse(feed)
end
if govtrack_data.districts.count > 1
raise "too many for this coordinate (strange)"
else
result = govtrack_data.districts.first
result.district = "#{result.us_state}#{"%02d" % result.district.to_i}"
end
[result]
end
def get_districts_by_zipcode(zipcode)
feed_url = "#{GOVTRACK_URL}perl/district-lookup.cgi?zipcode=#{zipcode}"
feed = Feedzirra::Feed.fetch_raw(feed_url)
districts = Feedzirra::Parser::GovTrackDistrict.parse(feed).districts
districts.each do |d|
d.district = "#{d.us_state}#{"%02d" % d.district.to_i}"
end
districts
end
def add_district_data(junior_senator, senior_senator, representative, district, us_state)
self.senators.push(junior_senator)
self.senators.push(senior_senator)
self.representative = representative
self.district = district
self.add_baseline_groups(us_state, district)
self.role = :registered # 7 = registered (or 6?)
self.save!
end
def add_baseline_groups(us_state, district)
[[us_state, :state],[district, :district],['USA', :country],['Dan Cole',:common]].each do |name, type|
g = PolcoGroup.find_or_create_by(:name => name, :type => type)
g.members.push(self)
g.member_count += 1
self.joined_groups.push(g)
end
end
def get_members(members)
legs = []
members.each do |member|
# need to clear previous members
if leg = Legislator.where(:govtrack_id => member.govtrack_id).first
legs << leg
else
raise "legislator #{member.govtrack_id} not found"
end
end
senators = legs.select { |l| l.title == 'Sen.' }.sort_by { |u| u.start_date }
members = Hash.new
members[:senior_senator] = senators.first
members[:junior_senator] = senators.last
members[:representative] = (legs - senators).first
members
end
=begin
def save_district_and_members(district, result)
# TODO -- UPDATE
self.district = district
self.legislators = []
result.members.each do |member|
# need to clear previous members
if leg = Legislator.where(:govtrack_id => member.govtrack_id).first
self.legislators << leg
else
raise "legislator #{member.govtrack_id} not found"
end
end
self.save!
end
=end
def get_geodata(params)
case params[:commit]
when "Yes" # the location is coming coming in from an ip lookup
result = Geocoder.coordinates(params[:location])
method = :ip_lookup
when "Submit Address" # then, we have an address to code
address = build_address(params)
result = Geocoder.coordinates(address)
method = :address
when "Submit Zip Code" # we have a zip code to code
result = get_district_by_zipcode(params[:zip_code])
method = :zip_code
when "Confirm address"
result = params[:address]
method = :confirmed_address
else # they did a zip code lookup
result = nil #Geocoder.coordinates(params[:full_zip])
method = :none
end
{:geo_data => result, :method => method}
end
def get_ip(ip)
Geocoder.coordinates(ip)
end
def joined_groups_tallies(bill)
results = Array.new
self.joined_groups.each do |g|
name = g.name
tally=bill.get_votes_by_name_and_type(g.name, g.type)
results.push({:name => name, :tally => tally})
end
results
end
def followed_groups_tallies(bill)
results = Array.new
self.followed_groups.each do |g|
name = g.name
tally=bill.get_votes_by_name_and_type(g.name, g.type)
results.push({:name => name, :tally => tally})
end
results
end
def district_number
self.district.match(/\d+/)[0]
end
def reps_vote_on(house_bill)
unless house_bill.member_votes.empty?
leg = self.representative # Legislator.where(state: self.us_state, district: self.district_number.to_i).first
if leg
out = {:rep => leg.full_name, :vote => house_bill.find_member_vote(leg)}
else
raise "no representative found for #{self.name}"
end
else
out = "no vote available"
end
out
end
def senators_vote_on(senate_bill)
out = Array.new
unless senate_bill.member_votes.empty?
legs = self.senators #.senators.where(state: self.us_state)
# result is mongoid criteria
if legs
legs.each do |senator|
# what if the member_vote is not found?
if senate_bill.find_member_vote(senator)
out.push({:senator=> senator.full_title, :vote => senate_bill.find_member_vote(senator)})
else
raise "#{senator.full_name} does not have a recorded record for #{senate_bill.title}"
end
end # legislators iteration
else
raise "no senators found for #{self.us_state}"
end
else
out = "Your senators have not yet voted on #{senate_bill.tiny_title}"
end
out
end
private
def reprocess_avatar
avatar.reprocess!
end
def gravatar_id
Digest::MD5.hexdigest(self.email.downcase) if self.email
end
def apply_trusted_services(omniauth)
# Merge user_info && extra.user_info
user_info = omniauth['user_info']
if omniauth['extra'] && omniauth['extra']['user_hash']
user_info.merge!(omniauth['extra']['user_hash'])
end
# try name or nickname
if self.name.blank?
self.name = user_info['name'] unless user_info['name'].blank?
self.name ||= user_info['nickname'] unless user_info['nickname'].blank?
self.name ||= (user_info['first_name']+" "+user_info['last_name']) unless \
user_info['first_name'].blank? || user_info['last_name'].blank?
end
if self.email.blank?
self.email = user_info['email'] unless user_info['email'].blank?
end
# Set a random password for omniauthenticated users
self.password, self.password_confirmation = String::random_string(20)
self.confirmed_at, self.confirmation_sent_at = Time.now
# Build a new Authentication and remember until :after_create -> save_new_authentication
@new_auth = authentications.build(:uid => omniauth['uid'], :provider => omniauth['provider'])
end
# Called :after_create
def save_new_authentication
@new_auth.save unless @new_auth.nil?
end
# Inform admin about sign ups and cancellations of accounts
def async_notify_on_creation
DelayedJob.enqueue('NewSignUpNotifier', Time.now, self.id)
end
# Inform admin about cancellations of accounts
def async_notify_on_cancellation
DelayedJob.enqueue('CancelAccountNotifier', Time.now, self.inspect)
end
# Inform admin if someone confirms an account
def notify_if_confirmed
if attribute_changed?('confirmed_at')
DelayedJob.enqueue('AccountConfirmedNotifier', Time.now, self.id)
end
end
# If created user is first user, confirm and make admin
def first_user_hook
if User.count < 2
self.confirmed_at = Time.now
self.role=:admin
self.save!
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment