Election results in the London Borough of Sutton.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

275 lines
8.8 KiB

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