Repository: Geosyntec/cloudside

Issue:
Storms that conclude near end of record extend too far
When the time between the last observed rainfall and the end of the record is less than the interevent dry duration, the current implementation of the storm parser (wrongly, IMO) extends the last storm to the end of the record.

You can see this in the "expected" test data in this repo. In the example below, the last rain falls at 13:55, but the storm continues on.
https://github.com/Geosyntec/cloudside/blob/4ad1f23964c2dc44d770fbdcae0a3586c99ff470/cloudside/tests/data/teststorm_simple.csv#L511-L519

Discussion:

Patch:
diff --git a/cloudside/__init__.py b/cloudside/__init__.py
index a7559cf..21e1ce2 100644
--- a/cloudside/__init__.py
+++ b/cloudside/__init__.py
@@ -7,4 +7,4 @@ from . import asos  # noqa
 
 __author__ = "Paul Hobson"
 __email__ = "pmhobson@gmail.com"
-__version__ = "0.2.1"
+__version__ = "0.3.0"
diff --git a/cloudside/storms.py b/cloudside/storms.py
index 769c625..a8756e8 100644
--- a/cloudside/storms.py
+++ b/cloudside/storms.py
@@ -1,3 +1,5 @@
+from datetime import timedelta
+
 import numpy
 import pandas
 
@@ -74,7 +76,7 @@ def parse_record(
     Returns
     -------
     parsed_storms : pandas.DataFrame
-        Copy of the origin `hydrodata` DataFrame, but resampled to a
+        Copy of the original `data` DataFrame, but resampled to a
         fixed frequency, columns possibly renamed, and a `storm` column
         added to denote the storm to which each record belongs. Records
         where `storm` == 0 are not a part of any storm.
@@ -139,6 +141,13 @@ def parse_record(
         )
     )
 
+    # fix trailing zeroes on the last storm
+    last_storm = res[stormcol].max()
+    last_storm_end = res.loc[
+        res[stormcol].eq(last_storm) & res[precipcol].gt(0)
+    ].index.max() + (2 * freq)
+    res.loc[last_storm_end:, stormcol] = 0
+
     if not debug:
         res = res.loc[:, res.columns.map(lambda c: not c.startswith("__"))]
 
diff --git a/cloudside/viz.py b/cloudside/viz.py
index eaf76cd..23a0e7a 100644
--- a/cloudside/viz.py
+++ b/cloudside/viz.py
@@ -511,11 +511,6 @@ def rose(
     return fig, rose
 
 
-@numpy.deprecate
-def windRose(dataframe, spdcol="wind_speed", dircol="wind_dir", **kwargs):
-    return rose(dataframe, spdcol, dircol, **kwargs)
-
-
 def _pct_fmt(x, pos=0):
     return "%0.1f%%" % (100 * x)
 
diff --git a/requirements.txt b/requirements.txt
index 27ba25e..8de2e6b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,7 @@
 click
 requests
-numpy
+numpy >= 2
 matplotlib
-pandas
+pandas >= 2
 tqdm
 metar >= 1.5


Test patch:
diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml
index 975e301..edabf60 100644
--- a/.github/workflows/lint_and_test.yml
+++ b/.github/workflows/lint_and_test.yml
@@ -51,7 +51,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: ["3.8", "3.9", "3.10"]
+        python-version: ["3.9", "3.10", "3.11", "3.12"]
     steps:
       - uses: actions/checkout@v2
       - name: Set up Python ${{ matrix.python-version }}
@@ -73,7 +73,7 @@ jobs:
       - name: Set up Python
         uses: actions/setup-python@v2
         with:
-          python-version: 3.9
+          python-version: "3.12"
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
@@ -89,16 +89,16 @@ jobs:
       - name: Setup Python
         uses: actions/setup-python@v2
         with:
-          python-version: 3.9
+          python-version: "3.12"
       - name: Generate coverage report
         run: |
           python -m pip install --upgrade pip
           pip install .[dev]
           coverage run -m pytest --doctest-modules --cov-report=xml
-      - name: Upload coverage to Codecov
-        uses: codecov/codecov-action@v1
-        with:
-          flags: unittests
-          name: codecov-umbrella
-          fail_ci_if_error: true
-          path_to_write_report: ./codecov_report.gz
+      # - name: Upload coverage to Codecov
+      #   uses: codecov/codecov-action@v1
+      #   with:
+      #     flags: unittests
+      #     name: codecov-umbrella
+      #     fail_ci_if_error: true
+      #     path_to_write_report: ./codecov_report.gz
diff --git a/cloudside/tests/__init__.py b/cloudside/tests/__init__.py
index 339de56..9576947 100644
--- a/cloudside/tests/__init__.py
+++ b/cloudside/tests/__init__.py
@@ -1,11 +1,11 @@
-from pkg_resources import resource_filename
+from importlib import resources
 from contextlib import contextmanager
 
 import pytest
 
 
 def get_test_file(filename):
-    return resource_filename("cloudside.tests.data", filename)
+    return resources.files("cloudside.tests.data").joinpath(filename)
 
 
 def raises(error):
diff --git a/cloudside/tests/data/teststorm_firstobs.csv b/cloudside/tests/data/teststorm_firstobs.csv
index 80027e7..cb8a105 100644
--- a/cloudside/tests/data/teststorm_firstobs.csv
+++ b/cloudside/tests/data/teststorm_firstobs.csv
@@ -510,32 +510,32 @@ date,rain,influent,effluent,storm
 2013-05-21 13:50:00,0.01,90.0,37.0,2
 2013-05-21 13:55:00,0.01,85.0,33.0,2
 2013-05-21 14:00:00,0.0,80.0,32.0,2
-2013-05-21 14:05:00,0.0,75.0,30.0,2
-2013-05-21 14:10:00,0.0,70.0,28.0,2
-2013-05-21 14:15:00,0.0,65.0,27.0,2
-2013-05-21 14:20:00,0.0,60.0,25.0,2
-2013-05-21 14:25:00,0.0,55.0,23.0,2
-2013-05-21 14:30:00,0.0,50.0,22.0,2
-2013-05-21 14:35:00,0.0,45.0,20.0,2
-2013-05-21 14:40:00,0.0,40.0,18.0,2
-2013-05-21 14:45:00,0.0,37.0,17.0,2
-2013-05-21 14:50:00,0.0,34.0,15.0,2
-2013-05-21 14:55:00,0.0,31.0,13.0,2
-2013-05-21 15:00:00,0.0,28.0,12.0,2
-2013-05-21 15:05:00,0.0,25.0,11.0,2
-2013-05-21 15:10:00,0.0,22.0,10.0,2
-2013-05-21 15:15:00,0.0,20.0,9.0,2
-2013-05-21 15:20:00,0.0,18.0,8.0,2
-2013-05-21 15:25:00,0.0,16.0,7.0,2
-2013-05-21 15:30:00,0.0,14.0,7.0,2
-2013-05-21 15:35:00,0.0,12.0,6.0,2
-2013-05-21 15:40:00,0.0,10.0,5.0,2
-2013-05-21 15:45:00,0.0,8.0,5.0,2
-2013-05-21 15:50:00,0.0,6.0,4.0,2
-2013-05-21 15:55:00,0.0,5.0,3.0,2
-2013-05-21 16:00:00,0.0,4.0,3.0,2
-2013-05-21 16:05:00,0.0,3.0,2.0,2
-2013-05-21 16:10:00,0.0,2.0,2.0,2
-2013-05-21 16:15:00,0.0,1.0,1.0,2
-2013-05-21 16:20:00,0.0,0.0,1.0,2
-2013-05-21 16:25:00,0.0,0.0,1.0,2
+2013-05-21 14:05:00,0.0,75.0,30.0,0
+2013-05-21 14:10:00,0.0,70.0,28.0,0
+2013-05-21 14:15:00,0.0,65.0,27.0,0
+2013-05-21 14:20:00,0.0,60.0,25.0,0
+2013-05-21 14:25:00,0.0,55.0,23.0,0
+2013-05-21 14:30:00,0.0,50.0,22.0,0
+2013-05-21 14:35:00,0.0,45.0,20.0,0
+2013-05-21 14:40:00,0.0,40.0,18.0,0
+2013-05-21 14:45:00,0.0,37.0,17.0,0
+2013-05-21 14:50:00,0.0,34.0,15.0,0
+2013-05-21 14:55:00,0.0,31.0,13.0,0
+2013-05-21 15:00:00,0.0,28.0,12.0,0
+2013-05-21 15:05:00,0.0,25.0,11.0,0
+2013-05-21 15:10:00,0.0,22.0,10.0,0
+2013-05-21 15:15:00,0.0,20.0,9.0,0
+2013-05-21 15:20:00,0.0,18.0,8.0,0
+2013-05-21 15:25:00,0.0,16.0,7.0,0
+2013-05-21 15:30:00,0.0,14.0,7.0,0
+2013-05-21 15:35:00,0.0,12.0,6.0,0
+2013-05-21 15:40:00,0.0,10.0,5.0,0
+2013-05-21 15:45:00,0.0,8.0,5.0,0
+2013-05-21 15:50:00,0.0,6.0,4.0,0
+2013-05-21 15:55:00,0.0,5.0,3.0,0
+2013-05-21 16:00:00,0.0,4.0,3.0,0
+2013-05-21 16:05:00,0.0,3.0,2.0,0
+2013-05-21 16:10:00,0.0,2.0,2.0,0
+2013-05-21 16:15:00,0.0,1.0,1.0,0
+2013-05-21 16:20:00,0.0,0.0,1.0,0
+2013-05-21 16:25:00,0.0,0.0,1.0,0
diff --git a/cloudside/tests/data/teststorm_simple.csv b/cloudside/tests/data/teststorm_simple.csv
index 7d14452..d39ebbc 100644
--- a/cloudside/tests/data/teststorm_simple.csv
+++ b/cloudside/tests/data/teststorm_simple.csv
@@ -511,32 +511,32 @@ date,rain,influent,effluent,storm
 2013-05-21 13:50:00,0.01,90.0,37.0,2
 2013-05-21 13:55:00,0.01,85.0,33.0,2
 2013-05-21 14:00:00,0.0,80.0,32.0,2
-2013-05-21 14:05:00,0.0,75.0,30.0,2
-2013-05-21 14:10:00,0.0,70.0,28.0,2
-2013-05-21 14:15:00,0.0,65.0,27.0,2
-2013-05-21 14:20:00,0.0,60.0,25.0,2
-2013-05-21 14:25:00,0.0,55.0,23.0,2
-2013-05-21 14:30:00,0.0,50.0,22.0,2
-2013-05-21 14:35:00,0.0,45.0,20.0,2
-2013-05-21 14:40:00,0.0,40.0,18.0,2
-2013-05-21 14:45:00,0.0,37.0,17.0,2
-2013-05-21 14:50:00,0.0,34.0,15.0,2
-2013-05-21 14:55:00,0.0,31.0,13.0,2
-2013-05-21 15:00:00,0.0,28.0,12.0,2
-2013-05-21 15:05:00,0.0,25.0,11.0,2
-2013-05-21 15:10:00,0.0,22.0,10.0,2
-2013-05-21 15:15:00,0.0,20.0,9.0,2
-2013-05-21 15:20:00,0.0,18.0,8.0,2
-2013-05-21 15:25:00,0.0,16.0,7.0,2
-2013-05-21 15:30:00,0.0,14.0,7.0,2
-2013-05-21 15:35:00,0.0,12.0,6.0,2
-2013-05-21 15:40:00,0.0,10.0,5.0,2
-2013-05-21 15:45:00,0.0,8.0,5.0,2
-2013-05-21 15:50:00,0.0,6.0,4.0,2
-2013-05-21 15:55:00,0.0,5.0,3.0,2
-2013-05-21 16:00:00,0.0,4.0,3.0,2
-2013-05-21 16:05:00,0.0,3.0,2.0,2
-2013-05-21 16:10:00,0.0,2.0,2.0,2
-2013-05-21 16:15:00,0.0,1.0,1.0,2
-2013-05-21 16:20:00,0.0,0.0,1.0,2
-2013-05-21 16:25:00,0.0,0.0,1.0,2
+2013-05-21 14:05:00,0.0,75.0,30.0,0
+2013-05-21 14:10:00,0.0,70.0,28.0,0
+2013-05-21 14:15:00,0.0,65.0,27.0,0
+2013-05-21 14:20:00,0.0,60.0,25.0,0
+2013-05-21 14:25:00,0.0,55.0,23.0,0
+2013-05-21 14:30:00,0.0,50.0,22.0,0
+2013-05-21 14:35:00,0.0,45.0,20.0,0
+2013-05-21 14:40:00,0.0,40.0,18.0,0
+2013-05-21 14:45:00,0.0,37.0,17.0,0
+2013-05-21 14:50:00,0.0,34.0,15.0,0
+2013-05-21 14:55:00,0.0,31.0,13.0,0
+2013-05-21 15:00:00,0.0,28.0,12.0,0
+2013-05-21 15:05:00,0.0,25.0,11.0,0
+2013-05-21 15:10:00,0.0,22.0,10.0,0
+2013-05-21 15:15:00,0.0,20.0,9.0,0
+2013-05-21 15:20:00,0.0,18.0,8.0,0
+2013-05-21 15:25:00,0.0,16.0,7.0,0
+2013-05-21 15:30:00,0.0,14.0,7.0,0
+2013-05-21 15:35:00,0.0,12.0,6.0,0
+2013-05-21 15:40:00,0.0,10.0,5.0,0
+2013-05-21 15:45:00,0.0,8.0,5.0,0
+2013-05-21 15:50:00,0.0,6.0,4.0,0
+2013-05-21 15:55:00,0.0,5.0,3.0,0
+2013-05-21 16:00:00,0.0,4.0,3.0,0
+2013-05-21 16:05:00,0.0,3.0,2.0,0
+2013-05-21 16:10:00,0.0,2.0,2.0,0
+2013-05-21 16:15:00,0.0,1.0,1.0,0
+2013-05-21 16:20:00,0.0,0.0,1.0,0
+2013-05-21 16:25:00,0.0,0.0,1.0,0
diff --git a/cloudside/tests/data/teststorm_singular.csv b/cloudside/tests/data/teststorm_singular.csv
index 313d256..fed626a 100644
--- a/cloudside/tests/data/teststorm_singular.csv
+++ b/cloudside/tests/data/teststorm_singular.csv
@@ -443,32 +443,32 @@ date,rain,influent,effluent,storm
 2013-05-21 13:50:00,0.01,90.0,37.0,3
 2013-05-21 13:55:00,0.01,85.0,33.0,3
 2013-05-21 14:00:00,0.0,80.0,32.0,3
-2013-05-21 14:05:00,0.0,75.0,30.0,3
-2013-05-21 14:10:00,0.0,70.0,28.0,3
-2013-05-21 14:15:00,0.0,65.0,27.0,3
-2013-05-21 14:20:00,0.0,60.0,25.0,3
-2013-05-21 14:25:00,0.0,55.0,23.0,3
-2013-05-21 14:30:00,0.0,50.0,22.0,3
-2013-05-21 14:35:00,0.0,45.0,20.0,3
-2013-05-21 14:40:00,0.0,40.0,18.0,3
-2013-05-21 14:45:00,0.0,37.0,17.0,3
-2013-05-21 14:50:00,0.0,34.0,15.0,3
-2013-05-21 14:55:00,0.0,31.0,13.0,3
-2013-05-21 15:00:00,0.0,28.0,12.0,3
-2013-05-21 15:05:00,0.0,25.0,11.0,3
-2013-05-21 15:10:00,0.0,22.0,10.0,3
-2013-05-21 15:15:00,0.0,20.0,9.0,3
-2013-05-21 15:20:00,0.0,18.0,8.0,3
-2013-05-21 15:25:00,0.0,16.0,7.0,3
-2013-05-21 15:30:00,0.0,14.0,7.0,3
-2013-05-21 15:35:00,0.0,12.0,6.0,3
-2013-05-21 15:40:00,0.0,10.0,5.0,3
-2013-05-21 15:45:00,0.0,8.0,5.0,3
-2013-05-21 15:50:00,0.0,6.0,4.0,3
-2013-05-21 15:55:00,0.0,5.0,3.0,3
-2013-05-21 16:00:00,0.0,4.0,3.0,3
-2013-05-21 16:05:00,0.0,3.0,2.0,3
-2013-05-21 16:10:00,0.0,2.0,2.0,3
-2013-05-21 16:15:00,0.0,1.0,1.0,3
-2013-05-21 16:20:00,0.0,0.0,1.0,3
-2013-05-21 16:25:00,0.0,0.0,1.0,3
+2013-05-21 14:05:00,0.0,75.0,30.0,0
+2013-05-21 14:10:00,0.0,70.0,28.0,0
+2013-05-21 14:15:00,0.0,65.0,27.0,0
+2013-05-21 14:20:00,0.0,60.0,25.0,0
+2013-05-21 14:25:00,0.0,55.0,23.0,0
+2013-05-21 14:30:00,0.0,50.0,22.0,0
+2013-05-21 14:35:00,0.0,45.0,20.0,0
+2013-05-21 14:40:00,0.0,40.0,18.0,0
+2013-05-21 14:45:00,0.0,37.0,17.0,0
+2013-05-21 14:50:00,0.0,34.0,15.0,0
+2013-05-21 14:55:00,0.0,31.0,13.0,0
+2013-05-21 15:00:00,0.0,28.0,12.0,0
+2013-05-21 15:05:00,0.0,25.0,11.0,0
+2013-05-21 15:10:00,0.0,22.0,10.0,0
+2013-05-21 15:15:00,0.0,20.0,9.0,0
+2013-05-21 15:20:00,0.0,18.0,8.0,0
+2013-05-21 15:25:00,0.0,16.0,7.0,0
+2013-05-21 15:30:00,0.0,14.0,7.0,0
+2013-05-21 15:35:00,0.0,12.0,6.0,0
+2013-05-21 15:40:00,0.0,10.0,5.0,0
+2013-05-21 15:45:00,0.0,8.0,5.0,0
+2013-05-21 15:50:00,0.0,6.0,4.0,0
+2013-05-21 15:55:00,0.0,5.0,3.0,0
+2013-05-21 16:00:00,0.0,4.0,3.0,0
+2013-05-21 16:05:00,0.0,3.0,2.0,0
+2013-05-21 16:10:00,0.0,2.0,2.0,0
+2013-05-21 16:15:00,0.0,1.0,1.0,0
+2013-05-21 16:20:00,0.0,0.0,1.0,0
+2013-05-21 16:25:00,0.0,0.0,1.0,0
diff --git a/cloudside/tests/test_asos.py b/cloudside/tests/test_asos.py
index ab0102c..a60d094 100644
--- a/cloudside/tests/test_asos.py
+++ b/cloudside/tests/test_asos.py
@@ -143,7 +143,7 @@ def test_fetch_files(ftp_login, ftp_retr):
         )
         assert isinstance(raw_paths, filter)
         assert all([(isinstance(rp, pathlib.Path) or (rp is None)) for rp in raw_paths])
-        assert ftp_login.called_once_with("tester@cloudside.net")
+        ftp_login.assert_called_once_with(passwd="tester@cloudside.net")
         assert ftp_retr.call_count == 5
 
 
diff --git a/cloudside/tests/test_viz.py b/cloudside/tests/test_viz.py
index a2885d1..3f97958 100644
--- a/cloudside/tests/test_viz.py
+++ b/cloudside/tests/test_viz.py
@@ -161,25 +161,6 @@ def test_rose(test_data):
     return fig
 
 
-@pytest.mark.mpl_image_compare(**IMG_OPTS)
-def test_windrose_short(short_data):
-    fig, ax1, ax2 = _make_polar_fig()
-    with warnings.catch_warnings():
-        warnings.simplefilter("ignore")
-        _ = viz.windRose(
-            short_data.assign(WindSpd=short_data["WindSpd"] * 1.15),
-            spd_units="mph",
-            ax=ax1,
-            spdcol="WindSpd",
-            dircol="WindDir",
-        )
-        _ = viz.windRose(
-            short_data, spdcol="WindSpd", dircol="WindDir", spd_units="kt", ax=ax2
-        )
-    quiet_layout(fig)
-    return fig
-
-
 @pytest.mark.mpl_image_compare(**IMG_OPTS)
 def test_rose_short(short_data):
     fig, ax1, ax2 = _make_polar_fig()