Mercurial > code > home > repos > gcalendarwatch
changeset 45:e53a1bc87f99
cleanup and some fixes to starred event graph
author | drewp@bigasterisk.com |
---|---|
date | Thu, 06 Jun 2024 15:57:26 -0700 |
parents | 1d9c4487f21a |
children | a53d79faac16 |
files | gcalendarwatch.py graphconvert.py ingest.py |
diffstat | 3 files changed, 69 insertions(+), 114 deletions(-) [+] |
line wrap: on
line diff
--- a/gcalendarwatch.py Mon Feb 19 13:53:51 2024 -0800 +++ b/gcalendarwatch.py Thu Jun 06 15:57:26 2024 -0700 @@ -1,6 +1,5 @@ """ -sync google calendar into mongodb, return queries from that as -JSON-LD. +sync google calendar into mongodb, return a few preset queries as RDF graphs gcalendarwatch.conf looks like this: { @@ -16,10 +15,9 @@ import functools import json import logging -import re import time import traceback -from typing import Any, Dict, Iterable, Optional, cast +from typing import Iterable, cast import background_loop import pymongo.collection @@ -27,14 +25,14 @@ from patchablegraph import PatchableGraph from patchablegraph.handler import GraphEvents, StaticGraph from pymongo import MongoClient -from rdflib import RDF, ConjunctiveGraph, Graph, Namespace, URIRef +from rdflib import Namespace, URIRef from starlette.applications import Starlette from starlette.requests import Request -from starlette.responses import HTMLResponse, JSONResponse, PlainTextResponse, Response +from starlette.responses import HTMLResponse, PlainTextResponse from starlette.routing import Route from starlette_exporter import PrometheusMiddleware, handle_metrics -from datetimemath import dayRange, parse +from datetimemath import dayRange from graphconvert import asGraph from ingest import SyncToMongo from localtypes import Conf, Record @@ -65,55 +63,6 @@ }""" -def asJsonLd(events): - ret = {'@graph': []} # type: Dict[Any, Any] - for ev in events: - ev['startTime'] = ev['startTime'].astimezone(tzlocal()).isoformat() - ev['endTime'] = ev['endTime'].astimezone(tzlocal()).isoformat() - ev['@id'] = ev.pop('uri') - ret['@graph'].append(ev) - - ret['@context'] = { - "xsd": "http://www.w3.org/2001/XMLSchema#", - "ev": "http://bigasterisk.com/event#", - "startTime": { - "@id": "ev:startTime", - "@type": "xsd:dateTime" - }, - "endTime": { - "@id": "ev:endTime", - "@type": "xsd:dateTime" - }, - "startDate": { - "@id": "ev:startDate", - "@type": "xsd:date" - }, - "endDate": { - "@id": "ev:endDate", - "@type": "xsd:date" - }, - "title": "ev:title", - "feed": { - "@id": "ev:feed", - "@type": "@id" - }, - "htmlLink": { - "@id": "ev:htmlLink", - "@type": "@id" - }, - } - return ret - - -def starred(graph: Graph, ev: URIRef) -> Optional[str]: - title = str(graph.value(ev, EV['title'])) - m = re.search(r'(.*)\*\s*$', title) - if m: - return m.group(1) - else: - return None - - class ReadMongoEvents(object): """read events from mongodb""" @@ -138,7 +87,7 @@ ) -> int: log.info(f"updating {cal or 'all'}") try: - n = sync.update(cal=cal) + n = sync.update(cal=cal, days=120) except Exception: traceback.print_exc() log.error("update failed") @@ -146,40 +95,6 @@ return n -# who uses this? pimscreen, frontdoor? they should use the GraphEvents version -def EventsPage(conf: Conf, read: ReadMongoEvents, req: Request) -> Response: - t1, t2 = dayRange(int(req.query_params.get('days', default='2'))) - if req.query_params.get('t1', default=None): - t1 = parse(req.query_params.get('t1', ''), tzinfo=tzlocal()) - if req.query_params.get('t2', default=None): - t2 = parse(req.query_params.get('t2', ''), tzinfo=tzlocal()) - log.info(f'get /events local t1={t1} t2={t2}') - return PlainTextResponse(media_type="text/n3", content=asGraph(conf, cals=[], events=read.getEvents(t1, t2)).serialize(format='n3')) - - -def Countdowns(countdownGraph, req: Request) -> Response: - rows = [] - graph = countdownGraph._graph - for ev in graph.subjects(RDF.type, EV['Event']): - starLabel = starred(graph, ev) - if starLabel is not None: - rows.append({'@type': 'countdown', 'time': graph.value(ev, EV['start']), 'label': starLabel}) - return JSONResponse(media_type="application/ld+json", - content={ - "@context": { - "countdown": "http://bigasterisk.com/countdown#CountdownEvent", - "label": "http://www.w3.org/2000/01/rdf-schema#label", - "time": { - "@id": "http://bigasterisk.com/event#time", - "@type": "xsd:dateTime" - }, - "xsd": "http://www.w3.org/2001/XMLSchema#", - "rdfs": "http://www.w3.org/2000/01/rdf-schema#" - }, - "@graph": rows, - }) - - def statusMsg(conf, loop: background_loop.Loop): period = conf['minutes_between_polls'] * 60 ago = time.time() - loop.lastSuccessRun @@ -221,7 +136,7 @@ sync = SyncToMongo(conf, mongoOut, agendaGraph, countdownGraph) read = ReadMongoEvents(mongoOut) - s, e = dayRange(60) + s, e = dayRange(120) sync.updateGraphs(read.getEvents(s, e)) loop = background_loop.loop_forever(functools.partial(update, sync=sync), conf['minutes_between_polls'] * 60) @@ -239,8 +154,6 @@ Route('/graph/calendar/countdown/events', GraphEvents(countdownGraph)), Route('/graph/currentEvents', StaticGraph(currentEventsGraph)), Route('/graph/currentEvents/events', GraphEvents(currentEventsGraph)), - Route('/countdowns.json', functools.partial(Countdowns, countdownGraph)), - Route('/events', functools.partial(EventsPage, conf, read)), Route('/pollNow', functools.partial(PollNow, loop), methods=['POST']) ])
--- a/graphconvert.py Mon Feb 19 13:53:51 2024 -0800 +++ b/graphconvert.py Thu Jun 06 15:57:26 2024 -0700 @@ -1,10 +1,12 @@ +from dateutil.tz import tzlocal +from rdflib import RDF, RDFS, ConjunctiveGraph, Literal, Namespace, URIRef + from localtypes import Conf, feedFromCalId -from rdflib import RDF, RDFS, ConjunctiveGraph, Literal, Namespace, URIRef EV = Namespace("http://bigasterisk.com/event#") -def asGraph(conf: Conf, cals, events, extraClasses=[], ctxUri:URIRef=EV['gcalendar']): +def asGraph(conf: Conf, cals, events, extraClasses=[], ctxUri: URIRef = EV['gcalendar']): ctx = ConjunctiveGraph(identifier=ctxUri) graph = ConjunctiveGraph() graph.namespace_manager.bind('ev', EV) @@ -22,7 +24,7 @@ if uri is None: raise ValueError(f"{ev=} had no event uri") - def add(p: URIRef, o: URIRef|Literal): + def add(p: URIRef, o: URIRef | Literal): return graph.add((uri, p, o, ctx)) add(RDF.type, EV['Event']) @@ -37,3 +39,43 @@ if 'htmlLink' in ev: add(EV['htmlLink'], URIRef(ev['htmlLink'])) return graph + + +def asJsonLd(events) -> dict: + ret: dict = {'@graph': []} + for ev in events: + ev['startTime'] = ev['startTime'].astimezone(tzlocal()).isoformat() + ev['endTime'] = ev['endTime'].astimezone(tzlocal()).isoformat() + ev['@id'] = ev.pop('uri') + ret['@graph'].append(ev) + + ret['@context'] = { + "xsd": "http://www.w3.org/2001/XMLSchema#", + "ev": "http://bigasterisk.com/event#", + "startTime": { + "@id": "ev:startTime", + "@type": "xsd:dateTime" + }, + "endTime": { + "@id": "ev:endTime", + "@type": "xsd:dateTime" + }, + "startDate": { + "@id": "ev:startDate", + "@type": "xsd:date" + }, + "endDate": { + "@id": "ev:endDate", + "@type": "xsd:date" + }, + "title": "ev:title", + "feed": { + "@id": "ev:feed", + "@type": "@id" + }, + "htmlLink": { + "@id": "ev:htmlLink", + "@type": "@id" + }, + } + return ret
--- a/ingest.py Mon Feb 19 13:53:51 2024 -0800 +++ b/ingest.py Thu Jun 06 15:57:26 2024 -0700 @@ -19,21 +19,19 @@ EV = Namespace("http://bigasterisk.com/event#") - - -def getFirstPageOfCalendars(service: Resource) -> Iterable[tuple[str, str | None, str | None]]: +def _getFirstPageOfCalendars(service: Resource) -> Iterable[tuple[str, str | None, str | None]]: for row in service.calendarList().list().execute()['items']: yield row['id'], row.get('summary'), row.get('description') -def recordFromEv(conf: Conf, calId: str, ev: Dict) -> Record: +def _recordFromEv(conf: Conf, calId: str, ev: Dict) -> Record: def dateOrTime(d): if 'date' in d: return d['date'] return d['dateTime'] - rec= { + rec = { 'uri': conf['event_uri_ns'] + ev['id'], 'feed': feedFromCalId(conf, calId), 'title': ev.get('summary', '?'), @@ -51,14 +49,16 @@ else: rec['%sTime' % field] = parse(val['dateTime']) rec['%sDate' % field] = parse(val['dateTime']).date().isoformat() - return rec + return cast(Record, rec) def filterStarred(recs: Sequence[Record], maxCount=15) -> List[Record]: recs = sorted(recs, key=lambda r: r['start']) out = [] for rec in recs: - if re.search(r'(.*)\*\s*$', rec['title']): + if m:=re.search(r'(.*)\*\s*$', rec['title']): + rec = rec.copy() + rec['title'] = m.group(1) out.append(rec) if len(out) >= maxCount: break @@ -79,18 +79,18 @@ def update(self, days=10, cal=None) -> int: start, end = dayRange(days) - idsFormerlyInRange = self.clearByStartTime(cal, start, end) + idsFormerlyInRange = self._clearByStartTime(cal, start, end) - totalNew, currentRecords = self.gatherNewEventsInRange(cal, start, end, idsFormerlyInRange) + totalNew, currentRecords = self._gatherNewEventsInRange(cal, start, end, idsFormerlyInRange) self.updateGraphs(currentRecords) return totalNew - def gatherNewEventsInRange(self, cal, start, end, idsFormerlyInRange): + def _gatherNewEventsInRange(self, cal, start, end, idsFormerlyInRange): totalNew = 0 currentRecords = [] try: - cals = getFirstPageOfCalendars(self.service) + cals = _getFirstPageOfCalendars(self.service) except HttpError: log.error('on getFirstPageOfCalendars') os.abort() @@ -102,12 +102,12 @@ if cal and calId != cal: continue try: - self.updateOneCal(start, end, idsFormerlyInRange, totalNew, currentRecords, calId) + self._updateOneCal(start, end, idsFormerlyInRange, totalNew, currentRecords, calId) except HttpError: log.error(f"on cal {calId}") return totalNew, currentRecords - def clearByStartTime(self, cal, start, end): + def _clearByStartTime(self, cal, start, end): spec: Dict[str, Any] = {"startTime": {"$gte": start, "$lte": end}} if cal is not None: spec['feed'] = feedFromCalId(self.conf, cal) @@ -116,7 +116,7 @@ log.info(f'cleared {n} records before reread') return idsFormerlyInRange - def updateOneCal(self, start, end, idsFormerlyInRange, totalNew, currentRecords, calId): + def _updateOneCal(self, start, end, idsFormerlyInRange, totalNew, currentRecords, calId): print('read %s' % calId) events = self.service.events().list( calendarId=calId, @@ -128,13 +128,13 @@ ).execute() for ev in events['items']: - rec = recordFromEv(self.conf, calId, ev) - self.upsertMongo(rec) + rec = _recordFromEv(self.conf, calId, ev) + self._upsertMongo(rec) if rec['uri'] not in idsFormerlyInRange: totalNew += 1 currentRecords.append(rec) - def upsertMongo(self, rec: Record) -> List[Record]: + def _upsertMongo(self, rec: Record) -> List[Record]: if self.collection.find_one({"_id": rec['uri']}) is not None: log.debug("existing record %s", rec['uri']) # this is not yet noticing updates