Log outage geometries to database #68
2 changed files with 35 additions and 36 deletions
|
@ -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:
|
def convert_outage_geometry(event) -> MultiPolygon:
|
||||||
|
@ -9,3 +10,21 @@ def convert_outage_geometry(event) -> MultiPolygon:
|
||||||
for ring in event["polygons"]["rings"]:
|
for ring in event["polygons"]["rings"]:
|
||||||
polygon_list.append(Polygon(ring))
|
polygon_list.append(Polygon(ring))
|
||||||
return MultiPolygon(polygon_list)
|
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
|
||||||
|
|
50
scl.py
50
scl.py
|
@ -5,17 +5,16 @@ from typing import Optional
|
||||||
import mastodon
|
import mastodon
|
||||||
import requests
|
import requests
|
||||||
import shapely
|
import shapely
|
||||||
import sqlalchemy.types as types
|
|
||||||
import staticmap
|
import staticmap
|
||||||
import yaml
|
import yaml
|
||||||
from mastodon import Mastodon
|
from mastodon import Mastodon
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
from shapely import Geometry, MultiPolygon, Point, Polygon, from_wkb, to_wkb
|
from shapely import Geometry
|
||||||
from sqlalchemy import create_engine, select
|
from sqlalchemy import create_engine, select
|
||||||
from sqlalchemy.exc import NoResultFound
|
from sqlalchemy.exc import NoResultFound
|
||||||
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
|
from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
|
||||||
|
|
||||||
from geospatial import convert_outage_geometry
|
from geospatial import DBGeometry, convert_outage_geometry
|
||||||
|
|
||||||
post_datetime_format = "%b %e %l:%M %p"
|
post_datetime_format = "%b %e %l:%M %p"
|
||||||
|
|
||||||
|
@ -118,16 +117,6 @@ def get_hashtag_string(event) -> str:
|
||||||
return hashtag_string
|
return hashtag_string
|
||||||
|
|
||||||
|
|
||||||
def convert_outage_geometry(event) -> MultiPolygon:
|
|
||||||
assert event["polygons"]["type"] == "polygon"
|
|
||||||
assert event["polygons"]["hasZ"] is False
|
|
||||||
assert event["polygons"]["hasM"] is False
|
|
||||||
polygon_list = []
|
|
||||||
for ring in event["polygons"]["rings"]:
|
|
||||||
polygon_list.append(Polygon(ring))
|
|
||||||
return MultiPolygon(polygon_list)
|
|
||||||
|
|
||||||
|
|
||||||
def do_initial_post(
|
def do_initial_post(
|
||||||
event,
|
event,
|
||||||
event_class,
|
event_class,
|
||||||
|
@ -272,31 +261,21 @@ Cause: {}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
post_result = mastodon_client.status_post(
|
post_result = {"id": "0123"}
|
||||||
status=post_text,
|
# post_result = mastodon_client.status_post(
|
||||||
media_ids=map_media_post,
|
# status=post_text,
|
||||||
visibility="public",
|
# media_ids=map_media_post,
|
||||||
language="en",
|
# visibility="public",
|
||||||
)
|
# language="en",
|
||||||
|
# )
|
||||||
post_id = post_result["id"]
|
post_id = post_result["id"]
|
||||||
|
|
||||||
return {"post_id": post_id, "map_media_post_id": map_media_post_id}
|
return {"post_id": post_id, "map_media_post_id": map_media_post_id}
|
||||||
|
|
||||||
|
|
||||||
class GeometryWkb(types.TypeDecorator):
|
|
||||||
impl = types.LargeBinary
|
|
||||||
cache_ok = True
|
|
||||||
|
|
||||||
def process_bind_param(self, value, dialect):
|
|
||||||
return to_wkb(value)
|
|
||||||
|
|
||||||
def process_result_value(self, value, dialect):
|
|
||||||
return from_wkb(value)
|
|
||||||
|
|
||||||
|
|
||||||
class Base(DeclarativeBase):
|
class Base(DeclarativeBase):
|
||||||
type_annotation_map = {
|
type_annotation_map = {
|
||||||
Geometry: GeometryWkb,
|
Geometry: DBGeometry,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -317,10 +296,11 @@ class SclOutage(Base):
|
||||||
max_num_people: Mapped[int] = mapped_column()
|
max_num_people: Mapped[int] = mapped_column()
|
||||||
neighborhood: Mapped[Optional[str]] = mapped_column()
|
neighborhood: Mapped[Optional[str]] = mapped_column()
|
||||||
city: Mapped[Optional[str]] = mapped_column()
|
city: Mapped[Optional[str]] = mapped_column()
|
||||||
outage_geometries: Mapped[Geometry] = mapped_column()
|
outage_geometries: Mapped[Optional[Geometry]] = mapped_column()
|
||||||
|
geometries_modified: Mapped[Optional[bool]] = mapped_column()
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
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}, outage_geometries={self.outage_geometries!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")
|
engine = create_engine("sqlite:///scl.db")
|
||||||
|
@ -393,9 +373,9 @@ with Session(engine) as session:
|
||||||
existing_record.max_num_people = event["numPeople"]
|
existing_record.max_num_people = event["numPeople"]
|
||||||
max_event_class = classify_event_size(existing_record.max_num_people)
|
max_event_class = classify_event_size(existing_record.max_num_people)
|
||||||
if existing_record.outage_geometries != outage_geometries:
|
if existing_record.outage_geometries != outage_geometries:
|
||||||
print("updating geometries")
|
print("Geometries modified")
|
||||||
existing_record.outage_geometries = outage_geometries
|
existing_record.outage_geometries = outage_geometries
|
||||||
|
existing_record.geometries_modified = True
|
||||||
|
|
||||||
if updated_properties:
|
if updated_properties:
|
||||||
updated_properties.sort()
|
updated_properties.sort()
|
||||||
|
|
Loading…
Reference in a new issue