-
Notifications
You must be signed in to change notification settings - Fork 0
/
ecmwf.py
141 lines (102 loc) · 5.4 KB
/
ecmwf.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import requests
import json
import datetime
import time
import logging
import pandas as pd
d10_plume = 'classical_plume'
d10_eps = 'classical_10d'
d15_eps = 'classical_15d'
ALL_EPSGRAM = [d10_plume,d10_eps,d15_eps]
class EcmwfApi():
def __init__(self,station_config):
class Station():
def __init__(self,name,lat,lon):
self.name = name
self.lat = lat
self.lon = lon
self.base_time = None
self._API_URL = "https://charts.ecmwf.int/opencharts-api/v1/"
self._stations = [Station(**station_data) for station_data in station_config]
self._epsgrams = ALL_EPSGRAM
self._plots_for_broadcast = {}
# populate stations with valid run
for Station in self._stations:
Station.base_time = self._latest_confirmed_run(Station)
logging.debug('init station {} with base_time {}'.format(Station.name,Station.base_time))
def _is_dangerous_time(self):
# time is dangerous around 11:59 - 12:01 and 23:59 - 00:01
return ( (datetime.time(11, 59, 0) < datetime.datetime.now().time() < datetime.time(12,1,0)) or \
(datetime.time(23, 59, 0) < datetime.datetime.now().time() < datetime.time(0,1,0)) )
def _latest_run(self):
t_now = datetime.datetime.now()
t_now_rounded = pd.Timestamp.now().round(freq='12H').to_pydatetime()
while (self._is_dangerous_time()):
time.sleep(10)
logging.info('snooze 10s because time close to noon/midnight: {}'.format(datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ')))
# rounding ends up in future
if t_now <= t_now_rounded:
t_now_rounded = t_now_rounded - datetime.timedelta(hours = 12)
return t_now_rounded.strftime('%Y-%m-%dT%H:%M:%SZ')
def _latest_confirmed_run(self,station):
eps_type = 'classical_plume'
product = 'opencharts_meteogram'
data = self._get_API_data_for_epsgram(station,self._latest_run(),product,eps_type)
# API cannot provide link for non-existing plot
try:
data["data"]["link"]["href"]
return self._latest_run()
except KeyError:
latest_run_time_object = datetime.datetime.strptime(self._latest_run(),'%Y-%m-%dT%H:%M:%SZ') - datetime.timedelta(hours = 12)
return latest_run_time_object.strftime('%Y-%m-%dT%H:%M:%SZ')
def _get_API_data_for_epsgram(self,station,base_time,product,eps_type):
get = '{}products/{}/?epsgram={}&base_time={}&station_name={}&lat={}&lon={}'.format(self._API_URL,
product,
eps_type,
base_time,
station.name,
station.lat,
station.lon)
result = requests.get(get)
return result.json()
def _request_epsgram_link_for_station(self,station, eps_type):
product = 'opencharts_meteogram'
data = self._get_API_data_for_epsgram(station,station.base_time,product,eps_type)
return data["data"]["link"]["href"]
def _save_image_of_station(self,image_api,station,eps_type):
image = requests.get(image_api)
file = "./{}_{}.png".format(station.name,eps_type)
with open(file, "wb") as img:
img.write(image.content)
logging.info("image saved in {}".format(file))
return file
def download_plots(self,requested_stations):
for Station in self._stations:
if Station.name in requested_stations:
self._download_plots(Station)
# copy because we reset _plots_for_broadcast now
plots_for_broadcast = self._plots_for_broadcast.copy()
self._plots_for_broadcast = {}
return plots_for_broadcast
def download_latest_plots(self):
if not self._is_dangerous_time():
for Station in self._stations:
if self._new_forecast_available(Station):
# update base_time with latest confirmed run
# base_time needs update before fetch
# if not updated, bot sends endless plots to users
Station.base_time = self._latest_confirmed_run(Station)
self._download_plots(Station)
# copy because we reset _plots_for_broadcast now
plots_for_broadcast = self._plots_for_broadcast.copy()
self._plots_for_broadcast = {}
return plots_for_broadcast
def _download_plots(self,Station):
logging.info('Fetch plots for {}'.format(Station.name))
plots = []
for type in self._epsgrams:
image_api = self._request_epsgram_link_for_station(Station,type)
plots.append(self._save_image_of_station(image_api,Station,type))
self._plots_for_broadcast[Station.name] = plots
def _new_forecast_available(self,Station):
return Station.base_time != self._latest_confirmed_run(Station)