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.
 
 
 
 

318 lines
7.5 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. if params[:postcode]
  57. @postcode = params[:postcode].strip.upcase
  58. if @p = Postcode.get(@postcode)
  59. # Postcode is valid and in LB Sutton
  60. @ward = Ward.get(@p.ward_id)
  61. flash[:notice] = "Postcode <strong>#{@postcode}</strong> is in #{@ward.name} ward"
  62. if @p.polling_station
  63. @ps_postcode = Postcode.get(@p.polling_station.postcode)
  64. flash[:polling_station] = "Your polling station is \
  65. <a href=\"http://www.openstreetmap.org/?mlat=%s&mlon=%s&zoom=16\">%s, %s, %s</a>" \
  66. % [ @ps_postcode.lat, @ps_postcode.lng, @p.polling_station.name, \
  67. @p.polling_station.address, @p.polling_station.postcode]
  68. end
  69. redirect "/bodies/sutton-council/elections/2014-05-22/wards/#{@ward.slug}"
  70. else
  71. flash.now[:error] = "<strong>#{@postcode}</strong> is not a postcode in Sutton"
  72. end
  73. end
  74. # Display a random postcode as default search term
  75. @random_pc = repository(:default).adapter.select("
  76. SELECT postcode
  77. FROM postcodes
  78. ORDER BY RANDOM()
  79. LIMIT 1
  80. ")
  81. @default_pc = @random_pc[0]
  82. @future_elections = Election.future
  83. @past_elections = Election.past
  84. haml :index
  85. end
  86. get '/bodies/:body/elections/:date' do
  87. @body = Body.first(:slug => params[:body])
  88. @election = Election.first(:body => @body, :d => params[:date])
  89. @elections_for_this_body = Election.all(:body => @body, :order => [:d])
  90. @total_seats = Candidacy.sum(:seats, :election => @election)
  91. @total_votes = Candidacy.sum(:votes, :election => @election)
  92. # There's got to be a better way to do this, either with SQL or Datamapper
  93. @total_districts = repository(:default).adapter.select("
  94. SELECT district_id
  95. FROM candidacies
  96. WHERE election_id = ?
  97. GROUP BY district_id
  98. ORDER BY district_id
  99. ", @election.id).count
  100. @results_by_party = repository(:default).adapter.select("
  101. SELECT
  102. p.colour,
  103. p.name,
  104. SUM(c.votes) AS votez,
  105. SUM(c.seats) AS seatz,
  106. COUNT(*) AS cands
  107. FROM candidacies c
  108. LEFT JOIN parties p ON p.id = c.party_id
  109. WHERE c.election_id = ?
  110. GROUP BY c.party_id, p.colour, p.name
  111. ORDER BY seatz DESC, votez DESC
  112. ", @election.id)
  113. @results_by_district = repository(:default).adapter.select("
  114. SELECT
  115. d.name,
  116. d.slug AS district_slug,
  117. SUM(c.seats) AS seats,
  118. SUM(c.votes) AS votez,
  119. COUNT(c.id) AS num_candidates
  120. FROM districts d, candidacies c
  121. WHERE
  122. c.district_id = d.id
  123. AND c.election_id = ?
  124. GROUP BY c.district_id, d.name, d.slug
  125. ORDER BY d.name
  126. ", @election.id)
  127. # For elections that haven't yet been held
  128. @districts_in_this_election = repository(:default).adapter.select("
  129. SELECT DISTINCT d.name, d.slug
  130. FROM candidacies c
  131. LEFT JOIN districts d
  132. ON c.district_id = d.id
  133. WHERE c.election_id = ?
  134. ORDER BY d.name
  135. ", @election.id)
  136. haml :electionsummary
  137. end
  138. # get '/bodies/:body/elections/:date/parties/:party' do
  139. # Not written yet. Show how this party did at this election.
  140. # end
  141. get '/bodies/?' do
  142. @bodies = Body.all
  143. haml :bodies
  144. end
  145. get '/bodies/:body/?' do
  146. @body = Body.first(:slug => params[:body])
  147. @elections = Election.all(:body => @body, :order => [:d.desc])
  148. @districts = District.all(:body => @body, :order => [:name])
  149. haml :body
  150. end
  151. # get '/wards/:slug/postcode/:postcode/?' do
  152. # @ward = Ward.first(:slug => params[:slug])
  153. # @postcode = params[:postcode]
  154. # haml :wards
  155. # end
  156. get '/candidates/:id/?' do
  157. if @deleted_candidate = DeletedCandidate.get(params[:id])
  158. redirect "/candidates/#{@deleted_candidate.candidate_id}", 301 # HTTP 301 Moved Permanently
  159. end
  160. if @candidate = Candidate.get(params[:id])
  161. @candidacies = repository(:default).adapter.select("
  162. SELECT
  163. e.d,
  164. c.*,
  165. p.name AS party_name,
  166. p.colour AS party_colour,
  167. b.name AS body_name,
  168. b.slug AS body_slug,
  169. b.districts_name AS districts_name,
  170. d.name AS district_name,
  171. d.slug AS district_slug
  172. FROM candidacies c
  173. INNER JOIN elections e
  174. ON c.election_id = e.id
  175. INNER JOIN parties p
  176. ON c.party_id = p.id
  177. INNER JOIN bodies b
  178. ON e.body_id = b.id
  179. INNER JOIN districts d
  180. ON c.district_id = d.id
  181. WHERE c.candidate_id = ?
  182. ORDER BY d
  183. ", @candidate.id)
  184. haml :candidate
  185. else
  186. 404
  187. end
  188. end
  189. get '/candidates/?' do
  190. @candidates = Candidate.all(:order => [ :surname, :forenames ])
  191. haml :candidates
  192. end
  193. get '/bodies/:body/elections/:date/:districts_name/:district' do
  194. @district = District.first(:slug => params[:district])
  195. @body = Body.first(:slug => params[:body])
  196. @election = Election.first(:body => @body, :d => params[:date])
  197. @candidacies = Candidacy.all(:district => @district, :election => @election, :order => [:votes.desc])
  198. @total_votes = Candidacy.sum(:votes, :district => @district, :election => @election)
  199. @total_candidates = Candidacy.count(:district => @district, :election => @election)
  200. @total_seats = Candidacy.sum(:seats, :district => @district, :election => @election)
  201. @districts_in_this_election = @election.candidacies.districts
  202. @poll = Poll.get(@district.id, @election.id)
  203. # Postgres: All the columns selected when using GROUP BY must either be aggregate functions or appear in the GROUP BY clause
  204. @results_by_party = repository(:default).adapter.select("
  205. SELECT
  206. p.name AS party_name,
  207. p.colour AS party_colour,
  208. COUNT(c.id) AS num_candidates,
  209. SUM(c.seats) AS num_seats,
  210. SUM(c.votes) AS total_votes
  211. FROM candidacies c
  212. LEFT JOIN parties p
  213. ON c.party_id = p.id
  214. WHERE c.district_id = ?
  215. AND c.election_id = ?
  216. GROUP BY p.name, p.colour
  217. ORDER BY total_votes DESC
  218. ", @district.id, @election.id)
  219. haml :resultsdistrict
  220. end
  221. get '/bodies/:body/:districts_name/:district' do
  222. @district = District.first(:slug => params[:district])
  223. @body = Body.first(:slug => params[:body])
  224. haml :district
  225. end
  226. get '/how-the-council-election-works' do
  227. haml :election
  228. end
  229. get '/how-the-parliament-election-works' do
  230. haml :parliament
  231. end
  232. # get '/voting' do
  233. # haml :voting
  234. # end
  235. get '/error' do
  236. haml :error
  237. end
  238. get '/about' do
  239. haml :about
  240. end
  241. # get '/aliens' do
  242. # haml :aliens
  243. # end
  244. get '/polling-stations' do
  245. @stations = PollingStation.all
  246. haml :pollingstations
  247. end
  248. not_found do
  249. haml :not_found
  250. end