Skip to content

Commit 75b84af

Browse files
authored
Merge pull request #331 from digital-land/local_plans
Local plans
2 parents 11b7ba9 + 481566c commit 75b84af

File tree

13 files changed

+11761
-29
lines changed

13 files changed

+11761
-29
lines changed

application/core/filters.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import numbers
1717
from application.settings import get_settings
1818
import logging
19+
from datetime import datetime
1920

2021
logger = logging.getLogger(__name__)
2122

@@ -252,7 +253,10 @@ def make_link_filter(eval_ctx, url, **kwargs):
252253

253254

254255
def get_entity_geometry(entity):
255-
data = entity.geojson.geometry if entity.geojson else None
256+
data = None
257+
if entity and entity.geojson is not None:
258+
data = entity.geojson.geometry
259+
256260
if data is None:
257261
logger.warning(
258262
f"No geojson for entity that has a typology of geography: {entity.entity}",
@@ -301,3 +305,21 @@ def get_os_oauth2_token():
301305
return "null"
302306
else:
303307
return jsonResult
308+
309+
310+
def format_date(date_str):
311+
if not date_str:
312+
return date_str
313+
314+
date_obj = datetime.strptime(date_str, "%Y-%m-%d")
315+
day = date_obj.day
316+
317+
# Determine the ordinal suffix
318+
if 11 <= day <= 13: # Special case for teens
319+
suffix = "th"
320+
else:
321+
suffix = {1: "st", 2: "nd", 3: "rd"}.get(day % 10, "th")
322+
323+
# Format the date with the ordinal suffix
324+
formatted_date = f"{day}{suffix} {date_obj.strftime('%B %Y')}"
325+
return formatted_date

application/core/templates.py

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
get_entity_geometry,
2525
get_entity_paint_options,
2626
get_os_oauth2_token,
27+
format_date,
2728
)
2829

2930
from application.core.utils import model_dumps
@@ -77,6 +78,7 @@ def random_int(n=1):
7778
templates.env.filters["slugify"] = to_slug
7879
templates.env.filters["extract_component_key"] = extract_component_key
7980
templates.env.filters["get_entity_geometry"] = get_entity_geometry
81+
templates.env.filters["format_date"] = format_date
8082

8183
# TODO This is a filter which should only need one variable, apparently ther
8284
# eis something called context processors that we should use

application/data_access/entity_queries.py

+47
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
)
1616
from application.db.models import EntityOrm, OldEntityOrm
1717
from application.search.enum import GeometryRelation, PeriodOption
18+
from sqlalchemy.types import Date
19+
from sqlalchemy.sql.expression import cast
1820

1921
logger = logging.getLogger(__name__)
2022

@@ -342,3 +344,48 @@ def _apply_limit_and_pagination_filters(query, params):
342344
if params.get("offset") is not None:
343345
query = query.offset(params["offset"])
344346
return query
347+
348+
349+
def get_linked_entities(
350+
session: Session, dataset: str, reference: str, linked_dataset: str = None
351+
) -> List[EntityModel]:
352+
query = (
353+
session.query(EntityOrm)
354+
.filter(EntityOrm.dataset == dataset)
355+
.filter(EntityOrm.json.contains({linked_dataset: reference}))
356+
)
357+
358+
if dataset in ["local-plan-timetable"]:
359+
query = query.order_by(cast(EntityOrm.json["event-date"].astext, Date).desc())
360+
361+
entities = query.all()
362+
return [entity_factory(e) for e in entities]
363+
364+
365+
def fetchEntityFromReference(
366+
session: Session, dataset: str, reference: str
367+
) -> EntityModel:
368+
entity = (
369+
session.query(EntityOrm)
370+
.filter(EntityOrm.dataset == dataset)
371+
.filter(EntityOrm.reference == reference)
372+
).one_or_none()
373+
374+
if entity:
375+
return entity_factory(entity)
376+
return None
377+
378+
379+
def get_organisations(session: Session) -> List[EntityModel]:
380+
organisations = (
381+
session.query(EntityOrm)
382+
.filter(EntityOrm.typology == "organisation")
383+
.filter(EntityOrm.organisation_entity.isnot(None))
384+
.filter(EntityOrm.name.isnot(None))
385+
.distinct()
386+
.all()
387+
)
388+
if organisations:
389+
return [entity_factory(e) for e in organisations]
390+
else:
391+
return []

application/routers/entity.py

+66
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
from application.data_access.entity_queries import (
2121
get_entity_query,
2222
get_entity_search,
23+
get_organisations,
2324
lookup_entity_link,
25+
get_linked_entities,
26+
fetchEntityFromReference,
2427
)
2528
from application.data_access.dataset_queries import get_dataset_names
2629

@@ -146,12 +149,16 @@ def handle_entity_response(
146149
dataset_fields = [dataset_field["dataset"] for dataset_field in dataset_fields]
147150

148151
dataset = get_dataset_query(session, e.dataset)
152+
149153
organisation_entity, _, _ = get_entity_query(session, e.organisation_entity)
150154

151155
entityLinkFields = [
152156
"article-4-direction",
153157
"permitted-development-rights",
154158
"tree-preservation-order",
159+
"local-plan-boundary",
160+
"local-plan",
161+
"local-plan-event",
155162
]
156163

157164
linked_entities = {}
@@ -166,12 +173,19 @@ def handle_entity_response(
166173
if linked_entity is not None:
167174
linked_entities[field] = linked_entity
168175

176+
# Fetch linked local plans/document/timetable
177+
local_plans, local_plan_boundary_geojson = fetch_linked_local_plans(
178+
session, e_dict_sorted
179+
)
180+
169181
return templates.TemplateResponse(
170182
"entity.html",
171183
{
172184
"request": request,
173185
"row": e_dict_sorted,
186+
"local_plan_geojson": local_plan_boundary_geojson,
174187
"linked_entities": linked_entities,
188+
"local_plans": local_plans,
175189
"entity": e,
176190
"pipeline_name": e.dataset,
177191
"references": [],
@@ -189,6 +203,51 @@ def handle_entity_response(
189203
)
190204

191205

206+
linked_datasets = {
207+
"local-plan-boundary": ["local-plan"],
208+
"local-plan": [
209+
"local-plan-document",
210+
"local-plan-timetable",
211+
"local-plan-boundary",
212+
],
213+
}
214+
215+
216+
def fetch_linked_local_plans(session: Session, e_dict_sorted: Dict = None):
217+
results = {}
218+
local_plan_boundary_geojson = None
219+
dataset = e_dict_sorted["dataset"]
220+
reference = e_dict_sorted["reference"]
221+
if dataset in linked_datasets:
222+
linked_dataset_value = linked_datasets[dataset]
223+
for linked_dataset in linked_dataset_value:
224+
if dataset == "local-plan" and linked_dataset == "local-plan-boundary":
225+
if linked_dataset in e_dict_sorted:
226+
local_plan_boundary_geojson = fetchEntityFromReference(
227+
session, linked_dataset, e_dict_sorted[linked_dataset]
228+
)
229+
linked_entities = get_linked_entities(
230+
session, linked_dataset, reference, linked_dataset=dataset
231+
)
232+
results[linked_dataset] = linked_entities
233+
234+
# Handle special case for "local-plan-timetable"
235+
if dataset == "local-plan" and linked_dataset == "local-plan-timetable":
236+
for entity in linked_entities:
237+
if (
238+
hasattr(entity, "local_plan_event")
239+
and entity.local_plan_event
240+
and not entity.local_plan_event.startswith("estimated")
241+
):
242+
entity.local_plan_event = fetchEntityFromReference(
243+
session, "local-plan-event", entity.local_plan_event
244+
)
245+
else:
246+
entity.local_plan_event = None
247+
248+
return results, local_plan_boundary_geojson
249+
250+
192251
def get_entity(
193252
request: Request,
194253
entity: int = Path(default=Required, description="Entity id"),
@@ -329,6 +388,12 @@ def search_entities(
329388
local_authorities = get_local_authorities(session, "local-authority")
330389
local_authorities = [la.dict() for la in local_authorities]
331390

391+
organisations = get_organisations(session)
392+
columns = ["entity", "organisation_entity", "name"]
393+
organisations_list = [
394+
organisation.dict(include=set(columns)) for organisation in organisations
395+
]
396+
332397
if links.get("prev") is not None:
333398
prev_url = links["prev"]
334399
else:
@@ -350,6 +415,7 @@ def search_entities(
350415
"datasets": datasets,
351416
"local_authorities": local_authorities,
352417
"typologies": typologies,
418+
"organisations": organisations_list,
353419
"query": {"params": params},
354420
"active_filters": [
355421
filter_name

0 commit comments

Comments
 (0)