simple-dwd-weatherforecast 2.0.28__tar.gz → 2.0.31__tar.gz
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.
- {simple_dwd_weatherforecast-2.0.28/simple_dwd_weatherforecast.egg-info → simple_dwd_weatherforecast-2.0.31}/PKG-INFO +1 -1
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/setup.py +1 -1
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/simple_dwd_weatherforecast/dwdforecast.py +175 -144
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/simple_dwd_weatherforecast/dwdmap.py +1 -1
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31/simple_dwd_weatherforecast.egg-info}/PKG-INFO +1 -1
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/LICENCE +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/README.md +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/setup.cfg +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/simple_dwd_weatherforecast/__init__.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/simple_dwd_weatherforecast/stations.json +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/simple_dwd_weatherforecast/uv_stations.json +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/simple_dwd_weatherforecast.egg-info/SOURCES.txt +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/simple_dwd_weatherforecast.egg-info/dependency_links.txt +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/simple_dwd_weatherforecast.egg-info/requires.txt +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/simple_dwd_weatherforecast.egg-info/top_level.txt +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/__init__.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/dummy_data.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/dummy_data_full.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/dummy_uv.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_daily_condition.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_daily_max.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_daily_min.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_daily_sum.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_day_values.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_forecast_condition.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_forecast_data.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_station_name.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_timeframe_avg.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_timeframe_condition.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_timeframe_max.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_timeframe_min.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_timeframe_sum.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_timeframe_values.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_is_in_timerange.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_is_valid_timeframe.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_location_tools.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_map.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_parsekml.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_region.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_reported_weather.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_station.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_stationsfile.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_update.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_update_hourly.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_uv_index.py +0 -0
- {simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_weather.py +0 -0
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
|
|
5
5
|
|
6
6
|
setuptools.setup(
|
7
7
|
name="simple_dwd_weatherforecast",
|
8
|
-
version="2.0.
|
8
|
+
version="2.0.31",
|
9
9
|
author="Max Fermor",
|
10
10
|
description="A simple tool to retrieve a weather forecast from DWD OpenData",
|
11
11
|
long_description=long_description,
|
@@ -400,8 +400,8 @@ class Weather:
|
|
400
400
|
else:
|
401
401
|
print("no report for this station available. Have you updated first?")
|
402
402
|
|
403
|
-
def get_uv_index(self, days_from_today: int) -> int:
|
404
|
-
if not self.uv_reports:
|
403
|
+
def get_uv_index(self, days_from_today: int, shouldUpdate=True) -> int:
|
404
|
+
if not self.uv_reports and shouldUpdate:
|
405
405
|
self.update(
|
406
406
|
force_hourly=False,
|
407
407
|
with_forecast=False,
|
@@ -418,7 +418,11 @@ class Weather:
|
|
418
418
|
day = "tomorrow"
|
419
419
|
elif days_from_today == 2:
|
420
420
|
day = "dayafter_to"
|
421
|
-
return
|
421
|
+
return (
|
422
|
+
self.uv_reports[self.nearest_uv_index_station]["forecast"][day]
|
423
|
+
if self.uv_reports
|
424
|
+
else None
|
425
|
+
)
|
422
426
|
|
423
427
|
def get_timeframe_max(
|
424
428
|
self,
|
@@ -766,79 +770,92 @@ class Weather:
|
|
766
770
|
self.report_data = {
|
767
771
|
"time": row["Parameter description"],
|
768
772
|
"date": row["surface observations"],
|
769
|
-
WeatherDataType.CONDITION.value[0]:
|
770
|
-
row[WeatherDataType.CONDITION.value[1]]
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
WeatherDataType.TEMPERATURE.value[0]:
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
773
|
+
WeatherDataType.CONDITION.value[0]: (
|
774
|
+
int(row[WeatherDataType.CONDITION.value[1]])
|
775
|
+
if row[WeatherDataType.CONDITION.value[1]] != self.NOT_AVAILABLE
|
776
|
+
else None
|
777
|
+
),
|
778
|
+
WeatherDataType.TEMPERATURE.value[0]: (
|
779
|
+
round(
|
780
|
+
float(
|
781
|
+
row[WeatherDataType.TEMPERATURE.value[1]]
|
782
|
+
.replace(self.NOT_AVAILABLE, "0.0")
|
783
|
+
.replace(",", ".")
|
784
|
+
)
|
785
|
+
+ 273.1,
|
786
|
+
1,
|
779
787
|
)
|
780
|
-
|
781
|
-
|
782
|
-
)
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
|
796
|
-
|
797
|
-
WeatherDataType.WIND_SPEED.value[0]:
|
798
|
-
|
799
|
-
|
800
|
-
|
801
|
-
|
802
|
-
|
803
|
-
|
804
|
-
|
805
|
-
)
|
806
|
-
|
807
|
-
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
812
|
-
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
WeatherDataType.
|
831
|
-
float(row[WeatherDataType.
|
832
|
-
|
833
|
-
|
834
|
-
)
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
788
|
+
if row[WeatherDataType.TEMPERATURE.value[1]] != self.NOT_AVAILABLE
|
789
|
+
else None
|
790
|
+
),
|
791
|
+
WeatherDataType.DEWPOINT.value[0]: (
|
792
|
+
round(
|
793
|
+
float(row[WeatherDataType.DEWPOINT.value[1]].replace(",", "."))
|
794
|
+
+ 273.1,
|
795
|
+
1,
|
796
|
+
)
|
797
|
+
if row[WeatherDataType.DEWPOINT.value[1]] != self.NOT_AVAILABLE
|
798
|
+
else None
|
799
|
+
),
|
800
|
+
WeatherDataType.PRESSURE.value[0]: (
|
801
|
+
float(row[WeatherDataType.PRESSURE.value[1]].replace(",", ".")) * 100
|
802
|
+
if row[WeatherDataType.PRESSURE.value[1]] != self.NOT_AVAILABLE
|
803
|
+
else None
|
804
|
+
),
|
805
|
+
WeatherDataType.WIND_SPEED.value[0]: (
|
806
|
+
round(
|
807
|
+
float(row[WeatherDataType.WIND_SPEED.value[1]].replace(",", "."))
|
808
|
+
/ 3.6,
|
809
|
+
1,
|
810
|
+
)
|
811
|
+
if row[WeatherDataType.WIND_SPEED.value[1]] != self.NOT_AVAILABLE
|
812
|
+
else None
|
813
|
+
),
|
814
|
+
WeatherDataType.WIND_DIRECTION.value[0]: (
|
815
|
+
int(row[WeatherDataType.WIND_DIRECTION.value[1]])
|
816
|
+
if row[WeatherDataType.WIND_DIRECTION.value[1]] != self.NOT_AVAILABLE
|
817
|
+
else None
|
818
|
+
),
|
819
|
+
WeatherDataType.WIND_GUSTS.value[0]: (
|
820
|
+
round(
|
821
|
+
float(row[WeatherDataType.WIND_GUSTS.value[1]].replace(",", "."))
|
822
|
+
/ 3.6,
|
823
|
+
1,
|
824
|
+
)
|
825
|
+
if row[WeatherDataType.WIND_GUSTS.value[1]] != self.NOT_AVAILABLE
|
826
|
+
else None
|
827
|
+
),
|
828
|
+
WeatherDataType.PRECIPITATION.value[0]: (
|
829
|
+
float(row[WeatherDataType.PRECIPITATION.value[1]].replace(",", "."))
|
830
|
+
if row[WeatherDataType.PRECIPITATION.value[1]] != self.NOT_AVAILABLE
|
831
|
+
else None
|
832
|
+
),
|
833
|
+
WeatherDataType.CLOUD_COVERAGE.value[0]: (
|
834
|
+
float(row[WeatherDataType.CLOUD_COVERAGE.value[1]].replace(",", "."))
|
835
|
+
if row[WeatherDataType.CLOUD_COVERAGE.value[1]] != self.NOT_AVAILABLE
|
836
|
+
else None
|
837
|
+
),
|
838
|
+
WeatherDataType.VISIBILITY.value[0]: (
|
839
|
+
float(row[WeatherDataType.VISIBILITY.value[1]].replace(",", ".")) * 1e3
|
840
|
+
if row[WeatherDataType.VISIBILITY.value[1]] != self.NOT_AVAILABLE
|
841
|
+
else None
|
842
|
+
),
|
843
|
+
WeatherDataType.SUN_IRRADIANCE.value[0]: (
|
844
|
+
round(
|
845
|
+
float(
|
846
|
+
row[WeatherDataType.SUN_IRRADIANCE.value[1]].replace(",", ".")
|
847
|
+
)
|
848
|
+
* 3.6,
|
849
|
+
1,
|
850
|
+
)
|
851
|
+
if row[WeatherDataType.SUN_IRRADIANCE.value[1]] != self.NOT_AVAILABLE
|
852
|
+
else None
|
853
|
+
),
|
854
|
+
WeatherDataType.HUMIDITY.value[0]: (
|
855
|
+
float(row[WeatherDataType.HUMIDITY.value[1]].replace(",", "."))
|
856
|
+
if row[WeatherDataType.HUMIDITY.value[1]] != self.NOT_AVAILABLE
|
857
|
+
else None
|
858
|
+
),
|
842
859
|
}
|
843
860
|
|
844
861
|
def get_weather_report(self, shouldUpdate=False):
|
@@ -852,21 +869,24 @@ class Weather:
|
|
852
869
|
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/537.36"
|
853
870
|
}
|
854
871
|
headers["If-None-Match"] = self.etags[url] if url in self.etags else ""
|
855
|
-
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
872
|
+
try:
|
873
|
+
request = requests.get(url, headers=headers, timeout=30)
|
874
|
+
# If resource has not been modified, return
|
875
|
+
if request.status_code == 304:
|
876
|
+
return
|
877
|
+
elif request.status_code != 200:
|
878
|
+
raise Exception(f"Unexpected status code {request.status_code}")
|
879
|
+
self.etags[url] = request.headers["ETag"]
|
880
|
+
uv_reports = json.loads(request.text)["content"]
|
881
|
+
# Match with existing stations
|
882
|
+
for uv_report in uv_reports:
|
883
|
+
station = get_station_by_name(
|
884
|
+
self.uv_index_stations_reference_names[uv_report["city"]]
|
885
|
+
)
|
886
|
+
# uv_report.update({"lat": station[1]["lat"], "lon": station[1]["lon"]})
|
887
|
+
self.uv_reports[station[0]] = uv_report
|
888
|
+
except Exception as error:
|
889
|
+
print(f"Error in download_weather_report: {type(error)} args: {error.args}")
|
870
890
|
|
871
891
|
def download_weather_report(self, region_code):
|
872
892
|
url = f"https://www.dwd.de/DWD/wetter/wv_allg/deutschland/text/vhdl13_{region_code}.html"
|
@@ -874,16 +894,19 @@ class Weather:
|
|
874
894
|
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.116 Safari/537.36"
|
875
895
|
}
|
876
896
|
headers["If-None-Match"] = self.etags[url] if url in self.etags else ""
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
897
|
+
try:
|
898
|
+
request = requests.get(url, headers=headers, timeout=30)
|
899
|
+
# If resource has not been modified, return
|
900
|
+
if request.status_code == 304:
|
901
|
+
return
|
902
|
+
self.etags[url] = request.headers["ETag"]
|
903
|
+
weather_report = request.text
|
904
|
+
a = weather_report.find(">")
|
905
|
+
if a != -1:
|
906
|
+
weather_report = weather_report[a + 1 :]
|
907
|
+
self.weather_report = weather_report
|
908
|
+
except Exception as error:
|
909
|
+
print(f"Error in download_weather_report: {type(error)} args: {error.args}")
|
887
910
|
|
888
911
|
def download_latest_kml(self, stationid, force_hourly=False):
|
889
912
|
if force_hourly:
|
@@ -891,15 +914,18 @@ class Weather:
|
|
891
914
|
else:
|
892
915
|
url = f"https://opendata.dwd.de/weather/local_forecasts/mos/MOSMIX_L/single_stations/{stationid}/kml/MOSMIX_L_LATEST_{stationid}.kmz"
|
893
916
|
headers = {"If-None-Match": self.etags[url] if url in self.etags else ""}
|
894
|
-
|
895
|
-
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
917
|
+
try:
|
918
|
+
request = requests.get(url, headers=headers, timeout=30)
|
919
|
+
# If resource has not been modified, return
|
920
|
+
if request.status_code == 304:
|
921
|
+
return
|
922
|
+
self.etags[url] = request.headers["ETag"]
|
923
|
+
with ZipFile(BytesIO(request.content), "r") as kmz:
|
924
|
+
# large RAM allocation
|
925
|
+
with kmz.open(kmz.namelist()[0], "r") as kml:
|
926
|
+
self.parse_kml(kml, force_hourly)
|
927
|
+
except Exception as error:
|
928
|
+
print(f"Error in download_weather_report: {type(error)} args: {error.args}")
|
903
929
|
|
904
930
|
def download_latest_report(self):
|
905
931
|
station_id = self.station_id
|
@@ -909,38 +935,43 @@ class Weather:
|
|
909
935
|
f"https://opendata.dwd.de/weather/weather_reports/poi/{station_id}-BEOB.csv"
|
910
936
|
)
|
911
937
|
headers = {"If-None-Match": self.etags[url] if url in self.etags else ""}
|
912
|
-
|
913
|
-
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
row["
|
933
|
-
"
|
934
|
-
]
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
946
|
-
|
938
|
+
try:
|
939
|
+
response = requests.get(url, headers=headers, timeout=30)
|
940
|
+
if response.status_code == 200:
|
941
|
+
content = response.content
|
942
|
+
reader = csv.DictReader(
|
943
|
+
content.decode("utf-8").splitlines(), delimiter=";"
|
944
|
+
)
|
945
|
+
is_first_run = True
|
946
|
+
backuprows = []
|
947
|
+
for row in reader:
|
948
|
+
if is_first_run:
|
949
|
+
is_first_run = False
|
950
|
+
reader.__next__()
|
951
|
+
continue
|
952
|
+
backuprows.append(reader.__next__())
|
953
|
+
backuprows.append(reader.__next__())
|
954
|
+
backuprows.append(reader.__next__())
|
955
|
+
|
956
|
+
# Some items are only reported each hour
|
957
|
+
for backuprow in backuprows:
|
958
|
+
if row["cloud_cover_total"] == self.NOT_AVAILABLE:
|
959
|
+
row["cloud_cover_total"] = backuprow["cloud_cover_total"]
|
960
|
+
if row["horizontal_visibility"] == self.NOT_AVAILABLE:
|
961
|
+
row["horizontal_visibility"] = backuprow[
|
962
|
+
"horizontal_visibility"
|
963
|
+
]
|
964
|
+
if row["present_weather"] == self.NOT_AVAILABLE:
|
965
|
+
row["present_weather"] = backuprow["present_weather"]
|
966
|
+
self.parse_csv_row(row)
|
967
|
+
# We only want the latest report
|
968
|
+
break
|
969
|
+
|
970
|
+
elif response.status_code == 304:
|
971
|
+
# The report is already up to date
|
972
|
+
print("Report is already up to date")
|
973
|
+
else:
|
974
|
+
# Handle other status codes
|
975
|
+
print(f"Failed to download report. Status code: {response.status_code}")
|
976
|
+
except Exception as error:
|
977
|
+
print(f"Error in download_weather_report: {type(error)} args: {error.args}")
|
@@ -40,7 +40,7 @@ def get_map(minx,miny,maxx,maxy, map_type: WeatherMapType, background_type: Weat
|
|
40
40
|
if image_width > 1200 or image_height > 1400:
|
41
41
|
raise ValueError("Width and height must not exceed 1200 and 1400 respectively. Please be kind to the DWD servers.")
|
42
42
|
|
43
|
-
url = f"https://maps.dwd.de/geoserver/dwd/wms?service=WMS&version=1.1.0&request=GetMap&layers={
|
43
|
+
url = f"https://maps.dwd.de/geoserver/dwd/wms?service=WMS&version=1.1.0&request=GetMap&layers={background_type.value},{map_type.value}&bbox={minx},{miny},{maxx},{maxy}&width={image_width}&height={image_height}&srs=EPSG:4326&styles=&format=image/png"
|
44
44
|
request = requests.get(url, stream=True)
|
45
45
|
if request.status_code == 200:
|
46
46
|
image = Image.open(BytesIO(request.content))
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/dummy_data_full.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_daily_max.py
RENAMED
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_daily_min.py
RENAMED
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_daily_sum.py
RENAMED
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_get_day_values.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_location_tools.py
RENAMED
File without changes
|
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_parsekml.py
RENAMED
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_region.py
RENAMED
File without changes
|
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_station.py
RENAMED
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_stationsfile.py
RENAMED
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_update.py
RENAMED
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_update_hourly.py
RENAMED
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_uv_index.py
RENAMED
File without changes
|
{simple_dwd_weatherforecast-2.0.28 → simple_dwd_weatherforecast-2.0.31}/tests/test_weather.py
RENAMED
File without changes
|