From 7991bd1cf3d8eb4e079d4ebf124866393eee968d Mon Sep 17 00:00:00 2001
From: "Cain, Payton David" <pcain@usgs.gov>
Date: Fri, 20 Aug 2021 20:12:34 +0000
Subject: [PATCH 1/6] _get_empty_channels method for ts factories

---
 geomagio/DerivedTimeseriesFactory.py | 29 +-------------
 geomagio/TimeseriesFactory.py        | 27 +++++++++++++
 geomagio/edge/EdgeFactory.py         | 59 ++++++++++++++++++++++------
 geomagio/edge/MiniSeedFactory.py     | 59 ++++++++++++++++++++++------
 4 files changed, 122 insertions(+), 52 deletions(-)

diff --git a/geomagio/DerivedTimeseriesFactory.py b/geomagio/DerivedTimeseriesFactory.py
index 535b8e875..b72e91f09 100644
--- a/geomagio/DerivedTimeseriesFactory.py
+++ b/geomagio/DerivedTimeseriesFactory.py
@@ -54,45 +54,18 @@ class DerivedTimeseriesFactory(TimeseriesFactory):
             )
         missing = get_missing(timeseries, channels)
         if missing and add_empty_channels:
-            timeseries += self._get_empty_channels(
+            timeseries += self.factory._get_empty_channels(
                 starttime=starttime,
                 endtime=endtime,
                 observatory=observatory,
                 channels=channels,
                 data_type=type,
                 interval=interval,
-                location=timeseries[0].stats.location,
             )
         # file-based factories return all channels found in file
         timeseries = Stream([t for t in timeseries if t.stats.channel in channels])
         return timeseries
 
-    def _get_empty_channels(
-        self,
-        starttime: UTCDateTime,
-        endtime: UTCDateTime,
-        observatory: str,
-        channels: List[str],
-        data_type: str,
-        interval: str,
-        location: str,
-    ) -> Stream:
-        """create empty channels"""
-        output_stream = Stream()
-        for channel in channels:
-            output_stream += TimeseriesUtility.create_empty_trace(
-                starttime=starttime,
-                endtime=endtime,
-                observatory=observatory,
-                channel=channel,
-                type=data_type,
-                interval=interval,
-                network="NT",
-                station=observatory,
-                location=location,
-            )
-        return output_stream
-
     def _get_derived_channels(
         self,
         starttime: UTCDateTime,
diff --git a/geomagio/TimeseriesFactory.py b/geomagio/TimeseriesFactory.py
index 064b868d6..fdaa13a7f 100644
--- a/geomagio/TimeseriesFactory.py
+++ b/geomagio/TimeseriesFactory.py
@@ -1,5 +1,6 @@
 """Abstract Timeseries Factory Interface."""
 from __future__ import absolute_import, print_function
+from typing import List
 
 import numpy
 import obspy.core
@@ -297,6 +298,32 @@ class TimeseriesFactory(object):
         """
         raise NotImplementedError('"write_file" not implemented')
 
+    def _get_empty_channels(
+        self,
+        starttime: obspy.core.UTCDateTime,
+        endtime: obspy.core.UTCDateTime,
+        observatory: str,
+        channels: List[str],
+        data_type: str,
+        interval: str,
+        location: str = "",
+    ) -> obspy.core.Stream:
+        """creates stream with empty channels"""
+        output_stream = obspy.core.Stream()
+        for channel in channels:
+            output_stream += TimeseriesUtility.create_empty_trace(
+                starttime=starttime,
+                endtime=endtime,
+                observatory=observatory,
+                channel=channel,
+                type=data_type,
+                interval=interval,
+                network="NT",
+                station=observatory,
+                location=location,
+            )
+        return output_stream
+
     def _get_file_from_url(self, url):
         """Get a file for writing.
 
diff --git a/geomagio/edge/EdgeFactory.py b/geomagio/edge/EdgeFactory.py
index 5dbde248f..8ee3728e8 100644
--- a/geomagio/edge/EdgeFactory.py
+++ b/geomagio/edge/EdgeFactory.py
@@ -9,6 +9,7 @@ to take advantage of it's newer realtime abilities.
 Edge is the USGS earthquake hazard centers replacement for earthworm.
 """
 from __future__ import absolute_import
+from typing import List, Optional
 
 import sys
 import numpy
@@ -22,7 +23,7 @@ from ..TimeseriesFactory import TimeseriesFactory
 from ..TimeseriesFactoryException import TimeseriesFactoryException
 from ..ObservatoryMetadata import ObservatoryMetadata
 from .RawInputClient import RawInputClient
-from .LegacySNCL import LegacySNCL
+from .LegacySNCL import LegacySNCL, get_location
 
 
 class EdgeFactory(TimeseriesFactory):
@@ -288,6 +289,42 @@ class EdgeFactory(TimeseriesFactory):
             trace.data = numpy.ma.masked_invalid(trace.data)
         return stream
 
+    def _get_empty_channels(
+        self,
+        starttime: obspy.core.UTCDateTime,
+        endtime: obspy.core.UTCDateTime,
+        observatory: str,
+        channels: List[str],
+        data_type: str,
+        interval: str,
+        location: Optional[str] = None,
+    ) -> obspy.core.Stream:
+        """creates stream with empty channels"""
+        output_stream = obspy.core.Stream()
+        for channel in channels:
+            trace = super()._get_empty_channels(
+                starttime=starttime,
+                endtime=endtime,
+                observatory=observatory,
+                channels=(channel,),
+                data_type=data_type,
+                interval=interval,
+                location=location
+                or get_location(
+                    element=channel,
+                    data_type=data_type,
+                ),
+            )
+            self._set_metadata(
+                stream=trace,
+                observatory=observatory,
+                channel=channel,
+                type=data_type,
+                interval=interval,
+            )
+            output_stream += trace
+        return output_stream
+
     def _get_timeseries(
         self,
         starttime,
@@ -346,19 +383,17 @@ class EdgeFactory(TimeseriesFactory):
         for trace in data:
             trace.data = trace.data.astype("i4")
         data.merge()
+        self._set_metadata(data, observatory, channel, type, interval)
         if data.count() == 0 and add_empty_channels:
-            data += TimeseriesUtility.create_empty_trace(
-                starttime,
-                endtime,
-                observatory,
-                channel,
-                type,
-                interval,
-                sncl.network,
-                sncl.station,
-                sncl.location,
+            data += self._get_empty_channels(
+                starttime=starttime,
+                endtime=endtime,
+                observatory=observatory,
+                channels=(channel,),
+                data_type=type,
+                interval=interval,
+                location=sncl.location,
             )
-        self._set_metadata(data, observatory, channel, type, interval)
         return data
 
     def _post_process(self, timeseries, starttime, endtime, channels):
diff --git a/geomagio/edge/MiniSeedFactory.py b/geomagio/edge/MiniSeedFactory.py
index d708b30f4..c1e56d518 100644
--- a/geomagio/edge/MiniSeedFactory.py
+++ b/geomagio/edge/MiniSeedFactory.py
@@ -11,6 +11,7 @@ Edge is the USGS earthquake hazard centers replacement for earthworm.
 from __future__ import absolute_import
 
 import sys
+from typing import List, Optional
 import numpy
 import numpy.ma
 
@@ -23,7 +24,7 @@ from ..TimeseriesFactory import TimeseriesFactory
 from ..TimeseriesFactoryException import TimeseriesFactoryException
 from ..ObservatoryMetadata import ObservatoryMetadata
 from .MiniSeedInputClient import MiniSeedInputClient
-from .SNCL import SNCL
+from .SNCL import SNCL, get_location
 
 
 class MiniSeedFactory(TimeseriesFactory):
@@ -307,6 +308,42 @@ class MiniSeedFactory(TimeseriesFactory):
             trace.data = numpy.ma.masked_invalid(trace.data)
         return stream
 
+    def _get_empty_channels(
+        self,
+        starttime: obspy.core.UTCDateTime,
+        endtime: obspy.core.UTCDateTime,
+        observatory: str,
+        channels: List[str],
+        data_type: str,
+        interval: str,
+        location: Optional[str] = None,
+    ) -> obspy.core.Stream:
+        """creates stream with empty channels"""
+        output_stream = obspy.core.Stream()
+        for channel in channels:
+            trace = super()._get_empty_channels(
+                starttime=starttime,
+                endtime=endtime,
+                observatory=observatory,
+                channels=(channel,),
+                data_type=data_type,
+                interval=interval,
+                location=location
+                or get_location(
+                    element=channel,
+                    data_type=data_type,
+                ),
+            )
+            self._set_metadata(
+                stream=trace,
+                observatory=observatory,
+                channel=channel,
+                type=data_type,
+                interval=interval,
+            )
+            output_stream += trace
+        return output_stream
+
     def _get_timeseries(
         self,
         starttime,
@@ -352,19 +389,17 @@ class MiniSeedFactory(TimeseriesFactory):
             sncl.network, sncl.station, sncl.location, sncl.channel, starttime, endtime
         )
         data.merge()
+        self._set_metadata(data, observatory, channel, type, interval)
         if data.count() == 0 and add_empty_channels:
-            data += TimeseriesUtility.create_empty_trace(
-                starttime,
-                endtime,
-                observatory,
-                channel,
-                type,
-                interval,
-                sncl.network,
-                sncl.station,
-                sncl.location,
+            data += self._get_empty_channels(
+                starttime=starttime,
+                endtime=endtime,
+                observatory=observatory,
+                channels=(channel,),
+                data_type=type,
+                interval=interval,
+                location=sncl.location,
             )
-        self._set_metadata(data, observatory, channel, type, interval)
         return data
 
     def _convert_timeseries(
-- 
GitLab


From 1cd12fbc051e2b4c592239a99714043d36089864 Mon Sep 17 00:00:00 2001
From: "Cain, Payton David" <pcain@usgs.gov>
Date: Fri, 20 Aug 2021 21:45:51 +0000
Subject: [PATCH 2/6] Update geomagio/DerivedTimeseriesFactory.py,
 geomagio/TimeseriesFactory.py, geomagio/edge/EdgeFactory.py,
 geomagio/edge/MiniSeedFactory.py files

---
 geomagio/DerivedTimeseriesFactory.py | 17 +++----
 geomagio/TimeseriesFactory.py        | 68 ++++++++++++++++++++--------
 geomagio/edge/EdgeFactory.py         | 62 ++-----------------------
 geomagio/edge/MiniSeedFactory.py     | 62 ++-----------------------
 4 files changed, 66 insertions(+), 143 deletions(-)

diff --git a/geomagio/DerivedTimeseriesFactory.py b/geomagio/DerivedTimeseriesFactory.py
index b72e91f09..01d7a5c44 100644
--- a/geomagio/DerivedTimeseriesFactory.py
+++ b/geomagio/DerivedTimeseriesFactory.py
@@ -54,14 +54,15 @@ class DerivedTimeseriesFactory(TimeseriesFactory):
             )
         missing = get_missing(timeseries, channels)
         if missing and add_empty_channels:
-            timeseries += self.factory._get_empty_channels(
-                starttime=starttime,
-                endtime=endtime,
-                observatory=observatory,
-                channels=channels,
-                data_type=type,
-                interval=interval,
-            )
+            for channel in missing:
+                timeseries += self.factory._get_empty_trace(
+                    starttime=starttime,
+                    endtime=endtime,
+                    observatory=observatory,
+                    channel=channel,
+                    data_type=type,
+                    interval=interval,
+                )
         # file-based factories return all channels found in file
         timeseries = Stream([t for t in timeseries if t.stats.channel in channels])
         return timeseries
diff --git a/geomagio/TimeseriesFactory.py b/geomagio/TimeseriesFactory.py
index fdaa13a7f..21fd3b37a 100644
--- a/geomagio/TimeseriesFactory.py
+++ b/geomagio/TimeseriesFactory.py
@@ -1,6 +1,5 @@
 """Abstract Timeseries Factory Interface."""
 from __future__ import absolute_import, print_function
-from typing import List
 
 import numpy
 import obspy.core
@@ -298,31 +297,37 @@ class TimeseriesFactory(object):
         """
         raise NotImplementedError('"write_file" not implemented')
 
-    def _get_empty_channels(
+    def _get_empty_trace(
         self,
         starttime: obspy.core.UTCDateTime,
         endtime: obspy.core.UTCDateTime,
         observatory: str,
-        channels: List[str],
+        channel: str,
         data_type: str,
         interval: str,
+        network: str = "NT",
         location: str = "",
-    ) -> obspy.core.Stream:
-        """creates stream with empty channels"""
-        output_stream = obspy.core.Stream()
-        for channel in channels:
-            output_stream += TimeseriesUtility.create_empty_trace(
-                starttime=starttime,
-                endtime=endtime,
-                observatory=observatory,
-                channel=channel,
-                type=data_type,
-                interval=interval,
-                network="NT",
-                station=observatory,
-                location=location,
-            )
-        return output_stream
+    ) -> obspy.core.Trace:
+        """creates empty trace"""
+        trace = TimeseriesUtility.create_empty_trace(
+            starttime=starttime,
+            endtime=endtime,
+            observatory=observatory,
+            channel=channel,
+            type=data_type,
+            interval=interval,
+            station=observatory,
+            network=network,
+            location=location,
+        )
+        self._set_metadata(
+            stream=[trace],
+            observatory=observatory,
+            channel=channel,
+            type=data_type,
+            interval=interval,
+        )
+        return trace
 
     def _get_file_from_url(self, url):
         """Get a file for writing.
@@ -532,3 +537,28 @@ class TimeseriesFactory(object):
         else:
             raise TimeseriesFactoryException('Unsupported type "%s"' % type)
         return type_name
+
+    def _set_metadata(
+        self,
+        stream: obspy.core.Trace,
+        observatory: str,
+        channel: str,
+        type: str,
+        interval: str,
+    ):
+        """set metadata for a given stream/channel
+        Parameters
+        ----------
+        observatory
+            observatory code
+        channel
+            edge channel code {MVH, MVE, MVD, ...}
+        type
+            data type {definitive, quasi-definitive, variation}
+        interval
+            interval length {minute, second}
+        """
+        for trace in stream:
+            self.observatoryMetadata.set_metadata(
+                trace.stats, observatory, channel, type, interval
+            )
diff --git a/geomagio/edge/EdgeFactory.py b/geomagio/edge/EdgeFactory.py
index 8ee3728e8..e02b68982 100644
--- a/geomagio/edge/EdgeFactory.py
+++ b/geomagio/edge/EdgeFactory.py
@@ -9,7 +9,7 @@ to take advantage of it's newer realtime abilities.
 Edge is the USGS earthquake hazard centers replacement for earthworm.
 """
 from __future__ import absolute_import
-from typing import List, Optional
+from typing import Optional
 
 import sys
 import numpy
@@ -289,42 +289,6 @@ class EdgeFactory(TimeseriesFactory):
             trace.data = numpy.ma.masked_invalid(trace.data)
         return stream
 
-    def _get_empty_channels(
-        self,
-        starttime: obspy.core.UTCDateTime,
-        endtime: obspy.core.UTCDateTime,
-        observatory: str,
-        channels: List[str],
-        data_type: str,
-        interval: str,
-        location: Optional[str] = None,
-    ) -> obspy.core.Stream:
-        """creates stream with empty channels"""
-        output_stream = obspy.core.Stream()
-        for channel in channels:
-            trace = super()._get_empty_channels(
-                starttime=starttime,
-                endtime=endtime,
-                observatory=observatory,
-                channels=(channel,),
-                data_type=data_type,
-                interval=interval,
-                location=location
-                or get_location(
-                    element=channel,
-                    data_type=data_type,
-                ),
-            )
-            self._set_metadata(
-                stream=trace,
-                observatory=observatory,
-                channel=channel,
-                type=data_type,
-                interval=interval,
-            )
-            output_stream += trace
-        return output_stream
-
     def _get_timeseries(
         self,
         starttime,
@@ -385,13 +349,14 @@ class EdgeFactory(TimeseriesFactory):
         data.merge()
         self._set_metadata(data, observatory, channel, type, interval)
         if data.count() == 0 and add_empty_channels:
-            data += self._get_empty_channels(
+            data += self._get_empty_trace(
                 starttime=starttime,
                 endtime=endtime,
                 observatory=observatory,
-                channels=(channel,),
+                channel=channel,
                 data_type=type,
                 interval=interval,
+                network=sncl.network,
                 location=sncl.location,
             )
         return data
@@ -503,22 +468,3 @@ class EdgeFactory(TimeseriesFactory):
         if self.forceout:
             ric.forceout()
         ric.close()
-
-    def _set_metadata(self, stream, observatory, channel, type, interval):
-        """set metadata for a given stream/channel
-        Parameters
-        ----------
-        observatory : str
-            observatory code
-        channel : str
-            edge channel code {MVH, MVE, MVD, ...}
-        type : str
-            data type {definitive, quasi-definitive, variation}
-        interval : str
-            interval length {minute, second}
-        """
-
-        for trace in stream:
-            self.observatoryMetadata.set_metadata(
-                trace.stats, observatory, channel, type, interval
-            )
diff --git a/geomagio/edge/MiniSeedFactory.py b/geomagio/edge/MiniSeedFactory.py
index c1e56d518..1b0684373 100644
--- a/geomagio/edge/MiniSeedFactory.py
+++ b/geomagio/edge/MiniSeedFactory.py
@@ -11,7 +11,7 @@ Edge is the USGS earthquake hazard centers replacement for earthworm.
 from __future__ import absolute_import
 
 import sys
-from typing import List, Optional
+from typing import Optional
 import numpy
 import numpy.ma
 
@@ -308,42 +308,6 @@ class MiniSeedFactory(TimeseriesFactory):
             trace.data = numpy.ma.masked_invalid(trace.data)
         return stream
 
-    def _get_empty_channels(
-        self,
-        starttime: obspy.core.UTCDateTime,
-        endtime: obspy.core.UTCDateTime,
-        observatory: str,
-        channels: List[str],
-        data_type: str,
-        interval: str,
-        location: Optional[str] = None,
-    ) -> obspy.core.Stream:
-        """creates stream with empty channels"""
-        output_stream = obspy.core.Stream()
-        for channel in channels:
-            trace = super()._get_empty_channels(
-                starttime=starttime,
-                endtime=endtime,
-                observatory=observatory,
-                channels=(channel,),
-                data_type=data_type,
-                interval=interval,
-                location=location
-                or get_location(
-                    element=channel,
-                    data_type=data_type,
-                ),
-            )
-            self._set_metadata(
-                stream=trace,
-                observatory=observatory,
-                channel=channel,
-                type=data_type,
-                interval=interval,
-            )
-            output_stream += trace
-        return output_stream
-
     def _get_timeseries(
         self,
         starttime,
@@ -391,13 +355,14 @@ class MiniSeedFactory(TimeseriesFactory):
         data.merge()
         self._set_metadata(data, observatory, channel, type, interval)
         if data.count() == 0 and add_empty_channels:
-            data += self._get_empty_channels(
+            data += self._get_empty_trace(
                 starttime=starttime,
                 endtime=endtime,
                 observatory=observatory,
-                channels=(channel,),
+                channel=channel,
                 data_type=type,
                 interval=interval,
+                network=sncl.network,
                 location=sncl.location,
             )
         return data
@@ -535,22 +500,3 @@ class MiniSeedFactory(TimeseriesFactory):
             trace.stats.channel = sncl.channel
         # finally, send to edge
         self.write_client.send(to_write)
-
-    def _set_metadata(self, stream, observatory, channel, type, interval):
-        """set metadata for a given stream/channel
-        Parameters
-        ----------
-        observatory : str
-            observatory code
-        channel : str
-            edge channel code {MVH, MVE, MVD, ...}
-        type : str
-            data type {definitive, quasi-definitive, variation}
-        interval : str
-            interval length {'day', 'hour', 'minute', 'second', 'tenhertz'}
-        """
-
-        for trace in stream:
-            self.observatoryMetadata.set_metadata(
-                trace.stats, observatory, channel, type, interval
-            )
-- 
GitLab


From cc5e8aa703266c64213032bfab5b1e12e5b09273 Mon Sep 17 00:00:00 2001
From: pcain <pcain@usgs.gov>
Date: Mon, 23 Aug 2021 15:14:11 +0000
Subject: [PATCH 3/6] _set_metadata as placeholder is TSFactory

---
 geomagio/DerivedTimeseriesFactory.py |  8 ++++++++
 geomagio/TimeseriesFactory.py        | 12 +----------
 geomagio/edge/EdgeFactory.py         | 30 +++++++++++++++++++++++++---
 geomagio/edge/MiniSeedFactory.py     | 30 +++++++++++++++++++++++++---
 4 files changed, 63 insertions(+), 17 deletions(-)

diff --git a/geomagio/DerivedTimeseriesFactory.py b/geomagio/DerivedTimeseriesFactory.py
index 01d7a5c44..c2a4fca2b 100644
--- a/geomagio/DerivedTimeseriesFactory.py
+++ b/geomagio/DerivedTimeseriesFactory.py
@@ -63,6 +63,14 @@ class DerivedTimeseriesFactory(TimeseriesFactory):
                     data_type=type,
                     interval=interval,
                 )
+        for channel in channels:
+            self.factory._set_metadata(
+                stream=timeseries.select(channel=channel),
+                observatory=observatory,
+                channel=channel,
+                type=type,
+                interval=interval,
+            )
         # file-based factories return all channels found in file
         timeseries = Stream([t for t in timeseries if t.stats.channel in channels])
         return timeseries
diff --git a/geomagio/TimeseriesFactory.py b/geomagio/TimeseriesFactory.py
index 21fd3b37a..fe4e63cb4 100644
--- a/geomagio/TimeseriesFactory.py
+++ b/geomagio/TimeseriesFactory.py
@@ -320,13 +320,6 @@ class TimeseriesFactory(object):
             network=network,
             location=location,
         )
-        self._set_metadata(
-            stream=[trace],
-            observatory=observatory,
-            channel=channel,
-            type=data_type,
-            interval=interval,
-        )
         return trace
 
     def _get_file_from_url(self, url):
@@ -558,7 +551,4 @@ class TimeseriesFactory(object):
         interval
             interval length {minute, second}
         """
-        for trace in stream:
-            self.observatoryMetadata.set_metadata(
-                trace.stats, observatory, channel, type, interval
-            )
+        return stream
diff --git a/geomagio/edge/EdgeFactory.py b/geomagio/edge/EdgeFactory.py
index e02b68982..43ad19524 100644
--- a/geomagio/edge/EdgeFactory.py
+++ b/geomagio/edge/EdgeFactory.py
@@ -9,7 +9,6 @@ to take advantage of it's newer realtime abilities.
 Edge is the USGS earthquake hazard centers replacement for earthworm.
 """
 from __future__ import absolute_import
-from typing import Optional
 
 import sys
 import numpy
@@ -23,7 +22,7 @@ from ..TimeseriesFactory import TimeseriesFactory
 from ..TimeseriesFactoryException import TimeseriesFactoryException
 from ..ObservatoryMetadata import ObservatoryMetadata
 from .RawInputClient import RawInputClient
-from .LegacySNCL import LegacySNCL, get_location
+from .LegacySNCL import LegacySNCL
 
 
 class EdgeFactory(TimeseriesFactory):
@@ -347,7 +346,6 @@ class EdgeFactory(TimeseriesFactory):
         for trace in data:
             trace.data = trace.data.astype("i4")
         data.merge()
-        self._set_metadata(data, observatory, channel, type, interval)
         if data.count() == 0 and add_empty_channels:
             data += self._get_empty_trace(
                 starttime=starttime,
@@ -359,6 +357,7 @@ class EdgeFactory(TimeseriesFactory):
                 network=sncl.network,
                 location=sncl.location,
             )
+        self._set_metadata(data, observatory, channel, type, interval)
         return data
 
     def _post_process(self, timeseries, starttime, endtime, channels):
@@ -468,3 +467,28 @@ class EdgeFactory(TimeseriesFactory):
         if self.forceout:
             ric.forceout()
         ric.close()
+
+    def _set_metadata(
+        self,
+        stream: obspy.core.Trace,
+        observatory: str,
+        channel: str,
+        type: str,
+        interval: str,
+    ):
+        """set metadata for a given stream/channel
+        Parameters
+        ----------
+        observatory
+            observatory code
+        channel
+            edge channel code {MVH, MVE, MVD, ...}
+        type
+            data type {definitive, quasi-definitive, variation}
+        interval
+            interval length {minute, second}
+        """
+        for trace in stream:
+            self.observatoryMetadata.set_metadata(
+                trace.stats, observatory, channel, type, interval
+            )
diff --git a/geomagio/edge/MiniSeedFactory.py b/geomagio/edge/MiniSeedFactory.py
index 1b0684373..7208e36e1 100644
--- a/geomagio/edge/MiniSeedFactory.py
+++ b/geomagio/edge/MiniSeedFactory.py
@@ -11,7 +11,6 @@ Edge is the USGS earthquake hazard centers replacement for earthworm.
 from __future__ import absolute_import
 
 import sys
-from typing import Optional
 import numpy
 import numpy.ma
 
@@ -24,7 +23,7 @@ from ..TimeseriesFactory import TimeseriesFactory
 from ..TimeseriesFactoryException import TimeseriesFactoryException
 from ..ObservatoryMetadata import ObservatoryMetadata
 from .MiniSeedInputClient import MiniSeedInputClient
-from .SNCL import SNCL, get_location
+from .SNCL import SNCL
 
 
 class MiniSeedFactory(TimeseriesFactory):
@@ -353,7 +352,6 @@ class MiniSeedFactory(TimeseriesFactory):
             sncl.network, sncl.station, sncl.location, sncl.channel, starttime, endtime
         )
         data.merge()
-        self._set_metadata(data, observatory, channel, type, interval)
         if data.count() == 0 and add_empty_channels:
             data += self._get_empty_trace(
                 starttime=starttime,
@@ -365,6 +363,7 @@ class MiniSeedFactory(TimeseriesFactory):
                 network=sncl.network,
                 location=sncl.location,
             )
+        self._set_metadata(data, observatory, channel, type, interval)
         return data
 
     def _convert_timeseries(
@@ -500,3 +499,28 @@ class MiniSeedFactory(TimeseriesFactory):
             trace.stats.channel = sncl.channel
         # finally, send to edge
         self.write_client.send(to_write)
+
+    def _set_metadata(
+        self,
+        stream: obspy.core.Trace,
+        observatory: str,
+        channel: str,
+        type: str,
+        interval: str,
+    ):
+        """set metadata for a given stream/channel
+        Parameters
+        ----------
+        observatory
+            observatory code
+        channel
+            edge channel code {MVH, MVE, MVD, ...}
+        type
+            data type {definitive, quasi-definitive, variation}
+        interval
+            interval length {minute, second}
+        """
+        for trace in stream:
+            self.observatoryMetadata.set_metadata(
+                trace.stats, observatory, channel, type, interval
+            )
-- 
GitLab


From 5399d9606ae706928c617b9dc8159ad8ef617a05 Mon Sep 17 00:00:00 2001
From: pcain <pcain@usgs.gov>
Date: Mon, 23 Aug 2021 15:25:13 +0000
Subject: [PATCH 4/6] noop in TSFactory._set_metadata

---
 geomagio/TimeseriesFactory.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/geomagio/TimeseriesFactory.py b/geomagio/TimeseriesFactory.py
index fe4e63cb4..38b89d448 100644
--- a/geomagio/TimeseriesFactory.py
+++ b/geomagio/TimeseriesFactory.py
@@ -551,4 +551,4 @@ class TimeseriesFactory(object):
         interval
             interval length {minute, second}
         """
-        return stream
+        pass
-- 
GitLab


From 7a25e5187d85e9126b91c730f31de8ed1585e90f Mon Sep 17 00:00:00 2001
From: pcain <pcain@usgs.gov>
Date: Mon, 23 Aug 2021 16:47:47 +0000
Subject: [PATCH 5/6] stream type hint, metadata after filtering

---
 geomagio/DerivedTimeseriesFactory.py | 4 ++--
 geomagio/TimeseriesFactory.py        | 2 +-
 geomagio/edge/EdgeFactory.py         | 2 +-
 geomagio/edge/MiniSeedFactory.py     | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/geomagio/DerivedTimeseriesFactory.py b/geomagio/DerivedTimeseriesFactory.py
index c2a4fca2b..c722bf312 100644
--- a/geomagio/DerivedTimeseriesFactory.py
+++ b/geomagio/DerivedTimeseriesFactory.py
@@ -63,6 +63,8 @@ class DerivedTimeseriesFactory(TimeseriesFactory):
                     data_type=type,
                     interval=interval,
                 )
+        # file-based factories return all channels found in file
+        timeseries = Stream([t for t in timeseries if t.stats.channel in channels])
         for channel in channels:
             self.factory._set_metadata(
                 stream=timeseries.select(channel=channel),
@@ -71,8 +73,6 @@ class DerivedTimeseriesFactory(TimeseriesFactory):
                 type=type,
                 interval=interval,
             )
-        # file-based factories return all channels found in file
-        timeseries = Stream([t for t in timeseries if t.stats.channel in channels])
         return timeseries
 
     def _get_derived_channels(
diff --git a/geomagio/TimeseriesFactory.py b/geomagio/TimeseriesFactory.py
index 38b89d448..3d92f9c58 100644
--- a/geomagio/TimeseriesFactory.py
+++ b/geomagio/TimeseriesFactory.py
@@ -533,7 +533,7 @@ class TimeseriesFactory(object):
 
     def _set_metadata(
         self,
-        stream: obspy.core.Trace,
+        stream: obspy.core.Stream,
         observatory: str,
         channel: str,
         type: str,
diff --git a/geomagio/edge/EdgeFactory.py b/geomagio/edge/EdgeFactory.py
index 43ad19524..c25955fe1 100644
--- a/geomagio/edge/EdgeFactory.py
+++ b/geomagio/edge/EdgeFactory.py
@@ -470,7 +470,7 @@ class EdgeFactory(TimeseriesFactory):
 
     def _set_metadata(
         self,
-        stream: obspy.core.Trace,
+        stream: obspy.core.Stream,
         observatory: str,
         channel: str,
         type: str,
diff --git a/geomagio/edge/MiniSeedFactory.py b/geomagio/edge/MiniSeedFactory.py
index 7208e36e1..94103caee 100644
--- a/geomagio/edge/MiniSeedFactory.py
+++ b/geomagio/edge/MiniSeedFactory.py
@@ -502,7 +502,7 @@ class MiniSeedFactory(TimeseriesFactory):
 
     def _set_metadata(
         self,
-        stream: obspy.core.Trace,
+        stream: obspy.core.Stream,
         observatory: str,
         channel: str,
         type: str,
-- 
GitLab


From b3829a0f232ae8de885bc7e7bc19e3d9b29b5f06 Mon Sep 17 00:00:00 2001
From: pcain <pcain@usgs.gov>
Date: Tue, 24 Aug 2021 13:07:47 -0600
Subject: [PATCH 6/6] _set_metadata and _get_empty for derived factory

---
 geomagio/DerivedTimeseriesFactory.py | 46 ++++++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 3 deletions(-)

diff --git a/geomagio/DerivedTimeseriesFactory.py b/geomagio/DerivedTimeseriesFactory.py
index c722bf312..648a33f95 100644
--- a/geomagio/DerivedTimeseriesFactory.py
+++ b/geomagio/DerivedTimeseriesFactory.py
@@ -1,6 +1,6 @@
 from typing import List, Optional
 
-from obspy import Stream, UTCDateTime
+from obspy import Stream, Trace, UTCDateTime
 
 from .algorithm import Algorithm, DeltaFAlgorithm, XYZAlgorithm
 from .TimeseriesFactory import TimeseriesFactory, TimeseriesUtility
@@ -55,7 +55,7 @@ class DerivedTimeseriesFactory(TimeseriesFactory):
         missing = get_missing(timeseries, channels)
         if missing and add_empty_channels:
             for channel in missing:
-                timeseries += self.factory._get_empty_trace(
+                timeseries += self._get_empty_trace(
                     starttime=starttime,
                     endtime=endtime,
                     observatory=observatory,
@@ -66,7 +66,7 @@ class DerivedTimeseriesFactory(TimeseriesFactory):
         # file-based factories return all channels found in file
         timeseries = Stream([t for t in timeseries if t.stats.channel in channels])
         for channel in channels:
-            self.factory._set_metadata(
+            self._set_metadata(
                 stream=timeseries.select(channel=channel),
                 observatory=observatory,
                 channel=channel,
@@ -160,6 +160,46 @@ class DerivedTimeseriesFactory(TimeseriesFactory):
                 )
         return Stream()
 
+    def _get_empty_trace(
+        self,
+        starttime: UTCDateTime,
+        endtime: UTCDateTime,
+        observatory: str,
+        channel: str,
+        data_type: str,
+        interval: str,
+        network: str = "NT",
+        location: str = "",
+    ) -> Trace:
+        """creates empty trace"""
+        return self.factory._get_empty_trace(
+            starttime,
+            endtime,
+            observatory,
+            channel,
+            data_type,
+            interval,
+            network=network,
+            location=location,
+        )
+
+    def _set_metadata(
+        self, stream: Stream, observatory: str, channel: str, type: str, interval: str
+    ):
+        """set metadata for a given stream/channel
+        Parameters
+        ----------
+        observatory
+            observatory code
+        channel
+            edge channel code {MVH, MVE, MVD, ...}
+        type
+            data type {definitive, quasi-definitive, variation}
+        interval
+            interval length {minute, second}
+        """
+        return self.factory._set_metadata(stream, observatory, channel, type, interval)
+
 
 def get_missing(input: Stream, desired: List[str]) -> List[str]:
     """Return missing channels from input"""
-- 
GitLab