Election results in the London Borough of Sutton.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 

319 lignes
7.9 KiB

  1. require 'rubygems'
  2. require 'sinatra'
  3. require 'haml'
  4. require './models'
  5. require 'rack-flash'
  6. set :root, File.dirname(__FILE__)
  7. enable :sessions
  8. use Rack::Flash
  9. class String
  10. def pluralize(num)
  11. if num == 1
  12. return self
  13. end
  14. case self[-1]
  15. when 'y'
  16. self[0..-2] + 'ies'
  17. when 's'
  18. self + "es"
  19. else
  20. self + "s"
  21. end
  22. end
  23. end
  24. helpers do
  25. # Format a number with commas for every ^3
  26. def commify(num)
  27. num.to_s.reverse.gsub(/(\d\d\d)(?=\d)(?!\d*\.)/,'\1,').reverse
  28. end
  29. # From http://snippets.dzone.com/posts/show/593
  30. def to_ordinal(num)
  31. num = num.to_i
  32. if (10...20) === num
  33. "#{num}th"
  34. else
  35. g = %w{ th st nd rd th th th th th th }
  36. a = num.to_s
  37. c = a[-1..-1].to_i
  38. a + g[c]
  39. end
  40. end
  41. def format_percent(num)
  42. sprintf("%.0f%%", num)
  43. end
  44. def short_date(d)
  45. d.strftime("%e %b %Y")
  46. end
  47. def long_date(d)
  48. d.strftime("%e %B %Y")
  49. end
  50. # Exception for Labour/Co-operative candidacies
  51. def party_name(labcoop, party_name)
  52. labcoop ? "Labour and Co-operative Party" : party_name
  53. end
  54. end
  55. get '/' do
  56. @election = Election.get(12) # FIXME magic number
  57. @election_title = "#{@election.body.name} #{@election.kind} #{long_date(@election.d)}"
  58. if params[:postcode]
  59. if @p = Postcode.get(params[:postcode].strip.upcase)
  60. # Postcode is valid and in LB Sutton
  61. if @election.body.district_name == 'constituency'
  62. @district = District.get(@p.constituency_id)
  63. else
  64. @district = District.get(@p.ward_id)
  65. end
  66. flash[:notice] = "Postcode <strong>#{@postcode}</strong> is in #{@district.name} #{@election.body.district_name}"
  67. if @p.polling_station
  68. @ps_postcode = Postcode.get(@p.polling_station.postcode)
  69. @polling_station = "Your polling station is \
  70. <a href=\"http://www.openstreetmap.org/?mlat=%s&mlon=%s&zoom=16\">%s, %s, %s</a>" \
  71. % [ @ps_postcode.lat, @ps_postcode.lng, @p.polling_station.name, \
  72. @p.polling_station.address, @p.polling_station.postcode]
  73. end
  74. redirect "/bodies/#{@election.body.slug}/elections/#{@election.d}/#{@election.body.districts_name}/#{@district.slug}"
  75. else
  76. flash.now[:error] = "<strong>#{@postcode}</strong> is not a postcode in Sutton"
  77. end
  78. end
  79. # Display a random postcode as default search term
  80. @random_pc = repository(:default).adapter.select("
  81. SELECT postcode
  82. FROM postcodes
  83. ORDER BY RANDOM()
  84. LIMIT 1
  85. ")
  86. @default_pc = @random_pc[0]
  87. @future_elections = Election.future
  88. @past_elections = Election.past
  89. haml :index
  90. end
  91. get '/bodies/:body/elections/:date' do
  92. @body = Body.first(:slug => params[:body])
  93. @election = Election.first(:body => @body, :d => params[:date])
  94. @elections_for_this_body = Election.all(:body => @body, :order => [:d])
  95. @total_seats = Candidacy.sum(:seats, :election => @election)
  96. @total_votes = Candidacy.sum(:votes, :election => @election)
  97. # There's got to be a better way to do this, either with SQL or Datamapper
  98. @total_districts = repository(:default).adapter.select("
  99. SELECT district_id
  100. FROM candidacies
  101. WHERE election_id = ?
  102. GROUP BY district_id
  103. ORDER BY district_id
  104. ", @election.id).count
  105. @results_by_party = repository(:default).adapter.select("
  106. SELECT
  107. p.colour,
  108. p.name,
  109. SUM(c.votes) AS votez,
  110. SUM(c.seats) AS seatz,
  111. COUNT(*) AS cands
  112. FROM candidacies c
  113. LEFT JOIN parties p ON p.id = c.party_id
  114. WHERE c.election_id = ?
  115. GROUP BY c.party_id, p.colour, p.name
  116. ORDER BY seatz DESC, votez DESC
  117. ", @election.id)
  118. # For elections that haven't yet been held
  119. # @districts_in_this_election = repository(:default).adapter.select("
  120. # SELECT DISTINCT d.name, d.slug
  121. #
  122. # FROM candidacies c
  123. # LEFT JOIN districts d
  124. # ON c.district_id = d.id
  125. #
  126. # WHERE c.election_id = ?
  127. #
  128. # ORDER BY d.name
  129. # ", @election.id)
  130. haml :electionsummary
  131. end
  132. # get '/bodies/:body/elections/:date/parties/:party' do
  133. # Not written yet. Show how this party did at this election.
  134. # end
  135. get '/bodies/:body/?' do
  136. @body = Body.first(:slug => params[:body])
  137. @districts = District.all(:body => @body, :order => [:name])
  138. @elections = repository(:default).adapter.select("
  139. SELECT
  140. e.id,
  141. e.kind,
  142. e.d,
  143. SUM(p.ballot_papers_issued)::float / SUM(p.electorate) * 100 AS turnout_percent
  144. FROM elections e
  145. LEFT JOIN polls p
  146. ON e.id = p.election_id
  147. WHERE e.body_id = ?
  148. GROUP BY p.election_id, e.id
  149. ORDER BY e.d DESC
  150. ", @body.id)
  151. haml :body
  152. end
  153. # get '/wards/:slug/postcode/:postcode/?' do
  154. # @ward = Ward.first(:slug => params[:slug])
  155. # @postcode = params[:postcode]
  156. # haml :wards
  157. # end
  158. get '/candidates/:id/?' do
  159. if @deleted_candidate = DeletedCandidate.get(params[:id])
  160. redirect "/candidates/#{@deleted_candidate.candidate_id}", 302 # HTTP 302 Moved Temporarily
  161. end
  162. if @candidate = Candidate.get(params[:id])
  163. @candidacies = repository(:default).adapter.select("
  164. SELECT
  165. e.d,
  166. c.*,
  167. p.name AS party_name,
  168. p.colour AS party_colour,
  169. b.name AS body_name,
  170. b.slug AS body_slug,
  171. b.districts_name AS districts_name,
  172. d.name AS district_name,
  173. d.slug AS district_slug
  174. FROM candidacies c
  175. INNER JOIN elections e
  176. ON c.election_id = e.id
  177. INNER JOIN parties p
  178. ON c.party_id = p.id
  179. INNER JOIN bodies b
  180. ON e.body_id = b.id
  181. INNER JOIN districts d
  182. ON c.district_id = d.id
  183. WHERE c.candidate_id = ?
  184. ORDER BY d
  185. ", @candidate.id)
  186. haml :candidate
  187. else
  188. 404
  189. end
  190. end
  191. get '/candidates/?' do
  192. @candidates = Candidate.all(:order => [ :surname, :forenames ])
  193. haml :candidates
  194. end
  195. get '/bodies/:body/elections/:date/:districts_name/:district' do
  196. @district = District.first(:slug => params[:district])
  197. @body = Body.first(:slug => params[:body])
  198. @election = Election.first(:body => @body, :d => params[:date])
  199. @candidacies = Candidacy.all(:district => @district, :election => @election, :order => [:votes.desc])
  200. @total_votes = Candidacy.sum(:votes, :district => @district, :election => @election)
  201. @total_candidates = Candidacy.count(:district => @district, :election => @election)
  202. @total_seats = Candidacy.sum(:seats, :district => @district, :election => @election)
  203. @districts_in_this_election = @election.candidacies.districts
  204. @poll = Poll.get(@district.id, @election.id)
  205. if @total_seats == 1
  206. @share_denominator = @total_votes
  207. elsif @poll && @poll.valid_ballot_papers
  208. @share_denominator = @poll.valid_ballot_papers
  209. else
  210. @share_denominator = @total_votes / @total_seats
  211. @share_message = "The vote share percentages have been estimated as we don't have data for the number of valid ballot papers in this poll."
  212. end
  213. # Postgres: All the columns selected when using GROUP BY must either be aggregate functions or appear in the GROUP BY clause
  214. @results_by_party = repository(:default).adapter.select("
  215. SELECT
  216. p.name AS party_name,
  217. p.colour AS party_colour,
  218. COUNT(c.id) AS num_candidates,
  219. SUM(c.seats) AS num_seats,
  220. SUM(c.votes) AS total_votes
  221. FROM candidacies c
  222. LEFT JOIN parties p
  223. ON c.party_id = p.id
  224. WHERE c.district_id = ?
  225. AND c.election_id = ?
  226. GROUP BY p.name, p.colour
  227. ORDER BY total_votes DESC
  228. ", @district.id, @election.id)
  229. haml :resultsdistrict
  230. end
  231. get '/bodies/:body/:districts_name/:district' do
  232. @district = District.first(:slug => params[:district])
  233. @body = Body.first(:slug => params[:body])
  234. haml :district
  235. end
  236. get '/guides/how-the-council-election-works' do
  237. haml :election
  238. end
  239. get '/guides/how-the-parliament-election-works' do
  240. haml :parliament
  241. end
  242. get '/error' do
  243. haml :error
  244. end
  245. get '/guides' do
  246. haml :guides
  247. end
  248. get '/about' do
  249. haml :about
  250. end
  251. not_found do
  252. haml :not_found
  253. end