Election results in the London Borough of Sutton.
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

276 строки
8.8 KiB

  1. require 'data_mapper'
  2. class Poll
  3. include DataMapper::Resource
  4. property :district_id, Integer, :key => true
  5. property :election_id, Integer, :key => true
  6. property :electorate, Integer # The number of people eligible to vote in this district in this election
  7. property :ballot_papers_issued, Integer # The number of ballot papers issued (includes spoiled ballots)
  8. property :seats, Integer, :required => true # The number of seats to be elected in this district in this election
  9. property :rejected_no_official_mark, Integer
  10. property :rejected_too_many_candidates, Integer
  11. property :rejected_identifiable_voter, Integer
  12. property :rejected_blank_or_uncertain, Integer
  13. def turnout_percent
  14. @ballot_papers_issued.to_f / @electorate.to_f * 100.0
  15. end
  16. def total_rejected_ballots
  17. if @rejected_no_official_mark
  18. @rejected_no_official_mark + \
  19. @rejected_too_many_candidates + \
  20. @rejected_identifiable_voter + \
  21. @rejected_blank_or_uncertain
  22. else
  23. nil
  24. end
  25. end
  26. def valid_ballot_papers
  27. self.total_rejected_ballots ? @ballot_papers_issued - self.total_rejected_ballots : nil
  28. end
  29. def successful_candidacies # Candidacies where the candidate was elected
  30. Candidacy.all(:election => @election, :district => @district, :order => [:position], :limit => @seats)
  31. end
  32. # Set candidacy.position for every candidacy in this poll
  33. # Returns array of candidacies, or false if we don't have results for this poll
  34. def set_positions
  35. # Check that every candidacy for this poll has its votes recorded (ie that the election results are known)
  36. if Candidacy.count(:conditions => { :district_id => @district_id, :election_id => @election_id, :votes => nil }) == 0
  37. return false
  38. end
  39. # Get the candidacies for this poll
  40. ccys = Candidacy.all(:conditions => { :district_id => @district_id, :election_id => @election_id }, :order => [:votes.desc])
  41. puts "Found no candidacies for this poll" if ccys.size == 0
  42. position = 1
  43. ccys.each do |ccy|
  44. position <= @seats ? ccy.seats = 1 : ccy.seats = 0
  45. ccy.position = position
  46. ccy.save
  47. position += 1
  48. end
  49. ccys
  50. end
  51. belongs_to :election
  52. belongs_to :district
  53. end
  54. class PollingStation
  55. include DataMapper::Resource
  56. property :id, String, :key => true, :length => 2 # e.g. "KA"
  57. property :name, String, :length => 255#, :required => true
  58. property :address, String, :length => 255#, :required => true
  59. property :postcode, String#, :required => true
  60. property :easting, Float, :required => true
  61. property :northing, Float, :required => true
  62. property :lat, Float, :required => true
  63. property :lng, Float, :required => true
  64. has n, :postcodes
  65. end
  66. class Postcode
  67. include DataMapper::Resource
  68. # Postcode natural key, uppercase with space, eg. "SM1 1EA"
  69. # Column names derived from Ordnance Survey CodePoint Open
  70. property :postcode, String, :key => true
  71. property :positional_quality_indicator, Integer
  72. property :eastings, Integer, :required => true
  73. property :northings, Integer, :required => true
  74. property :country_code, String, :required => true
  75. property :nhs_regional_ha_code, String, :required => true
  76. property :nhs_ha_code, String, :required => true
  77. property :admin_county_code, String # NULL within Greater London
  78. property :admin_district_code, String, :required => true # e.g. London Borough of Sutton
  79. property :admin_ward_code, String, :required => true # e.g. Sutton Central
  80. property :lat, Float, :required => true
  81. property :lng, Float, :required => true
  82. property :ward_id, Integer, :required => true # Sutton Council
  83. property :constituency_id, Integer, :required => false # UK Parliament
  84. property :polling_station_id, String, :length => 2
  85. belongs_to :district, :child_key => [:ward_id]
  86. belongs_to :polling_station
  87. def self.finder(postcode)
  88. postcode = postcode.strip.upcase
  89. if o = self.get(postcode)
  90. return o
  91. end
  92. result = Pat.get(postcode)
  93. unless result.code == 404
  94. # cache API result
  95. self.create(
  96. :postcode => postcode,
  97. :lat => result['geo']['lat'],
  98. :lng => result['geo']['lng'],
  99. :district_name => result['administrative']['district']['title'],
  100. :district_code => result['administrative']['district']['uri'].match(/.+\/(.+)$/)[1],
  101. :ward_name => result['administrative']['ward']['title'],
  102. :ward_code => result['administrative']['ward']['uri'].match(/.+\/(.+)$/)[1]
  103. )
  104. else
  105. # invalid postcode
  106. nil
  107. end
  108. end
  109. end
  110. class Candidate
  111. include DataMapper::Resource
  112. property :id, Serial
  113. property :forenames, String, :required => true
  114. property :surname, String, :required => true, :index => true
  115. has n, :candidacies
  116. def short_name
  117. @forenames.split(' ')[0] + ' ' + @surname
  118. end
  119. def name
  120. @forenames + ' ' + @surname
  121. end
  122. def url
  123. "/candidates/" + @id.to_s
  124. end
  125. end
  126. class DeletedCandidate
  127. include DataMapper::Resource
  128. property :old_candidate_id, Integer, :key => true # ID of candidate that has been merged/deleted
  129. property :candidate_id, Integer, :required => true # ID of candidate that has been kept
  130. end
  131. class Candidacy
  132. include DataMapper::Resource
  133. property :id, Serial
  134. property :election_id, Integer, :required => true
  135. property :candidate_id, Integer, :required => true
  136. property :party_id, Integer
  137. property :district_id, Integer, :required => true
  138. property :votes, Integer
  139. property :address, String, :length => 200
  140. property :postcode, String
  141. property :position, Integer # Position of this candidate in this district. (1..n)
  142. property :seats, Integer # Number of seats won by this candidacy (0 or 1)
  143. property :labcoop, Boolean, :default => false # Candidacy is for joint Labour/Co-op party
  144. belongs_to :election
  145. belongs_to :candidate
  146. belongs_to :party
  147. belongs_to :district
  148. end
  149. class Campaign
  150. include DataMapper::Resource
  151. property :party_id, Integer, :key => true
  152. property :election_id, Integer, :key => true
  153. property :party_url, String, :length => 255
  154. property :manifesto_html_url, String, :length => 255
  155. property :manifesto_pdf_url, String, :length => 255
  156. belongs_to :party
  157. belongs_to :election
  158. end
  159. class Election
  160. include DataMapper::Resource
  161. property :id, Serial
  162. property :body_id, Integer, :required => true
  163. property :d, Date, :required => true, :index => true
  164. property :reason, String, :length => 255
  165. property :kind, String, :length => 255
  166. has n, :candidacies
  167. has n, :polls
  168. belongs_to :body
  169. has n, :campaigns
  170. def self.past
  171. self.all(:d.lt => Time.now.to_s, :order => [ :d.desc ])
  172. end
  173. def self.future
  174. self.all(:d.gte => Time.now.to_s, :order => [ :d.desc ])
  175. end
  176. # electorate and ballot_papers_issued assume there's a Poll object for every district in this election
  177. def electorate
  178. Poll.sum(:electorate, :election => self)
  179. end
  180. def ballot_papers_issued
  181. Poll.sum(:ballot_papers_issued, :election => self)
  182. end
  183. def set_positions
  184. polls.each { |p| p.set_positions }
  185. end
  186. end
  187. class District
  188. include DataMapper::Resource
  189. property :id, Serial
  190. property :body_id, Integer, :required => true
  191. property :name, String, :length => 255, :required => true
  192. property :slug, String
  193. property :ons_district_code, String
  194. belongs_to :body
  195. has n, :postcodes, :child_key => [:ward_id]
  196. has n, :polls
  197. def self.slugify(name)
  198. name.gsub(/[^\w\s-]/, '').gsub(/\s+/, '-').downcase
  199. end
  200. end
  201. class Body
  202. include DataMapper::Resource
  203. property :id, Serial
  204. property :name, String, :length => 255, :required => true
  205. property :district_name, String, :length => 255, :required => true # singular
  206. property :districts_name, String, :length => 255, :required => true # plural
  207. property :slug, String, :length => 255
  208. has n, :elections
  209. has n, :districts
  210. end
  211. class Party
  212. include DataMapper::Resource
  213. property :id, Serial
  214. property :name, String, :required => true
  215. property :colour, String
  216. has n, :candidacies
  217. has n, :campaigns
  218. end
  219. DataMapper.setup(:default, ENV['DATABASE_URL'] || "postgres://postgres@localhost:5432/suttonelections")
  220. DataMapper.auto_upgrade!