Skip to content
Merged
119 changes: 115 additions & 4 deletions server/stt/lupaus_export/enrich_related.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,41 @@ def _find_many(
return []


def _get_planning_item_coverage_status_from_mongo(pl: Dict[str, Any]) -> Dict[str, Any]:
"""
For some reason item.coverages is like coverages': ['Kuvauskeikka', 'Teksti']
but we need "planning.coverages.news_coverage_status"-data from mongo by item _id
Get the 'news_coverage_status' from the 'coverages' of a planning item.
Args:
pl: A dictionary representing a planning item.
Returns:
The value of 'news_coverage_status' if found, otherwise an empty dictionary.
"""
if not pl:
return {}
# skip if no coverages
if "coverages" not in pl or not pl["coverages"]:
return {}
pl_id = pl.get("_id")
# skip if no id for some reason
if not pl_id:
return {}
pl_from_mongo = _find_many(
"planning",
# get only coverages that have "planning.g2_content_type" = "teksti"
{"_id": pl_id, "coverages.planning.g2_content_type": "teksti"},
projection={"coverages": 1, "_id": 0},
)
if not pl_from_mongo:
return {}
if ("coverages" not in pl_from_mongo[0]) or (not pl_from_mongo[0]["coverages"]):
return {}
for cov in pl_from_mongo[0]["coverages"]:
if "news_coverage_status" in cov and cov["news_coverage_status"]:
return cov["news_coverage_status"]
return {}


def get_priority_from_agenda_item(item: Dict[str, Any]) -> str:
"""
Extracts the 'priority' value from an agenda item.
Expand Down Expand Up @@ -173,6 +208,33 @@ def get_priority_from_agenda_item(item: Dict[str, Any]) -> str:
return ""


def get_category_from_agenda_item(item: Dict[str, Any]) -> str:
"""
Extracts the "scheme": "categories" value from an agenda item subjects.

The function looks for the 'categories' in the 'subject' field of the item,
which is expected to be a list of dictionaries. Each dictionary may contain
a 'scheme' key. If a dictionary with 'scheme' equal to 'categories' is found,
the corresponding 'name' value is returned.

If 'categories' is not found, an empty string is returned.

Args:
item: A dictionary representing an agenda item.
Returns:
The name of 'categories' if found, otherwise an empty string.
"""
# categories is stored in subject like:
# subject: [{'scheme': 'categories', 'name': 'Kulttuuri', 'qcode': '4'}]
if not item:
return ""
if "subject" in item:
for sub in item["subject"]:
if sub.get("scheme") == "categories":
return sub.get("name", "")
return ""


def get_numeric_value_from_priority(priority: str) -> str:
"""
Extracts the numeric value from a priority string.
Expand Down Expand Up @@ -214,7 +276,7 @@ def _set_priority_fields(pl: Dict[str, Any]) -> None:
pl["stt_priority_numeric"] = priority_numeric


def set_stt_fields(
def _set_stt_fields(
agendas: List[Dict[str, Any]],
by_key: Dict[str, Dict[str, Any]],
events: List[Dict[str, Any]],
Expand All @@ -229,6 +291,8 @@ def set_stt_fields(
for ag in agendas or []:
new_items: List[Dict[str, Any]] = []
for pl in ag.get("items") or []:
news_coverage_status = _get_planning_item_coverage_status_from_mongo(pl)
pl["news_coverage_status"] = news_coverage_status
_set_priority_fields(pl)
if not pl.get("stt_priority"):
continue # exclude items without priority
Expand All @@ -247,9 +311,56 @@ def set_stt_fields(
pl["related_events_expanded"] = expanded
new_items.append(pl)
ag["items"] = new_items
# group agenda items by category
grouped_agendas = _group_agenda_items_by_category(ag)
# and attach to agenda
ag["grouped_items"] = grouped_agendas
# get main topic items (highest priority)
main_topic_items = _get_items_with_highest_priority(ag)
ag["main_topic_items"] = main_topic_items
return agendas


def _group_agenda_items_by_category(
ag: Dict[str, Any],
) -> Dict[str, List[Dict[str, Any]]]:
"""
Groups agenda items by their category.

Args:
agendas: A list of agenda dictionaries. Each agenda may include an "items"
list; each item may include a "categories" field.

Returns:
A dictionary where keys are categories and values are lists of agenda items
belonging to those categories. Items without a category are grouped under
the key 'Uncategorized'.
"""
categorized_items: Dict[str, List[Dict[str, Any]]] = {}
for pl in ag.get("items") or []:
category_name = get_category_from_agenda_item(pl) or "Uncategorized"
if category_name not in categorized_items:
categorized_items[category_name] = []
categorized_items[category_name].append(pl)
return categorized_items


def _get_items_with_highest_priority(
ag: Dict[str, Any],
) -> List[Dict[str, Any]]:
# if stt_priority_numeric is 3300, it is the highest priority
highest_priority = 3300
main_topic_items = []
for pl in ag.get("items") or []:
try:
priority_numeric = int(pl.get("stt_priority_numeric", "0"))
if priority_numeric == highest_priority:
main_topic_items.append(pl)
except ValueError:
continue
return main_topic_items


def enrich_planning_agendas(agendas: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
Adds item.related_events_expanded = [event, ...]
Expand All @@ -259,7 +370,7 @@ def enrich_planning_agendas(agendas: List[Dict[str, Any]]) -> List[Dict[str, Any
logger.info(f"Enriching agendas: found {len(ev_ids)} unique related event IDs")
logger.info(f"Enriching agendas: event IDs: {ev_ids}")
if not ev_ids:
set_stt_fields(agendas, {}, [])
_set_stt_fields(agendas, {}, [])
return agendas

# Search events by _ids
Expand All @@ -275,7 +386,7 @@ def enrich_planning_agendas(agendas: List[Dict[str, Any]]) -> List[Dict[str, Any
},
)
if not events:
set_stt_fields(agendas, {}, [])
_set_stt_fields(agendas, {}, [])
return agendas

# Map by both _id and guid for resilience
Expand All @@ -284,7 +395,7 @@ def enrich_planning_agendas(agendas: List[Dict[str, Any]]) -> List[Dict[str, Any
if "_id" in e:
by_key[str(e["_id"])] = e
# Attach to each planning item
set_stt_fields(agendas, by_key, events)
_set_stt_fields(agendas, by_key, events)

# Sort related_events_expanded by start date
for ag in agendas or []:
Expand Down
116 changes: 91 additions & 25 deletions server/templates/lupaus_events_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@
{%- endif -%}
{%- endmacro %}

{# Macro to render a single planning item category from subject -> scheme = "categories" #}
{% macro render_planning_item_category(item) -%}
{%- for sub in item.subject | default([]) -%}
{%- if sub.scheme == "categories" -%}
({{ sub.name | upper }})
{%- endif -%}
{%- endfor -%}
{%- endmacro %}

{# macro to render STT priority text #}
{% macro stt_priority_text(item) -%}
{%- set has_str = (item.stt_priority is defined) and (item.stt_priority | trim) -%}
Expand All @@ -20,37 +29,94 @@
{# empty output when not set #}
{%- elif item.stt_priority == 'Vain tulokset' -%}
{{- item.stt_priority -}}
{%- elif item.stt_priority == 'Vain tulokset' -%}
{{- item.stt_priority -}}
{%- else -%}
Mittaversio korkeintaan {{ item.stt_priority_numeric }} merkkiä
Mittaversio korkeintaan {{ item.stt_priority_numeric }} merkkiä
{%- endif -%}
{%- endmacro %}

{# One paragraph per agenda item #}
{# Macro to render a single planning item paragraph (reused for grouped & ungrouped) #}
{% macro render_planning_item(item) -%}
{%- set bits = [] -%}
{%- if item.slugline %}{% set _ = bits.append(item.slugline) %}{% endif -%}
{%- if item.description_text %}{% set _ = bits.append(item.description_text) %}{% endif -%}
{%- for ev in item.related_events_expanded or [] -%}
{%- if ev.name and ev.name != item.slugline %}{% set _ = bits.append(ev.name) %}{% endif -%}
{%- if ev.dates and ev.dates.start %}{% set _ = bits.append('klo ' ~ (ev.dates.start | fi_time)) %}{% endif -%}
{%- if ev.location is iterable -%}
{%- set ev_loc = lupaus_location(ev.location) -%}
{%- if ev_loc %}{% set _ = bits.append(ev_loc) %}{% endif -%}
{%- endif -%}
{%- endfor -%}
{# Call the priority macro #}
{%- set urg = stt_priority_text(item) -%}
{%- if urg %}{% set _ = bits.append(urg) %}{% endif -%}
{# Add coverages if available #}
{%- if item.coverages is defined and item.coverages -%}
{%- set cov_list = item.coverages if item.coverages is iterable else [item.coverages] -%}
{%- if cov_list | length > 0 -%}
{%- set _ = bits.append('Kuvitus: ' ~ (cov_list | join(', '))) -%}
{%- endif -%}
{%- endif -%}
{%- if bits %}<p>- {{- bits | join(', ') -}}.</p>{% endif -%}
<p></p>
{%- endmacro %}

{# Macro to render a single main topic item paragraph #}
{% macro render_main_topic_item(item) -%}
{%- set bits = [] -%}
{%- if item.slugline %}{% set _ = bits.append(item.slugline) %}{% endif -%}
{%- set category_str = render_planning_item_category(item) -%}
{%- if category_str %}{% set _ = bits.append(category_str) %}{% endif -%}
{%- if bits %}<p>- {{- bits | join(' ') -}}.</p>{% endif -%}
<p></p>
{%- endmacro %}

{# One paragraph per agenda item; grouped by category if grouped_items present #}
{%- for agenda in agendas or [] -%}
{%- for item in agenda['items'] | default([]) -%}
{%- set bits = [] -%}
{%- if item.slugline %}{% set _ = bits.append(item.slugline) %}{% endif -%}
{%- if item.description_text %}{% set _ = bits.append(item.description_text) %}{% endif -%}
{%- for ev in item.related_events_expanded or [] -%}
{%- if ev.name and ev.name != item.slugline %}{% set _ = bits.append(ev.name) %}{% endif -%}
{%- if ev.dates and ev.dates.start %}{% set _ = bits.append('klo ' ~ (ev.dates.start | fi_time)) %}{% endif -%}
{%- if ev.location is iterable -%}
{%- set ev_loc = lupaus_location(ev.location) -%}
{%- if ev_loc %}{% set _ = bits.append(ev_loc) %}{% endif -%}
{%- endif -%}
{%- endfor -%}
{# Call the priority macro #}
{%- set urg = stt_priority_text(item) -%}
{%- if urg %}{% set _ = bits.append(urg) %}{% endif -%}
{# Add coverages if available #}
{%- if item.coverages is defined and item.coverages -%}
{%- set cov_list = item.coverages if item.coverages is iterable else [item.coverages] -%}
{%- if cov_list | length > 0 -%}
{%- set _ = bits.append('Kuvitus: ' ~ (cov_list | join(', '))) -%}
{%- endif -%}
{%- set main_topic_items = agenda.get('main_topic_items') -%}
{%- if main_topic_items and main_topic_items | count > 0 -%}
<h2>KÄRKIAIHEITAMME</h2>
{%- for item in main_topic_items -%}
{{ render_main_topic_item(item) }}
{%- endfor -%}
{%- endif -%}
{%- set grouped = agenda.get('grouped_items') -%}
{%- if grouped and grouped.items() | count > 0 -%}
{%- for category, items in grouped.items() -%}
<h2>{{ category | upper }}</h2>
{%- set coverage = namespace(intended=[], undecided=[], other=[]) -%}
{%- for item in items -%}
{%- set status = item.news_coverage_status or {} -%}
{%- set status_name = (status.name or '') | lower -%}
{%- if status_name == 'coverage intended' -%}
{%- set _ = coverage.intended.append(item) -%}
{%- elif status_name == 'coverage not decided yet' -%}
{%- set _ = coverage.undecided.append(item) -%}
{%- else -%}
{%- set _ = coverage.other.append(item) -%}
{%- endif -%}
{%- if bits %}<p>{{- bits | join(', ') -}}.</p>{% endif -%}
{# add empty line between items #}
<p></p>
{%- endfor -%}

{%- for item in coverage.intended -%}
{{ render_planning_item(item) }}
{%- endfor -%}

{%- if coverage.undecided -%}
<h2>JOS AIHETTA</h2>
{%- for item in coverage.undecided -%}
{{ render_planning_item(item) }}
{%- endfor -%}
{%- endif -%}

{%- if coverage.other -%}
<h2>EI</h2>
{%- for item in coverage.other -%}
{{ render_planning_item(item) }}
{%- endfor -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
{%- endfor -%}