From ae2e33ebcf1e78f6d3b1174625ba71e6aaa915a5 Mon Sep 17 00:00:00 2001
From: Jeremy Fee <jmfee@usgs.gov>
Date: Thu, 12 May 2016 15:43:03 -0600
Subject: [PATCH] Implement common TimeseriesFactory._put_timeseries method,
 integrate into IAGA factory

---
 geomagio/TimeseriesFactory.py              | 78 ++++++++++++++++-
 geomagio/iaga2002/IAGA2002Factory.py       | 98 ++++------------------
 test/iaga2002_test/IAGA2002Factory_test.py | 24 ------
 3 files changed, 90 insertions(+), 110 deletions(-)
 delete mode 100644 test/iaga2002_test/IAGA2002Factory_test.py

diff --git a/geomagio/TimeseriesFactory.py b/geomagio/TimeseriesFactory.py
index 443b1519f..2d9ec3f81 100644
--- a/geomagio/TimeseriesFactory.py
+++ b/geomagio/TimeseriesFactory.py
@@ -84,13 +84,13 @@ class TimeseriesFactory(object):
         """
         raise NotImplementedError('"get_timeseries" not implemented')
 
-    def parse_string(self, iaga2002String):
+    def parse_string(self, data):
         """Parse the contents of a string in the format of an IAGA2002 file.
 
         Parameters
         ----------
-        iaga2002String : str
-            string containing IAGA2002 content.
+        data : str
+            string containing parsable content.
 
         Returns
         -------
@@ -129,6 +129,20 @@ class TimeseriesFactory(object):
         """
         raise NotImplementedError('"put_timeseries" not implemented')
 
+    def write_file(self, fh, timeseries, channels):
+        """Write timeseries data to the given file object.
+
+        Parameters
+        ----------
+        fh : writable
+            file handle where data is written.
+        timeseries : obspy.core.Stream
+            stream containing traces to store.
+        channels : list
+            list of channels to store.
+        """
+        raise NotImplementedError('"write_file" not implemented')
+
     def _get_file_from_url(self, url):
         """Get a file for writing.
 
@@ -387,3 +401,61 @@ class TimeseriesFactory(object):
             raise TimeseriesFactoryException(
                     'Unsupported type "%s"' % type)
         return type_name
+
+    def _put_timeseries(self, timeseries, starttime=None, endtime=None,
+            channels=None, type=None, interval=None):
+        """A basic implementation of put_timeseries using write_file.
+
+        Parameters
+        ----------
+        timeseries : obspy.core.Stream
+            stream containing traces to store.
+        starttime : UTCDateTime
+            time of first sample in timeseries to store.
+            uses first sample if unspecified.
+        endtime : UTCDateTime
+            time of last sample in timeseries to store.
+            uses last sample if unspecified.
+        channels : array_like
+            list of channels to store, optional.
+            uses default if unspecified.
+        type : {'definitive', 'provisional', 'quasi-definitive', 'variation'}
+            data type, optional.
+            uses default if unspecified.
+        interval : {'daily', 'hourly', 'minute', 'monthly', 'second'}
+            data interval, optional.
+            uses default if unspecified.
+        Raises
+        ------
+        TimeseriesFactoryException
+            if any errors occur.
+        """
+        if not self.urlTemplate.startswith('file://'):
+            raise TimeseriesFactoryException('Only file urls are supported')
+        channels = channels or self.channels
+        type = type or self.type
+        interval = interval or self.interval
+        stats = timeseries[0].stats
+        delta = stats.delta
+        observatory = stats.station
+        starttime = starttime or stats.starttime
+        endtime = endtime or stats.endtime
+
+        urlIntervals = Util.get_intervals(
+                starttime=starttime,
+                endtime=endtime,
+                size=self.urlInterval)
+        for urlInterval in urlIntervals:
+            url = self._get_url(
+                    observatory=observatory,
+                    date=urlInterval['start'],
+                    type=type,
+                    interval=interval,
+                    channels=channels)
+            url_data = timeseries.slice(
+                    starttime=urlInterval['start'],
+                    # subtract delta to omit the sample at end: `[start, end)`
+                    endtime=(urlInterval['end'] - delta))
+            url_file = Util.get_file_from_url(url, createParentDirectory=True)
+            with open(url_file, 'wb') as fh:
+                self.write_file(fh, url_data, channels)
diff --git a/geomagio/iaga2002/IAGA2002Factory.py b/geomagio/iaga2002/IAGA2002Factory.py
index 36f479385..9f17624ed 100644
--- a/geomagio/iaga2002/IAGA2002Factory.py
+++ b/geomagio/iaga2002/IAGA2002Factory.py
@@ -3,7 +3,6 @@
 import obspy.core
 from .. import ChannelConverter
 from ..TimeseriesFactory import TimeseriesFactory
-from ..TimeseriesFactoryException import TimeseriesFactoryException
 from IAGA2002Parser import IAGA2002Parser
 from IAGA2002Writer import IAGA2002Writer
 
@@ -105,54 +104,6 @@ class IAGA2002Factory(TimeseriesFactory):
             stream += obspy.core.Trace(data[channel], stats)
         return stream
 
-    def _get_days(self, starttime, endtime):
-        """Get days between (inclusive) starttime and endtime.
-
-        Parameters
-        ----------
-        starttime : obspy.core.UTCDateTime
-            the start time
-        endtime : obspy.core.UTCDateTime
-            the end time
-
-        Returns
-        -------
-        array_like
-            list of times, one per day, for all days between and including
-            ``starttime`` and ``endtime``.
-
-        Raises
-        ------
-        TimeseriesFactoryException
-            if starttime is after endtime
-        """
-        if starttime > endtime:
-            raise TimeseriesFactoryException(
-                    'starttime must be before endtime')
-        days = []
-        day = starttime
-        lastday = (endtime.year, endtime.month, endtime.day)
-        while True:
-            days.append(day)
-            if lastday == (day.year, day.month, day.day):
-                break
-            # move to next day
-            day = obspy.core.UTCDateTime(day.timestamp + 86400)
-        return days
-
-    def write_file(self, fh, timeseries, channels):
-        """writes timeseries data to the given file object.
-
-        Parameters
-        ----------
-        fh: file object
-        timeseries : obspy.core.Stream
-            stream containing traces to store.
-        channels : array_like
-            list of channels to store
-        """
-        IAGA2002Writer().write(fh, timeseries, channels)
-
     def put_timeseries(self, timeseries, starttime=None, endtime=None,
             channels=None, type=None, interval=None):
         """Store timeseries data.
@@ -177,42 +128,23 @@ class IAGA2002Factory(TimeseriesFactory):
             data interval, optional.
             uses default if unspecified.
         """
-        if not self.urlTemplate.startswith('file://'):
-            raise TimeseriesFactoryException('Only file urls are supported')
-        channels = channels or self.channels
-        type = type or self.type
-        interval = interval or self.interval
-        stats = timeseries[0].stats
-        observatory = stats.station
-        starttime = starttime or stats.starttime
-        endtime = endtime or stats.endtime
-        days = self._get_days(starttime, endtime)
-        for day in days:
-            day_filename = self._get_file_from_url(
-                    self._get_url(observatory, day, type, interval))
-            day_timeseries = self._get_slice(timeseries, day, interval)
-            with open(day_filename, 'wb') as fh:
-                self.write_file(fh, day_timeseries, channels)
-
-    def _get_slice(self, timeseries, day, interval):
-        """Get the first and last time for a day
+        return self._put_timeseries(
+                timeseries=timeseries,
+                starttime=starttime,
+                endtime=endtime,
+                channels=channels,
+                type=type,
+                interval=interval)
+
+    def write_file(self, fh, timeseries, channels):
+        """writes timeseries data to the given file object.
 
         Parameters
         ----------
+        fh: file object
         timeseries : obspy.core.Stream
-            timeseries to slice
-        day : UTCDateTime
-            time in day to slice
-
-        Returns
-        -------
-        obspy.core.Stream
-            sliced stream
+            stream containing traces to store.
+        channels : array_like
+            list of channels to store
         """
-        day = day.datetime
-        start = obspy.core.UTCDateTime(day.year, day.month, day.day, 0, 0, 0)
-        if interval == 'minute':
-            end = start + 86340.0
-        else:
-            end = start + 86399.999999
-        return timeseries.slice(start, end)
+        IAGA2002Writer().write(fh, timeseries, channels)
diff --git a/test/iaga2002_test/IAGA2002Factory_test.py b/test/iaga2002_test/IAGA2002Factory_test.py
deleted file mode 100644
index 388ef6870..000000000
--- a/test/iaga2002_test/IAGA2002Factory_test.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""Tests for IAGA2002Factory."""
-
-from geomagio.iaga2002 import IAGA2002Factory
-from obspy.core.utcdatetime import UTCDateTime
-from nose.tools import assert_equals
-
-
-def test__get_days():
-    """iaga2002_test.IAGA2002Factory_test.test__get_days()
-
-    Call the _get_days method with starttime and endtime separated by more
-    than one day.
-    Verify it returns all days between the given starttime and endtime.
-    """
-    starttime = UTCDateTime('2014-01-01')
-    endtime = UTCDateTime('2014-01-07')
-    assert_equals(IAGA2002Factory()._get_days(starttime, endtime), [
-            UTCDateTime('2014-01-01'),
-            UTCDateTime('2014-01-02'),
-            UTCDateTime('2014-01-03'),
-            UTCDateTime('2014-01-04'),
-            UTCDateTime('2014-01-05'),
-            UTCDateTime('2014-01-06'),
-            UTCDateTime('2014-01-07')])
-- 
GitLab