Log outage geometries to database (#68)
Some checks failed
ci/woodpecker/push/lint Pipeline was successful
ci/woodpecker/push/vulnerability-scan Pipeline failed

To enable us to detect changes to to the geometry for future updating of the map (#67)

Reviewed-on: #68
This commit is contained in:
Liam Steckler 2024-11-23 17:14:30 -08:00
parent fbdc85bc29
commit e6722876bd
2 changed files with 38 additions and 7 deletions

View file

@ -1,4 +1,5 @@
from shapely import MultiPolygon, Polygon
from shapely import MultiPolygon, Polygon, Geometry, to_wkb, from_wkb
from sqlalchemy.types import TypeDecorator, LargeBinary
def convert_outage_geometry(event) -> MultiPolygon:
@ -9,3 +10,21 @@ def convert_outage_geometry(event) -> MultiPolygon:
for ring in event["polygons"]["rings"]:
polygon_list.append(Polygon(ring))
return MultiPolygon(polygon_list)
class DBGeometry(TypeDecorator):
impl = LargeBinary
cache_ok = True
def process_bind_param(self, value, dialect):
if isinstance(value, Geometry):
value = to_wkb(value)
return value
def process_result_value(self, value, dialect):
if value is None:
return value
else:
if not isinstance(value, Geometry):
value = from_wkb(value)
return value

24
scl.py
View file

@ -5,15 +5,16 @@ from typing import Optional
import mastodon
import requests
import shapely
import staticmap
import yaml
from mastodon import Mastodon
from PIL import Image, ImageDraw, ImageFont
from shapely import Geometry
from sqlalchemy import create_engine, select
from sqlalchemy.exc import NoResultFound
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
from staticmap import Polygon, StaticMap
from geospatial import convert_outage_geometry
from geospatial import DBGeometry, convert_outage_geometry
post_datetime_format = "%b %e %l:%M %p"
@ -36,7 +37,7 @@ mastodon_client = Mastodon(
)
class AttribStaticMap(StaticMap, object):
class AttribStaticMap(staticmap.StaticMap, object):
def __init__(self, *args, **kwargs):
self.attribution = "© Stadia Maps © OpenMapTiles © OpenStreetMap"
super(AttribStaticMap, self).__init__(*args, **kwargs)
@ -137,7 +138,7 @@ def do_initial_post(
)
assert event["polygons"]["type"] == "polygon"
for ring in event["polygons"]["rings"]:
polygon = Polygon(
polygon = staticmap.Polygon(
ring,
# Appending 7F to the fill_color makes it 50% transparent
fill_color="{}7F".format(event_class["outage_color"]),
@ -272,7 +273,9 @@ Cause: {}
class Base(DeclarativeBase):
pass
type_annotation_map = {
Geometry: DBGeometry,
}
class SclOutage(Base):
@ -292,9 +295,11 @@ class SclOutage(Base):
max_num_people: Mapped[int] = mapped_column()
neighborhood: Mapped[Optional[str]] = mapped_column()
city: Mapped[Optional[str]] = mapped_column()
outage_geometries: Mapped[Optional[Geometry]] = mapped_column()
geometries_modified: Mapped[Optional[bool]] = mapped_column()
def __repr__(self) -> str:
return f"SclOutage(scl_outage_id={self.scl_outage_id!r}, most_recent_post_id={self.most_recent_post_id!r}, initial_post_id={self.initial_post_id!r}, map_media_post_id={self.map_media_post_id!r}, last_updated_time={self.last_updated_time!r}, no_longer_in_response_time={self.no_longer_in_response_time!r}, start_time={self.start_time!r}, num_people={self.num_people!r}, max_num_people={self.max_num_people!r}, neighborhood={self.neighborhood!r}, city={self.city!r})"
return f"SclOutage(scl_outage_id={self.scl_outage_id!r}, most_recent_post_id={self.most_recent_post_id!r}, initial_post_id={self.initial_post_id!r}, map_media_post_id={self.map_media_post_id!r}, last_updated_time={self.last_updated_time!r}, no_longer_in_response_time={self.no_longer_in_response_time!r}, start_time={self.start_time!r}, num_people={self.num_people!r}, max_num_people={self.max_num_people!r}, neighborhood={self.neighborhood!r}, city={self.city!r}, outage_geometries={self.outage_geometries!r}, geometries_modified={self.geometries_modified!r})"
engine = create_engine("sqlite:///scl.db")
@ -366,6 +371,10 @@ with Session(engine) as session:
# Used to determine the maximum number of people affected by this outage, to determine if it's worth posting about
existing_record.max_num_people = event["numPeople"]
max_event_class = classify_event_size(existing_record.max_num_people)
if existing_record.outage_geometries != outage_geometries:
print("Geometries modified")
existing_record.outage_geometries = outage_geometries
existing_record.geometries_modified = True
if updated_properties:
updated_properties.sort()
@ -426,6 +435,8 @@ with Session(engine) as session:
existing_record.map_media_post_id = initial_post_result[
"map_media_post_id"
]
else:
print("Existing record was found, and no properties were updated.")
session.commit()
except NoResultFound:
@ -476,6 +487,7 @@ with Session(engine) as session:
max_num_people=event["numPeople"],
neighborhood=neighborhood,
city=city,
outage_geometries=outage_geometries,
)
session.add(new_outage_record)
session.commit()