def setSourceState(self, source: SourceUri, state: URIRef): """ add a patch to the COLLECTOR graph about the state of this source. state=None to remove the source. """ oldState = self._sourceState.get(source, None) if state == oldState: return log.info('source state %s -> %s', source, state) if oldState is None: self._sourceState[source] = state self.applyPatch( COLLECTOR, Patch(addQuads=[ (COLLECTOR, ROOM['source'], source, COLLECTOR), (source, ROOM['state'], state, COLLECTOR), ])) elif state is None: del self._sourceState[source] self.applyPatch( COLLECTOR, Patch(delQuads=[ (COLLECTOR, ROOM['source'], source, COLLECTOR), (source, ROOM['state'], oldState, COLLECTOR), ])) else: self._sourceState[source] = state self.applyPatch( COLLECTOR, Patch(addQuads=[ (source, ROOM['state'], state, COLLECTOR), ], delQuads=[ (source, ROOM['state'], oldState, COLLECTOR), ]))
def messageReceived(self, message: bytes): if message == b'PING': self.sendMessage('PONG') return log.debug("got message from %r: %s", self, message[:32]) p = Patch(jsonRepr=message.decode('utf8')) self.settings.db.patch(p, sender=self.connectionId)
def startCardRead(self, cardUri, text): self.masterGraph.patch( Patch(addQuads=[(sensor, ROOM['reading'], cardUri, ctx), (cardUri, ROOM['cardText'], text, ctx)], delQuads=[])) log.info('%s :cardText %s .', cardUri.n3(), text.n3()) self._sendOneshot([(sensor, ROOM['startReading'], cardUri), (cardUri, ROOM['cardText'], text)])
def fileGone(self) -> None: """ our file is gone; remove the statements from that context """ myQuads = [(s, p, o, self.uri) for s, p, o in self.getSubgraph(self.uri) ] log.debug("dropping all statements from context %s", self.uri) if myQuads: self.patch(Patch(delQuads=myQuads), dueToFileChange=True)
def _onStatements(self, stmts): g = self.settings.masterGraph for s, p, o in stmts: patch = g.getObjectPatch(CTX, s, p, o) if o == ROOM['unset']: patch = Patch(delQuads=patch.delQuads) g.patch(patch) nquads = g.serialize(None, format='nquads') self.settings.dbFile.setContent(nquads)
def endCardRead(self, cardUri): log.debug(f'{cardUri} has been gone for {self.expireSecs} sec') delQuads = [] for spo in self.masterGraph._graph.triples( (sensor, ROOM['reading'], cardUri)): delQuads.append(spo + (ctx, )) for spo in self.masterGraph._graph.triples( (cardUri, ROOM['cardText'], None)): delQuads.append(spo + (ctx, )) self.masterGraph.patch(Patch(addQuads=[], delQuads=delQuads))
def onMessage(self, payload, isBinary): msg = json.loads(payload) if 'connectedAs' in msg: self.connectionId = msg['connectedAs'] log.info(f'rdfdb calls us {self.connectionId}') elif 'patch' in msg: p = Patch(jsonRepr=payload.decode('utf8')) log.debug("received patch %s", p.shortSummary()) self.sg.onPatchFromDb(p) else: log.warn('unknown msg from websocket: %s...', payload[:32])
def _onFullGraph(self, message): try: g = ConjunctiveGraph() g.parse(StringInputSource(message), format='json-ld') p = Patch(addGraph=g) self._sendPatch(p, fullGraph=True) except Exception: log.error(traceback.format_exc()) raise self._fullGraphReceived = True self._fullGraphTime = time.time() self._patchesReceived += 1
def removeMappingNode(self, context, node): """ removes the statements with this node as subject or object, which is the right amount of statements to remove a node that patchMapping made. """ p = Patch(delQuads=[ spo + (context, ) for spo in chain( self._graph.triples((None, None, node), context=context), self._graph.triples((node, None, None), context=context)) ]) self.patch(p)
def patchSubgraph(self, context, newGraph): """ replace all statements in 'context' with the quads in newGraph. This is not cooperating with currentState. """ old = set( quadsWithContextUris(self._graph.quads( (None, None, None, context)))) new = set(quadsWithContextUris(newGraph)) p = Patch(delQuads=old - new, addQuads=new - old) self.patch(p) return p # for debugging
def getObjectPatch(self, context, subject, predicate, newObject): """send a patch which removes existing values for (s,p,*,c) and adds (s,p,newObject,c). Values in other graphs are not affected. newObject can be None, which will remove all (subj,pred,*) statements. """ existing = [] for spoc in quadsWithContextUris( self._graph.quads((subject, predicate, None, context))): existing.append(spoc) toAdd = ([(subject, predicate, newObject, context)] if newObject is not None else []) return Patch(delQuads=existing, addQuads=toAdd).simplify()
def update(masterGraph, eventsInGraph, coll): eventsInGraph = set() recentEvents = set() fetched_nonboring_docs = 0 for doc in coll.find({}, sort=[('t', -1)], limit=1000): uri = uriFromMongoEvent(doc['_id']) recentEvents.add(uri) if uri in eventsInGraph: fetched_nonboring_docs += 1 else: try: masterGraph.patch(Patch(addQuads=quadsForEvent(doc))) eventsInGraph.add(uri) fetched_nonboring_docs += 1 except Boring: pass if fetched_nonboring_docs > 100: break for uri in eventsInGraph.difference(recentEvents): oldStatements = list(masterGraph.quads((uri, None, None, None))) masterGraph.patch(Patch(delQuads=oldStatements)) eventsInGraph.remove(uri)
def patchMapping(self, context, subject, predicate, nodeClass, keyPred, valuePred, newKey, newValue): """ creates/updates a structure like this: ?subject ?predicate [ a ?nodeClass; ?keyPred ?newKey; ?valuePred ?newValue ] . There should be a complementary readMapping that gets you a value since that's tricky too """ # as long as currentState is expensive and has the # tripleFilter optimization, this looks like a mess. If # currentState became cheap, a lot of code here could go away. with self.currentState(tripleFilter=(subject, predicate, None)) as current: adds = set([]) for setting in current.objects(subject, predicate): with self.currentState(tripleFilter=(setting, keyPred, None)) as current2: match = current2.value(setting, keyPred) == newKey if match: break else: setting = URIRef(subject + "/map/%s" % random.randrange(999999999)) adds.update([ (subject, predicate, setting, context), (setting, RDF.type, nodeClass, context), (setting, keyPred, newKey, context), ]) with self.currentState(tripleFilter=(setting, valuePred, None)) as current: dels = set([]) for prev in current.objects(setting, valuePred): dels.add((setting, valuePred, prev, context)) adds.add((setting, valuePred, newValue, context)) if adds != dels: self.patch(Patch(delQuads=dels, addQuads=adds))
def replaceSourceStatements(self, source: SourceUri, stmts: Sequence[Statement]): log.debug('replaceSourceStatements with %s stmts', len(stmts)) newStmts = set(stmts) with self.postDeleteStatements() as garbage: for stmt, (sources, handlers) in self.table.items(): if source in sources: if stmt not in stmts: sources.remove(source) if not sources and not handlers: garbage.add(stmt) else: if stmt in stmts: sources.add(source) newStmts.discard(stmt) self.applySourcePatch(source, Patch(addQuads=newStmts, delQuads=[]))
def __init__(self, graph, ip, key): self.graph = graph self.ip, self.key = ip, key self.api = APIFactory(ip, psk=key) self.gateway = Gateway() devices_command = self.gateway.get_devices() self.devices_commands = self.api.request(devices_command) self.devices = self.api.request(self.devices_commands) self.ctx = ROOM['tradfriHub'] self.graph.patch(Patch( addQuads=[(s,p,o,self.ctx) for s,p,o in self.deviceStatements()])) self.curStmts = [] task.LoopingCall(self.updateCur).start(60) for dev in self.devices: self.startObserve(dev)
def makeSyncPatch(self, handler: PatchSink, sources: Set[SourceUri]): # todo: this could run all handlers at once, which is how we # use it anyway adds = [] dels = [] with self.postDeleteStatements() as garbage: for stmt, (stmtSources, handlers) in self.table.items(): belongsInHandler = not sources.isdisjoint(stmtSources) handlerHasIt = handler in handlers # log.debug("%s belong=%s has=%s", # abbrevStmt(stmt), belongsInHandler, handlerHasIt) if belongsInHandler and not handlerHasIt: adds.append(stmt) handlers.add(handler) elif not belongsInHandler and handlerHasIt: dels.append(stmt) handlers.remove(handler) if not handlers and not stmtSources: garbage.add(stmt) return Patch(addQuads=adds, delQuads=dels)
def addClient(self, newClient: WebsocketClient) -> None: log.info("new connection: sending all graphs to %r" % newClient) newClient.sendPatch( Patch(addQuads=self.graph.quads(ALLSTMTS), delQuads=[])) self.clients.append(newClient) stats.clients = len(self.clients)
def updateCur(self, dev=None): cur = [(s,p,o,self.ctx) for s,p,o in self.currentStateStatements([dev] if dev else self.devices)] self.graph.patch(Patch(addQuads=cur, delQuads=self.curStmts)) self.curStmts = cur
def lostRdfdbConnection(self) -> None: self.isConnected = False self.patch(Patch(delQuads=self._graph.quads())) log.info(f'cleared graph to {len(self._graph)}') log.error('graph is not updating- you need to restart') self.connectSocket()