pthelma 1.1.0__tar.gz → 2.1.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. {pthelma-1.1.0 → pthelma-2.1.0}/.github/workflows/run-tests.yml +1 -0
  2. {pthelma-1.1.0 → pthelma-2.1.0}/CHANGELOG.rst +14 -0
  3. {pthelma-1.1.0/src/pthelma.egg-info → pthelma-2.1.0}/PKG-INFO +2 -2
  4. {pthelma-1.1.0 → pthelma-2.1.0}/docs/evaporation/api.rst +1 -1
  5. {pthelma-1.1.0 → pthelma-2.1.0}/docs/haggregate/api.rst +8 -1
  6. {pthelma-1.1.0 → pthelma-2.1.0}/docs/haggregate/usage.rst +7 -6
  7. {pthelma-1.1.0 → pthelma-2.1.0}/docs/htimeseries.rst +2 -2
  8. {pthelma-1.1.0 → pthelma-2.1.0}/docs/rocc.rst +1 -1
  9. {pthelma-1.1.0 → pthelma-2.1.0}/src/enhydris_cache/cli.py +0 -2
  10. {pthelma-1.1.0 → pthelma-2.1.0}/src/evaporation/cli.py +5 -7
  11. {pthelma-1.1.0 → pthelma-2.1.0}/src/evaporation/evaporation.py +1 -1
  12. {pthelma-1.1.0 → pthelma-2.1.0}/src/haggregate/haggregate.py +6 -6
  13. {pthelma-1.1.0 → pthelma-2.1.0}/src/haggregate/regularize.c +123 -123
  14. {pthelma-1.1.0 → pthelma-2.1.0}/src/hspatial/cli.py +2 -4
  15. {pthelma-1.1.0 → pthelma-2.1.0}/src/pthelma/_version.py +2 -2
  16. {pthelma-1.1.0 → pthelma-2.1.0/src/pthelma.egg-info}/PKG-INFO +2 -2
  17. {pthelma-1.1.0 → pthelma-2.1.0}/src/rocc/calculation.c +123 -123
  18. {pthelma-1.1.0 → pthelma-2.1.0}/tests/enhydris_cache/test_cli.py +32 -22
  19. {pthelma-1.1.0 → pthelma-2.1.0}/tests/evaporation/test_cli.py +220 -197
  20. {pthelma-1.1.0 → pthelma-2.1.0}/tests/evaporation/test_evaporation.py +5 -5
  21. {pthelma-1.1.0 → pthelma-2.1.0}/tests/haggregate/test_haggregate.py +33 -15
  22. {pthelma-1.1.0 → pthelma-2.1.0}/tests/hspatial/test_cli.py +41 -31
  23. {pthelma-1.1.0 → pthelma-2.1.0}/.github/workflows/build_wheels.yml +0 -0
  24. {pthelma-1.1.0 → pthelma-2.1.0}/.gitignore +0 -0
  25. {pthelma-1.1.0 → pthelma-2.1.0}/.readthedocs.yaml +0 -0
  26. {pthelma-1.1.0 → pthelma-2.1.0}/GPL-3 +0 -0
  27. {pthelma-1.1.0 → pthelma-2.1.0}/LICENSE.rst +0 -0
  28. {pthelma-1.1.0 → pthelma-2.1.0}/MANIFEST.in +0 -0
  29. {pthelma-1.1.0 → pthelma-2.1.0}/README.rst +0 -0
  30. {pthelma-1.1.0 → pthelma-2.1.0}/docs/Makefile +0 -0
  31. {pthelma-1.1.0 → pthelma-2.1.0}/docs/changelog.rst +0 -0
  32. {pthelma-1.1.0 → pthelma-2.1.0}/docs/conf.py +0 -0
  33. {pthelma-1.1.0 → pthelma-2.1.0}/docs/enhydris_api_client.rst +0 -0
  34. {pthelma-1.1.0 → pthelma-2.1.0}/docs/enhydris_cache/api.rst +0 -0
  35. {pthelma-1.1.0 → pthelma-2.1.0}/docs/enhydris_cache/usage.rst +0 -0
  36. {pthelma-1.1.0 → pthelma-2.1.0}/docs/evaporation/usage.rst +0 -0
  37. {pthelma-1.1.0 → pthelma-2.1.0}/docs/hspatial/api.rst +0 -0
  38. {pthelma-1.1.0 → pthelma-2.1.0}/docs/hspatial/testutils.rst +0 -0
  39. {pthelma-1.1.0 → pthelma-2.1.0}/docs/hspatial/usage.rst +0 -0
  40. {pthelma-1.1.0 → pthelma-2.1.0}/docs/index.rst +0 -0
  41. {pthelma-1.1.0 → pthelma-2.1.0}/docs/install.rst +0 -0
  42. {pthelma-1.1.0 → pthelma-2.1.0}/docs/license.rst +0 -0
  43. {pthelma-1.1.0 → pthelma-2.1.0}/docs/requirements.txt +0 -0
  44. {pthelma-1.1.0 → pthelma-2.1.0}/pyproject.toml +0 -0
  45. {pthelma-1.1.0 → pthelma-2.1.0}/setup.cfg +0 -0
  46. {pthelma-1.1.0 → pthelma-2.1.0}/setup.py +0 -0
  47. {pthelma-1.1.0 → pthelma-2.1.0}/src/enhydris_api_client/__init__.py +0 -0
  48. {pthelma-1.1.0 → pthelma-2.1.0}/src/enhydris_cache/__init__.py +0 -0
  49. {pthelma-1.1.0 → pthelma-2.1.0}/src/enhydris_cache/enhydris_cache.py +0 -0
  50. {pthelma-1.1.0 → pthelma-2.1.0}/src/evaporation/__init__.py +0 -0
  51. {pthelma-1.1.0 → pthelma-2.1.0}/src/haggregate/__init__.py +0 -0
  52. {pthelma-1.1.0 → pthelma-2.1.0}/src/haggregate/cli.py +0 -0
  53. {pthelma-1.1.0 → pthelma-2.1.0}/src/haggregate/regularize.pyx +0 -0
  54. {pthelma-1.1.0 → pthelma-2.1.0}/src/hspatial/__init__.py +0 -0
  55. {pthelma-1.1.0 → pthelma-2.1.0}/src/hspatial/hspatial.py +0 -0
  56. {pthelma-1.1.0 → pthelma-2.1.0}/src/hspatial/test.py +0 -0
  57. {pthelma-1.1.0 → pthelma-2.1.0}/src/htimeseries/__init__.py +0 -0
  58. {pthelma-1.1.0 → pthelma-2.1.0}/src/htimeseries/htimeseries.py +0 -0
  59. {pthelma-1.1.0 → pthelma-2.1.0}/src/htimeseries/timezone_utils.py +0 -0
  60. {pthelma-1.1.0 → pthelma-2.1.0}/src/pthelma/__init__.py +0 -0
  61. {pthelma-1.1.0 → pthelma-2.1.0}/src/pthelma.egg-info/SOURCES.txt +0 -0
  62. {pthelma-1.1.0 → pthelma-2.1.0}/src/pthelma.egg-info/dependency_links.txt +0 -0
  63. {pthelma-1.1.0 → pthelma-2.1.0}/src/pthelma.egg-info/entry_points.txt +0 -0
  64. {pthelma-1.1.0 → pthelma-2.1.0}/src/pthelma.egg-info/requires.txt +0 -0
  65. {pthelma-1.1.0 → pthelma-2.1.0}/src/pthelma.egg-info/top_level.txt +0 -0
  66. {pthelma-1.1.0 → pthelma-2.1.0}/src/rocc/__init__.py +0 -0
  67. {pthelma-1.1.0 → pthelma-2.1.0}/src/rocc/calculation.pyx +0 -0
  68. {pthelma-1.1.0 → pthelma-2.1.0}/tests/__init__.py +0 -0
  69. {pthelma-1.1.0 → pthelma-2.1.0}/tests/enhydris_api_client/__init__.py +0 -0
  70. {pthelma-1.1.0 → pthelma-2.1.0}/tests/enhydris_api_client/test_e2e.py +0 -0
  71. {pthelma-1.1.0 → pthelma-2.1.0}/tests/enhydris_api_client/test_misc.py +0 -0
  72. {pthelma-1.1.0 → pthelma-2.1.0}/tests/enhydris_api_client/test_station.py +0 -0
  73. {pthelma-1.1.0 → pthelma-2.1.0}/tests/enhydris_api_client/test_timeseries.py +0 -0
  74. {pthelma-1.1.0 → pthelma-2.1.0}/tests/enhydris_api_client/test_timeseriesgroup.py +0 -0
  75. {pthelma-1.1.0 → pthelma-2.1.0}/tests/enhydris_api_client/test_tsdata.py +0 -0
  76. {pthelma-1.1.0 → pthelma-2.1.0}/tests/enhydris_cache/__init__.py +0 -0
  77. {pthelma-1.1.0 → pthelma-2.1.0}/tests/enhydris_cache/test_enhydris_cache.py +0 -0
  78. {pthelma-1.1.0 → pthelma-2.1.0}/tests/evaporation/__init__.py +0 -0
  79. {pthelma-1.1.0 → pthelma-2.1.0}/tests/haggregate/__init__.py +0 -0
  80. {pthelma-1.1.0 → pthelma-2.1.0}/tests/haggregate/test_cli.py +0 -0
  81. {pthelma-1.1.0 → pthelma-2.1.0}/tests/haggregate/test_regularize.py +0 -0
  82. {pthelma-1.1.0 → pthelma-2.1.0}/tests/hspatial/__init__.py +0 -0
  83. {pthelma-1.1.0 → pthelma-2.1.0}/tests/hspatial/test_hspatial.py +0 -0
  84. {pthelma-1.1.0 → pthelma-2.1.0}/tests/htimeseries/__init__.py +0 -0
  85. {pthelma-1.1.0 → pthelma-2.1.0}/tests/htimeseries/test_htimeseries.py +0 -0
  86. {pthelma-1.1.0 → pthelma-2.1.0}/tests/htimeseries/test_timezone_utils.py +0 -0
  87. {pthelma-1.1.0 → pthelma-2.1.0}/tests/rocc/__init__.py +0 -0
  88. {pthelma-1.1.0 → pthelma-2.1.0}/tests/rocc/test_rocc.py +0 -0
@@ -37,6 +37,7 @@ jobs:
37
37
 
38
38
  - name: Install Dependencies
39
39
  run: |
40
+ sudo apt-get update
40
41
  sudo apt-get install -y gdal-bin libgdal-dev postgresql-14-postgis-3
41
42
  source ~/.venv/bin/activate
42
43
  # numpy<2 is needed for gdal to contain support for gdal array
@@ -2,6 +2,20 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+
6
+ 2.1.0 (2025-01-10)
7
+ ==================
8
+
9
+ * haggregate: The ``missing_flag`` parameter to ``aggregate()`` can now
10
+ include the substring "{}", which will be replaced with the number of
11
+ missing values.
12
+
13
+ 2.0.0 (2024-12-04)
14
+ ==================
15
+
16
+ We now use "h" instead of "H", and generally we use modern pandas time
17
+ step ("frequency") designations.
18
+
5
19
  1.1.0 (2024-12-01)
6
20
  ==================
7
21
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: pthelma
3
- Version: 1.1.0
3
+ Version: 2.1.0
4
4
  Summary: Utilities for hydrological and meteorological time series processing
5
5
  Author-email: Antonis Christofides <antonis@antonischristofides.com>
6
6
  Maintainer-email: Antonis Christofides <antonis@antonischristofides.com>
@@ -44,7 +44,7 @@ evaporation API
44
44
  (negative for west or south). Only *latitude* needs to be specified
45
45
  for calculating daily evaporation.
46
46
 
47
- *time_step* is a string: "D" for daily, "H" for hourly.
47
+ *time_step* is a string: "D" for daily, "h" for hourly.
48
48
 
49
49
  In order to estimate the outgoing radiation, the ratio of incoming
50
50
  solar radiation to clear sky solar radiation is used as a
@@ -34,7 +34,14 @@ haggregate API
34
34
  than *min_count* source records corresponding, the resulting
35
35
  destination record is null; otherwise, the destination record is
36
36
  derived even though some records are missing. In that case, the flag
37
- specified by *missing_flag* is raised in the destination record.
37
+ specified by *missing_flag* is raised in the destination record. If
38
+ missing flag contains the string ``{}``, it is replaced with the
39
+ number of missing records. The recommended setting for *missing_flag*
40
+ is ``MISSING{}``, which, for example, will result in ``MISSING3``
41
+ when three records are missing. The default for *missing_flag* is
42
+ ``MISS`` for backwards compatibility (it was ``MISS`` for some years,
43
+ however this was undocumented), but this is deprecated; use
44
+ ``MISSING{}``.
38
45
 
39
46
  If an error occurs, such as *ts* not having a strictly regular step,
40
47
  :exc:`AggregateError` (or a subclass) is raised.
@@ -53,9 +53,9 @@ explanatory comments that follow it:
53
53
  loglevel = INFO
54
54
  logfile = /var/log/haggregate/haggregate.log
55
55
  base_dir = /var/cache/timeseries/
56
- target_step = 1H
56
+ target_step = 1h
57
57
  min_count = 2
58
- missing_flag = DATEINSERT
58
+ missing_flag = MISSING{}
59
59
 
60
60
  [temperature]
61
61
  source_file = temperature-10min.hts
@@ -69,7 +69,7 @@ explanatory comments that follow it:
69
69
 
70
70
  With the above configuration file, ``haggregate`` will log information
71
71
  in the file specified by ``logfile``. It will aggregate the
72
- specified time series into hourly (``1H``). The filenames specified with
72
+ specified time series into hourly (``1h``). The filenames specified with
73
73
  :option:`source_file` and :option:`target_file` are relative to
74
74
  ``base_dir``. For the temperature, source records will be
75
75
  averaged, whereas for rainfall they will be summed.
@@ -108,8 +108,8 @@ General parameters
108
108
  .. option:: target_step
109
109
 
110
110
  A string specifying the target time step, as a pandas "frequency".
111
- Examples of steps are "1D" for day, "1H" for hour, "1T" or "1min" for
112
- minute. You can also use larger multipliers, like "30T" for 30 minutes.
111
+ Examples of steps are "1D" for day, "1h" for hour, "1min" for
112
+ minute. You can also use larger multipliers, like "30min" for 30 minutes.
113
113
  The program hasn't been tested for monthly or larger time steps.
114
114
 
115
115
  .. option:: target_timestamp_offset
@@ -133,7 +133,8 @@ General parameters
133
133
  to a destination record, the resulting destination record is null;
134
134
  otherwise, the destination record is derived even though some records
135
135
  are missing. In that case, the flag specified by
136
- :option:`missing_flag` is raised in the destination record.
136
+ :option:`missing_flag` is raised in the destination record. See
137
+ :meth:`~haggregate.aggregate` for details on :option:`missing_flag`.
137
138
 
138
139
  Time series sections
139
140
  --------------------
@@ -250,8 +250,8 @@ The parameters available are:
250
250
 
251
251
  **Time_step**
252
252
  In version 5, a pandas "frequency" string such as ``10min`` (10
253
- minutes), ``H`` (hour), or ``2M`` (two months). If missing or empty,
254
- the time series is without time step.
253
+ minutes), ``1h`` (one hour), or ``2M`` (two months). If missing or
254
+ empty, the time series is without time step.
255
255
 
256
256
  Up to version 4, a comma-separated pair of integers; the number of
257
257
  minutes and months in the time step (one of the two must be zero).
@@ -18,7 +18,7 @@ rocc - Rate-of-change check for time series
18
18
  thresholds=(
19
19
  Threshold("10min", 10),
20
20
  Threshold("20min", 15),
21
- Threshold("H", 40),
21
+ Threshold("h", 40),
22
22
  ),
23
23
  symmetric=True,
24
24
  flag="MYFLAG",
@@ -2,7 +2,6 @@ import configparser
2
2
  import datetime as dt
3
3
  import logging
4
4
  import os
5
- import sys
6
5
  import traceback
7
6
 
8
7
  import click
@@ -81,7 +80,6 @@ class AppConfig:
81
80
  try:
82
81
  self._parse_config()
83
82
  except (OSError, configparser.Error) as e:
84
- sys.stderr.write(str(e))
85
83
  raise click.ClickException(str(e))
86
84
 
87
85
  def _parse_config(self):
@@ -2,7 +2,6 @@ import configparser
2
2
  import datetime as dt
3
3
  import logging
4
4
  import os
5
- import sys
6
5
  import traceback
7
6
  from glob import glob
8
7
  from io import StringIO
@@ -100,7 +99,6 @@ class AppConfig:
100
99
  try:
101
100
  self._parse_config()
102
101
  except (OSError, configparser.Error) as e:
103
- sys.stderr.write(str(e))
104
102
  raise click.ClickException(str(e))
105
103
 
106
104
  def _parse_config(self):
@@ -155,10 +153,10 @@ class AppConfig:
155
153
  self.time_step = s
156
154
 
157
155
  def _check_time_step(self, s):
158
- if s not in ("D", "H"):
156
+ if s not in ("D", "h"):
159
157
  raise WrongValueError(
160
158
  '"{}" is not an appropriate time step; in this version of '
161
- "vaporize, the step must be either D or H.".format(s)
159
+ "vaporize, the step must be either D or h.".format(s)
162
160
  )
163
161
 
164
162
  def _parse_unit_converters(self):
@@ -412,11 +410,11 @@ class ProcessAtPoint:
412
410
  self.pet.unit = "mm"
413
411
  self.pet.timezone = self.timezone
414
412
  self.pet.variable = "Potential Evapotranspiration"
415
- self.pet.precision = 2 if self.config.time_step == "H" else 1
413
+ self.pet.precision = 2 if self.config.time_step == "h" else 1
416
414
  self.pet.location = self.location
417
415
 
418
416
  def _determine_variables_to_use_in_calculation(self):
419
- if self.config.time_step == "H":
417
+ if self.config.time_step == "h":
420
418
  vars = ["temperature", "humidity", "wind_speed", "solar_radiation"]
421
419
  if "pressure" in self.input_timeseries:
422
420
  vars.append("pressure")
@@ -571,7 +569,7 @@ class ProcessSpatial:
571
569
  self._read_input_geofile(var, timestamp)
572
570
 
573
571
  def _get_variables(self):
574
- if self.config.time_step == "H":
572
+ if self.config.time_step == "h":
575
573
  return {
576
574
  "temperature",
577
575
  "humidity",
@@ -45,7 +45,7 @@ class PenmanMonteith(object):
45
45
  self.unit_converters = unit_converters
46
46
 
47
47
  def calculate(self, **kwargs):
48
- if self.time_step == "H":
48
+ if self.time_step == "h":
49
49
  return self.calculate_hourly(**kwargs)
50
50
  elif self.time_step == "D":
51
51
  return self.calculate_daily(**kwargs)
@@ -75,8 +75,8 @@ class Aggregation:
75
75
  def get_result_flags(self):
76
76
  max_count = int(pd.Timedelta(self.result.time_step) / self.source.freq)
77
77
  values_count = self.resampler.count()
78
- self.result.data["flags"] = (values_count < max_count).apply(
79
- lambda x: self.missing_flag if x else ""
78
+ self.result.data["flags"] = (max_count - values_count).apply(
79
+ lambda x: self.missing_flag.format(x) if x else ""
80
80
  )
81
81
 
82
82
 
@@ -106,7 +106,7 @@ class SourceTimeseries(HTimeseries):
106
106
  )
107
107
  except ValueError:
108
108
  raise CannotInferFrequency()
109
- first_timestamp = (current_range[0] - pd.Timedelta("1S")).floor(target_step)
109
+ first_timestamp = (current_range[0] - pd.Timedelta("1s")).floor(target_step)
110
110
  end_timestamp = current_range[-1].ceil(target_step)
111
111
  new_range = pd.date_range(first_timestamp, end_timestamp, freq=self.freq)
112
112
  self.data = self.data.reindex(new_range)
@@ -116,8 +116,8 @@ class AggregatedTimeseries(HTimeseries):
116
116
  def set_metadata(self, source_timeseries):
117
117
  for attr in attrs:
118
118
  setattr(self, attr, getattr(source_timeseries, attr, None))
119
- if self.time_step not in ("1H", "1D"):
120
- raise AggregateError("The target step can currently only be 1H or 1D")
119
+ if self.time_step not in ("1h", "1D"):
120
+ raise AggregateError("The target step can currently only be 1h or 1D")
121
121
  if hasattr(source_timeseries, "title"):
122
122
  self.title = "Aggregated " + source_timeseries.title
123
123
  if hasattr(source_timeseries, "comment"):
@@ -140,7 +140,7 @@ class AggregatedTimeseries(HTimeseries):
140
140
 
141
141
 
142
142
  def _get_offset_in_minutes(timestamp_offset):
143
- m = re.match(r"(-?)(\d*)(T|min)$", timestamp_offset)
143
+ m = re.match(r"(-?)(\d*)min$", timestamp_offset)
144
144
  if not m:
145
145
  raise AggregateError(
146
146
  "The target timestamp offset can currently only be a number of minutes "