tonik 0.0.1__py3-none-any.whl → 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tonik/__init__.py CHANGED
@@ -1 +1 @@
1
- from .xarray2hdf5 import xarray2hdf5
1
+ from .lockerroom import Locker, LockerRoom
tonik/lockerroom.py ADDED
@@ -0,0 +1,290 @@
1
+ from datetime import datetime, timedelta
2
+ import logging
3
+ import logging.config
4
+ import os
5
+ import re
6
+ import tempfile
7
+
8
+ import pandas as pd
9
+ import xarray as xr
10
+
11
+ from .xarray2hdf5 import xarray2hdf5
12
+
13
+
14
+ ERROR_LOG_FILENAME = "tonik.log"
15
+
16
+ LOGGING_CONFIG = {
17
+ "version": 1,
18
+ "disable_existing_loggers": False,
19
+ "formatters": {
20
+ "default": { # The formatter name, it can be anything that I wish
21
+ "format": "%(asctime)s:%(name)s:%(process)d:%(lineno)d " "%(levelname)s %(message)s", # What to add in the message
22
+ "datefmt": "%Y-%m-%d %H:%M:%S", # How to display dates
23
+ },
24
+ "json": { # The formatter name
25
+ "()": "pythonjsonlogger.jsonlogger.JsonFormatter", # The class to instantiate!
26
+ # Json is more complex, but easier to read, display all attributes!
27
+ "format": """
28
+ asctime: %(asctime)s
29
+ created: %(created)f
30
+ filename: %(filename)s
31
+ funcName: %(funcName)s
32
+ levelname: %(levelname)s
33
+ levelno: %(levelno)s
34
+ lineno: %(lineno)d
35
+ message: %(message)s
36
+ module: %(module)s
37
+ msec: %(msecs)d
38
+ name: %(name)s
39
+ pathname: %(pathname)s
40
+ process: %(process)d
41
+ processName: %(processName)s
42
+ relativeCreated: %(relativeCreated)d
43
+ thread: %(thread)d
44
+ threadName: %(threadName)s
45
+ exc_info: %(exc_info)s
46
+ """,
47
+ "datefmt": "%Y-%m-%d %H:%M:%S", # How to display dates
48
+ },
49
+ },
50
+ "handlers": {
51
+ "logfile": { # The handler name
52
+ "formatter": "json", # Refer to the formatter defined above
53
+ "level": "ERROR", # FILTER: Only ERROR and CRITICAL logs
54
+ "class": "logging.handlers.RotatingFileHandler", # OUTPUT: Which class to use
55
+ "filename": ERROR_LOG_FILENAME, # Param for class above. Defines filename to use, load it from constant
56
+ "backupCount": 2, # Param for class above. Defines how many log files to keep as it grows
57
+ },
58
+ "simple": { # The handler name
59
+ "formatter": "default", # Refer to the formatter defined above
60
+ "class": "logging.StreamHandler", # OUTPUT: Same as above, stream to console
61
+ "stream": "ext://sys.stdout",
62
+ },
63
+ },
64
+ "loggers": {
65
+ "zizou": { # The name of the logger, this SHOULD match your module!
66
+ "level": "DEBUG", # FILTER: only INFO logs onwards from "tryceratops" logger
67
+ "handlers": [
68
+ "simple", # Refer the handler defined above
69
+ ],
70
+ },
71
+ },
72
+ "root": {
73
+ "level": "ERROR", # FILTER: only INFO logs onwards
74
+ "handlers": [
75
+ "logfile", # Refer the handler defined above
76
+ ]
77
+ },
78
+ }
79
+
80
+ logging.config.dictConfig(LOGGING_CONFIG)
81
+ logger = logging.getLogger("__name__")
82
+
83
+
84
+ class LockerRoom:
85
+ """
86
+ Query computed features
87
+
88
+ :param rootdir: Path to parent directory.
89
+ :type rootdir: str
90
+ :param starttime: Begin of request
91
+ :type starttime: :class:`datetime.datetime`
92
+ :param endtime: Begin of request
93
+ :type endtime: :class:`datetime.datetime`
94
+
95
+ >>> import datetime
96
+ >>> fq = FeatureRequest()
97
+ >>> start = datetime.datetime(2012,1,1,0,0,0)
98
+ >>> end = datetime.datetime(2012,1,2,23,59,59)
99
+ >>> group = 'Whakaari'
100
+ >>> site = 'WIZ'
101
+ >>> chan = 'HHZ'
102
+ >>> fq.group = group
103
+ >>> fq.starttime = start
104
+ >>> fq.endtime = end
105
+ >>> fq.site = site
106
+ >>> fq.channel = chan
107
+ >>> rsam = fq("rsam")
108
+ """
109
+ def __init__(self, group, rootdir=tempfile.gettempdir(),
110
+ starttime=None, endtime=None):
111
+ self.groupdir = os.path.join(rootdir, group)
112
+ self.lockers = {}
113
+
114
+ def get_locker(self, site, location, channel):
115
+ key = (site, location, channel)
116
+ if key not in self.lockers:
117
+ self.lockers[key] = Locker(site, location, channel, rootdir=self.groupdir)
118
+ return self.lockers[key]
119
+
120
+ def __repr__(self):
121
+ rstr = f"LockerRoom: {self.groupdir}\n"
122
+ for site, location, channel in self.lockers.keys():
123
+ rstr += f"Site: {site}, Location: {location}, Channel: {channel}\n"
124
+ return rstr
125
+
126
+ def get_starttime(self):
127
+ return self.__starttime
128
+
129
+ def set_starttime(self, time):
130
+ if time is None:
131
+ self.__starttime = None
132
+ self.__sdate = None
133
+ return
134
+ self.__starttime = time
135
+ self.__sdate = '{}{:02d}{:02d}'.format(time.year,
136
+ time.month,
137
+ time.day)
138
+ for key, locker in self.lockers.items():
139
+ locker.starttime = time
140
+
141
+ def get_endtime(self):
142
+ return self.__endtime
143
+
144
+ def set_endtime(self, time):
145
+ if time is None:
146
+ self.__endtime = None
147
+ self.__edate = None
148
+ return
149
+ self.__endtime = time
150
+ self.__edate = '{}{:02d}{:02d}'.format(time.year,
151
+ time.month,
152
+ time.day)
153
+ for key, locker in self.lockers.items():
154
+ locker.endtime = time
155
+
156
+ starttime = property(get_starttime, set_starttime)
157
+ endtime = property(get_endtime, set_endtime)
158
+
159
+
160
+ class Locker:
161
+ def __init__(self, site=None, location=None, channel=None,
162
+ rootdir=None, starttime=None, endtime=None,
163
+ interval='10min'):
164
+
165
+ self.site = site
166
+ self.location = location
167
+ self.channel = channel
168
+ self.starttime = starttime
169
+ self.endtime = endtime
170
+ self.rootdir = rootdir
171
+ self.interval = interval
172
+
173
+ def __call__(self, feature, stack_length=None):
174
+ """
175
+ Request a particular feature
176
+
177
+ :param feature: Feature name
178
+ :type feature: str
179
+ :param stack_length: length of moving average in time
180
+ :type stack_length: str
181
+
182
+ """
183
+ if self.endtime <= self.starttime:
184
+ raise ValueError('Startime has to be smaller than endtime.')
185
+
186
+ feature = feature.lower()
187
+ filename = os.path.join(self.sitedir, '%s.nc' % feature)
188
+ if not os.path.isfile(filename):
189
+ raise ValueError('Feature {} does not exist.'.format(feature))
190
+
191
+ logger.debug(f"Reading feature {feature} between {self.starttime} and {self.endtime}")
192
+ num_periods = None
193
+ if stack_length is not None:
194
+ valid_stack_units = ['W', 'D', 'H', 'T', 'min', 'S']
195
+ if not re.match(r'\d*\s*(\w*)', stack_length).group(1)\
196
+ in valid_stack_units:
197
+ raise ValueError(
198
+ 'Stack length should be one of: {}'.
199
+ format(', '.join(valid_stack_units))
200
+ )
201
+
202
+ if pd.to_timedelta(stack_length) < pd.to_timedelta(self.interval):
203
+ raise ValueError('Stack length {} is less than interval {}'.
204
+ format(stack_length, self.interval))
205
+
206
+ # Rewind starttime to account for stack length
207
+ self.starttime -= pd.to_timedelta(stack_length)
208
+
209
+ num_periods = (pd.to_timedelta(stack_length)/
210
+ pd.to_timedelta(self.interval))
211
+ if not num_periods.is_integer():
212
+ raise ValueError(
213
+ 'Stack length {} / interval {} = {}, but it needs'
214
+ ' to be a whole number'.
215
+ format(stack_length, self.interval, num_periods))
216
+
217
+ xd_index = dict(datetime=slice(self.starttime,
218
+ (self.endtime-
219
+ pd.to_timedelta(self.interval))))
220
+ with xr.open_dataset(filename, group='original', engine='h5netcdf') as ds:
221
+ ds.sortby("datetime")
222
+ rq = ds.loc[xd_index].load()
223
+
224
+ # Stack features
225
+ if stack_length is not None:
226
+ logger.debug("Stacking feature...")
227
+ try:
228
+ xdf = rq[feature].rolling(datetime=int(num_periods),
229
+ center=False,
230
+ min_periods=1).mean()
231
+ # Return requested timeframe to that defined in initialisation
232
+ self.starttime += pd.to_timedelta(stack_length)
233
+ xdf_new = xdf.loc[
234
+ self.starttime:
235
+ self.endtime-pd.to_timedelta(self.interval)]
236
+ xdf_new = xdf_new.rename(feature)
237
+ except ValueError as e:
238
+ logger.error(e)
239
+ logger.error('Stack length {} is not valid for feature {}'.
240
+ format(stack_length, feature))
241
+ else:
242
+ return xdf_new
243
+
244
+ return rq[feature]
245
+
246
+ def get_site(self):
247
+ return self.__site
248
+
249
+ def set_site(self, value):
250
+ self.__site = value
251
+
252
+ def get_location(self):
253
+ return self.__location
254
+
255
+ def set_location(self, value):
256
+ self.__location = value
257
+
258
+ def get_channel(self):
259
+ return self.__channel
260
+
261
+ def set_channel(self, value):
262
+ self.__channel = value
263
+
264
+ @property
265
+ def sitedir(self):
266
+ try:
267
+ __sdir = os.path.join(self.rootdir,
268
+ self.site,
269
+ self.location,
270
+ self.channel)
271
+ os.makedirs(__sdir, exist_ok=True)
272
+ return __sdir
273
+ except TypeError:
274
+ return None
275
+
276
+ site = property(get_site, set_site)
277
+ location = property(get_location, set_location)
278
+ channel = property(get_channel, set_channel)
279
+
280
+ def load(self, *args, **kwargs):
281
+ """
282
+ Load a feature from disk
283
+ """
284
+ self.__call__(*args, **kwargs)
285
+
286
+ def save(self, data):
287
+ """
288
+ Save a feature to disk
289
+ """
290
+ xarray2hdf5(data, self.sitedir)
tonik/xarray2hdf5.py CHANGED
@@ -3,10 +3,9 @@ import logging
3
3
  import os
4
4
  from warnings import filterwarnings
5
5
 
6
- from cftime import num2date, date2num, date2index
6
+ from cftime import num2date, date2num
7
7
  import h5netcdf
8
8
  import numpy as np
9
- import xarray as xr
10
9
 
11
10
 
12
11
  def xarray2hdf5(xArray, fdir, rootGroupName="original", timedim="datetime"):
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tonik
3
- Version: 0.0.1
3
+ Version: 0.0.2
4
4
  Summary: A collection of tools to integrate with GNS Science's time series classification platform.
5
- Project-URL: Homepage, https://github.com/tsc-tools/tonik
5
+ Project-URL: Homepage, https://tsc-tools.github.io/tonik.github.io
6
6
  Project-URL: Issues, https://github.com/tsc-tools/tonik/issues
7
7
  Author-email: Yannik Behr <y.behr@gns.cri.nz>, Christof Mueller <c.mueller@gns.cri.nz>
8
8
  License-File: LICENSE
@@ -15,6 +15,7 @@ Requires-Dist: h5py
15
15
  Requires-Dist: netcdf4
16
16
  Requires-Dist: pandas
17
17
  Requires-Dist: pytest
18
+ Requires-Dist: python-json-logger
18
19
  Requires-Dist: xarray
19
20
  Description-Content-Type: text/markdown
20
21
 
@@ -0,0 +1,7 @@
1
+ tonik/__init__.py,sha256=hv2lVOfFLABQo5KsLFoA439lSst0asGuQYiAK0tZvEw,42
2
+ tonik/lockerroom.py,sha256=6OOkf68UNQZrGLfnOo9bJsaeb-5PPH0NDGwCAdDzlqg,10162
3
+ tonik/xarray2hdf5.py,sha256=biQ3KVt0QrxJhOWm38FwglzYkLhPtO13G1B1vVF2c6o,4090
4
+ tonik-0.0.2.dist-info/METADATA,sha256=OBRXcvVKJwc5_Qv5MpXdDg0tC8Sg4f4N90yxwud57oo,916
5
+ tonik-0.0.2.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
6
+ tonik-0.0.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
7
+ tonik-0.0.2.dist-info/RECORD,,
@@ -1,6 +0,0 @@
1
- tonik/__init__.py,sha256=d7gnshn92xGI-U7YTs7Q5cMWjvLW45m-EXJ5IcYaZrs,36
2
- tonik/xarray2hdf5.py,sha256=sImRJ80EQ3yI_7xJg34VfS8SSIzkDtRHda3Mg959xPs,4122
3
- tonik-0.0.1.dist-info/METADATA,sha256=KS91xVocqwwtwLKprecpfp0JU15s-1xERkMqKBSpPS4,873
4
- tonik-0.0.1.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
5
- tonik-0.0.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
6
- tonik-0.0.1.dist-info/RECORD,,
File without changes