From 64b2984d16805c21ae3c814a53aee5abb503ea29 Mon Sep 17 00:00:00 2001
From: Rufai Omowunmi Balogun <rbalogun@eurac.edu>
Date: Wed, 4 Oct 2023 12:46:52 +0200
Subject: [PATCH] rebase merge request

---
 ...st_climatology.cpython-38-pytest-7.1.2.pyc | Bin 0 -> 5507 bytes
 setup.cfg                                     |   4 +-
 smodex/__init__.py                            |   8 +
 smodex/sm_downloader.py                       | 190 ++++++++++++++++++
 tests/test_climatology.py                     |   3 +
 5 files changed, 203 insertions(+), 2 deletions(-)
 create mode 100644 __pycache__/test_climatology.cpython-38-pytest-7.1.2.pyc
 create mode 100644 smodex/__init__.py
 create mode 100644 smodex/sm_downloader.py

diff --git a/__pycache__/test_climatology.cpython-38-pytest-7.1.2.pyc b/__pycache__/test_climatology.cpython-38-pytest-7.1.2.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..8ad705a75283e8926a9f14e25983d119a6d83e7d
GIT binary patch
literal 5507
zcmb_gO^h5z6|Vl7o}QkcUGI8-lRy)2<binCcKnBljg6fUAdZ3^Ahdv1d#ZPLc6+)f
z)jiJ6(8D2{tHDAXkTBBh1t~dmLR`2Y&Kx-O34w&<D+eSj#PYrBncem}&X2<E_N!Ob
zud2R!_3FJ>{hfN<V)*^z!ZZFKW*PfCRf<0il`DASUx0AtFs^VlP#jg3T1Quz!a60#
zQ1Ulrr!3!?jw#EEQ<0_RRApIn>auJ&Q?i_Prcr9ak<N@WqcDGZbC&D8^e)>`oTJ>}
z<#(Ag$4y>AInOO#MY+IhypD2_tG8IQF}P%3i^9Zfhq2x11xdRb_;%vQiQV#IKepE*
zVJ92D9e3PT(C&Ci6h!O&<4wC0@m}CRz{1#TO(m<}x^eB`eM_PC@sD`Y#5FuIDJ*3p
ztan$z{OaJjYr^*unzPf5geUqoZwtSbMBwD)z|s!a9g{%0c!$Z1&K8M-Yb2d6+T1ly
z_IQtp5+-=qT-}H|{;Ig`0c|f_je*wRUX?=>ma)?9XGT846oFGgm?p}gkF0>kq(-@d
z1Wdw43he;bcD11*YtTDaQBxDPsg6``>?%Vo(KdBnrX5(<Qf*EBSiE&#Pg$y_3a{SR
zc<rt_)KeY0st^9Q?uWjBwqiT(huD^+-Lki@3gPwbIBEyjly;o-gl}K>5^P^$SvG!G
z#d>^Ea#Z8!?+Pzm_wC~l!x$ak{zeG>G%r#=-_BpQCT0;Qo=9vj<hGB8y4%s39ebU9
zQ$gFoY&U%%2n!RA0b*zpx>$tnN$8XAgs%PS-rnAW-$KU^HefjI-%!;-wW+l;?2j<P
ziwa1l6Y|UqyriA<xSv&mC|s9pQ+G=7+joLfd2u=~UdW3xO-)p3kQ$LXNK<i)Rul;y
zH%plsb{#Xoap-z2KQo{U9(6KxTR7$M3Ls<w@>q$EhG{{(B-3djj)|>tiIb+76NB(B
zwxp%IaUVMuV(X+7R5FLR@gfMTG?a!~Q*=D???1X>{oAMrIG4vQl9V7V@=Y+4Gc{Bs
zr^5PRov=V<q$b*~^0Q|Zmgq?-Rdy6mbw^3H9gQq&WbDEVeUF`G-)1-2n=C1h%#`ga
z@JVEKrNKL`4ZpP|591v#Xmh)8f^6Zh`N9ud@DPugN&G?PmmfF#cwWNfKH}g@yUog;
zXl=Cb_*u0uc~f=j?NDNJ<?K#%QiY|y)~sZu&KA5wW_0^>^l3NSaB7+LT`%bQF9{I|
zL1#YG;zVS#t*F!OVP6YVWJbKvOWFatb`s(H&64yES#6BR?Rv>ZHeKkEw^a7^l3Dw%
z(?4tx^U@(rFz<I+0i<{ygq1DDQpp-r#b5?g_jG+m6VKw!BmSv@209ng8kVsl_@J?W
z;`&|kQ1B)@un5?O#!KXxBu73<PxKM2ViWvz_QMy4u#L@fVkVWew4?Cy0vmJY0^3n}
z<*qWaQtelaTX{RU>aTUOmbWgX>ZnS#1kY7lVk5+vT?Kq&!mp`ABdMdG1{<uTI&9E@
z4VKgLs3H657;S2S4b7CeX>gTfYIAyMrPk(=VKuF$CRR~PYxk7+NBjt%NoGc~yKKvd
z|C<~g%_UR2$~{Gx;OB`CRY-tOtzAWy)m??pPWoVsGt?_JWv|Op0z7)nO1=i^qxVbm
zY*<eg(6X3fJv+)f3ZK*2(85z4)(+vTjPa>4E8@dkBbsZB@O>Kpi3sNl2>(L8(uzd*
zSL#JP5Y9{f;~`ucEsxLh9M1E?VCM}A?=q_2x?x{Ch}w2nAgH$xT`1zWa5xZ$?X@;C
zf~c3^XsrC-4)jCo^vRQFPn<l3f61Raaq9Gm7fxl$_4l-_Bu{gu;S^{3n(*HeM`7K9
z{N%vMJE4>vnu>JhgQ-;tfbJv!x&v(`Yzay|9-7C#f8to@#4-K=v1D)X)apw&ub!2M
z-1Y9Py99*DIdpjBWt{LwU*7iO*heTlcD&m^-;Avvj~V-tY&tV(0yAzpJ!t|nZaUeF
zSIB8t!xeuU(K@z<$7!6i&A4d|Gc}5{I=K^xjdYK2qjmcyiJT^K#sN7?<os)6Q9>$3
zpUTuO7e`206fy)k5kcM|GkRhBt)8Fhecuy~7W)CT9JE`$SjKCol0<IkCoR5~m0Cd*
zBd)?Fu2F=Q9#T9-BRx+e>C)vHIryx0rQ3IJ_u9xD!#Fd7sO1H4uxORO%@f(88*fCt
zfV;x)3U@t-kd(QhhtOSdV~?UoS7eQ=Qnl?UlwLWTb&>la+jRZyZs3Io@lhzACJfJj
zG#k<j%Z0*2Q@oAEXXx!`K^zUySJ+hj9FY@5PJv`q0-JB*sbb{?r|{-nUdiXbaM)!p
z%4K~(q<9$<#1}zWd0H`5IBsoPc~+U(Gc;3sT05$k8aZ=v<0hVfHCRnLb*hC_)5LZ3
zZqX2vA8p-!;`uxMF~`o}*pckR!NaZdQp&giXN!~*j+b$B!XxplAPlH*?Qr#U5xA@V
zKpj%}*ES8fd>zzCOR2G?k~@z7Nl5^&rR6n+*QtF?-PL&`sf;Y?ZhwyV)f8!kxDT#I
z9Gp@^@(3?{YG3mAQW60|{87FI2gxbPBjoVu`#Kz~NzO7g+Y0y!LIeT=IXdZVXW?i|
zc*;PiZB9wFQ$@6JuJ5DA1n<$u@j~KdNJzv85U<PGpNJXKiIfIbNdH7ie>&U~z0XO#
zTL@*A)O+<&z28&i%#)QIXnua8Pe@ElYzS%hRIx0vftMkHc7n9OP>{YXr4jfb{fd+Z
z59tNTfASr$<PKCp3-)qPi=W4M6Wj+oP{jQwi6qDUXF2<a;r^GDCfu5!l(d$9I@~HH
zIlBt1zBa6<HL3mjA?<5Vu=~Y*eQNPfB`ySB;QFP+1s?iblssvFsUV$OKhg$B=k^aC
z(o0hMlWV^wwO>D^ef<gce}G#b+dtvf$M*kZ-1^x53AaAB|4)UR&m!?y9^8<*JY^qw
zh9RN=7p--><9i`pDP`7CNJo$b;NF2KfYcvi9Kk4z5eQ;Aj`%#Bqxb@mV?>?<$(RiJ
z;)~SqB@m<sHi|cJ#VdDkq1_1jP356_=~7b}yi8&D2@x7|Au!vQ^2mN}62`$C1pAd-
zD9`?7xE{|iZlS0=nNt^()QeRjCqXEXpBpp^3B`qGOfmfeKBf#_`4D7rd*FXGw6PSH
zUE=<h#|t23J?IxePFfD~jxY-SLT*9T=Cn9X=ns+$x;csSAkN&npA>IRbQ^1JIXWpw
zyhzJE;vz2yWC}pB(-Xd+SSh|j<Pwp~M7~Po3J9+1v`z$iIp0-km%;q&#Jxh~Ya~{P
z+h+w`fAX-N2k-O6Nd9H<P3rY6BCis;L4>~S6t3vYGP-}@YC=fH^zDZki0_)Js7z(c
zdq!zy9L06kWwl45`gUH|CK3i4x9rFLiA3XY%b~h>9rHGqva;*)sO7quIZ3&s8nY!g
z&$m``gTfhE5&qkvogm5DZ_3K!Hn}A-nUg6MZm7N6Nd1xc$5|=tb-I0-Gde~WA3wZU
z?z>}b%a1i;8c4(fh%=XeRa@D=-^x#e`GvZ?*50NMQF)#wb5Xjr$_z&?rl5Jb2l5Mv
j`C|T6@iOJ@u??cDCYxcVf<JXaYnrZDC3DGKG>`ueVqVqF

literal 0
HcmV?d00001

diff --git a/setup.cfg b/setup.cfg
index ac6b862..646d788 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -30,7 +30,7 @@ classifiers =
 [options]
 include_package_data = True
 package_dir =
-    = src
+    = smodex
 packages = find:
 python_requires = >=3.7
 install_requires =
@@ -49,7 +49,7 @@ docs =
 #     smodex = smodex.__main__:main
 
 [options.packages.find]
-where = src
+where = smodex
 
 [options.package_data]
 * = *.yaml, *.ini
diff --git a/smodex/__init__.py b/smodex/__init__.py
new file mode 100644
index 0000000..45b4eb8
--- /dev/null
+++ b/smodex/__init__.py
@@ -0,0 +1,8 @@
+from smodex import sm_anomaly
+from smodex import sm_climatology
+from smodex import sm_downloader
+from smodex import version
+from smodex import visual_sma_ts
+
+
+__all__ = ["sm_anomaly", "sm_climatology", "sm_downloader", "visual_sma_ts", "version"]
diff --git a/smodex/sm_downloader.py b/smodex/sm_downloader.py
new file mode 100644
index 0000000..139b100
--- /dev/null
+++ b/smodex/sm_downloader.py
@@ -0,0 +1,190 @@
+"""
+    Soil Moisture Downloader: Configured to download datasets from the Climate Data Store
+    Downloads hourly soil moisture datasets for full year
+"""
+import argparse
+import json
+import logging
+import os
+import sys
+
+import numpy as np
+import pandas as pd
+
+import cdsapi
+
+
+logger = logging.getLogger(__name__)
+logger.setLevel(logging.INFO)
+formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
+stream_handler = logging.StreamHandler(sys.stdout)
+stream_handler.setFormatter(formatter)
+logger.addHandler(stream_handler)
+
+
+c = cdsapi.Client()
+
+
+class SMDownload:
+    """Soil Moisture Downloader"""
+
+    def __init__(
+        self,
+        start_date: str,
+        end_date: str,
+        api: str,
+        area: tuple = (50.775, 2.775, 42.275, 18.025),
+        depth: tuple = (1, 2, 3, 4),
+        download_path: str = "./sm_downloaded/",
+    ) -> None:
+        self.start_date = start_date
+        self.end_date = end_date
+        self.api = api
+        self.download_path = download_path
+        self.area = area
+        self.depth = depth
+
+    def era5_sm_downloader(self, year):
+        """downloader from ERA5 API"""
+
+        variables = []
+        for dep in self.depth:
+            variables.append(f"volumetric_soil_water_layer_{dep}")
+
+        c.retrieve(
+            "reanalysis-era5-single-levels",
+            {
+                "product_type": "reanalysis",
+                "variable": variables,
+                "year": int(year),
+                "month": [
+                    "01",
+                    "02",
+                    "03",
+                    "04",
+                    "05",
+                    "06",
+                    "07",
+                    "08",
+                    "09",
+                    "10",
+                    "11",
+                    "12",
+                ],
+                "day": [
+                    "01",
+                    "02",
+                    "03",
+                    "04",
+                    "05",
+                    "06",
+                    "07",
+                    "08",
+                    "09",
+                    "10",
+                    "11",
+                    "12",
+                    "13",
+                    "14",
+                    "15",
+                    "16",
+                    "17",
+                    "18",
+                    "19",
+                    "20",
+                    "21",
+                    "22",
+                    "23",
+                    "24",
+                    "25",
+                    "26",
+                    "27",
+                    "28",
+                    "29",
+                    "30",
+                    "31",
+                ],
+                "time": [
+                    "00:00",
+                    "06:00",
+                    "12:00",
+                    "18:00",
+                ],
+                # 'time': [
+                #     '00:00', '01:00', '02:00',
+                #     '03:00', '04:00', '05:00',
+                #     '06:00', '07:00', '08:00',
+                #     '09:00', '10:00', '11:00',
+                #     '12:00', '13:00', '14:00',
+                #     '15:00', '16:00', '17:00',
+                #     '18:00', '19:00', '20:00',
+                #     '21:00', '22:00', '23:00',
+                #     ],
+                "area": self.area,
+                "format": "netcdf",
+            },
+            self.download_path + f"ERA5_SM_{year}.nc",
+        )
+
+    def downloader(self):
+        """download"""
+        # TODO: Extend to other APIs.
+
+        date_ranges = pd.date_range(start=self.start_date, end=self.end_date).year
+        years = np.unique(date_ranges)
+
+        for yr in years:
+            if self.api == "era5":
+                logger.info(f"Initiating downloading of ERA5 Soil Moisture for {yr}")
+                if not os.path.exists(self.download_path):
+                    os.makedirs(self.download_path)
+                self.era5_sm_downloader(year=yr)
+                logger.info(f"Downloaded ERA5 Soil Moisture for {yr}")
+
+
+if __name__ == "__main__":
+    # command line option
+    parser = argparse.ArgumentParser(
+        description="Downloads soil moisture \
+                                     datasets from start date to end date"
+    )
+    parser.add_argument(
+        "start_date",
+        type=str,
+        help="initial date to start \
+                                     downloading from e.g. 1990-01-01",
+    )
+    parser.add_argument(
+        "end_date",
+        type=str,
+        help="end date to stop \
+                                     downloading datasets from e.g. 2030-12-31",
+    )
+    parser.add_argument("api", type=str, help="download portal API e.g. era5, lpdaac, etc.")
+    parser.add_argument(
+        "-a",
+        "--area",
+        type=json.loads,
+        help="bounding box area for downloading \
+                         datasets e.g. [50.775, 2.775, 42.275, 18.025]",
+    )
+    parser.add_argument(
+        "-d",
+        "--depth",
+        type=json.loads,
+        help="volumetric \
+                        soil moisture depths e.g. [1, 2, 3, 4]",
+    )
+    parser.add_argument(
+        "path",
+        type=str,
+        help="directory to save the \
+                        downloaded datasets e.g. /sm_downloaded/",
+    )
+
+    args = parser.parse_args()
+
+    SMDownload = SMDownload(  # type: ignore [misc, assignment]
+        args.start_date, args.end_date, args.api, args.area, args.depth, args.path
+    )
+    SMDownload.downloader()  # type: ignore [call-arg]
diff --git a/tests/test_climatology.py b/tests/test_climatology.py
index 3f07c47..1ee7efb 100644
--- a/tests/test_climatology.py
+++ b/tests/test_climatology.py
@@ -152,6 +152,9 @@ def test_get_climatology_stack():
 #     captured = capsys.readouterr()
 #     assert "Soil Moisture Stack created" in captured.out
 #     assert "Climatology computation complete" in captured.out
+<<<<<<< HEAD
 
 if __name__ == "__main__":
     pytest.main()
+=======
+>>>>>>> e26b415 (rebase merge request)
-- 
GitLab