grib2sail 0.1.0__py3-none-any.whl → 0.2.0__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.
- grib2sail/_version.py +2 -2
- grib2sail/downloader.py +41 -89
- grib2sail/downloader_arom.py +115 -0
- grib2sail/token.py +6 -4
- grib2sail/variables.py +0 -20
- grib2sail/variables_arom.py +17 -0
- {grib2sail-0.1.0.dist-info → grib2sail-0.2.0.dist-info}/METADATA +3 -3
- grib2sail-0.2.0.dist-info/RECORD +15 -0
- grib2sail-0.1.0.dist-info/RECORD +0 -13
- {grib2sail-0.1.0.dist-info → grib2sail-0.2.0.dist-info}/WHEEL +0 -0
- {grib2sail-0.1.0.dist-info → grib2sail-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {grib2sail-0.1.0.dist-info → grib2sail-0.2.0.dist-info}/top_level.txt +0 -0
grib2sail/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.
|
|
32
|
-
__version_tuple__ = version_tuple = (0,
|
|
31
|
+
__version__ = version = '0.2.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 2, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
grib2sail/downloader.py
CHANGED
|
@@ -1,102 +1,54 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
2
|
+
import threading
|
|
3
3
|
import requests
|
|
4
|
-
import
|
|
4
|
+
from rich.progress import Progress
|
|
5
5
|
|
|
6
6
|
from grib2sail.logger import logger
|
|
7
|
+
from grib2sail.downloader_arom import handle_fetch_error_arom, download_arom
|
|
7
8
|
import grib2sail.variables as v
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
thread_local = threading.local()
|
|
11
|
+
|
|
12
|
+
def get_session():
|
|
13
|
+
if not hasattr(thread_local, 'session'):
|
|
14
|
+
thread_local.session = requests.Session()
|
|
15
|
+
return thread_local.session
|
|
9
16
|
|
|
10
17
|
def download_gribs(m, s, d, lat, lon):
|
|
11
18
|
if m.startswith('arome'):
|
|
12
|
-
|
|
19
|
+
download_arom(m, s, d, lat, lon)
|
|
13
20
|
else:
|
|
14
21
|
logger.error_exit(f"Downloader failed: unexpected model: {m}")
|
|
15
22
|
|
|
16
|
-
def
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
def get_layers(model, urls, header):
|
|
24
|
+
# Downloading every layers
|
|
25
|
+
layers = [None] * len(urls)
|
|
26
|
+
with Progress() as progress:
|
|
27
|
+
# Showing a progress bar
|
|
28
|
+
task = progress.add_task('Downloading layers...', total=len(urls))
|
|
29
|
+
|
|
30
|
+
# Downloading the layer
|
|
31
|
+
with ThreadPoolExecutor(max_workers=10) as executor:
|
|
32
|
+
futures = [
|
|
33
|
+
executor.submit(fetch, i, url, header, model)
|
|
34
|
+
for i, url in enumerate(urls)
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
for future in as_completed(futures):
|
|
38
|
+
idx, layer = future.result()
|
|
39
|
+
layers[idx] = layer
|
|
40
|
+
progress.advance(task)
|
|
41
|
+
return layers
|
|
42
|
+
|
|
43
|
+
def fetch(idx, url, headers, model):
|
|
32
44
|
try:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
)
|
|
45
|
+
session = get_session()
|
|
46
|
+
r = session.get(url, headers=headers,timeout = 60)
|
|
47
|
+
r.raise_for_status()
|
|
48
|
+
return idx, r.content
|
|
38
49
|
except Exception as e:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# Parse the GetCapabilities XML response to find the latest available coverage
|
|
42
|
-
lines = [line for line in capa.text.splitlines() if coverages[0] in line]
|
|
43
|
-
if lines:
|
|
44
|
-
# Forecast available dates look like 1970-01-01T00:00:00Z
|
|
45
|
-
# The last line holds the lastest available forecast run
|
|
46
|
-
latestRun = re.search(
|
|
47
|
-
r"\d{4}-\d{2}-\d{2}T\d{2}\.\d{2}\.\d{2}Z",
|
|
48
|
-
lines[-1]
|
|
49
|
-
)
|
|
50
|
-
if latestRun:
|
|
51
|
-
latestRun = latestRun.group()
|
|
50
|
+
if model in v.MODELS[:2]:
|
|
51
|
+
handle_fetch_error_arom(e)
|
|
52
52
|
else:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
else:
|
|
56
|
-
msg = "Error fetching AROM capabilities, couldn't find latest run"
|
|
57
|
-
logger.error_exit(msg)
|
|
58
|
-
|
|
59
|
-
# Download all layers as individual grib files into one output file
|
|
60
|
-
file = Path(f"{model}_{latestRun}_{step}.grib2")
|
|
61
|
-
file.unlink(missing_ok=True)
|
|
62
|
-
with open(file, "ab") as outfile, Progress() as progress:
|
|
63
|
-
# Select forecast prevision time based on user input
|
|
64
|
-
# 3600 means layer is the prevision for 1h after latestRun
|
|
65
|
-
times = list(range(
|
|
66
|
-
int(step[:-1]) * 3600,
|
|
67
|
-
172800+1,
|
|
68
|
-
int(step[:-1]) * 3600)
|
|
69
|
-
)
|
|
70
|
-
logger.debug(f"forecast to downloads are {times}")
|
|
71
|
-
# Showing a progress bar
|
|
72
|
-
task = progress.add_task(
|
|
73
|
-
'Downloading layers...',
|
|
74
|
-
total = len(coverages) * len(times)
|
|
75
|
-
)
|
|
76
|
-
for coverage in coverages:
|
|
77
|
-
for time in times:
|
|
78
|
-
paramCovId = f"&coverageid={coverage}{latestRun}"
|
|
79
|
-
subTime = f"&subset=time({time})"
|
|
80
|
-
subLat = f"&subset=lat({lat[0]},{lat[1]})"
|
|
81
|
-
subLon = f"&subset=long({lon[0]},{lon[1]})"
|
|
82
|
-
if 'SPECIFIC_HEIGHT' in coverage:
|
|
83
|
-
subHeight = '&subset=height(10)'
|
|
84
|
-
else:
|
|
85
|
-
subHeight = ''
|
|
86
|
-
paramSubset = subTime + subLat + subLon + subHeight
|
|
87
|
-
url=v.AROM_URLS[f"{model}_cov"]+ paramCovId + paramSubset
|
|
88
|
-
logger.debug(f"Downloading {url}")
|
|
89
|
-
try:
|
|
90
|
-
r = requests.get(
|
|
91
|
-
url,
|
|
92
|
-
headers = {'Authorization': f"Bearer {token}"},
|
|
93
|
-
timeout = 60)
|
|
94
|
-
r.raise_for_status()
|
|
95
|
-
outfile.write(r.content)
|
|
96
|
-
except requests.exceptions.HTTPError:
|
|
97
|
-
logger.warning(
|
|
98
|
-
f"Failed to download {url} status code is {r.status_code}"
|
|
99
|
-
)
|
|
100
|
-
except Exception as e:
|
|
101
|
-
logger.error_exit(f"Download failed: {e}", to_clean=[file])
|
|
102
|
-
progress.update(task, advance=1)
|
|
53
|
+
logger.error_exit(f"Download failed: {e}")
|
|
54
|
+
return idx, None
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import re
|
|
3
|
+
import requests
|
|
4
|
+
import time as t
|
|
5
|
+
|
|
6
|
+
import grib2sail.variables as v
|
|
7
|
+
import grib2sail.variables_arom as va
|
|
8
|
+
import grib2sail.downloader as d
|
|
9
|
+
from grib2sail.logger import logger
|
|
10
|
+
from grib2sail.token import get_arome_token
|
|
11
|
+
|
|
12
|
+
def download_arom(model, step, data, lat, lon):
|
|
13
|
+
token = get_arome_token()
|
|
14
|
+
|
|
15
|
+
# Coverages list all the individual layers categories to download
|
|
16
|
+
coverages = []
|
|
17
|
+
if v.DATAS[0] in data:
|
|
18
|
+
coverages += [va.AROM_DATAS['wind_u'], va.AROM_DATAS['wind_v']]
|
|
19
|
+
if v.DATAS[1] in data:
|
|
20
|
+
coverages += [va.AROM_DATAS['wind_gust']]
|
|
21
|
+
if v.DATAS[2] in data:
|
|
22
|
+
coverages += [va.AROM_DATAS['pressure']]
|
|
23
|
+
if v.DATAS[3] in data:
|
|
24
|
+
coverages += [va.AROM_DATAS['cloud']]
|
|
25
|
+
|
|
26
|
+
# Get latest available forecast date from arome /GetCapabilities api endpoint
|
|
27
|
+
logger.info('Finding latest available forecast')
|
|
28
|
+
session = d.get_session()
|
|
29
|
+
try:
|
|
30
|
+
capa = session.get(
|
|
31
|
+
va.AROM_URLS[f"{model}_capa"],
|
|
32
|
+
headers = {'Authorization': f"Bearer {token}"},
|
|
33
|
+
timeout = 60,
|
|
34
|
+
)
|
|
35
|
+
except Exception as e:
|
|
36
|
+
logger.error_exit(f"Failed to contact METEO FRANCE servers: {e}")
|
|
37
|
+
|
|
38
|
+
# Parse the GetCapabilities XML response to find the latest available coverage
|
|
39
|
+
lines = [line for line in capa.text.splitlines() if coverages[0] in line]
|
|
40
|
+
if lines:
|
|
41
|
+
# Forecast available dates look like 1970-01-01T00:00:00Z
|
|
42
|
+
# The last line holds the lastest available forecast run
|
|
43
|
+
latestRun = re.search(
|
|
44
|
+
r"\d{4}-\d{2}-\d{2}T\d{2}\.\d{2}\.\d{2}Z",
|
|
45
|
+
lines[-1]
|
|
46
|
+
)
|
|
47
|
+
if latestRun:
|
|
48
|
+
latestRun = latestRun.group()
|
|
49
|
+
else:
|
|
50
|
+
msg = "Error fetching AROM capabilities, couldn't find latest date"
|
|
51
|
+
logger.error_exit(msg)
|
|
52
|
+
else:
|
|
53
|
+
msg = "Error fetching AROM capabilities, couldn't find latest run"
|
|
54
|
+
logger.error_exit(msg)
|
|
55
|
+
|
|
56
|
+
# Select forecast prevision time based on user input
|
|
57
|
+
# 3600 means layer is the prevision for 1h after latestRun
|
|
58
|
+
times = list(range(
|
|
59
|
+
int(step[:-1]) * 3600,
|
|
60
|
+
172800+1,
|
|
61
|
+
int(step[:-1]) * 3600)
|
|
62
|
+
)
|
|
63
|
+
logger.debug(f"Forecast to download are {times}")
|
|
64
|
+
|
|
65
|
+
# Generating the urls to retreive requested layers
|
|
66
|
+
header = {'Authorization': f"Bearer {token}"}
|
|
67
|
+
urls = []
|
|
68
|
+
for coverage in coverages:
|
|
69
|
+
for time in times:
|
|
70
|
+
paramCovId = f"&coverageid={coverage}{latestRun}"
|
|
71
|
+
subTime = f"&subset=time({time})"
|
|
72
|
+
subLat = f"&subset=lat({lat[0]},{lat[1]})"
|
|
73
|
+
subLon = f"&subset=long({lon[0]},{lon[1]})"
|
|
74
|
+
if 'SPECIFIC_HEIGHT' in coverage:
|
|
75
|
+
subHeight = '&subset=height(10)'
|
|
76
|
+
else:
|
|
77
|
+
subHeight = ''
|
|
78
|
+
paramSubset = subTime + subLat + subLon + subHeight
|
|
79
|
+
urls.append(va.AROM_URLS[f"{model}_cov"]+ paramCovId + paramSubset)
|
|
80
|
+
|
|
81
|
+
# Downloading the layers
|
|
82
|
+
layers = []
|
|
83
|
+
if len(urls) < 100:
|
|
84
|
+
layers = d.get_layers(model, urls, header)
|
|
85
|
+
else:
|
|
86
|
+
msg = f"The requested grib has {len(urls)} layers, but MeteoFrance"
|
|
87
|
+
msg += ' servers limit requests to 100 per minute. This program will'
|
|
88
|
+
msg += ' sleep 1 minute every 100 layer util the complete grib file'
|
|
89
|
+
msg += ' is downloaded. You might want to consider reducing the number'
|
|
90
|
+
msg += ' of layers by increasing the step or reducing the number of'
|
|
91
|
+
msg += ' data'
|
|
92
|
+
logger.warning(msg)
|
|
93
|
+
for i in range(0, len(urls), 100):
|
|
94
|
+
layers.extend(d.get_layers(model, urls[i:i+100], header))
|
|
95
|
+
if i+100 < len(urls):
|
|
96
|
+
logger.info('Sleeping 1 minute...')
|
|
97
|
+
t.sleep(60)
|
|
98
|
+
|
|
99
|
+
# Output the file once all the layers have been downloaded
|
|
100
|
+
file = Path(f"{model}_{latestRun}_{step}.grib2")
|
|
101
|
+
file.unlink(missing_ok=True)
|
|
102
|
+
with open(file, "wb") as outfile:
|
|
103
|
+
for layer in layers:
|
|
104
|
+
if layer:
|
|
105
|
+
outfile.write(layer)
|
|
106
|
+
|
|
107
|
+
def handle_fetch_error_arom(e):
|
|
108
|
+
if isinstance(e, requests.exceptions.HTTPError):
|
|
109
|
+
url = e.response.url
|
|
110
|
+
layer = re.search(r"coverageid=(.*?)__", url).group(1)
|
|
111
|
+
time = int(re.search(r"subset=time\(([^()]*)", url).group(1)) / 3600
|
|
112
|
+
logger.warning(f"Missing layer: {layer} at time: {int(time)}h")
|
|
113
|
+
logger.debug(f"Error was {e}")
|
|
114
|
+
else:
|
|
115
|
+
logger.error_exit(f"Download failed: {e}")
|
grib2sail/token.py
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import keyring
|
|
3
3
|
import getpass
|
|
4
|
-
import requests
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
import grib2sail.downloader as d
|
|
6
|
+
from grib2sail import variables_arom as va
|
|
7
7
|
from grib2sail.logger import logger
|
|
8
8
|
|
|
9
9
|
def get_arome_token():
|
|
10
|
+
logger.info('Authenticating to MeteoFrance')
|
|
10
11
|
appId = get_arome_appid()
|
|
12
|
+
session = d.get_session()
|
|
11
13
|
try:
|
|
12
|
-
response =
|
|
13
|
-
|
|
14
|
+
response = session.post(
|
|
15
|
+
va.AROM_URLS['token'],
|
|
14
16
|
data = { 'grant_type': 'client_credentials' },
|
|
15
17
|
headers = { 'Authorization': f"Basic {appId}" },
|
|
16
18
|
timeout = 60,
|
grib2sail/variables.py
CHANGED
|
@@ -1,23 +1,3 @@
|
|
|
1
1
|
MODELS = ['arome_antilles', 'arome001']
|
|
2
2
|
STEPS = ['1h', '3h', '6h', '12h']
|
|
3
3
|
DATAS = ['wind', 'wind_gust', 'pressure', 'cloud', 'rain']
|
|
4
|
-
|
|
5
|
-
AROM_DATAS = {
|
|
6
|
-
'wind_u': 'U_COMPONENT_OF_WIND__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND___',
|
|
7
|
-
'wind_v': 'V_COMPONENT_OF_WIND__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND___',
|
|
8
|
-
'wind_gust': 'WIND_SPEED_GUST__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND___',
|
|
9
|
-
'pressure': 'PRESSURE__MEAN_SEA_LEVEL___',
|
|
10
|
-
'cloud': 'TOTAL_CLOUD_COVER__GROUND_OR_WATER_SURFACE___'
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
AROM_URLS = {
|
|
14
|
-
'token': 'https://portail-api.meteofrance.fr/token',
|
|
15
|
-
f"{MODELS[0]}_cov": 'https://public-api.meteofrance.fr/public/arome/1.0/wcs/MF-NWP-HIGHRES-AROME-OM-0025-ANTIL-WCS/GetCoverage?service=WCS&version=2.0.1&format=application/wmo-grib',
|
|
16
|
-
f"{MODELS[1]}_cov": 'https://public-api.meteofrance.fr/public/arome/1.0/wcs/MF-NWP-HIGHRES-AROME-001-FRANCE-WCS/GetCoverage?service=WCS&version=2.0.1&format=application/wmo-grib',
|
|
17
|
-
f"{MODELS[0]}_capa": 'https://public-api.meteofrance.fr/public/arome/1.0/wcs/MF-NWP-HIGHRES-AROME-OM-0025-ANTIL-WCS/GetCapabilities?service=WCS&version=1.3.0&language=eng',
|
|
18
|
-
f"{MODELS[1]}_capa": 'https://public-api.meteofrance.fr/public/arome/1.0/wcs/MF-NWP-HIGHRES-AROME-001-FRANCE-WCS/GetCapabilities?service=WCS&version=1.3.0&language=eng',
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import grib2sail.variables as v
|
|
2
|
+
|
|
3
|
+
AROM_DATAS = {
|
|
4
|
+
'wind_u': 'U_COMPONENT_OF_WIND__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND___',
|
|
5
|
+
'wind_v': 'V_COMPONENT_OF_WIND__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND___',
|
|
6
|
+
'wind_gust': 'WIND_SPEED_GUST__SPECIFIC_HEIGHT_LEVEL_ABOVE_GROUND___',
|
|
7
|
+
'pressure': 'PRESSURE__MEAN_SEA_LEVEL___',
|
|
8
|
+
'cloud': 'TOTAL_CLOUD_COVER__GROUND_OR_WATER_SURFACE___'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
AROM_URLS = {
|
|
12
|
+
'token': 'https://portail-api.meteofrance.fr/token',
|
|
13
|
+
f"{v.MODELS[0]}_cov": 'https://public-api.meteofrance.fr/public/arome/1.0/wcs/MF-NWP-HIGHRES-AROME-OM-0025-ANTIL-WCS/GetCoverage?service=WCS&version=2.0.1&format=application/wmo-grib',
|
|
14
|
+
f"{v.MODELS[1]}_cov": 'https://public-api.meteofrance.fr/public/arome/1.0/wcs/MF-NWP-HIGHRES-AROME-001-FRANCE-WCS/GetCoverage?service=WCS&version=2.0.1&format=application/wmo-grib',
|
|
15
|
+
f"{v.MODELS[0]}_capa": 'https://public-api.meteofrance.fr/public/arome/1.0/wcs/MF-NWP-HIGHRES-AROME-OM-0025-ANTIL-WCS/GetCapabilities?service=WCS&version=1.3.0&language=eng',
|
|
16
|
+
f"{v.MODELS[1]}_capa": 'https://public-api.meteofrance.fr/public/arome/1.0/wcs/MF-NWP-HIGHRES-AROME-001-FRANCE-WCS/GetCapabilities?service=WCS&version=1.3.0&language=eng',
|
|
17
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: grib2sail
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Grib files downloader for sailing purposes
|
|
5
5
|
Author-email: Chinkara <poubelledechinkara@outlook.com>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -17,7 +17,7 @@ Dynamic: license-file
|
|
|
17
17
|
# GRIB2Sail
|
|
18
18
|
|
|
19
19
|

|
|
20
|
-

|
|
21
21
|

|
|
22
22
|
|
|
23
23
|
Grib files downloader for sailing purposes.
|
|
@@ -37,7 +37,7 @@ To download GRIB from meteofrance's models (Aome), you must create a free
|
|
|
37
37
|
account on meteofrance.fr. The procedure is as follow:
|
|
38
38
|
1. Create an account on [the Météo-France API portal](https://portail-api.meteofrance.fr)
|
|
39
39
|
2. Subscribe to the desired service (Arome)
|
|
40
|
-
3. Go to "My API" then, from your
|
|
40
|
+
3. Go to "My API" then, from your subscribed model: "Generate Token"
|
|
41
41
|
4. Checkout the curl field at the bottom, it looks like :
|
|
42
42
|
```bash
|
|
43
43
|
curl -k -X POST https://portal-api.meteofrance.fr/token -d "grant_type=client_credentials" -H "Authorization: Basic ABCDEF1234abcdef"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
grib2sail/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
grib2sail/__main__.py,sha256=U5PTpeJRtXGpcO8YPUdCD2hORF8T68IM6JW1-dDyYqE,37
|
|
3
|
+
grib2sail/_version.py,sha256=Dg8AmJomLVpjKL6prJylOONZAPRtB86LOce7dorQS_A,704
|
|
4
|
+
grib2sail/cli.py,sha256=3IcGISWNF9XpDhSdSqb6z5bnc2YFmKlQKtTPH0M7hzI,2247
|
|
5
|
+
grib2sail/downloader.py,sha256=ZQDaPabDuKlb7CqGxwabI73T5pKC_jVdqcCyT6A6ch4,1566
|
|
6
|
+
grib2sail/downloader_arom.py,sha256=FA9IDvhcBiEY7uJuHTcuLY-Ku4US5uPV5wLks3maNuw,4038
|
|
7
|
+
grib2sail/logger.py,sha256=_erYrZlKPjhDu-reFkyylodFgSVtOcXJg5F1drYeQNY,456
|
|
8
|
+
grib2sail/token.py,sha256=gNYCBNEGmjMb_RGq79wvtflhKxqiiX4GXpqfMR3eiJ0,1443
|
|
9
|
+
grib2sail/variables.py,sha256=M0ZcusbURgPuI1ZbRjnCqRr1Ke-2057cz3EycCHYB2E,133
|
|
10
|
+
grib2sail/variables_arom.py,sha256=RNJZT7O5EFa4HtVYrUuH7EHwxZ9u_63F1vkBUekfOfo,1169
|
|
11
|
+
grib2sail-0.2.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
12
|
+
grib2sail-0.2.0.dist-info/METADATA,sha256=7NqzFLPAM-6KFKU6Dk_bVF74t8FgxfFufzjpFaPJeRE,2545
|
|
13
|
+
grib2sail-0.2.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
14
|
+
grib2sail-0.2.0.dist-info/top_level.txt,sha256=ubF1tLZ8ZWARUC9s6_0fRsuUKbVqOZmrp8UEGEBm8aE,10
|
|
15
|
+
grib2sail-0.2.0.dist-info/RECORD,,
|
grib2sail-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
grib2sail/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
grib2sail/__main__.py,sha256=U5PTpeJRtXGpcO8YPUdCD2hORF8T68IM6JW1-dDyYqE,37
|
|
3
|
-
grib2sail/_version.py,sha256=5jwwVncvCiTnhOedfkzzxmxsggwmTBORdFL_4wq0ZeY,704
|
|
4
|
-
grib2sail/cli.py,sha256=3IcGISWNF9XpDhSdSqb6z5bnc2YFmKlQKtTPH0M7hzI,2247
|
|
5
|
-
grib2sail/downloader.py,sha256=9dyBk02ZA1XgB0DXkUtEmo1fMNosPR-B7fs4wPbLkQA,3581
|
|
6
|
-
grib2sail/logger.py,sha256=_erYrZlKPjhDu-reFkyylodFgSVtOcXJg5F1drYeQNY,456
|
|
7
|
-
grib2sail/token.py,sha256=ciyJNhJuXCHlPEFOIJfqpE0XNJr3FwGbdJmhagwzy04,1346
|
|
8
|
-
grib2sail/variables.py,sha256=JInLyk0ShWBffNjokDW7u2RAyRSUj9KZxRiQXVppwU4,1266
|
|
9
|
-
grib2sail-0.1.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
10
|
-
grib2sail-0.1.0.dist-info/METADATA,sha256=7Oa_OtDxJgJyI0Q7e1TmaYGtVrVaF64gFKj1dXj8wRs,2527
|
|
11
|
-
grib2sail-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
-
grib2sail-0.1.0.dist-info/top_level.txt,sha256=ubF1tLZ8ZWARUC9s6_0fRsuUKbVqOZmrp8UEGEBm8aE,10
|
|
13
|
-
grib2sail-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|