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.
 
 
 
 

250 lines
8.0 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. belongs_to :election
  33. belongs_to :district
  34. end
  35. class PollingStation
  36. include DataMapper::Resource
  37. property :id, String, :key => true, :length => 2 # e.g. "KA"
  38. property :name, String, :length => 255#, :required => true
  39. property :address, String, :length => 255#, :required => true
  40. property :postcode, String#, :required => true
  41. property :easting, Float, :required => true
  42. property :northing, Float, :required => true
  43. property :lat, Float, :required => true
  44. property :lng, Float, :required => true
  45. has n, :postcodes
  46. end
  47. class Postcode
  48. include DataMapper::Resource
  49. # Postcode natural key, uppercase with space, eg. "SM1 1EA"
  50. # Column names derived from Ordnance Survey CodePoint Open
  51. property :postcode, String, :key => true
  52. property :positional_quality_indicator, Integer
  53. property :eastings, Integer, :required => true
  54. property :northings, Integer, :required => true
  55. property :country_code, String, :required => true
  56. property :nhs_regional_ha_code, String, :required => true
  57. property :nhs_ha_code, String, :required => true
  58. property :admin_county_code, String # NULL within Greater London
  59. property :admin_district_code, String, :required => true # e.g. London Borough of Sutton
  60. property :admin_ward_code, String, :required => true # e.g. Sutton Central
  61. property :lat, Float, :required => true
  62. property :lng, Float, :required => true
  63. property :ward_id, Integer, :required => true # Sutton Council
  64. property :constituency_id, Integer, :required => false # UK Parliament
  65. property :polling_station_id, String, :length => 2
  66. belongs_to :district, :child_key => [:ward_id]
  67. belongs_to :polling_station
  68. def self.finder(postcode)
  69. postcode = postcode.strip.upcase
  70. if o = self.get(postcode)
  71. return o
  72. end
  73. result = Pat.get(postcode)
  74. unless result.code == 404
  75. # cache API result
  76. self.create(
  77. :postcode => postcode,
  78. :lat => result['geo']['lat'],
  79. :lng => result['geo']['lng'],
  80. :district_name => result['administrative']['district']['title'],
  81. :district_code => result['administrative']['district']['uri'].match(/.+\/(.+)$/)[1],
  82. :ward_name => result['administrative']['ward']['title'],
  83. :ward_code => result['administrative']['ward']['uri'].match(/.+\/(.+)$/)[1]
  84. )
  85. else
  86. # invalid postcode
  87. nil
  88. end
  89. end
  90. end
  91. class Candidate
  92. include DataMapper::Resource
  93. property :id, Serial
  94. property :forenames, String, :required => true
  95. property :surname, String, :required => true, :index => true
  96. has n, :candidacies
  97. def short_name
  98. @forenames.split(' ')[0] + ' ' + @surname
  99. end
  100. def name
  101. @forenames + ' ' + @surname
  102. end
  103. def url
  104. "/candidates/" + @id.to_s
  105. end
  106. end
  107. class DeletedCandidate
  108. include DataMapper::Resource
  109. property :old_candidate_id, Integer, :key => true # ID of candidate that has been merged/deleted
  110. property :candidate_id, Integer, :required => true # ID of candidate that has been kept
  111. end
  112. class Candidacy
  113. include DataMapper::Resource
  114. property :id, Serial
  115. property :election_id, Integer, :required => true
  116. property :candidate_id, Integer, :required => true
  117. property :party_id, Integer
  118. property :district_id, Integer, :required => true
  119. property :votes, Integer
  120. property :address, String, :length => 200
  121. property :postcode, String
  122. property :position, Integer # Position of this candidate in this district. (1..n)
  123. property :seats, Integer # Number of seats won by this candidacy (0 or 1)
  124. property :labcoop, Boolean, :default => false # Candidacy is for joint Labour/Co-op party
  125. belongs_to :election
  126. belongs_to :candidate
  127. belongs_to :party
  128. belongs_to :district
  129. end
  130. class Campaign
  131. include DataMapper::Resource
  132. property :party_id, Integer, :key => true
  133. property :election_id, Integer, :key => true
  134. property :party_url, String, :length => 255
  135. property :manifesto_html_url, String, :length => 255
  136. property :manifesto_pdf_url, String, :length => 255
  137. belongs_to :party
  138. belongs_to :election
  139. end
  140. class Election
  141. include DataMapper::Resource
  142. property :id, Serial
  143. property :body_id, Integer, :required => true
  144. property :d, Date, :required => true, :index => true
  145. property :reason, String, :length => 255
  146. property :kind, String, :length => 255
  147. has n, :candidacies
  148. has n, :polls
  149. belongs_to :body
  150. has n, :campaigns
  151. def self.past
  152. self.all(:d.lt => Time.now.to_s, :order => [ :d.desc ])
  153. end
  154. def self.future
  155. self.all(:d.gte => Time.now.to_s, :order => [ :d.desc ])
  156. end
  157. # electorate and ballot_papers_issued assume there's a Poll object for every district in this election
  158. def electorate
  159. Poll.sum(:electorate, :election => self)
  160. end
  161. def ballot_papers_issued
  162. Poll.sum(:ballot_papers_issued, :election => self)
  163. end
  164. end
  165. class District
  166. include DataMapper::Resource
  167. property :id, Serial
  168. property :body_id, Integer, :required => true
  169. property :name, String, :length => 255, :required => true
  170. property :slug, String
  171. property :seats, Integer
  172. property :ons_district_code, String
  173. belongs_to :body
  174. has n, :postcodes, :child_key => [:ward_id]
  175. has n, :polls
  176. def self.slugify(name)
  177. name.gsub(/[^\w\s-]/, '').gsub(/\s+/, '-').downcase
  178. end
  179. end
  180. class Body
  181. include DataMapper::Resource
  182. property :id, Serial
  183. property :name, String, :length => 255, :required => true
  184. property :district_name, String, :length => 255, :required => true # singular
  185. property :districts_name, String, :length => 255, :required => true # plural
  186. property :slug, String, :length => 255
  187. has n, :elections
  188. has n, :districts
  189. end
  190. class Party
  191. include DataMapper::Resource
  192. property :id, Serial
  193. property :name, String, :required => true
  194. property :colour, String
  195. has n, :candidacies
  196. has n, :campaigns
  197. end
  198. DataMapper.setup(:default, ENV['DATABASE_URL'] || "postgres://postgres@localhost:5432/suttonelections")
  199. DataMapper.auto_upgrade!