2020-08-24 14:43:28 +01:00
|
|
|
import json
|
2020-07-24 16:45:41 +01:00
|
|
|
import os
|
|
|
|
|
import sqlite3
|
2020-08-10 10:46:31 +01:00
|
|
|
from pathlib import Path
|
|
|
|
|
|
2020-07-24 16:45:41 +01:00
|
|
|
|
|
|
|
|
class BroadcastAreasRepository(object):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.database = Path(__file__).resolve().parent / 'broadcast-areas.sqlite3'
|
|
|
|
|
|
|
|
|
|
def conn(self):
|
|
|
|
|
return sqlite3.connect(str(self.database))
|
|
|
|
|
|
|
|
|
|
def delete_db(self):
|
|
|
|
|
os.remove(str(self.database))
|
|
|
|
|
|
|
|
|
|
def create_tables(self):
|
|
|
|
|
with self.conn() as conn:
|
|
|
|
|
conn.execute("""
|
|
|
|
|
CREATE TABLE broadcast_area_libraries (
|
|
|
|
|
id TEXT PRIMARY KEY,
|
2020-07-31 13:21:21 +01:00
|
|
|
name TEXT NOT NULL,
|
2020-08-13 17:33:58 +01:00
|
|
|
name_singular TEXT NOT NULL,
|
2020-07-31 13:21:21 +01:00
|
|
|
is_group BOOLEAN NOT NULL
|
|
|
|
|
)""")
|
|
|
|
|
|
|
|
|
|
conn.execute("""
|
|
|
|
|
CREATE TABLE broadcast_area_library_groups (
|
|
|
|
|
id TEXT PRIMARY KEY,
|
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
|
broadcast_area_library_id TEXT NOT NULL
|
2020-07-24 16:45:41 +01:00
|
|
|
)""")
|
|
|
|
|
|
|
|
|
|
conn.execute("""
|
|
|
|
|
CREATE TABLE broadcast_areas (
|
|
|
|
|
id TEXT PRIMARY KEY,
|
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
|
broadcast_area_library_id TEXT NOT NULL,
|
2020-07-31 13:21:21 +01:00
|
|
|
broadcast_area_library_group_id TEXT,
|
2020-07-24 16:45:41 +01:00
|
|
|
|
|
|
|
|
FOREIGN KEY (broadcast_area_library_id)
|
2020-07-31 13:21:21 +01:00
|
|
|
REFERENCES broadcast_area_libraries(id),
|
|
|
|
|
|
|
|
|
|
FOREIGN KEY (broadcast_area_library_group_id)
|
|
|
|
|
REFERENCES broadcast_area_library_groups(id)
|
2020-07-24 16:45:41 +01:00
|
|
|
)""")
|
|
|
|
|
|
2020-08-12 10:11:44 +01:00
|
|
|
conn.execute("""
|
2020-08-24 14:43:28 +01:00
|
|
|
CREATE TABLE broadcast_area_polygons (
|
2020-08-12 10:11:44 +01:00
|
|
|
id TEXT PRIMARY KEY,
|
2020-08-24 14:43:28 +01:00
|
|
|
polygons TEXT NOT NULL,
|
|
|
|
|
simple_polygons TEXT NOT NULL
|
2020-08-12 10:11:44 +01:00
|
|
|
)""")
|
|
|
|
|
|
2020-07-24 16:45:41 +01:00
|
|
|
conn.execute("""
|
|
|
|
|
CREATE INDEX broadcast_areas_broadcast_area_library_id
|
|
|
|
|
ON broadcast_areas (broadcast_area_library_id);
|
|
|
|
|
""")
|
|
|
|
|
|
2020-07-31 13:21:21 +01:00
|
|
|
conn.execute("""
|
|
|
|
|
CREATE INDEX broadcast_areas_broadcast_area_library_group_id
|
|
|
|
|
ON broadcast_areas (broadcast_area_library_group_id);
|
|
|
|
|
""")
|
|
|
|
|
|
2020-08-24 20:40:42 +01:00
|
|
|
def delete_library_data(self):
|
|
|
|
|
# delete everything except broadcast_area_polygons
|
|
|
|
|
with self.conn() as conn:
|
|
|
|
|
conn.execute('DELETE FROM broadcast_area_libraries;')
|
|
|
|
|
conn.execute('DELETE FROM broadcast_area_library_groups;')
|
|
|
|
|
conn.execute('DELETE FROM broadcast_areas;')
|
|
|
|
|
|
2020-08-13 17:33:58 +01:00
|
|
|
def insert_broadcast_area_library(self, id, *, name, name_singular, is_group):
|
2020-07-24 16:45:41 +01:00
|
|
|
|
|
|
|
|
q = """
|
2020-08-13 17:33:58 +01:00
|
|
|
INSERT INTO broadcast_area_libraries (id, name, name_singular, is_group)
|
|
|
|
|
VALUES (?, ?, ?, ?)
|
2020-07-24 16:45:41 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
with self.conn() as conn:
|
2020-08-13 17:33:58 +01:00
|
|
|
conn.execute(q, (id, name, name_singular, is_group))
|
2020-07-31 13:21:21 +01:00
|
|
|
|
2020-08-24 20:40:42 +01:00
|
|
|
def insert_broadcast_areas(self, areas, keep_old_features):
|
2020-07-24 16:45:41 +01:00
|
|
|
|
2020-08-12 10:11:44 +01:00
|
|
|
areas_q = """
|
2020-07-24 16:45:41 +01:00
|
|
|
INSERT INTO broadcast_areas (
|
|
|
|
|
id, name,
|
2020-08-12 10:11:44 +01:00
|
|
|
broadcast_area_library_id, broadcast_area_library_group_id
|
|
|
|
|
)
|
|
|
|
|
VALUES (?, ?, ?, ?)
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
features_q = """
|
2020-08-24 14:43:28 +01:00
|
|
|
INSERT INTO broadcast_area_polygons (
|
2020-08-12 10:11:44 +01:00
|
|
|
id,
|
2020-08-24 14:43:28 +01:00
|
|
|
polygons, simple_polygons
|
2020-07-24 16:45:41 +01:00
|
|
|
)
|
2020-08-12 10:11:44 +01:00
|
|
|
VALUES (?, ?, ?)
|
2020-07-24 16:45:41 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
with self.conn() as conn:
|
2020-08-24 14:43:28 +01:00
|
|
|
for id, name, area_id, group, polygons, simple_polygons in areas:
|
2020-08-12 10:11:44 +01:00
|
|
|
conn.execute(areas_q, (
|
|
|
|
|
id, name, area_id, group,
|
|
|
|
|
))
|
2020-08-24 20:40:42 +01:00
|
|
|
if not keep_old_features:
|
|
|
|
|
conn.execute(features_q, (
|
|
|
|
|
id, json.dumps(polygons), json.dumps(simple_polygons),
|
|
|
|
|
))
|
2020-07-24 16:45:41 +01:00
|
|
|
|
|
|
|
|
def query(self, sql, *args):
|
|
|
|
|
with self.conn() as conn:
|
|
|
|
|
cursor = conn.cursor()
|
|
|
|
|
cursor.execute(sql, (*args,))
|
|
|
|
|
return cursor.fetchall()
|
|
|
|
|
|
|
|
|
|
def get_libraries(self):
|
2020-08-13 17:33:58 +01:00
|
|
|
q = "SELECT id, name, name_singular, is_group FROM broadcast_area_libraries"
|
2020-07-24 16:45:41 +01:00
|
|
|
results = self.query(q)
|
2020-08-13 17:33:58 +01:00
|
|
|
libraries = [(row[0], row[1], row[2], row[3]) for row in results]
|
2020-07-24 16:45:41 +01:00
|
|
|
return sorted(libraries)
|
|
|
|
|
|
|
|
|
|
def get_library_description(self, library_id):
|
|
|
|
|
q = """
|
|
|
|
|
WITH
|
2020-08-12 12:02:56 +01:00
|
|
|
areas AS (
|
|
|
|
|
SELECT * FROM broadcast_areas
|
|
|
|
|
WHERE broadcast_area_library_id = ?
|
|
|
|
|
AND broadcast_area_library_group_id IS NULL
|
|
|
|
|
),
|
2020-07-24 16:45:41 +01:00
|
|
|
area_count AS (SELECT COUNT(*) AS c FROM areas),
|
|
|
|
|
subset_area_count AS (SELECT c - 4 FROM area_count),
|
Always show 4 items in the example list
If a library has lots of items then the first 3 should be shown, with
a count of how many more there are, for a total of 4 list items:
> a, b, c, and 23 more
If the library only has 4 items then all 4 should be shown, with
consistent use of conjunction and Oxford comma[1]:
> a, b, c, and d
This keeps the lengths of the examples nice and consistent.
1. We use an Oxford comma because it helps disambiguate when an area
itself has a comma or ‘and’ in it, for example ‘Armagh City, Banbridge
and Craigavon’
2020-08-12 12:17:40 +01:00
|
|
|
description_area_names AS (SELECT name FROM areas ORDER BY name ASC LIMIT 3),
|
2020-07-24 16:45:41 +01:00
|
|
|
description_areas_joined AS (
|
|
|
|
|
SELECT GROUP_CONCAT(name, ", ") FROM description_area_names
|
|
|
|
|
)
|
|
|
|
|
SELECT
|
|
|
|
|
CASE (SELECT * FROM subset_area_count)
|
|
|
|
|
WHEN 0 THEN
|
|
|
|
|
(SELECT * FROM description_areas_joined)
|
Always show 4 items in the example list
If a library has lots of items then the first 3 should be shown, with
a count of how many more there are, for a total of 4 list items:
> a, b, c, and 23 more
If the library only has 4 items then all 4 should be shown, with
consistent use of conjunction and Oxford comma[1]:
> a, b, c, and d
This keeps the lengths of the examples nice and consistent.
1. We use an Oxford comma because it helps disambiguate when an area
itself has a comma or ‘and’ in it, for example ‘Armagh City, Banbridge
and Craigavon’
2020-08-12 12:17:40 +01:00
|
|
|
|| ", and "
|
|
|
|
|
|| (SELECT name from areas ORDER BY name DESC limit 1)
|
2020-07-24 16:45:41 +01:00
|
|
|
ELSE
|
|
|
|
|
(SELECT * FROM description_areas_joined)
|
2020-08-04 09:35:01 +01:00
|
|
|
|| ", and "
|
2020-07-24 16:45:41 +01:00
|
|
|
|| (SELECT * FROM subset_area_count)
|
|
|
|
|
|| " more…"
|
|
|
|
|
END
|
|
|
|
|
"""
|
|
|
|
|
description = self.query(q, library_id)[0][0]
|
|
|
|
|
return description
|
|
|
|
|
|
2020-08-12 09:45:22 +01:00
|
|
|
def get_areas(self, area_ids):
|
|
|
|
|
q = """
|
|
|
|
|
SELECT id, name
|
|
|
|
|
FROM broadcast_areas
|
|
|
|
|
WHERE id IN ({})
|
|
|
|
|
""".format(("?," * len(area_ids))[:-1])
|
2020-07-24 16:45:41 +01:00
|
|
|
|
2020-08-12 09:45:22 +01:00
|
|
|
results = self.query(q, *area_ids)
|
2020-07-24 16:45:41 +01:00
|
|
|
|
2020-08-12 09:45:22 +01:00
|
|
|
areas = [
|
|
|
|
|
(row[0], row[1])
|
|
|
|
|
for row in results
|
|
|
|
|
]
|
2020-07-24 16:45:41 +01:00
|
|
|
|
2020-08-12 09:45:22 +01:00
|
|
|
return areas
|
2020-07-24 16:45:41 +01:00
|
|
|
|
|
|
|
|
def get_all_areas_for_library(self, library_id):
|
add counties page
What was previously ward -> local authority is now a ward -> local
authority -> county. County only covers rural counties and not
metropolitan boroughs and other unitary authorities. Previously, there
was a page full of local authorities (unitary authorities and
districts), and each one of those would have a list of electoral wards.
However, now there are counties that contain a list of districts - so
this needs a new page - a checkbox for "select the county" and then a
list of links to district pages.
If you want to select multiple districts, you'll need to go into each
one of those sub-sections in turn and click select all.
Needed to tweak the query to retrieve the list of areas in a list for a
library. Previously, it just returned anything at top level (ie: didn't
have a parent). However, rural districts now have parents (the rural
counties themselves). So the query now returns "everything that isn't a
leaf node", or in more specific terms, everything that has at least
other row referring to it as a parent. So no electoral wards, since
they dont have any children, but yes to districts and counties.
2020-09-04 15:22:32 +01:00
|
|
|
# only interested in areas with children - local authorities, counties, unitary authorities. not wards.
|
2020-07-24 16:45:41 +01:00
|
|
|
q = """
|
2020-08-12 09:34:13 +01:00
|
|
|
SELECT id, name
|
2020-07-28 18:38:40 +01:00
|
|
|
FROM broadcast_areas
|
add counties page
What was previously ward -> local authority is now a ward -> local
authority -> county. County only covers rural counties and not
metropolitan boroughs and other unitary authorities. Previously, there
was a page full of local authorities (unitary authorities and
districts), and each one of those would have a list of electoral wards.
However, now there are counties that contain a list of districts - so
this needs a new page - a checkbox for "select the county" and then a
list of links to district pages.
If you want to select multiple districts, you'll need to go into each
one of those sub-sections in turn and click select all.
Needed to tweak the query to retrieve the list of areas in a list for a
library. Previously, it just returned anything at top level (ie: didn't
have a parent). However, rural districts now have parents (the rural
counties themselves). So the query now returns "everything that isn't a
leaf node", or in more specific terms, everything that has at least
other row referring to it as a parent. So no electoral wards, since
they dont have any children, but yes to districts and counties.
2020-09-04 15:22:32 +01:00
|
|
|
JOIN (
|
|
|
|
|
SELECT DISTINCT broadcast_area_library_group_id
|
|
|
|
|
FROM broadcast_areas
|
|
|
|
|
WHERE broadcast_area_library_group_id IS NOT NULL
|
|
|
|
|
) AS parent_broadcast_areas ON parent_broadcast_areas.broadcast_area_library_group_id = broadcast_areas.id
|
2020-07-24 16:45:41 +01:00
|
|
|
WHERE broadcast_area_library_id = ?
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
results = self.query(q, library_id)
|
|
|
|
|
|
2020-08-12 09:34:13 +01:00
|
|
|
return [
|
|
|
|
|
(row[0], row[1])
|
2020-07-24 16:45:41 +01:00
|
|
|
for row in results
|
|
|
|
|
]
|
|
|
|
|
|
2020-07-31 13:21:21 +01:00
|
|
|
def get_all_areas_for_group(self, group_id):
|
|
|
|
|
q = """
|
2020-08-12 09:34:13 +01:00
|
|
|
SELECT id, name
|
2020-07-31 13:21:21 +01:00
|
|
|
FROM broadcast_areas
|
|
|
|
|
WHERE broadcast_area_library_group_id = ?
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
results = self.query(q, group_id)
|
|
|
|
|
|
|
|
|
|
areas = [
|
2020-08-12 09:34:13 +01:00
|
|
|
(row[0], row[1])
|
2020-07-31 13:21:21 +01:00
|
|
|
for row in results
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
return areas
|
|
|
|
|
|
|
|
|
|
def get_all_groups_for_library(self, library_id):
|
|
|
|
|
q = """
|
|
|
|
|
SELECT id, name
|
2020-07-31 14:30:42 +01:00
|
|
|
FROM broadcast_areas
|
|
|
|
|
WHERE broadcast_area_library_group_id = NULL
|
|
|
|
|
AND broadcast_area_library_id = ?
|
2020-07-31 13:21:21 +01:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
results = self.query(q, library_id)
|
|
|
|
|
|
|
|
|
|
areas = [
|
|
|
|
|
(row[0], row[1])
|
|
|
|
|
for row in results
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
return areas
|
2020-08-12 09:34:13 +01:00
|
|
|
|
2020-08-24 14:43:28 +01:00
|
|
|
def get_polygons_for_area(self, area_id):
|
2020-08-12 09:34:13 +01:00
|
|
|
q = """
|
2020-08-24 14:43:28 +01:00
|
|
|
SELECT polygons
|
|
|
|
|
FROM broadcast_area_polygons
|
2020-08-12 09:34:13 +01:00
|
|
|
WHERE id = ?
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
results = self.query(q, area_id)
|
|
|
|
|
|
2020-08-24 14:43:28 +01:00
|
|
|
return json.loads(results[0][0])
|
2020-08-12 09:34:13 +01:00
|
|
|
|
2020-08-24 14:43:28 +01:00
|
|
|
def get_simple_polygons_for_area(self, area_id):
|
2020-08-12 09:34:13 +01:00
|
|
|
q = """
|
2020-08-24 14:43:28 +01:00
|
|
|
SELECT simple_polygons
|
|
|
|
|
FROM broadcast_area_polygons
|
2020-08-12 09:34:13 +01:00
|
|
|
WHERE id = ?
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
results = self.query(q, area_id)
|
|
|
|
|
|
2020-08-24 14:43:28 +01:00
|
|
|
return json.loads(results[0][0])
|