Commit 36466702 authored by Sharp Hall's avatar Sharp Hall

Update for 2019 General Election

* Use Python 3
* Use new data format
* Update elections.yaml with info about current election
* Update live_results.py with static csv URL
parent 5832f96d
Pipeline #93666957 passed with stage
in 2 minutes and 28 seconds
FROM debian:stable
FROM debian:testing
RUN mkdir /app && \
apt-get update && \
apt-get install -y python-pip python-matplotlib python-cartopy \
python-scipy \
apt-get install -y python3-pip python3-matplotlib python3-cartopy \
python3-scipy \
fonts-tlwg-loma-otf fonts-tlwg-loma-ttf \
optipng curl
......@@ -11,7 +11,7 @@ WORKDIR /app
COPY . /app
RUN pip install -r requirements.txt
RUN pip3 install -r requirements.txt
RUN ./download_prereq_data.sh
CMD python elections.py
CMD python3 elections.py
......@@ -50,6 +50,31 @@ def get_first(record, *keys):
return record[key]
def load_election_data_new_format(filename, scaffold=False):
races = collections.defaultdict(lambda: {'raw_data': []})
with open(filename, 'rb') as f:
sheet = list(csv.reader(f))
header, body = sheet[:2], sheet[2:]
data = []
for row in body:
if row[2] == 'COUNTY TOTALS':
continue
carts = [0 if sum(int(x) for x in row[3:] if x != '') == 0 else 1, 1]
for i, col in enumerate(row):
if i < 3 or col == '':
continue
data.append({
'Votes': 0 if scaffold else int(col),
'Carts': [0, 1] if scaffold else carts,
'Choice': header[1][i].strip(),
'Race': header[0][i].strip(),
'Division': row[2].strip()
})
for record in data:
races[record['Race']]['raw_data'].append(record)
return races
def load_election_data(filename, scaffold=False):
races = collections.defaultdict(lambda: {'raw_data': []})
if filename.endswith('txt'):
......@@ -78,7 +103,7 @@ def load_election_data(filename, scaffold=False):
'Choice': record[3],
'Race': record[2],
'Division': str(record[0]).zfill(2) + '-' + str(record[1]).zfill(2)
} for record, votecount in votecounts.iteritems()]
} for record, votecount in votecounts.items()]
for record in data:
if record['Race'].lower() != 'no vote':
if scaffold:
......@@ -136,8 +161,12 @@ def analyze(in_file, geojson_file='Political_Divisions.geojson',
return election
# populate election['races'] with raw data
races = load_election_data(in_file, scaffold=scaffold)
for race_name, race in races.iteritems():
try:
races = load_election_data(in_file, scaffold=scaffold)
except KeyError:
races = load_election_data_new_format(in_file, scaffold=scaffold)
for race_name, race in races.items():
race['name'] = uncapitalize(race_name)
election['races'] = races.values()
......@@ -178,6 +207,6 @@ def analyze(in_file, geojson_file='Political_Divisions.geojson',
# total
race['total_votes'] = sum(race['results'].values())
election['races'].sort(key=lambda race: -race['total_votes'])
election['races'] = sorted(election['races'], key=lambda race: -race['total_votes'])
return election
......@@ -114,7 +114,7 @@ def make_map(election_name, race_name, race_data, map_data,
mp = sgeom.MultiPolygon([division['geom'] for division in included_divisions +
incomplete_included_divisions])
bounds = mp.bounds
ax.set_extent([bounds[n] for n in (0, 2, 1, 3)], ccrs.Geodetic())
ax.set_extent([bounds[n] for n in [0, 2, 1, 3]], ccrs.Geodetic())
ax.add_geometries(map(lambda x: x['geom'], excluded_divisions),
ccrs.PlateCarree(),
facecolor='#cccccc',
......@@ -149,7 +149,7 @@ def make_map(election_name, race_name, race_data, map_data,
for point in pointcollections[choice]:
points.append((point.x, point.y, get_color(color, colors=colors)))
points.sort(key=lambda x: random.random)
points.sort(key=lambda x: random.random())
# randomize points to avoid one vote choice plastering another
ax.scatter(np.array([p[0] for p in points]),
np.array([p[1] for p in points]), marker='.',
......
---
- name: "November 5, 2019 General Election"
date: "2019-11-05"
live: true
geojson: "2018_Political_Divisions.geojson"
after: "2019-11-05 20:00"
turnout_after: "2019-11-05 07:00"
show_divisons_reporting: true
scaffold: true
- name: "May 21, 2019 Primary Election"
date: "2019-05-21"
......
......@@ -5,7 +5,7 @@ import datetime
from utils import format_time, is_it_after
EXPORT_FILES = 'http://phillyelectionresults.com/ExportFiles.html'
EXPORT_FILE = 'https://files7.philadelphiavotes.com/election-results/2019_GENERAL/enr/division-level-results.csv'
TURNOUT_URL = 'https://jtannen.github.io/precinct_turnout_philadelphia.csv'
TMP_FILE = '/tmp/live_election.txt'
......@@ -31,13 +31,7 @@ def hash_file(filename):
def get_current_export():
print('Checking {}...'.format(EXPORT_FILES))
response = requests.get(EXPORT_FILES)
for text in response.text.split('"'):
if text.startswith('http') and 'PRECINCT' in text:
print('Found {}'.format(text))
return text
print('No suitable URL')
return EXPORT_FILE
def process_live_election(election):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment