Election results in the London Borough of Sutton.
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

models.rb 8.8 KiB

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