|
- #!/usr/bin/env ruby
- # Generate a static site
- require 'logger'
- require 'haml'
- require_relative '../models'
-
- OUTPUT_DIR = '_site'
- VIEWS_DIR = File.join('..', 'views')
- LAYOUT_FN = File.join(VIEWS_DIR, 'layout.haml')
-
- @log = Logger.new($stdout)
- @log.level = Logger::INFO
- @log.info "Build starts."
- @log.info "Output directory is: #{OUTPUT_DIR}"
-
- class String
- def pluralize(num)
- if num == 1
- return self
- end
-
- case self[-1]
- when 'y'
- self[0..-2] + 'ies'
- when 's'
- self + "es"
- else
- self + "s"
- end
- end
- end
-
- def commify(num)
- num.to_s.reverse.gsub(/(\d\d\d)(?=\d)(?!\d*\.)/,'\1,').reverse
- end
-
- # From http://snippets.dzone.com/posts/show/593
- def to_ordinal(num)
- num = num.to_i
- if (10...20) === num
- "#{num}th"
- else
- g = %w{ th st nd rd th th th th th th }
- a = num.to_s
- c = a[-1..-1].to_i
- a + g[c]
- end
- end
-
- def format_percent(num)
- sprintf("%.0f%%", num)
- end
-
- def short_date(d)
- d.strftime("%e %b %Y")
- end
-
- def long_date(d)
- d.strftime("%e %B %Y")
- end
-
- # Exception for Labour/Co-operative candidacies
- def party_name(labcoop, party_name)
- labcoop ? "Labour and Co-operative Party" : party_name
- end
-
- def write_page(path_items, template, locals = {})
- dir = File.join(path_items)
- FileUtils.mkdir_p(dir)
- @log.debug dir
- fn = File.join(dir, 'index.html')
-
- # https://stackoverflow.com/questions/6125265/using-layouts-in-haml-files-independently-of-rails
- html = Haml::Engine.new(File.read(LAYOUT_FN)).render do
- Haml::Engine.new(File.read(File.join(VIEWS_DIR, "#{template}.haml"))).render(Object.new, locals)
- end
-
- File.write(fn, html)
- @log.info fn
- # TODO - add page to sitemap.xml or sitemap.txt
- # https://support.google.com/webmasters/answer/183668?hl=en&ref_topic=4581190
- end
-
- test_dir = File.join(Dir.pwd, OUTPUT_DIR)
- # FIXME - clear output directory at the start of every run
- # FileUtils.rm_rf(test_dir)
-
- Dir.mkdir(test_dir) unless File.directory?(test_dir)
- Dir.chdir(test_dir)
-
- # Copy `public` dir to output dir
- FileUtils.copy_entry(File.join('..', 'public'), '.')
-
- # Home page
- locals = {
- future_elections: Election.future,
- past_elections: Election.past
- }
- write_page('.', 'index', locals)
-
- # Election pages
- Election.each do |e|
- locals = {
- body: Body.first(:slug => e.body.slug),
- election: Election.first(:body => e.body, :d => e.d),
- elections_for_this_body: Election.all(:body => e.body, :order => [:d]),
- total_seats: Candidacy.sum(:seats, :election => e),
- total_votes: Candidacy.sum(:votes, :election => e)
- }
-
- # There's got to be a better way to do this, either with SQL or Datamapper
- locals['total_districts'] = repository(:default).adapter.select("
- SELECT district_id
- FROM candidacies
- WHERE election_id = ?
- GROUP BY district_id
- ORDER BY district_id
- ", e.id).count
-
- locals['results_by_party'] = repository(:default).adapter.select("
- SELECT
- p.colour,
- p.name,
- SUM(c.votes) AS votez,
- SUM(c.seats) AS seatz,
- COUNT(*) AS cands
-
- FROM candidacies c
-
- LEFT JOIN parties p ON p.id = c.party_id
-
- WHERE c.election_id = ?
-
- GROUP BY c.party_id, p.colour, p.name
-
- ORDER BY seatz DESC, votez DESC
- ", e.id)
-
- write_page(['bodies', e.body.slug, 'elections', e.d.to_s], 'electionsummary', locals)
-
-
- # District results for this election (resultsdistrict)
- # Loop through all districts in this election
- e.candidacies.districts.each do |d|
- total_seats = Candidacy.sum(:seats, :district => d, :election => e)
- total_votes = Candidacy.sum(:votes, :district => d, :election => e)
- poll = Poll.get(d.id, e.id)
-
- locals = {
- district: d,
- body: d.body,
- election: e,
- candidacies: Candidacy.all(:district => d, :election => e, :order => [:position]),
- total_votes: total_votes,
- total_candidates: Candidacy.count(:district => d, :election => e),
- total_seats: total_seats,
- districts_in_this_election: e.candidacies.districts,
- poll: poll
- }
-
- locals['share_message'] = nil
- if total_seats == 1
- locals['share_denominator'] = total_votes
- elsif poll && poll.valid_ballot_papers
- locals['share_denominator'] = poll.valid_ballot_papers
- else
- locals['share_denominator'] = total_votes / total_seats
- locals['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."
- end
-
- # Postgres: All the columns selected when using GROUP BY must either be aggregate functions or appear in the GROUP BY clause
- locals['results_by_party'] = repository(:default).adapter.select("
- SELECT
- p.name AS party_name,
- p.colour AS party_colour,
- COUNT(c.id) AS num_candidates,
- SUM(c.seats) AS num_seats,
- SUM(c.votes) AS total_votes
-
- FROM candidacies c
-
- LEFT JOIN parties p
- ON c.party_id = p.id
-
- WHERE c.district_id = ?
- AND c.election_id = ?
-
- GROUP BY p.name, p.colour
-
- ORDER BY total_votes DESC
- ", d.id, e.id)
-
- write_page(['bodies', e.body.slug, 'elections', e.d.to_s, e.body.districts_name, d.slug], 'resultsdistrict', locals)
- end
- end
-
- # Candidate index
- locals = { candidates: Candidate.all(:order => [ :surname, :forenames ]) }
- write_page('candidates', 'candidates', locals)
-
- # Candidate pages
- # FIXME: What do we do about deleted candidates/redirects?
- Candidate.each do |c|
- locals = {
- candidate: c
- }
-
- locals['candidacies'] = repository(:default).adapter.select("
- SELECT
- e.d,
- c.*,
- p.name AS party_name,
- p.colour AS party_colour,
- b.name AS body_name,
- b.slug AS body_slug,
- b.districts_name AS districts_name,
- d.name AS district_name,
- d.slug AS district_slug
-
- FROM candidacies c
-
- INNER JOIN elections e
- ON c.election_id = e.id
-
- INNER JOIN parties p
- ON c.party_id = p.id
-
- INNER JOIN bodies b
- ON e.body_id = b.id
-
- INNER JOIN districts d
- ON c.district_id = d.id
-
- WHERE c.candidate_id = ?
-
- ORDER BY d
- ", c.id)
-
- write_page(['candidates', c.id.to_s], 'candidate', locals)
- end
-
- # Bodies index
- dir = 'bodies'
- FileUtils.mkdir_p(dir)
- @log.debug dir
- fn = File.join(dir, 'index.html')
- FileUtils.touch(fn) # empty file
- @log.info fn
-
- # Body detail pages
- Body.each do |b|
- locals = {
- body: b,
- districts: District.all(:body => b, :order => [:name])
- }
-
- locals['elections'] = repository(:default).adapter.select("
- SELECT
- e.id,
- e.kind,
- e.d,
- SUM(p.ballot_papers_issued)::float / SUM(p.electorate) * 100 AS turnout_percent
-
- FROM elections e
-
- LEFT JOIN polls p
- ON e.id = p.election_id
-
- WHERE e.body_id = ?
-
- GROUP BY p.election_id, e.id
- ORDER BY e.d DESC
- ", b.id)
-
- write_page(['bodies', b.slug], 'body', locals)
-
- # Districts for this body
- b.districts.each do |d|
- locals = {
- district: d,
- body: b
- }
- write_page(['bodies', b.slug, b.districts_name, d.slug], 'district', locals)
- end
- end
-
- write_page('about', 'about')
- write_page('guides', 'guides')
- write_page(%w(guides how-the-parliament-election-works), 'parliament')
- write_page(%w(guides how-the-council-election-works), 'election')
-
- @log.info "Build complete."
|