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.

models.rb 8.8 KiB

13 years ago
12 years ago
12 years ago
12 years ago
13 years ago
13 years ago
11 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
11 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
12 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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!