chellow 1704969359.0.0__py3-none-any.whl → 1705585731.0.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.
Potentially problematic release.
This version of chellow might be problematic. Click here for more details.
- chellow/__init__.py +1 -0
- chellow/e/cfd.py +305 -0
- chellow/e/lcc.py +164 -0
- chellow/e/views.py +18 -0
- chellow/templates/e/lcc.html +55 -0
- chellow/templates/home.html +1 -0
- chellow/views.py +1 -0
- {chellow-1704969359.0.0.dist-info → chellow-1705585731.0.0.dist-info}/METADATA +1 -1
- {chellow-1704969359.0.0.dist-info → chellow-1705585731.0.0.dist-info}/RECORD +10 -7
- {chellow-1704969359.0.0.dist-info → chellow-1705585731.0.0.dist-info}/WHEEL +0 -0
chellow/__init__.py
CHANGED
chellow/e/cfd.py
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
from datetime import datetime as Datetime
|
|
2
|
+
|
|
3
|
+
from dateutil.relativedelta import relativedelta
|
|
4
|
+
from sqlalchemy import null, or_, select
|
|
5
|
+
|
|
6
|
+
from chellow.e.lcc import api_search, api_sql
|
|
7
|
+
from chellow.models import Contract, RateScript
|
|
8
|
+
from chellow.utils import ct_datetime, hh_format, to_ct, to_utc
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def hh(data_source):
|
|
12
|
+
try:
|
|
13
|
+
cfd_cache = data_source.caches["cfd"]
|
|
14
|
+
except KeyError:
|
|
15
|
+
cfd_cache = data_source.caches["cfd"] = {}
|
|
16
|
+
|
|
17
|
+
for h in data_source.hh_data:
|
|
18
|
+
try:
|
|
19
|
+
h["cfd-rate"] = cfd_cache[h["start-date"]]
|
|
20
|
+
except KeyError:
|
|
21
|
+
h_start = h["start-date"]
|
|
22
|
+
daily_contract = Contract.get_non_core_by_name(
|
|
23
|
+
data_source.sess, "cfd_reconciled_daily_levy_rates"
|
|
24
|
+
)
|
|
25
|
+
daily_rs = data_source.sess.scalars(
|
|
26
|
+
select(RateScript).where(
|
|
27
|
+
RateScript.contract == daily_contract,
|
|
28
|
+
RateScript.start_date <= h_start,
|
|
29
|
+
or_(
|
|
30
|
+
RateScript.finish_date == null(),
|
|
31
|
+
RateScript.finish_date >= h_start,
|
|
32
|
+
),
|
|
33
|
+
)
|
|
34
|
+
).one_or_none()
|
|
35
|
+
if daily_rs.finish_date is None and h_start >= to_utc(
|
|
36
|
+
to_ct(daily_rs.start_date) + relativedelta(months=3)
|
|
37
|
+
):
|
|
38
|
+
base_rate = data_source.non_core_rate(
|
|
39
|
+
"cfd_in_period_tracking", h_start
|
|
40
|
+
)["rate_gbp_per_kwh"]
|
|
41
|
+
else:
|
|
42
|
+
base_rate = data_source.non_core_rate(
|
|
43
|
+
"cfd_reconciled_daily_levy_rates", h_start
|
|
44
|
+
)["rate_gbp_per_kwh"]
|
|
45
|
+
|
|
46
|
+
effective_ocl_rate = data_source.non_core_rate(
|
|
47
|
+
"cfd_operational_costs_levy", h["start-date"]
|
|
48
|
+
)["record"]["Effective_OCL_Rate_GBP_Per_MWh"]
|
|
49
|
+
if effective_ocl_rate == "":
|
|
50
|
+
levy_rate_str = data_source.non_core_rate(
|
|
51
|
+
"cfd_operational_costs_levy", h["start-date"]
|
|
52
|
+
)["record"]["OCL_Rate_GBP_Per_MWh"]
|
|
53
|
+
else:
|
|
54
|
+
levy_rate_str = effective_ocl_rate
|
|
55
|
+
levy_rate = float(levy_rate_str) / 1000
|
|
56
|
+
h["cfd-rate"] = cfd_cache[h["start-date"]] = float(base_rate) + levy_rate
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def lcc_import(sess, log, set_progress, s):
|
|
60
|
+
import_in_period_tracking(sess, log, set_progress, s)
|
|
61
|
+
import_operational_costs_levy(sess, log, set_progress, s)
|
|
62
|
+
import_reconciled_daily_levy_rates(sess, log, set_progress, s)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _quarters(s):
|
|
66
|
+
quarter = {}
|
|
67
|
+
res_j = api_search(
|
|
68
|
+
s, "2fc2fad9-ad57-4901-982a-f92d4ef6c622", sort="Settlement_Date"
|
|
69
|
+
)
|
|
70
|
+
for record in res_j["result"]["records"]:
|
|
71
|
+
settlement_date_str = record["Settlement_Date"]
|
|
72
|
+
settlement_date_ct = to_ct(
|
|
73
|
+
Datetime.strptime(settlement_date_str[:10], "%Y-%m-%d")
|
|
74
|
+
)
|
|
75
|
+
settlement_date = to_utc(settlement_date_ct)
|
|
76
|
+
|
|
77
|
+
quarter[settlement_date] = record
|
|
78
|
+
settlement_next_ct = settlement_date_ct + relativedelta(days=1)
|
|
79
|
+
|
|
80
|
+
if settlement_next_ct.month in (1, 4, 7, 10) and settlement_next_ct.day == 1:
|
|
81
|
+
yield quarter
|
|
82
|
+
quarter = {}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _parse_number(num_str):
|
|
86
|
+
if num_str == "":
|
|
87
|
+
return 0
|
|
88
|
+
else:
|
|
89
|
+
return float(num_str)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _parse_date(date_str):
|
|
93
|
+
return to_utc(to_ct(Datetime.strptime(date_str[:10], "%Y-%m-%d")))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def import_in_period_tracking(sess, log, set_progress, s):
|
|
97
|
+
log("Starting to check for new LCC CfD In-Period Tracking")
|
|
98
|
+
|
|
99
|
+
contract_name = "cfd_in_period_tracking"
|
|
100
|
+
contract = Contract.find_non_core_by_name(sess, contract_name)
|
|
101
|
+
if contract is None:
|
|
102
|
+
contract = Contract.insert_non_core(
|
|
103
|
+
sess, contract_name, "", {}, to_utc(ct_datetime(1996, 4, 1)), None, {}
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
for quarter in _quarters(s):
|
|
107
|
+
quarter_start = sorted(quarter.keys())[0]
|
|
108
|
+
rs = sess.execute(
|
|
109
|
+
select(RateScript).where(
|
|
110
|
+
RateScript.contract == contract,
|
|
111
|
+
RateScript.start_date == quarter_start,
|
|
112
|
+
)
|
|
113
|
+
).scalar_one_or_none()
|
|
114
|
+
if rs is None:
|
|
115
|
+
rs = contract.insert_rate_script(sess, quarter_start, {"records": {}})
|
|
116
|
+
|
|
117
|
+
gbp = 0
|
|
118
|
+
kwh = 0
|
|
119
|
+
for record in quarter.values():
|
|
120
|
+
gbp += _parse_number(record["Actual_CFD_Payments_GBP"])
|
|
121
|
+
gbp += _parse_number(record["Expected_CFD_Payments_GBP"])
|
|
122
|
+
kwh += _parse_number(record["Actual_Eligible_Demand_MWh"]) * 1000
|
|
123
|
+
kwh += _parse_number(record["Expected_Eligible_Demand_MWh"]) * 1000
|
|
124
|
+
|
|
125
|
+
rate = gbp / kwh
|
|
126
|
+
|
|
127
|
+
rs_script = rs.make_script()
|
|
128
|
+
records = rs_script["records"]
|
|
129
|
+
for k, v in sorted(quarter.items()):
|
|
130
|
+
records[hh_format(k)] = v
|
|
131
|
+
rs_script["rate_gbp_per_kwh"] = rate
|
|
132
|
+
rs.update(rs_script)
|
|
133
|
+
sess.commit()
|
|
134
|
+
log("Finished LCC CfD In-Period Tracking")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def import_operational_costs_levy(sess, log, set_progress, s):
|
|
138
|
+
log("Starting to check for new LCC CfD Operational Costs Levy")
|
|
139
|
+
|
|
140
|
+
contract_name = "cfd_operational_costs_levy"
|
|
141
|
+
contract = Contract.find_non_core_by_name(sess, contract_name)
|
|
142
|
+
if contract is None:
|
|
143
|
+
contract = Contract.insert_non_core(
|
|
144
|
+
sess, contract_name, "", {}, to_utc(ct_datetime(1996, 4, 1)), None, {}
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
res_j = api_search(s, "44f41eac-61b3-4e8d-8c52-3eda7b8e8517", sort="Period_Start")
|
|
148
|
+
for record in res_j["result"]["records"][1:]: # skip title row
|
|
149
|
+
period_start_str = record["Period_Start"]
|
|
150
|
+
period_start = to_utc(
|
|
151
|
+
to_ct(Datetime.strptime(period_start_str[:10], "%Y-%m-%d"))
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
rs = sess.execute(
|
|
155
|
+
select(RateScript).where(
|
|
156
|
+
RateScript.contract == contract,
|
|
157
|
+
RateScript.start_date == period_start,
|
|
158
|
+
)
|
|
159
|
+
).scalar_one_or_none()
|
|
160
|
+
if rs is None:
|
|
161
|
+
rs = contract.insert_rate_script(sess, period_start, {})
|
|
162
|
+
|
|
163
|
+
rs_script = rs.make_script()
|
|
164
|
+
rs_script["record"] = record
|
|
165
|
+
rs.update(rs_script)
|
|
166
|
+
sess.commit()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
RUN_TYPES = ("II", "SF", "R1", "R2", "R3", "RF", "DF")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _reconciled_days(s, search_from):
|
|
173
|
+
runs = {}
|
|
174
|
+
res_j = api_sql(
|
|
175
|
+
s,
|
|
176
|
+
f"""SELECT * from "e0e163cb-ba36-416d-83fe-976992d61516"
|
|
177
|
+
WHERE "Settlement_Date" > '{search_from}' ORDER BY "Settlement_Date"
|
|
178
|
+
""",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
records = res_j["result"]["records"]
|
|
182
|
+
if len(records) > 0:
|
|
183
|
+
prev_settlement_date = _parse_date(records[0]["Settlement_Date"])
|
|
184
|
+
runs = {}
|
|
185
|
+
for record in records:
|
|
186
|
+
settlement_date = _parse_date(record["Settlement_Date"])
|
|
187
|
+
if settlement_date != prev_settlement_date:
|
|
188
|
+
yield prev_settlement_date, runs
|
|
189
|
+
runs = {}
|
|
190
|
+
|
|
191
|
+
settlement_run_type = record["Settlement_Run_Type"]
|
|
192
|
+
runs[settlement_run_type] = record
|
|
193
|
+
prev_settlement_date = settlement_date
|
|
194
|
+
|
|
195
|
+
yield settlement_date, runs
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _reconciled_daily_quarters(s, search_from):
|
|
199
|
+
quarter = {}
|
|
200
|
+
for settlement_date, lcc_runs in _reconciled_days(s, search_from):
|
|
201
|
+
try:
|
|
202
|
+
runs = quarter[settlement_date]
|
|
203
|
+
except KeyError:
|
|
204
|
+
runs = quarter[settlement_date] = {}
|
|
205
|
+
|
|
206
|
+
for run_type, record in lcc_runs.items():
|
|
207
|
+
runs[run_type] = record
|
|
208
|
+
|
|
209
|
+
settlement_next_ct = to_ct(settlement_date) + relativedelta(days=1)
|
|
210
|
+
|
|
211
|
+
if settlement_next_ct.month in (1, 4, 7, 10) and settlement_next_ct.day == 1:
|
|
212
|
+
yield quarter
|
|
213
|
+
quarter = {}
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def import_reconciled_daily_levy_rates(sess, log, set_progress, s):
|
|
217
|
+
log("Starting to check for new LCC CfD Reconciled Daily Levy Rates")
|
|
218
|
+
|
|
219
|
+
contract_name = "cfd_reconciled_daily_levy_rates"
|
|
220
|
+
contract = Contract.find_non_core_by_name(sess, contract_name)
|
|
221
|
+
if contract is None:
|
|
222
|
+
contract = Contract.insert_non_core(
|
|
223
|
+
sess, contract_name, "", {}, to_utc(ct_datetime(1996, 4, 1)), None, {}
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
search_from = "1996-04-01"
|
|
227
|
+
for rs in sess.scalars(
|
|
228
|
+
select(RateScript)
|
|
229
|
+
.where(RateScript.contract == contract)
|
|
230
|
+
.order_by(RateScript.start_date.desc())
|
|
231
|
+
):
|
|
232
|
+
script = rs.make_script()
|
|
233
|
+
try:
|
|
234
|
+
records = script["records"]
|
|
235
|
+
except KeyError:
|
|
236
|
+
break
|
|
237
|
+
|
|
238
|
+
complete = True
|
|
239
|
+
rs_start_ct = to_ct(rs.start_date)
|
|
240
|
+
quarter_finish_ct = (
|
|
241
|
+
rs_start_ct + relativedelta(months=3) - relativedelta(days=1)
|
|
242
|
+
)
|
|
243
|
+
day_ct = rs_start_ct
|
|
244
|
+
while day_ct <= quarter_finish_ct:
|
|
245
|
+
try:
|
|
246
|
+
records[hh_format(to_utc(day_ct))]["DF"]
|
|
247
|
+
except KeyError:
|
|
248
|
+
complete = False
|
|
249
|
+
break
|
|
250
|
+
|
|
251
|
+
day_ct += relativedelta(days=1)
|
|
252
|
+
|
|
253
|
+
if complete:
|
|
254
|
+
search_from = quarter_finish_ct.strftime("%Y-%m-%d")
|
|
255
|
+
else:
|
|
256
|
+
break
|
|
257
|
+
|
|
258
|
+
for quarter in _reconciled_daily_quarters(s, search_from):
|
|
259
|
+
quarter_start = sorted(quarter.keys())[0]
|
|
260
|
+
rs = sess.execute(
|
|
261
|
+
select(RateScript).where(
|
|
262
|
+
RateScript.contract == contract,
|
|
263
|
+
RateScript.start_date == quarter_start,
|
|
264
|
+
)
|
|
265
|
+
).scalar_one_or_none()
|
|
266
|
+
if rs is None:
|
|
267
|
+
rs = contract.insert_rate_script(sess, quarter_start, {"records": {}})
|
|
268
|
+
|
|
269
|
+
gbp = 0
|
|
270
|
+
kwh = 0
|
|
271
|
+
run_types_reverse = list(reversed(RUN_TYPES))
|
|
272
|
+
for runs in quarter.values():
|
|
273
|
+
top_run = None
|
|
274
|
+
for run_type in run_types_reverse:
|
|
275
|
+
if run_type in runs:
|
|
276
|
+
top_run = runs[run_type]
|
|
277
|
+
break
|
|
278
|
+
|
|
279
|
+
eligible_mwh = _parse_number(top_run["Reconciled_Eligible_Demand_MWh"])
|
|
280
|
+
gbp += (
|
|
281
|
+
_parse_number(top_run["Reconciled_Daily_Levy_Rate_GBP_Per_MWh"])
|
|
282
|
+
* eligible_mwh
|
|
283
|
+
)
|
|
284
|
+
kwh += eligible_mwh * 1000
|
|
285
|
+
|
|
286
|
+
rate = gbp / kwh
|
|
287
|
+
|
|
288
|
+
rs_script = rs.make_script()
|
|
289
|
+
records = rs_script["records"]
|
|
290
|
+
for dt, runs in sorted(quarter.items()):
|
|
291
|
+
date_str = hh_format(dt)
|
|
292
|
+
try:
|
|
293
|
+
rs_runs = records[date_str]
|
|
294
|
+
except KeyError:
|
|
295
|
+
rs_runs = records[date_str] = {}
|
|
296
|
+
|
|
297
|
+
for run_type, record in runs.items():
|
|
298
|
+
rs_runs[run_type] = record
|
|
299
|
+
|
|
300
|
+
rs_script["rate_gbp_per_kwh"] = rate
|
|
301
|
+
rs.update(rs_script)
|
|
302
|
+
sess.commit()
|
|
303
|
+
|
|
304
|
+
log("Finished LCC CfD Reconciled Daily Levy Rates")
|
|
305
|
+
sess.commit()
|
chellow/e/lcc.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import collections
|
|
3
|
+
import threading
|
|
4
|
+
import traceback
|
|
5
|
+
from datetime import timedelta
|
|
6
|
+
from importlib import import_module
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from werkzeug.exceptions import BadRequest
|
|
11
|
+
|
|
12
|
+
from chellow.models import (
|
|
13
|
+
Contract,
|
|
14
|
+
Session,
|
|
15
|
+
)
|
|
16
|
+
from chellow.utils import ct_datetime_now, hh_format, utc_datetime_now
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
importer = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def api_sql(s, sql):
|
|
23
|
+
url = "https://dp.lowcarboncontracts.uk/api/3/action/datastore_search_sql"
|
|
24
|
+
params = {"sql": sql}
|
|
25
|
+
res = s.get(url, params=params)
|
|
26
|
+
try:
|
|
27
|
+
res_j = res.json()
|
|
28
|
+
except requests.exceptions.JSONDecodeError as e:
|
|
29
|
+
raise BadRequest(
|
|
30
|
+
f"Couldn't parse as JSON the content from {url} with error {e}: "
|
|
31
|
+
f"{res.text}"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
if "success" in res_j and not res_j["success"]:
|
|
35
|
+
raise BadRequest(res_j)
|
|
36
|
+
|
|
37
|
+
return res_j
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def api_search(s, resource_id, sort=None):
|
|
41
|
+
url = "https://dp.lowcarboncontracts.uk/api/3/action/datastore_search"
|
|
42
|
+
params = {"resource_id": resource_id}
|
|
43
|
+
if sort is not None:
|
|
44
|
+
params["sort"] = sort
|
|
45
|
+
res = s.get(url, params=params)
|
|
46
|
+
try:
|
|
47
|
+
res_j = res.json()
|
|
48
|
+
except requests.exceptions.JSONDecodeError as e:
|
|
49
|
+
raise BadRequest(
|
|
50
|
+
f"Couldn't parse as JSON the content from {url} with error {e}: "
|
|
51
|
+
f"{res.text}"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if "success" in res_j and not res_j["success"]:
|
|
55
|
+
raise BadRequest(res_j)
|
|
56
|
+
|
|
57
|
+
return res_j
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def run_import(sess, log, set_progress):
|
|
61
|
+
log("Starting to import data from the Low Carbon Contracts Company")
|
|
62
|
+
s = requests.Session()
|
|
63
|
+
s.verify = False
|
|
64
|
+
|
|
65
|
+
for mod_name in ("chellow.e.cfd",):
|
|
66
|
+
mod = import_module(mod_name)
|
|
67
|
+
mod.lcc_import(sess, log, set_progress, s)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
LAST_RUN_KEY = "last_run"
|
|
71
|
+
GLOBAL_ALERT = (
|
|
72
|
+
"There's a problem with a <a href='/lcc'>Low Carbon Contracts import</a>."
|
|
73
|
+
)
|
|
74
|
+
LCC_STATE_KEY = "lcc"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class LowCarbonContracts(threading.Thread):
|
|
78
|
+
def __init__(self):
|
|
79
|
+
super().__init__(name="Low Carbon Contracts")
|
|
80
|
+
self.messages = collections.deque(maxlen=500)
|
|
81
|
+
self.progress = ""
|
|
82
|
+
self.stopped = threading.Event()
|
|
83
|
+
self.going = threading.Event()
|
|
84
|
+
self.global_alert = None
|
|
85
|
+
|
|
86
|
+
def stop(self):
|
|
87
|
+
self.stopped.set()
|
|
88
|
+
self.going.set()
|
|
89
|
+
self.join()
|
|
90
|
+
|
|
91
|
+
def go(self):
|
|
92
|
+
self.going.set()
|
|
93
|
+
|
|
94
|
+
def log(self, message):
|
|
95
|
+
self.messages.appendleft(
|
|
96
|
+
f"{ct_datetime_now().strftime('%Y-%m-%d %H:%M:%S')} - {message}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def set_progress(self, progress):
|
|
100
|
+
self.progress = progress
|
|
101
|
+
|
|
102
|
+
def run(self):
|
|
103
|
+
while not self.stopped.is_set():
|
|
104
|
+
with Session() as sess:
|
|
105
|
+
try:
|
|
106
|
+
config = Contract.get_non_core_by_name(sess, "configuration")
|
|
107
|
+
state = config.make_state()
|
|
108
|
+
lcc_state = state.get(LCC_STATE_KEY, {})
|
|
109
|
+
except BaseException as e:
|
|
110
|
+
msg = f"{e.description} " if isinstance(e, BadRequest) else ""
|
|
111
|
+
self.log(f"{msg}{traceback.format_exc()}")
|
|
112
|
+
self.global_alert = GLOBAL_ALERT
|
|
113
|
+
sess.rollback()
|
|
114
|
+
|
|
115
|
+
last_run = lcc_state.get(LAST_RUN_KEY)
|
|
116
|
+
if last_run is None or utc_datetime_now() - last_run > timedelta(days=1):
|
|
117
|
+
self.going.set()
|
|
118
|
+
|
|
119
|
+
if self.going.is_set():
|
|
120
|
+
self.global_alert = None
|
|
121
|
+
with Session() as sess:
|
|
122
|
+
try:
|
|
123
|
+
config = Contract.get_non_core_by_name(sess, "configuration")
|
|
124
|
+
state = config.make_state()
|
|
125
|
+
try:
|
|
126
|
+
lcc_state = state[LCC_STATE_KEY]
|
|
127
|
+
except KeyError:
|
|
128
|
+
lcc_state = state[LCC_STATE_KEY] = {}
|
|
129
|
+
|
|
130
|
+
lcc_state[LAST_RUN_KEY] = utc_datetime_now()
|
|
131
|
+
config.update_state(state)
|
|
132
|
+
sess.commit()
|
|
133
|
+
run_import(sess, self.log, self.set_progress)
|
|
134
|
+
except BaseException as e:
|
|
135
|
+
msg = f"{e.description} " if isinstance(e, BadRequest) else ""
|
|
136
|
+
self.log(f"{msg}{traceback.format_exc()}")
|
|
137
|
+
self.global_alert = GLOBAL_ALERT
|
|
138
|
+
sess.rollback()
|
|
139
|
+
finally:
|
|
140
|
+
self.going.clear()
|
|
141
|
+
self.log("Finished importing Low Carbon Contracts data.")
|
|
142
|
+
|
|
143
|
+
else:
|
|
144
|
+
self.log(
|
|
145
|
+
f"The importer was last run at {hh_format(last_run)}. There will "
|
|
146
|
+
f"be another import when 24 hours have elapsed since the last run."
|
|
147
|
+
)
|
|
148
|
+
self.going.wait(60 * 60)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_importer():
|
|
152
|
+
return importer
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def startup():
|
|
156
|
+
global importer
|
|
157
|
+
importer = LowCarbonContracts()
|
|
158
|
+
importer.start()
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@atexit.register
|
|
162
|
+
def shutdown():
|
|
163
|
+
if importer is not None:
|
|
164
|
+
importer.stop()
|
chellow/e/views.py
CHANGED
|
@@ -29,6 +29,7 @@ from werkzeug.exceptions import BadRequest
|
|
|
29
29
|
from zish import dumps, loads
|
|
30
30
|
|
|
31
31
|
import chellow.e.dno_rate_parser
|
|
32
|
+
import chellow.e.lcc
|
|
32
33
|
from chellow.e.computer import SupplySource, contract_func, forecast_date
|
|
33
34
|
from chellow.e.energy_management import totals_runner
|
|
34
35
|
from chellow.models import (
|
|
@@ -2330,6 +2331,23 @@ def lafs_get():
|
|
|
2330
2331
|
return render_template("lafs.html", dno=dno, llfc=llfc, lafs=lafs)
|
|
2331
2332
|
|
|
2332
2333
|
|
|
2334
|
+
@e.route("/lcc")
|
|
2335
|
+
def lcc_get():
|
|
2336
|
+
importer = chellow.e.lcc.importer
|
|
2337
|
+
|
|
2338
|
+
return render_template(
|
|
2339
|
+
"lcc.html",
|
|
2340
|
+
importer=importer,
|
|
2341
|
+
)
|
|
2342
|
+
|
|
2343
|
+
|
|
2344
|
+
@e.route("/lcc", methods=["POST"])
|
|
2345
|
+
def lcc_post():
|
|
2346
|
+
importer = chellow.e.lcc.importer
|
|
2347
|
+
importer.go()
|
|
2348
|
+
return chellow_redirect("/lcc", 303)
|
|
2349
|
+
|
|
2350
|
+
|
|
2333
2351
|
@e.route("/llfcs")
|
|
2334
2352
|
def llfcs_get():
|
|
2335
2353
|
dno_id = req_int("dno_id")
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}
|
|
4
|
+
» Low Carbon Contracts Importer
|
|
5
|
+
{% endblock %}
|
|
6
|
+
|
|
7
|
+
{% block nav %}
|
|
8
|
+
Low Carbon Contracts Importer
|
|
9
|
+
{% endblock %}
|
|
10
|
+
|
|
11
|
+
{% block content %}
|
|
12
|
+
{% if importer %}
|
|
13
|
+
<ul>
|
|
14
|
+
<li><a href="#controls">Controls</a></li>
|
|
15
|
+
<li><a href="#log">Log</a></li>
|
|
16
|
+
</ul>
|
|
17
|
+
|
|
18
|
+
<h4 id="controls">Controls</h4>
|
|
19
|
+
|
|
20
|
+
<table>
|
|
21
|
+
<tr>
|
|
22
|
+
<th>Is Going?</th>
|
|
23
|
+
<td>{{importer.going.is_set()}}</td>
|
|
24
|
+
</tr>
|
|
25
|
+
<tr>
|
|
26
|
+
<th>Import Now</th>
|
|
27
|
+
<td>
|
|
28
|
+
<form method="post" action="/e/lcc">
|
|
29
|
+
<fieldset {% if importer.going.is_set() %}disabled{% endif %}>
|
|
30
|
+
<input type="submit" value="Import" name="import">
|
|
31
|
+
</fieldset>
|
|
32
|
+
</form>
|
|
33
|
+
</td>
|
|
34
|
+
</tr>
|
|
35
|
+
{% if importer.going.is_set() %}
|
|
36
|
+
<tr>
|
|
37
|
+
<th>Progress</th>
|
|
38
|
+
<td>{{importer.progress}}</td>
|
|
39
|
+
</tr>
|
|
40
|
+
{% endif %}
|
|
41
|
+
</table>
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
<h4 id="log">Log</h4>
|
|
45
|
+
|
|
46
|
+
<ul>
|
|
47
|
+
{% for message in importer.messages %}
|
|
48
|
+
<li>{{message}}</li>
|
|
49
|
+
{% endfor %}
|
|
50
|
+
</ul>
|
|
51
|
+
|
|
52
|
+
{% else %}
|
|
53
|
+
<p>Importer not present.</p>
|
|
54
|
+
{% endif %}
|
|
55
|
+
{% endblock %}
|
chellow/templates/home.html
CHANGED
|
@@ -213,6 +213,7 @@
|
|
|
213
213
|
<li><a href="/api/v1" >API</a></li>
|
|
214
214
|
<li><a href="/rate_server">Automatic Importer: Rate Server</a></li>
|
|
215
215
|
<li><a href="/national_grid">Automatic Importer: National Grid</a></li>
|
|
216
|
+
<li><a href="/e/lcc">Automatic Importer: Low Carbon Contracts</a></li>
|
|
216
217
|
</ul>
|
|
217
218
|
|
|
218
219
|
<ul>
|
chellow/views.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: chellow
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1705585731.0.0
|
|
4
4
|
Summary: Web Application for checking UK energy bills.
|
|
5
5
|
Project-URL: Homepage, https://github.com/WessexWater/chellow
|
|
6
6
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
chellow/__init__.py,sha256=
|
|
1
|
+
chellow/__init__.py,sha256=WrcFJlp7ESOoBat74QtTfAP0-y7ZglNG_nd84ZJza7E,10351
|
|
2
2
|
chellow/api.py,sha256=mk17TfweR76DPFC8lX2SArTjai6y6YshASxqO1w-_-s,11036
|
|
3
3
|
chellow/bank_holidays.py,sha256=mezbpl0aU_IXLoONZ3jleWus61ZrSV_2AMSRm9RASr0,4267
|
|
4
4
|
chellow/commands.py,sha256=ESBe9ZWj1c3vdZgqMZ9gFvYAB3hRag2R1PzOwuw9yFo,1302
|
|
@@ -11,13 +11,14 @@ chellow/proxy.py,sha256=cVXIktPlX3tQ1BYcwxq0nJXKE6r3DtFTtfFHPq55HaM,1351
|
|
|
11
11
|
chellow/rate_server.py,sha256=XFcyKgo2r7GBybm6TRLBaBRqDpgk4vmhqgRoM3nflXM,5626
|
|
12
12
|
chellow/testing.py,sha256=Od4HHH6pZrhJ_De118_F55RJEKmAvhUH2S24QE9qFQk,3635
|
|
13
13
|
chellow/utils.py,sha256=32qPWEGzCPKPhSM7ztpzcR6ZG74sVZanScDIdK50Rq4,19308
|
|
14
|
-
chellow/views.py,sha256
|
|
14
|
+
chellow/views.py,sha256=FHQfRYh35Bz38l5LL4l3OzjrHwMJU0y41waIlYRXkxI,78512
|
|
15
15
|
chellow/e/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
chellow/e/aahedc.py,sha256=d2usudp7KYWpU6Pk3fal5EQ47EbvkvKeaFGylnb3NWw,606
|
|
17
17
|
chellow/e/bill_importer.py,sha256=y1bpn49xDwltj0PRFowWsjAVamcD8eyxUbCTdGxEZiE,7389
|
|
18
18
|
chellow/e/bmarketidx.py,sha256=0JCBBnP3xfLq2eFjXO_u-gzHyTizab4Pp6PRK6OZLcw,7134
|
|
19
19
|
chellow/e/bsuos.py,sha256=YtEhce5589kJWNp0HlA7N9L1hHl2KjYkUm54cUCOB8w,15080
|
|
20
20
|
chellow/e/ccl.py,sha256=CYeFrg0TNF7Av49nqxa0guA8CMcJhdMpBRmOq8PH6G0,564
|
|
21
|
+
chellow/e/cfd.py,sha256=K0aF-ch20ZlX0XkTxqqjAGDrP-ZzNeWcGssikU7ObLc,10413
|
|
21
22
|
chellow/e/computer.py,sha256=J8VvYjcSeJNrG2Ak8Zi2t2bb_8tMrkHWny4JQlgIyy8,67032
|
|
22
23
|
chellow/e/dno_rate_parser.py,sha256=3IqGM2v6Str7S3Rc9FZ7lRWBKthfb3zSjpJLSETPcEE,20631
|
|
23
24
|
chellow/e/duos.py,sha256=ISTcNqe9KNjVNM2Qs8IBoQxnmSXOt5W_G7tZxp4T78M,28870
|
|
@@ -27,6 +28,7 @@ chellow/e/hh_parser_bg_csv.py,sha256=IOjM-QliKzvSXyhslmxz504XWnsGkVKEaCQ48SNeNeQ
|
|
|
27
28
|
chellow/e/hh_parser_df2.py,sha256=G4kt4N4mNjUa1Z7B1Ov6rAyZmWDyGEVFYscdghToMRo,3558
|
|
28
29
|
chellow/e/hh_parser_simple_csv.py,sha256=lVRprIEjDpTQdeXm2xcLmRAyre2LWec8ueVwcCISZPo,2082
|
|
29
30
|
chellow/e/laf_import.py,sha256=aqkcbjnvfBPszBLSNg6getP7iW1uWiTVHy6N5Z5x39U,5514
|
|
31
|
+
chellow/e/lcc.py,sha256=q5iGbeYZdnozkIdbGuxXoJO-1KDO7AHqYOXEGBIaNHI,4963
|
|
30
32
|
chellow/e/mdd_importer.py,sha256=9GN-at0DC3vOjd-0N2U856O3hInnjkMWWcRfxzQ5DjA,32042
|
|
31
33
|
chellow/e/rcrc.py,sha256=zjRdtqYjKoelNNq_ckAJi5ThAyw9142fhlFICnzCocY,6039
|
|
32
34
|
chellow/e/ro.py,sha256=dZKZv_9wXSWuwcb3jiKavoD_9ot-PZseNVeEEe0siLo,596
|
|
@@ -35,7 +37,7 @@ chellow/e/system_price.py,sha256=F2GF_TNapfpbhoYjuGYAUCtD4JnccOUfd3w2ClG55qc,803
|
|
|
35
37
|
chellow/e/tlms.py,sha256=m6bUY1LNRlPoZ8va7Y3TlXAIktlTz36UVygFoDqwXIc,9586
|
|
36
38
|
chellow/e/tnuos.py,sha256=qybrPCVukQ2l0RIdqn8XtHEbAU0izGoqUrFLoLVUoVI,4126
|
|
37
39
|
chellow/e/triad.py,sha256=gR4tyPVlbwcrOvUaOgzUUzDtmWPOLoXRwzBilKIRlTo,13183
|
|
38
|
-
chellow/e/views.py,sha256=
|
|
40
|
+
chellow/e/views.py,sha256=HIoyHeKR-j8wvxoyk3ZytTsCjU7g3ejFYVh56e6MxKE,212153
|
|
39
41
|
chellow/e/bill_parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
40
42
|
chellow/e/bill_parsers/activity_mop_stark_xlsx.py,sha256=UgWXDPzQkQghyj_lfgBqoSJpHB-t-qOdSaB8qY6GLog,4071
|
|
41
43
|
chellow/e/bill_parsers/annual_mop_stark_xlsx.py,sha256=-HMoIfa_utXYKA44RuC0Xqv3vd2HLeQU_4P0iBUd3WA,4219
|
|
@@ -114,7 +116,7 @@ chellow/templates/downloads.html,sha256=R9QPcFz-PLJOX7rDlmquIk-Hp9Iq-thzWCTfOexS
|
|
|
114
116
|
chellow/templates/edi_viewer.html,sha256=szUthgHOdph6Of-7f_1LeC_zYlNJaMeS5ctn6xTAeiM,1437
|
|
115
117
|
chellow/templates/general_import.html,sha256=SMemHGu466Be3_cAWSS6Hn-Kv-1psbEMDD6NQG6bfbI,1013
|
|
116
118
|
chellow/templates/general_imports.html,sha256=x6V4xO7gznQR5l3PQKVNZaqvp2S2E8wRz4DysZk17Cw,13026
|
|
117
|
-
chellow/templates/home.html,sha256=
|
|
119
|
+
chellow/templates/home.html,sha256=1QckikyQ6wvoKZHbDfGkxZJvvoR6Zo9HaQvDFfoT3j4,5592
|
|
118
120
|
chellow/templates/input_date.html,sha256=rpgB5n0LfN8Y5djN_ZiuSxqdskxzCoKrEqI7hyJkVQo,1248
|
|
119
121
|
chellow/templates/local_report.html,sha256=pV7_0QwyQ-D3OS9LXrly5pq3qprZnwTCoq6vCnMTkS4,1332
|
|
120
122
|
chellow/templates/local_reports.html,sha256=4wbfVkY4wUfSSfWjxqIsvCpIsa9k7H_dGAjznrG5jNM,701
|
|
@@ -214,6 +216,7 @@ chellow/templates/e/gsp_group.html,sha256=yzLHZHQPHZAQtjLBszWEc-htw8-MEhSu3kLo-K
|
|
|
214
216
|
chellow/templates/e/gsp_groups.html,sha256=HagDJBJ7YzZDNhDq9ThI2uMrsW_viSuBapB2X4yC3Bk,453
|
|
215
217
|
chellow/templates/e/hh_datum_edit.html,sha256=TJPvQ7FHw1yjkBwpXul0ZBs8UQtZR98wJ3fLjuQc2XA,1824
|
|
216
218
|
chellow/templates/e/lafs.html,sha256=7MQvmoGCY-JCSDDos7vJVRKWAvqqM0eqhA0-hvaO1_o,996
|
|
219
|
+
chellow/templates/e/lcc.html,sha256=LLWgB9iNG2jee3p1DrbeDtUR3CPQIXlyDvGQDUsWKEs,1021
|
|
217
220
|
chellow/templates/e/llfc.html,sha256=B0tw7PHvEEvxJWhAGe9dZji_J9apAz-TpNlkqvYFKfE,1168
|
|
218
221
|
chellow/templates/e/llfc_edit.html,sha256=YwtEf8DwoX_t3RHNxPbZYFzikBL-Ly7UyjHs-YuJ3bk,1038
|
|
219
222
|
chellow/templates/e/llfcs.html,sha256=OlY9x3XKs0OAoa4ZOZoRokYEjIZ_isfoBtn3DGL-u6o,1228
|
|
@@ -358,6 +361,6 @@ chellow/templates/g/supply_note_edit.html,sha256=6UQf_qbhFDys3cVsTp-c7ABWZpggW9R
|
|
|
358
361
|
chellow/templates/g/supply_notes.html,sha256=WR3YwGh_qqTklSJ7JqWX6BKBc9rk_jMff4RiWZiF2CM,936
|
|
359
362
|
chellow/templates/g/unit.html,sha256=KouNVU0-i84afANkLQ_heJ0uDfJ9H5A05PuLqb8iCN8,438
|
|
360
363
|
chellow/templates/g/units.html,sha256=p5Nd-lAIboKPEOO6N451hx1bcKxMg4BDODnZ-43MmJc,441
|
|
361
|
-
chellow-
|
|
362
|
-
chellow-
|
|
363
|
-
chellow-
|
|
364
|
+
chellow-1705585731.0.0.dist-info/METADATA,sha256=ZDTCqHhMHVtFiAbtdq07DCo3uAmfHY6zX_bQdJuNgeE,12160
|
|
365
|
+
chellow-1705585731.0.0.dist-info/WHEEL,sha256=mRYSEL3Ih6g5a_CVMIcwiF__0Ae4_gLYh01YFNwiq1k,87
|
|
366
|
+
chellow-1705585731.0.0.dist-info/RECORD,,
|
|
File without changes
|