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.
 
 
 
 

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