From 34166f91860cb54688fc12ac3bf211510c4e4df5 Mon Sep 17 00:00:00 2001
From: Jeremy Fee <jmfee@usgs.gov>
Date: Thu, 1 Nov 2018 17:23:38 -0600
Subject: [PATCH] Add "get_starttime" algorithm API method, limited integration
 to controller

---
 geomagio/Controller.py                | 18 ++++++++++++------
 geomagio/algorithm/Algorithm.py       | 15 +++++++++++++++
 geomagio/algorithm/SqDistAlgorithm.py |  5 +++++
 3 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/geomagio/Controller.py b/geomagio/Controller.py
index f4e7b5aae..2703efaee 100644
--- a/geomagio/Controller.py
+++ b/geomagio/Controller.py
@@ -84,6 +84,8 @@ class Controller(object):
                     end=endtime,
                     observatory=obs,
                     channels=channels)
+            if input_start is None or input_end is None:
+                continue
             timeseries += self._inputFactory.get_timeseries(
                     observatory=obs,
                     starttime=input_start,
@@ -158,11 +160,13 @@ class Controller(object):
                 algorithm.get_input_channels()
         output_channels = options.outchannels or \
                 algorithm.get_output_channels()
+        starttime = algorithm.get_starttime() or options.starttime
+        endtime = options.endtime
         # input
         timeseries = input_timeseries or self._get_input_timeseries(
                 observatory=options.observatory,
-                starttime=options.starttime,
-                endtime=options.endtime,
+                starttime=starttime,
+                endtime=endtime,
                 channels=input_channels)
         if timeseries.count() == 0:
             return
@@ -174,8 +178,8 @@ class Controller(object):
         processed = algorithm.process(timeseries)
         # trim if --no-trim is not set
         if not options.no_trim:
-            processed.trim(starttime=options.starttime,
-                    endtime=options.endtime)
+            processed.trim(starttime=starttime,
+                    endtime=endtime)
         if options.rename_output_channel:
             processed = self._rename_channels(
                     timeseries=processed,
@@ -183,8 +187,8 @@ class Controller(object):
         # output
         self._outputFactory.put_timeseries(
                 timeseries=processed,
-                starttime=options.starttime,
-                endtime=options.endtime,
+                starttime=starttime,
+                endtime=endtime,
                 channels=output_channels)
 
     def run_as_update(self, options, update_count=0):
@@ -211,6 +215,8 @@ class Controller(object):
             if update_count >= options.update_limit:
                 return
         algorithm = self._algorithm
+        if algorithm.get_starttime() is not None:
+            raise Error('Stateful algorithms should NOT use run_as_update')
         input_channels = options.inchannels or \
                 algorithm.get_input_channels()
         output_observatory = options.output_observatory
diff --git a/geomagio/algorithm/Algorithm.py b/geomagio/algorithm/Algorithm.py
index c7410f8d6..c26a8d434 100644
--- a/geomagio/algorithm/Algorithm.py
+++ b/geomagio/algorithm/Algorithm.py
@@ -111,6 +111,21 @@ class Algorithm(object):
                 return False
         return True
 
+    def get_starttime(self):
+        """Check whether algorithm has a stateful start time.
+
+        When an algorithm reports a start time, the Controller attempts to
+        only process data moving forward.
+
+        Returns
+        -------
+        UTCDateTime:
+            Time at which Controller should start processing,
+            or None (default) if algorithm is stateless.
+        """
+        return None
+
+
     @classmethod
     def add_arguments(cls, parser):
         """Add command line arguments to argparse parser.
diff --git a/geomagio/algorithm/SqDistAlgorithm.py b/geomagio/algorithm/SqDistAlgorithm.py
index e73a8c016..088459dad 100644
--- a/geomagio/algorithm/SqDistAlgorithm.py
+++ b/geomagio/algorithm/SqDistAlgorithm.py
@@ -86,6 +86,11 @@ class SqDistAlgorithm(Algorithm):
               'reinitializing.', file=sys.stderr)
         return (start - 3 * 30 * 24 * 60 * 60, end)
 
+    def get_starttime(self):
+        """Return the next_starttime from the state, if it is set.
+        """
+        return self.next_starttime
+
     def load_state(self):
         """Load algorithm state from a file.
 
-- 
GitLab