wxdata 0.1.1__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.
- wxdata/__init__.py +137 -0
- wxdata/calc/__init__.py +3 -0
- wxdata/calc/derived_fields.py +44 -0
- wxdata/calc/kinematics.py +29 -0
- wxdata/calc/thermodynamics.py +49 -0
- wxdata/calc/unit_conversion.py +63 -0
- wxdata/client/__init__.py +1 -0
- wxdata/client/client.py +251 -0
- wxdata/ecmwf/__init__.py +7 -0
- wxdata/ecmwf/ecmwf.py +745 -0
- wxdata/ecmwf/file_funcs.py +110 -0
- wxdata/ecmwf/paths.py +47 -0
- wxdata/ecmwf/url_scanners.py +628 -0
- wxdata/fems/__init__.py +7 -0
- wxdata/fems/fems.py +667 -0
- wxdata/fems/raws_sigs.py +1792 -0
- wxdata/gefs/__init__.py +7 -0
- wxdata/gefs/exception_messages.py +90 -0
- wxdata/gefs/file_funcs.py +175 -0
- wxdata/gefs/gefs.py +1171 -0
- wxdata/gefs/paths.py +58 -0
- wxdata/gefs/process.py +3154 -0
- wxdata/gefs/url_scanners.py +2430 -0
- wxdata/gfs/__init__.py +5 -0
- wxdata/gfs/exception_messages.py +30 -0
- wxdata/gfs/gfs.py +1112 -0
- wxdata/gfs/paths.py +51 -0
- wxdata/gfs/url_scanners.py +1529 -0
- wxdata/metars/__init__.py +1 -0
- wxdata/metars/metar_obs.py +106 -0
- wxdata/noaa/__init__.py +1 -0
- wxdata/noaa/nws.py +532 -0
- wxdata/rtma/__init__.py +4 -0
- wxdata/rtma/file_funcs.py +68 -0
- wxdata/rtma/keys.py +56 -0
- wxdata/rtma/process.py +291 -0
- wxdata/rtma/rtma.py +511 -0
- wxdata/rtma/url_scanners.py +617 -0
- wxdata/soundings/__init__.py +1 -0
- wxdata/soundings/wyoming_soundings.py +658 -0
- wxdata/utils/__init__.py +26 -0
- wxdata/utils/coords.py +121 -0
- wxdata/utils/ecmwf_post_processing.py +1049 -0
- wxdata/utils/file_funcs.py +204 -0
- wxdata/utils/file_scanner.py +155 -0
- wxdata/utils/gefs_post_processing.py +3100 -0
- wxdata/utils/gfs_post_processing.py +2011 -0
- wxdata/utils/nomads_gribfilter.py +187 -0
- wxdata/utils/recycle_bin.py +74 -0
- wxdata/utils/rtma_post_processing.py +299 -0
- wxdata/utils/tools.py +212 -0
- wxdata-0.1.1.dist-info/METADATA +319 -0
- wxdata-0.1.1.dist-info/RECORD +56 -0
- wxdata-0.1.1.dist-info/WHEEL +5 -0
- wxdata-0.1.1.dist-info/licenses/LICENSE +21 -0
- wxdata-0.1.1.dist-info/top_level.txt +1 -0
wxdata/__init__.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This file hosts all of the functions in the WxData Python library that directly interact with the user.
|
|
3
|
+
|
|
4
|
+
(C) Eric J. Drewitz 2025
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
This section of functions are for users who want full wxdata functionality.
|
|
10
|
+
|
|
11
|
+
These functions do the following:
|
|
12
|
+
|
|
13
|
+
1) Scan for the latest available data.
|
|
14
|
+
- If the data on your local machine is not up to date, new data will download automatically.
|
|
15
|
+
- If the data on your local machine is up to date, new data download is bypassed.
|
|
16
|
+
- This is a safeguard that prevents excessive requests on the data servers.
|
|
17
|
+
|
|
18
|
+
2) Builds the wxdata directory to store the weather data files.
|
|
19
|
+
- Scans for the directory branch and builds the branch if it does not exist.
|
|
20
|
+
|
|
21
|
+
3) Downloads the data.
|
|
22
|
+
- Users can define their VPN/PROXY IP Address as a (dict) in their script and pass their
|
|
23
|
+
VPN/PROXY IP address into the function to avoid SSL Certificate errors when requesting data.
|
|
24
|
+
- Algorithm allows for up to 5 retries with a 30 second break between each retry to resolve connection
|
|
25
|
+
interruptions while not overburdening the data servers.
|
|
26
|
+
|
|
27
|
+
4) Pre-processes the data via filename formatting and correctly filing in the wxdata directory.
|
|
28
|
+
|
|
29
|
+
5) Post-processing by doing the following tasks:
|
|
30
|
+
- Remapping GRIB2 variable keys into plain language variable keys.
|
|
31
|
+
- Fixing dataset build errors and grouping all variables together.
|
|
32
|
+
- Transforms longitude from 0 to 360 degrees into -180 to 180 degrees.
|
|
33
|
+
- Subsetting the data to the latitude/longitude boundaries specified by the user.
|
|
34
|
+
- Converting temperature from kelvin to units the user wants (default is Celsius).
|
|
35
|
+
- Returning a post-processed xarray.array to the user.
|
|
36
|
+
|
|
37
|
+
6) Preserves system memory by doing the following:
|
|
38
|
+
- Deleting old data files before each new download.
|
|
39
|
+
- When clear_recycle_bin=True, the user's recycle bin is also cleared.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
# Global Forecast System (GFS)
|
|
43
|
+
from wxdata.gfs.gfs import(
|
|
44
|
+
gfs_0p25,
|
|
45
|
+
gfs_0p25_secondary_parameters,
|
|
46
|
+
gfs_0p50
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Global Ensemble Forecast System (GEFS)
|
|
51
|
+
from wxdata.gefs.gefs import(
|
|
52
|
+
|
|
53
|
+
gefs_0p50,
|
|
54
|
+
gefs_0p50_secondary_parameters,
|
|
55
|
+
gefs_0p25
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# European Centre for Medium-Range Weather Forecasts (ECMWF)
|
|
59
|
+
from wxdata.ecmwf.ecmwf import(
|
|
60
|
+
ecmwf_ifs,
|
|
61
|
+
ecmwf_aifs,
|
|
62
|
+
ecmwf_ifs_high_res,
|
|
63
|
+
ecmwf_ifs_wave
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# FEMS RAWS Network
|
|
67
|
+
from wxdata.fems.fems import(
|
|
68
|
+
get_single_station_data,
|
|
69
|
+
get_raws_sig_data,
|
|
70
|
+
get_nfdrs_forecast_data
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Real-Time Mesoscale Analysis (RTMA)
|
|
74
|
+
from wxdata.rtma.rtma import(
|
|
75
|
+
rtma,
|
|
76
|
+
rtma_comparison
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# NOAA
|
|
80
|
+
# Storm Prediction Center Outlooks
|
|
81
|
+
# National Weather Service Forecasts
|
|
82
|
+
from wxdata.noaa.nws import get_ndfd_grids
|
|
83
|
+
|
|
84
|
+
# Observed Upper-Air Soundings
|
|
85
|
+
# (University of Wyoming Database)
|
|
86
|
+
from wxdata.soundings.wyoming_soundings import get_observed_sounding_data
|
|
87
|
+
|
|
88
|
+
# METAR Observational Data (From NOAA)
|
|
89
|
+
from wxdata.metars.metar_obs import download_metar_data
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
"""
|
|
93
|
+
This section hosts the utility functions accessable to the user.
|
|
94
|
+
|
|
95
|
+
These functions provide helpful utilities when analyzing weather data.
|
|
96
|
+
|
|
97
|
+
Utility functions are geared towards the following types of users:
|
|
98
|
+
|
|
99
|
+
1) Users who want to use their own scripts to download the data however, they
|
|
100
|
+
would like to use the wxdata post-processing capabilities.
|
|
101
|
+
|
|
102
|
+
2) Users who want to make hemispheric graphics or any graphics where cyclic points
|
|
103
|
+
resolve missing data along the prime meridian or international dateline.
|
|
104
|
+
"""
|
|
105
|
+
# Global Forecast System (GFS)
|
|
106
|
+
import wxdata.utils.gfs_post_processing as gfs_post_processing
|
|
107
|
+
|
|
108
|
+
# Global Ensemble Forecast System (GEFS)
|
|
109
|
+
import wxdata.utils.gefs_post_processing as gefs_post_processing
|
|
110
|
+
|
|
111
|
+
# European Centre for Medium-Range Weather Forecasts (ECMWF)
|
|
112
|
+
import wxdata.utils.ecmwf_post_processing as ecmwf_post_processing
|
|
113
|
+
|
|
114
|
+
# Real-Time Mesoscale Analysis (RTMA)
|
|
115
|
+
from wxdata.utils.rtma_post_processing import process_rtma_data
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# WxData function using cartopy to make cyclic points
|
|
119
|
+
# This is for users who wish to make graphics that cross the -180/180 degree longitude line
|
|
120
|
+
# This is commonly used for Hemispheric graphics
|
|
121
|
+
# Function that converts the longitude dimension in an xarray.array
|
|
122
|
+
# From 0 to 360 to -180 to 180
|
|
123
|
+
from wxdata.utils.coords import(
|
|
124
|
+
cyclic_point,
|
|
125
|
+
shift_longitude
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Functions to pixel query and query pixels along a line between points A and B
|
|
129
|
+
from wxdata.utils.tools import(
|
|
130
|
+
pixel_query,
|
|
131
|
+
line_query
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# This is the wxdata HTTPS Client with full VPN/PROXY Support
|
|
135
|
+
import wxdata.client.client as client
|
|
136
|
+
|
|
137
|
+
|
wxdata/calc/__init__.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This file hosts the functions that will calculate derived model fields.
|
|
3
|
+
|
|
4
|
+
These calculations will occur after the post-processing.
|
|
5
|
+
|
|
6
|
+
(C) Eric J. Drewitz
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import xarray as xr
|
|
10
|
+
import metpy.calc as mpcalc
|
|
11
|
+
|
|
12
|
+
from metpy.units import units
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def rtma_derived_fields(ds,
|
|
16
|
+
convert_temperature,
|
|
17
|
+
convert_to):
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
if convert_temperature == True:
|
|
21
|
+
if convert_to == 'celsius':
|
|
22
|
+
unit = 'degC'
|
|
23
|
+
else:
|
|
24
|
+
unit = 'degF'
|
|
25
|
+
else:
|
|
26
|
+
unit = 'kelvin'
|
|
27
|
+
except Exception as e:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
ds['2m_apparent_temperature'] = mpcalc.apparent_temperature(ds['2m_temperature'] * units(unit), ds['2m_relative_humidity'], ds['10m_wind_speed'] * units('m/s'))
|
|
32
|
+
except Exception as e:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
ds = ds.metpy.dequantify()
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
ds['2m_dew_point_depression'] = ds['2m_temperature'] - ds['2m_dew_point']
|
|
40
|
+
except Exception as e:
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
return ds
|
|
44
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This file hosts functions that perform calculations using kinematics equations
|
|
3
|
+
|
|
4
|
+
(C) Eric J. Drewitz 2025
|
|
5
|
+
"""
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
def get_u_and_v(wind_speed,
|
|
9
|
+
wind_dir):
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
This function calculates the u and u wind components
|
|
13
|
+
|
|
14
|
+
Required Arguments:
|
|
15
|
+
|
|
16
|
+
1) wind_speed (Float or Integer)
|
|
17
|
+
|
|
18
|
+
2) wind_direction (Float or Integer)
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
|
|
23
|
+
u and v wind components
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
u = wind_speed * np.cos(wind_dir)
|
|
27
|
+
v = wind_speed * np.sin(wind_dir)
|
|
28
|
+
|
|
29
|
+
return u, v
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This file hosts functions that perform calculations using thermodynamics equations
|
|
3
|
+
|
|
4
|
+
(C) Eric J. Drewitz 2025
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
def saturation_vapor_pressure(temperature):
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
This function calculates the saturation vapor pressure from temperature.
|
|
13
|
+
This function uses the formula from Bolton 1980.
|
|
14
|
+
|
|
15
|
+
Required Arguments:
|
|
16
|
+
|
|
17
|
+
1) temperature (Float or Integer)
|
|
18
|
+
|
|
19
|
+
Returns
|
|
20
|
+
-------
|
|
21
|
+
|
|
22
|
+
The saturation vapor pressure
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
e = 6.112 * np.exp(17.67 * (temperature) / (temperature + 243.5))
|
|
26
|
+
return e
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def relative_humidity(temperature,
|
|
30
|
+
dewpoint):
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
This function calculates the relative humidity from temperature and dewpoint.
|
|
34
|
+
|
|
35
|
+
Required Arguments:
|
|
36
|
+
|
|
37
|
+
1) temperature (Float or Integer)
|
|
38
|
+
|
|
39
|
+
2) dewpoint (Float or Integer)
|
|
40
|
+
|
|
41
|
+
Returns
|
|
42
|
+
-------
|
|
43
|
+
|
|
44
|
+
The relative humidity
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
e = saturation_vapor_pressure(dewpoint)
|
|
48
|
+
e_s = saturation_vapor_pressure(temperature)
|
|
49
|
+
return (e / e_s) * 100
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This file hosts the functions that convert units
|
|
3
|
+
|
|
4
|
+
(C) Eric J. Drewitz
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
def convert_temperature_units(ds,
|
|
8
|
+
convert_to,
|
|
9
|
+
cat='mean'):
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
This function converts temperature variables from Kelvin to Fahrenheit
|
|
13
|
+
|
|
14
|
+
Required Arguments:
|
|
15
|
+
|
|
16
|
+
1) ds (xarray.array) - The xarray data array
|
|
17
|
+
|
|
18
|
+
2) convert_to (String) - Convert to Celsius or Fahrenheit
|
|
19
|
+
|
|
20
|
+
Optional Arguments: None
|
|
21
|
+
|
|
22
|
+
Returns
|
|
23
|
+
-------
|
|
24
|
+
|
|
25
|
+
Temperature variables in Fahrenheit or Celsius
|
|
26
|
+
"""
|
|
27
|
+
convert_to = convert_to.lower()
|
|
28
|
+
cat = cat.lower()
|
|
29
|
+
|
|
30
|
+
params = list(ds.data_vars.keys())
|
|
31
|
+
|
|
32
|
+
keyword1 = 'temperature'
|
|
33
|
+
keyword2 = 'dew_point'
|
|
34
|
+
keyword3 = 'dew_point_depression'
|
|
35
|
+
for param in params:
|
|
36
|
+
if keyword1 in param:
|
|
37
|
+
if convert_to == 'celsius':
|
|
38
|
+
if cat != 'spread':
|
|
39
|
+
ds[param] = ds[param] - 273.15
|
|
40
|
+
else:
|
|
41
|
+
ds[param] = ds[param]
|
|
42
|
+
else:
|
|
43
|
+
if cat != 'spread':
|
|
44
|
+
frac = 9/5
|
|
45
|
+
ds[param] = ds[param] - 273.15
|
|
46
|
+
ds[param] = (ds[param] * frac) + 32
|
|
47
|
+
else:
|
|
48
|
+
ds[param] = ds[param] * 1.8
|
|
49
|
+
if keyword2 in param:
|
|
50
|
+
if convert_to == 'celsius':
|
|
51
|
+
if cat != 'spread':
|
|
52
|
+
ds[param] = ds[param] - 273.15
|
|
53
|
+
else:
|
|
54
|
+
ds[param] = ds[param]
|
|
55
|
+
else:
|
|
56
|
+
if cat != 'spread':
|
|
57
|
+
frac = 9/5
|
|
58
|
+
ds[param] = ds[param] - 273.15
|
|
59
|
+
ds[param] = (ds[param] * frac) + 32
|
|
60
|
+
else:
|
|
61
|
+
ds[param] = ds[param] * 1.8
|
|
62
|
+
|
|
63
|
+
return ds
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import wxdata.client.client
|
wxdata/client/client.py
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import time
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from io import BytesIO
|
|
8
|
+
from wxdata.utils.recycle_bin import *
|
|
9
|
+
|
|
10
|
+
def get_gridded_data(url,
|
|
11
|
+
path,
|
|
12
|
+
filename,
|
|
13
|
+
proxies=None,
|
|
14
|
+
chunk_size=8192,
|
|
15
|
+
notifications='on',
|
|
16
|
+
clear_recycle_bin=True):
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
This function is the client that retrieves gridded weather/climate data (GRIB2 and NETCDF) files.
|
|
20
|
+
This client supports VPN/PROXY connections.
|
|
21
|
+
|
|
22
|
+
Required Arguments:
|
|
23
|
+
|
|
24
|
+
1) url (String) - The download URL to the file.
|
|
25
|
+
|
|
26
|
+
2) path (String) - The directory where the file is saved to.
|
|
27
|
+
|
|
28
|
+
3) filename (String) - The name the user wishes to save the file as.
|
|
29
|
+
|
|
30
|
+
Optional Arguments:
|
|
31
|
+
|
|
32
|
+
1) proxies (dict or None) - Default=None. If the user is using proxy server(s), the user must change the following:
|
|
33
|
+
|
|
34
|
+
proxies=None ---> proxies={
|
|
35
|
+
'http':'http://url',
|
|
36
|
+
'https':'https://url'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
2) chunk_size (Integer) - Default=8192. The size of the chunks when writing the GRIB/NETCDF data to a file.
|
|
40
|
+
|
|
41
|
+
3) notifications (String) - Default='on'. Notification when a file is downloaded and saved to {path}
|
|
42
|
+
|
|
43
|
+
4) clear_recycle_bin (Boolean) - Default=True. When set to True, the contents in your recycle/trash bin will be deleted with each run
|
|
44
|
+
of the program you are calling WxData. This setting is to help preserve memory on the machine.
|
|
45
|
+
|
|
46
|
+
Returns
|
|
47
|
+
-------
|
|
48
|
+
|
|
49
|
+
Gridded weather/climate data files (GRIB2 or NETCDF) saved to {path}
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
if clear_recycle_bin == True:
|
|
53
|
+
clear_recycle_bin_windows()
|
|
54
|
+
clear_trash_bin_mac()
|
|
55
|
+
clear_trash_bin_linux()
|
|
56
|
+
else:
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
os.makedirs(f"{path}")
|
|
61
|
+
except Exception as e:
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
if proxies == None:
|
|
65
|
+
try:
|
|
66
|
+
with requests.get(url, stream=True) as r:
|
|
67
|
+
r.raise_for_status()
|
|
68
|
+
with open(f"{path}/{filename}", 'wb') as f:
|
|
69
|
+
for chunk in r.iter_content(chunk_size=chunk_size):
|
|
70
|
+
f.write(chunk)
|
|
71
|
+
if notifications == 'on':
|
|
72
|
+
print(f"Successfully saved {filename} to f:{path}")
|
|
73
|
+
else:
|
|
74
|
+
pass
|
|
75
|
+
except requests.exceptions.RequestException as e:
|
|
76
|
+
for i in range(0, 6, 1):
|
|
77
|
+
if i < 3:
|
|
78
|
+
print(f"Alert: Network connection unstable.\nWaiting 30 seconds then automatically trying again.\nAttempts remaining: {5 - i}")
|
|
79
|
+
time.sleep(30)
|
|
80
|
+
else:
|
|
81
|
+
print(f"Alert: Network connection unstable.\nWaiting 60 seconds then automatically trying again.\nAttempts remaining: {5 - i}")
|
|
82
|
+
time.sleep(60)
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
with requests.get(url, stream=True) as r:
|
|
86
|
+
r.raise_for_status()
|
|
87
|
+
with open(f"{path}/{filename}", 'wb') as f:
|
|
88
|
+
for chunk in r.iter_content(chunk_size=chunk_size):
|
|
89
|
+
f.write(chunk)
|
|
90
|
+
if notifications == 'on':
|
|
91
|
+
print(f"Successfully saved {filename} to f:{path}")
|
|
92
|
+
break
|
|
93
|
+
except requests.exceptions.RequestException as e:
|
|
94
|
+
i = i
|
|
95
|
+
if i >= 5:
|
|
96
|
+
print(f"Error - File Cannot Be Downloaded.\nError Code: {e}")
|
|
97
|
+
sys.exit(1)
|
|
98
|
+
|
|
99
|
+
else:
|
|
100
|
+
try:
|
|
101
|
+
with requests.get(url, stream=True, proxies=proxies) as r:
|
|
102
|
+
r.raise_for_status()
|
|
103
|
+
with open(f"{path}/{filename}", 'wb') as f:
|
|
104
|
+
for chunk in r.iter_content(chunk_size=chunk_size):
|
|
105
|
+
f.write(chunk)
|
|
106
|
+
if notifications == 'on':
|
|
107
|
+
print(f"Successfully saved {filename} to f:{path}")
|
|
108
|
+
else:
|
|
109
|
+
pass
|
|
110
|
+
except requests.exceptions.RequestException as e:
|
|
111
|
+
for i in range(0, 6, 1):
|
|
112
|
+
if i < 3:
|
|
113
|
+
print(f"Alert: Network connection unstable.\nWaiting 30 seconds then automatically trying again.\nAttempts remaining: {5 - i}")
|
|
114
|
+
time.sleep(30)
|
|
115
|
+
else:
|
|
116
|
+
print(f"Alert: Network connection unstable.\nWaiting 60 seconds then automatically trying again.\nAttempts remaining: {5 - i}")
|
|
117
|
+
time.sleep(60)
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
with requests.get(url, stream=True, proxies=proxies) as r:
|
|
121
|
+
r.raise_for_status()
|
|
122
|
+
with open(f"{path}/{filename}", 'wb') as f:
|
|
123
|
+
for chunk in r.iter_content(chunk_size=chunk_size):
|
|
124
|
+
f.write(chunk)
|
|
125
|
+
if notifications == 'on':
|
|
126
|
+
print(f"Successfully saved {filename} to f:{path}")
|
|
127
|
+
break
|
|
128
|
+
except requests.exceptions.RequestException as e:
|
|
129
|
+
i = i
|
|
130
|
+
if i >= 5:
|
|
131
|
+
print(f"Error - File Cannot Be Downloaded.\nError Code: {e}")
|
|
132
|
+
sys.exit(1)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def get_csv_data(url,
|
|
136
|
+
path,
|
|
137
|
+
filename,
|
|
138
|
+
proxies=None,
|
|
139
|
+
notifications='on',
|
|
140
|
+
return_pandas_df=True,
|
|
141
|
+
clear_recycle_bin=True):
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
This function is the client that retrieves CSV files from the web.
|
|
145
|
+
This client supports VPN/PROXY connections.
|
|
146
|
+
User also has the ability to read the CSV file and return a Pandas.DataFrame()
|
|
147
|
+
|
|
148
|
+
Required Arguments:
|
|
149
|
+
|
|
150
|
+
1) url (String) - The download URL to the file.
|
|
151
|
+
|
|
152
|
+
2) path (String) - The directory where the file is saved to.
|
|
153
|
+
|
|
154
|
+
3) filename (String) - The name the user wishes to save the file as.
|
|
155
|
+
|
|
156
|
+
Optional Arguments:
|
|
157
|
+
|
|
158
|
+
1) proxies (dict or None) - Default=None. If the user is using proxy server(s), the user must change the following:
|
|
159
|
+
|
|
160
|
+
proxies=None ---> proxies={
|
|
161
|
+
'http':'http://url',
|
|
162
|
+
'https':'https://url'
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
2) notifications (String) - Default='on'. Notification when a file is downloaded and saved to {path}
|
|
166
|
+
|
|
167
|
+
3) return_pandas_df (Boolean) - Default=True. When set to True, a Pandas.DataFrame() of the data inside the CSV file will be returned to the user.
|
|
168
|
+
|
|
169
|
+
4) clear_recycle_bin (Boolean) - Default=True. When set to True, the contents in your recycle/trash bin will be deleted with each run
|
|
170
|
+
of the program you are calling WxData. This setting is to help preserve memory on the machine.
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
|
|
176
|
+
A CSV file saved to {path}
|
|
177
|
+
|
|
178
|
+
if return_pandas_df=True - A Pandas.DataFrame()
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
if clear_recycle_bin == True:
|
|
182
|
+
clear_recycle_bin_windows()
|
|
183
|
+
clear_trash_bin_mac()
|
|
184
|
+
clear_trash_bin_linux()
|
|
185
|
+
else:
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
try:
|
|
189
|
+
os.makedirs(f"{path}")
|
|
190
|
+
except Exception as e:
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
if proxies==None:
|
|
194
|
+
try:
|
|
195
|
+
response = requests.get(url, stream=True)
|
|
196
|
+
except Exception as e:
|
|
197
|
+
for i in range(0, 6, 1):
|
|
198
|
+
if i < 3:
|
|
199
|
+
print(f"Alert: Network connection unstable.\nWaiting 30 seconds then automatically trying again.\nAttempts remaining: {5 - i}")
|
|
200
|
+
time.sleep(30)
|
|
201
|
+
else:
|
|
202
|
+
print(f"Alert: Network connection unstable.\nWaiting 60 seconds then automatically trying again.\nAttempts remaining: {5 - i}")
|
|
203
|
+
time.sleep(60)
|
|
204
|
+
|
|
205
|
+
try:
|
|
206
|
+
response = requests.get(url, stream=True)
|
|
207
|
+
break
|
|
208
|
+
except Exception as e:
|
|
209
|
+
i = i
|
|
210
|
+
if i >= 5:
|
|
211
|
+
print(f"Error - File Cannot Be Downloaded.\nError Code: {e}")
|
|
212
|
+
sys.exit(1)
|
|
213
|
+
|
|
214
|
+
else:
|
|
215
|
+
try:
|
|
216
|
+
response = requests.get(url, stream=True, proxies=proxies)
|
|
217
|
+
except Exception as e:
|
|
218
|
+
for i in range(0, 6, 1):
|
|
219
|
+
if i < 3:
|
|
220
|
+
print(f"Alert: Network connection unstable.\nWaiting 30 seconds then automatically trying again.\nAttempts remaining: {5 - i}")
|
|
221
|
+
time.sleep(30)
|
|
222
|
+
else:
|
|
223
|
+
print(f"Alert: Network connection unstable.\nWaiting 60 seconds then automatically trying again.\nAttempts remaining: {5 - i}")
|
|
224
|
+
time.sleep(60)
|
|
225
|
+
|
|
226
|
+
try:
|
|
227
|
+
response = requests.get(url, stream=True, proxies=proxies)
|
|
228
|
+
break
|
|
229
|
+
except Exception as e:
|
|
230
|
+
i = i
|
|
231
|
+
if i >= 5:
|
|
232
|
+
print(f"Error - File Cannot Be Downloaded.\nError Code: {e}")
|
|
233
|
+
sys.exit(1)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
data_stream = BytesIO(response.content)
|
|
237
|
+
|
|
238
|
+
df = pd.read_csv(data_stream)
|
|
239
|
+
|
|
240
|
+
df.to_csv(f"{path}/{filename}", index=False)
|
|
241
|
+
if notifications == True:
|
|
242
|
+
print(f"{filename} saved to {path}")
|
|
243
|
+
else:
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
if return_pandas_df == True:
|
|
247
|
+
|
|
248
|
+
return df
|
|
249
|
+
|
|
250
|
+
else:
|
|
251
|
+
pass
|