[docs]classDataSyncWatcher:""" Base class for all DataSync watchers. """prunes_changes=Falseretry_attempts=1retry_delay=1# secondsdef__init__(self,config,key,dbkey=None,**kwargs):""" Constructor. Note that while arbitrary kwargs are allowed, the default constructor method does *not* process these in any way. So if you need to accept some kwargs then you must also define the processing thereof. The primary reason for this is because these kwargs, if present, will *always* be simple strings due to how they are read from config. Therefore the burden is on each custom watcher, to interpret them as whatever data type is necessary. """self.config=configself.key=keyself.dbkey=dbkeyself.delay=1# secondsself.app=self.config.get_app()self.model=self.app.model
[docs]defsetup(self):""" This method is called when the watcher thread is first started. """
[docs]deflocalize_lastrun(self,session,lastrun):""" Calculates a timestamp using `lastrun` as the starting point, but also taking into account a possible (?) time drift between the local and "other" server. """# get current time according to "other" server, and convert to UTCbefore=self.app.make_utc()other_now=session.execute(sa.text('select current_timestamp'))\
.fetchone()[0]after=self.app.make_utc()other_now=self.app.make_utc(self.app.localtime(other_now))# get current time according to local server (i.e. Rattail), in UTClocal_now=after# drift is essentially the difference between the two timestamps. it# is meant to be a positive value (or zero) since it will be# "subtracted" from the `lastrun` time in order to obtain the timestamp# we should use for "other" queries. note that we also add one second# to the drift, just to be on the safe side.ifother_now<local_now:drift=local_now-other_nowelse:drift=datetime.timedelta(seconds=0)drift+=datetime.timedelta(seconds=1)# convert lastrun time to local timezone, for "other" querieslastrun=self.app.localtime(lastrun,tzinfo=False)# and finally, apply the driftreturnlastrun-drift
[docs]defget_changes(self,lastrun):""" This must be implemented by the subclass. It should check the source database for pending changes, and return a list of :class:`rattail.db.model.DataSyncChange` instances representing the source changes. """return[]
[docs]defprune_changes(self,keys):""" Prune change records from the source database, if relevant. """
[docs]defprocess_changes(self,session,changes):""" Process (consume) a batch of changes. """
[docs]classNullWatcher(DataSyncWatcher):""" Null watcher, will never actually check for or report any changes. """
[docs]classErrorTestWatcher(DataSyncWatcher):""" Watcher which always raises an error when attempting to get changes. Useful for testing error handling etc. """
[docs]defget_changes(self,lastrun):raiseRuntimeError("Fake exception, to test error handling")