chellow 1744710468.0.0__py3-none-any.whl → 1745313690.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 +2 -0
- chellow/e/bill_parsers/mm.py +1 -1
- chellow/e/bill_parsers/nonsettlement_dc_stark_xlsx.py +24 -14
- chellow/e/dno_rate_parser.py +10 -10
- chellow/fake_batch_updater.py +305 -0
- chellow/reports/report_missing_bills.py +158 -0
- chellow/templates/e/dc_contract.html +12 -1
- chellow/templates/e/mop_contract.html +11 -0
- chellow/templates/e/supplier_contract.html +11 -0
- chellow/templates/fake_batch_updater.html +99 -0
- chellow/templates/home.html +1 -0
- chellow/templates/report_run_missing_e_bills.html +97 -0
- chellow/utils.py +5 -2
- chellow/views.py +53 -3
- {chellow-1744710468.0.0.dist-info → chellow-1745313690.0.0.dist-info}/METADATA +1 -1
- {chellow-1744710468.0.0.dist-info → chellow-1745313690.0.0.dist-info}/RECORD +17 -13
- {chellow-1744710468.0.0.dist-info → chellow-1745313690.0.0.dist-info}/WHEEL +0 -0
chellow/__init__.py
CHANGED
|
@@ -22,6 +22,7 @@ import chellow.e.hh_importer
|
|
|
22
22
|
import chellow.e.rcrc
|
|
23
23
|
import chellow.e.system_price
|
|
24
24
|
import chellow.e.views
|
|
25
|
+
import chellow.fake_batch_updater
|
|
25
26
|
import chellow.gas.cv
|
|
26
27
|
import chellow.gas.views
|
|
27
28
|
import chellow.national_grid
|
|
@@ -62,6 +63,7 @@ def get_importer_modules():
|
|
|
62
63
|
chellow.national_grid,
|
|
63
64
|
chellow.rate_server,
|
|
64
65
|
chellow.rrun,
|
|
66
|
+
chellow.fake_batch_updater,
|
|
65
67
|
)
|
|
66
68
|
|
|
67
69
|
|
chellow/e/bill_parsers/mm.py
CHANGED
|
@@ -59,7 +59,7 @@ def _handle_0100(headers, pre_record, record):
|
|
|
59
59
|
headers["issue_date"] = issue_date
|
|
60
60
|
if late_payment is not None:
|
|
61
61
|
headers["late_payment"] = late_payment
|
|
62
|
-
headers["account"] = pre_record[
|
|
62
|
+
headers["account"] = pre_record[43:52]
|
|
63
63
|
headers["reference"] = pre_record[41:46]
|
|
64
64
|
headers["kwh"] = Decimal("0")
|
|
65
65
|
headers["breakdown"] = defaultdict(int, {"vat": {}})
|
|
@@ -4,6 +4,7 @@ from openpyxl import load_workbook
|
|
|
4
4
|
|
|
5
5
|
from werkzeug.exceptions import BadRequest
|
|
6
6
|
|
|
7
|
+
from chellow.e.computer import hh_rate
|
|
7
8
|
from chellow.models import Session
|
|
8
9
|
from chellow.utils import ct_datetime, parse_mpan_core, to_utc
|
|
9
10
|
|
|
@@ -16,12 +17,6 @@ def get_start_date(title_row, row, name):
|
|
|
16
17
|
return to_utc(get_ct_date(title_row, row, name))
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
def get_finish_date(title_row, row, name):
|
|
20
|
-
d = get_ct_date(title_row, row, name)
|
|
21
|
-
|
|
22
|
-
return to_utc(ct_datetime(d.year, d.month, d.day, 23, 30))
|
|
23
|
-
|
|
24
|
-
|
|
25
20
|
def get_value(title_row, row, name):
|
|
26
21
|
idx = None
|
|
27
22
|
name = name.strip().lower()
|
|
@@ -61,9 +56,6 @@ def get_int(title_row, row, name):
|
|
|
61
56
|
return int(get_value(title_row, row, name))
|
|
62
57
|
|
|
63
58
|
|
|
64
|
-
METER_RATE = Decimal("60.00")
|
|
65
|
-
|
|
66
|
-
|
|
67
59
|
class Parser:
|
|
68
60
|
def __init__(self, f):
|
|
69
61
|
self.book = load_workbook(f)
|
|
@@ -86,6 +78,7 @@ class Parser:
|
|
|
86
78
|
|
|
87
79
|
def make_raw_bills(self):
|
|
88
80
|
row_index = None
|
|
81
|
+
caches = {}
|
|
89
82
|
try:
|
|
90
83
|
with Session() as sess:
|
|
91
84
|
bills = []
|
|
@@ -101,14 +94,30 @@ class Parser:
|
|
|
101
94
|
mpan_core = parse_mpan_core(
|
|
102
95
|
str(get_int(title_row, row, "mpan ref"))
|
|
103
96
|
)
|
|
104
|
-
|
|
97
|
+
start_date_ct = get_ct_date(title_row, row, "start")
|
|
98
|
+
start_date = to_utc(start_date_ct)
|
|
105
99
|
issue_date = start_date
|
|
106
|
-
|
|
100
|
+
finish_date_ct = get_ct_date(title_row, row, "end")
|
|
101
|
+
finish_date = to_utc(
|
|
102
|
+
ct_datetime(
|
|
103
|
+
finish_date_ct.year,
|
|
104
|
+
finish_date_ct.month,
|
|
105
|
+
finish_date_ct.day,
|
|
106
|
+
23,
|
|
107
|
+
30,
|
|
108
|
+
)
|
|
109
|
+
)
|
|
107
110
|
check = get_str(title_row, row, "check")
|
|
108
111
|
if check != "Billed":
|
|
109
112
|
continue
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
rates = hh_rate(sess, caches, 0, start_date)
|
|
114
|
+
meter_rate = rates["annual_rates"]["non_settlement"]["*"]["IP"][
|
|
115
|
+
"*"
|
|
116
|
+
]["gbp_per_meter"]
|
|
117
|
+
months = (finish_date_ct.year - start_date_ct.year) * 12 + (
|
|
118
|
+
finish_date_ct.month - start_date_ct.month + 1
|
|
119
|
+
)
|
|
120
|
+
net = round(Decimal(float(meter_rate) / 12 * months), 2)
|
|
112
121
|
vat = round(net * Decimal("0.2"), 2)
|
|
113
122
|
|
|
114
123
|
breakdown = {
|
|
@@ -116,7 +125,8 @@ class Parser:
|
|
|
116
125
|
"cop": ["5"],
|
|
117
126
|
"settlement-status": ["non_settlement"],
|
|
118
127
|
"msn": [msn],
|
|
119
|
-
"meter-rate": [
|
|
128
|
+
"meter-rate": [meter_rate],
|
|
129
|
+
"months": months,
|
|
120
130
|
"meter-gbp": net,
|
|
121
131
|
}
|
|
122
132
|
|
chellow/e/dno_rate_parser.py
CHANGED
|
@@ -650,15 +650,15 @@ def update_vls(sess, logger, vls, dno_code, fy_start, rs_finish):
|
|
|
650
650
|
llfc = sess.execute(q).scalar_one_or_none()
|
|
651
651
|
|
|
652
652
|
if llfc is None:
|
|
653
|
-
|
|
653
|
+
logger(
|
|
654
654
|
f"There is no LLFC with the code '{vl_code}' associated with the DNO "
|
|
655
|
-
f"{dno.
|
|
655
|
+
f"{dno.dno_code} from {hh_format(fy_start)} to {hh_format(rs_finish)}."
|
|
656
656
|
)
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
657
|
+
else:
|
|
658
|
+
vl_voltage_level = VoltageLevel.get_by_code(sess, vl["voltage_level"])
|
|
659
|
+
llfc.voltage_level = vl_voltage_level
|
|
660
|
+
|
|
661
|
+
llfc.is_substation = vl["is_substation"]
|
|
662
|
+
if sess.is_modified(llfc):
|
|
663
|
+
logger(f"Updated LLFC {llfc.code} of DNO {dno_code}")
|
|
664
|
+
sess.flush()
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import atexit
|
|
2
|
+
import collections
|
|
3
|
+
import threading
|
|
4
|
+
import traceback
|
|
5
|
+
from datetime import timedelta
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import false, null, select
|
|
8
|
+
|
|
9
|
+
from werkzeug.exceptions import BadRequest
|
|
10
|
+
|
|
11
|
+
from chellow.e.computer import contract_func
|
|
12
|
+
from chellow.gas.engine import g_contract_func
|
|
13
|
+
from chellow.models import (
|
|
14
|
+
Batch,
|
|
15
|
+
BillType,
|
|
16
|
+
Contract,
|
|
17
|
+
GBatch,
|
|
18
|
+
GBill,
|
|
19
|
+
GContract,
|
|
20
|
+
GRateScript,
|
|
21
|
+
GReadType,
|
|
22
|
+
GSupply,
|
|
23
|
+
GUnit,
|
|
24
|
+
RateScript,
|
|
25
|
+
ReadType,
|
|
26
|
+
Session,
|
|
27
|
+
Supply,
|
|
28
|
+
Tpr,
|
|
29
|
+
)
|
|
30
|
+
from chellow.utils import (
|
|
31
|
+
c_months_u,
|
|
32
|
+
ct_datetime_now,
|
|
33
|
+
hh_format,
|
|
34
|
+
keydefaultdict,
|
|
35
|
+
utc_datetime_now,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
importer = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def run_import(sess, log, set_progress):
|
|
43
|
+
log("Starting to update the fake batches")
|
|
44
|
+
caches = {}
|
|
45
|
+
|
|
46
|
+
now_ct = ct_datetime_now()
|
|
47
|
+
(last_month_start, last_month_finish), (
|
|
48
|
+
current_month_start,
|
|
49
|
+
current_month_finish,
|
|
50
|
+
) = list(c_months_u(finish_year=now_ct.year, finish_month=now_ct.month, months=2))
|
|
51
|
+
|
|
52
|
+
for last_rate_script in sess.scalars(
|
|
53
|
+
select(RateScript)
|
|
54
|
+
.join(Contract, RateScript.contract_id == Contract.id)
|
|
55
|
+
.where(RateScript.finish_date == null())
|
|
56
|
+
):
|
|
57
|
+
contract = last_rate_script.contract
|
|
58
|
+
fb_func = contract_func(caches, contract, "make_fake_bills")
|
|
59
|
+
if fb_func is None:
|
|
60
|
+
continue
|
|
61
|
+
|
|
62
|
+
fake_batch_name = f"fake_e_batch_{contract.id}"
|
|
63
|
+
|
|
64
|
+
fake_batch = sess.scalar_one_or_none(
|
|
65
|
+
select(Batch).where(
|
|
66
|
+
Batch.contract == contract,
|
|
67
|
+
Batch.reference == fake_batch_name,
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if fake_batch is not None and fake_batch.start_date < current_month_start:
|
|
72
|
+
fake_batch.delete(sess)
|
|
73
|
+
sess.flush()
|
|
74
|
+
fake_batch = None
|
|
75
|
+
|
|
76
|
+
if fake_batch is None:
|
|
77
|
+
fake_batch = contract.insert_batch(sess, fake_batch_name, "Fake Batch")
|
|
78
|
+
bill_types = keydefaultdict(lambda k: BillType.get_by_code(sess, k))
|
|
79
|
+
|
|
80
|
+
tprs = keydefaultdict(
|
|
81
|
+
lambda k: None if k is None else Tpr.get_by_code(sess, k)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
read_types = keydefaultdict(lambda k: ReadType.get_by_code(sess, k))
|
|
85
|
+
for raw_bill in fb_func(
|
|
86
|
+
sess,
|
|
87
|
+
log,
|
|
88
|
+
last_month_start,
|
|
89
|
+
last_month_finish,
|
|
90
|
+
current_month_start,
|
|
91
|
+
current_month_finish,
|
|
92
|
+
):
|
|
93
|
+
mpan_core = raw_bill["mpan_core"]
|
|
94
|
+
supply = Supply.get_by_mpan_core(sess, mpan_core)
|
|
95
|
+
bill = fake_batch.insert_bill(
|
|
96
|
+
sess,
|
|
97
|
+
raw_bill["account"],
|
|
98
|
+
raw_bill["reference"],
|
|
99
|
+
raw_bill["issue_date"],
|
|
100
|
+
raw_bill["start_date"],
|
|
101
|
+
raw_bill["finish_date"],
|
|
102
|
+
raw_bill["kwh"],
|
|
103
|
+
raw_bill["net"],
|
|
104
|
+
raw_bill["vat"],
|
|
105
|
+
raw_bill["gross"],
|
|
106
|
+
bill_types[raw_bill["bill_type_code"]],
|
|
107
|
+
raw_bill["breakdown"],
|
|
108
|
+
supply,
|
|
109
|
+
)
|
|
110
|
+
for raw_read in raw_bill["reads"]:
|
|
111
|
+
bill.insert_read(
|
|
112
|
+
sess,
|
|
113
|
+
tprs[raw_read["tpr_code"]],
|
|
114
|
+
raw_read["coefficient"],
|
|
115
|
+
raw_read["units"],
|
|
116
|
+
raw_read["msn"],
|
|
117
|
+
raw_read["mpan"],
|
|
118
|
+
raw_read["prev_date"],
|
|
119
|
+
raw_read["prev_value"],
|
|
120
|
+
read_types[raw_read["prev_type_code"]],
|
|
121
|
+
raw_read["pres_date"],
|
|
122
|
+
raw_read["pres_value"],
|
|
123
|
+
read_types[raw_read["pres_type_code"]],
|
|
124
|
+
)
|
|
125
|
+
for last_rate_script in sess.scalars(
|
|
126
|
+
select(GRateScript)
|
|
127
|
+
.join(GContract, GRateScript.g_contract_id == GContract.id)
|
|
128
|
+
.where(GContract.is_industry == false(), GRateScript.finish_date == null())
|
|
129
|
+
):
|
|
130
|
+
g_contract = last_rate_script.g_contract
|
|
131
|
+
log(f"Looking at gas contract {g_contract.name}")
|
|
132
|
+
fb_func = g_contract_func(caches, g_contract, "make_fake_bills")
|
|
133
|
+
if fb_func is None:
|
|
134
|
+
log("Doesn't have a make_fake_bills function so skipping")
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
fake_batch_name = f"fake_g_batch_{g_contract.id}"
|
|
138
|
+
|
|
139
|
+
fake_batch = sess.scalars(
|
|
140
|
+
select(GBatch).where(
|
|
141
|
+
GBatch.g_contract == g_contract,
|
|
142
|
+
GBatch.reference == fake_batch_name,
|
|
143
|
+
)
|
|
144
|
+
).one_or_none()
|
|
145
|
+
|
|
146
|
+
if fake_batch is not None:
|
|
147
|
+
first_fake_bill = sess.scalars(
|
|
148
|
+
select(GBill)
|
|
149
|
+
.where(GBill.g_batch == fake_batch)
|
|
150
|
+
.order_by(GBill.start_date)
|
|
151
|
+
).first()
|
|
152
|
+
if (
|
|
153
|
+
first_fake_bill is None
|
|
154
|
+
or first_fake_bill.start_date < current_month_start
|
|
155
|
+
):
|
|
156
|
+
fake_batch.delete(sess)
|
|
157
|
+
sess.flush()
|
|
158
|
+
fake_batch = None
|
|
159
|
+
|
|
160
|
+
if fake_batch is None:
|
|
161
|
+
fake_batch = g_contract.insert_g_batch(sess, fake_batch_name, "Fake Batch")
|
|
162
|
+
raw_bills = fb_func(
|
|
163
|
+
sess,
|
|
164
|
+
log,
|
|
165
|
+
last_month_start,
|
|
166
|
+
last_month_finish,
|
|
167
|
+
current_month_start,
|
|
168
|
+
current_month_finish,
|
|
169
|
+
)
|
|
170
|
+
if raw_bills is not None and len(raw_bills) > 0:
|
|
171
|
+
for raw_bill in raw_bills:
|
|
172
|
+
bill_type = BillType.get_by_code(sess, raw_bill["bill_type_code"])
|
|
173
|
+
g_supply = GSupply.get_by_mprn(sess, raw_bill["mprn"])
|
|
174
|
+
g_bill = fake_batch.insert_g_bill(
|
|
175
|
+
sess,
|
|
176
|
+
g_supply,
|
|
177
|
+
bill_type,
|
|
178
|
+
raw_bill["reference"],
|
|
179
|
+
raw_bill["account"],
|
|
180
|
+
raw_bill["issue_date"],
|
|
181
|
+
raw_bill["start_date"],
|
|
182
|
+
raw_bill["finish_date"],
|
|
183
|
+
raw_bill["kwh"],
|
|
184
|
+
raw_bill["net_gbp"],
|
|
185
|
+
raw_bill["vat_gbp"],
|
|
186
|
+
raw_bill["gross_gbp"],
|
|
187
|
+
raw_bill["raw_lines"],
|
|
188
|
+
raw_bill["breakdown"],
|
|
189
|
+
)
|
|
190
|
+
sess.flush()
|
|
191
|
+
for raw_read in raw_bill["reads"]:
|
|
192
|
+
prev_type = GReadType.get_by_code(
|
|
193
|
+
sess, raw_read["prev_type_code"]
|
|
194
|
+
)
|
|
195
|
+
pres_type = GReadType.get_by_code(
|
|
196
|
+
sess, raw_read["pres_type_code"]
|
|
197
|
+
)
|
|
198
|
+
g_unit = GUnit.get_by_code(sess, raw_read["unit"])
|
|
199
|
+
g_bill.insert_g_read(
|
|
200
|
+
sess,
|
|
201
|
+
raw_read["msn"],
|
|
202
|
+
g_unit,
|
|
203
|
+
raw_read["correction_factor"],
|
|
204
|
+
raw_read["calorific_value"],
|
|
205
|
+
raw_read["prev_value"],
|
|
206
|
+
raw_read["prev_date"],
|
|
207
|
+
prev_type,
|
|
208
|
+
raw_read["pres_value"],
|
|
209
|
+
raw_read["pres_date"],
|
|
210
|
+
pres_type,
|
|
211
|
+
)
|
|
212
|
+
sess.commit()
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
LAST_RUN_KEY = "fake_batch_updater_last_run"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class FakeBatchUpdater(threading.Thread):
|
|
219
|
+
def __init__(self):
|
|
220
|
+
super().__init__(name="Fake Batch Updater")
|
|
221
|
+
self.messages = collections.deque(maxlen=500)
|
|
222
|
+
self.progress = ""
|
|
223
|
+
self.stopped = threading.Event()
|
|
224
|
+
self.going = threading.Event()
|
|
225
|
+
self.global_alert = None
|
|
226
|
+
|
|
227
|
+
def stop(self):
|
|
228
|
+
self.stopped.set()
|
|
229
|
+
self.going.set()
|
|
230
|
+
self.join()
|
|
231
|
+
|
|
232
|
+
def go(self):
|
|
233
|
+
self.going.set()
|
|
234
|
+
|
|
235
|
+
def log(self, message):
|
|
236
|
+
self.messages.appendleft(
|
|
237
|
+
f"{ct_datetime_now().strftime('%Y-%m-%d %H:%M:%S')} - {message}"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def set_progress(self, progress):
|
|
241
|
+
self.progress = progress
|
|
242
|
+
|
|
243
|
+
def run(self):
|
|
244
|
+
while not self.stopped.is_set():
|
|
245
|
+
with Session() as sess:
|
|
246
|
+
try:
|
|
247
|
+
config = Contract.get_non_core_by_name(sess, "configuration")
|
|
248
|
+
state = config.make_state()
|
|
249
|
+
except BaseException as e:
|
|
250
|
+
msg = f"{e.description} " if isinstance(e, BadRequest) else ""
|
|
251
|
+
self.log(f"{msg}{traceback.format_exc()}")
|
|
252
|
+
self.global_alert = (
|
|
253
|
+
"There's a problem with a <a href='/fake_batch_updater'>"
|
|
254
|
+
"Fake Batch Updater</a>."
|
|
255
|
+
)
|
|
256
|
+
sess.rollback()
|
|
257
|
+
|
|
258
|
+
last_run = state.get(LAST_RUN_KEY)
|
|
259
|
+
if last_run is None or utc_datetime_now() - last_run > timedelta(days=1):
|
|
260
|
+
self.going.set()
|
|
261
|
+
|
|
262
|
+
if self.going.is_set():
|
|
263
|
+
self.global_alert = None
|
|
264
|
+
with Session() as sess:
|
|
265
|
+
try:
|
|
266
|
+
config = Contract.get_non_core_by_name(sess, "configuration")
|
|
267
|
+
state = config.make_state()
|
|
268
|
+
state[LAST_RUN_KEY] = utc_datetime_now()
|
|
269
|
+
config.update_state(state)
|
|
270
|
+
sess.commit()
|
|
271
|
+
run_import(sess, self.log, self.set_progress)
|
|
272
|
+
except BaseException as e:
|
|
273
|
+
msg = f"{e.description} " if isinstance(e, BadRequest) else ""
|
|
274
|
+
self.log(f"{msg}{traceback.format_exc()}")
|
|
275
|
+
self.global_alert = (
|
|
276
|
+
"There's a problem with a "
|
|
277
|
+
"<a href='/fake_batch_updater'>Fake Batch Updater</a>."
|
|
278
|
+
)
|
|
279
|
+
sess.rollback()
|
|
280
|
+
finally:
|
|
281
|
+
self.going.clear()
|
|
282
|
+
self.log("Finished updating fake batches.")
|
|
283
|
+
|
|
284
|
+
else:
|
|
285
|
+
self.log(
|
|
286
|
+
f"The updater was last run at {hh_format(last_run)}. There will "
|
|
287
|
+
f"be another update when a hour has elapsed since the last run."
|
|
288
|
+
)
|
|
289
|
+
self.going.wait(60 * 60)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def get_importer():
|
|
293
|
+
return importer
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def startup():
|
|
297
|
+
global importer
|
|
298
|
+
importer = FakeBatchUpdater()
|
|
299
|
+
importer.start()
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
@atexit.register
|
|
303
|
+
def shutdown():
|
|
304
|
+
if importer is not None:
|
|
305
|
+
importer.stop()
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import threading
|
|
3
|
+
import traceback
|
|
4
|
+
|
|
5
|
+
from flask import g, redirect
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from sqlalchemy import null, or_, select
|
|
9
|
+
from sqlalchemy.orm import joinedload
|
|
10
|
+
|
|
11
|
+
from chellow.dloads import open_file
|
|
12
|
+
from chellow.models import Batch, Bill, Contract, Era, RSession, ReportRun, User
|
|
13
|
+
from chellow.utils import (
|
|
14
|
+
c_months_u,
|
|
15
|
+
csv_make_val,
|
|
16
|
+
hh_max,
|
|
17
|
+
hh_min,
|
|
18
|
+
hh_range,
|
|
19
|
+
req_date,
|
|
20
|
+
req_int,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def content(user_id, report_run_id, contract_id, months_length, finish_date):
|
|
25
|
+
f = writer = None
|
|
26
|
+
try:
|
|
27
|
+
with RSession() as sess:
|
|
28
|
+
caches = {}
|
|
29
|
+
contract = Contract.get_by_id(sess, contract_id)
|
|
30
|
+
user = User.get_by_id(sess, user_id)
|
|
31
|
+
f = open_file(
|
|
32
|
+
f"missing_bills_{contract.id}.csv", user, mode="w", newline=""
|
|
33
|
+
)
|
|
34
|
+
writer = csv.writer(f, lineterminator="\n")
|
|
35
|
+
titles = (
|
|
36
|
+
"contract_name",
|
|
37
|
+
"month_start",
|
|
38
|
+
"month_finish",
|
|
39
|
+
"site_code",
|
|
40
|
+
"site_name",
|
|
41
|
+
"imp_mpan_core",
|
|
42
|
+
"exp_mpan_core",
|
|
43
|
+
"account",
|
|
44
|
+
)
|
|
45
|
+
writer.writerow(titles)
|
|
46
|
+
|
|
47
|
+
months = list(
|
|
48
|
+
c_months_u(
|
|
49
|
+
finish_year=finish_date.year,
|
|
50
|
+
finish_month=finish_date.month,
|
|
51
|
+
months=months_length,
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
for month_start, month_finish in months:
|
|
56
|
+
missing_bills = {}
|
|
57
|
+
missing_account = {}
|
|
58
|
+
account_missing_tuple = hh_range(caches, month_start, month_finish)
|
|
59
|
+
for era in sess.scalars(
|
|
60
|
+
select(Era)
|
|
61
|
+
.where(
|
|
62
|
+
Era.start_date <= month_finish,
|
|
63
|
+
or_(Era.finish_date == null(), Era.finish_date >= month_start),
|
|
64
|
+
or_(
|
|
65
|
+
Era.mop_contract == contract,
|
|
66
|
+
Era.dc_contract == contract,
|
|
67
|
+
Era.imp_supplier_contract == contract,
|
|
68
|
+
Era.exp_supplier_contract == contract,
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
.options(joinedload(Era.supply))
|
|
72
|
+
):
|
|
73
|
+
chunk_start = hh_max(era.start_date, month_start)
|
|
74
|
+
chunk_finish = hh_min(era.finish_date, month_finish)
|
|
75
|
+
missing_set = set(hh_range(caches, chunk_start, chunk_finish))
|
|
76
|
+
if era.mop_contract == contract:
|
|
77
|
+
account = era.mop_account
|
|
78
|
+
elif era.dc_contract == contract:
|
|
79
|
+
account = era.dc_account
|
|
80
|
+
elif era.imp_supplier_contract == contract:
|
|
81
|
+
account = era.imp_supplier_account
|
|
82
|
+
elif era.imp_supplier_contract == contract:
|
|
83
|
+
account = era.exp_supplier_account
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
account_missing_set = missing_account[account]
|
|
87
|
+
except KeyError:
|
|
88
|
+
account_missing_set = missing_account[account] = set(
|
|
89
|
+
account_missing_tuple
|
|
90
|
+
)
|
|
91
|
+
supply = era.supply
|
|
92
|
+
|
|
93
|
+
for bill in sess.scalars(
|
|
94
|
+
select(Bill)
|
|
95
|
+
.join(Batch)
|
|
96
|
+
.where(
|
|
97
|
+
Batch.contract == contract,
|
|
98
|
+
Bill.supply == supply,
|
|
99
|
+
Bill.start_date <= chunk_finish,
|
|
100
|
+
Bill.finish_date >= chunk_start,
|
|
101
|
+
)
|
|
102
|
+
):
|
|
103
|
+
found_set = set(
|
|
104
|
+
hh_range(caches, bill.start_date, bill.finish_date)
|
|
105
|
+
)
|
|
106
|
+
missing_set.difference_update(found_set)
|
|
107
|
+
account_missing_set.difference_update(found_set)
|
|
108
|
+
|
|
109
|
+
if len(missing_set) > 0:
|
|
110
|
+
site = era.get_physical_site(sess)
|
|
111
|
+
|
|
112
|
+
values = {
|
|
113
|
+
"contract_id": contract.id,
|
|
114
|
+
"contract_name": contract.name,
|
|
115
|
+
"month_start": month_start,
|
|
116
|
+
"month_finish": month_finish,
|
|
117
|
+
"era_id": era.id,
|
|
118
|
+
"supply_id": supply.id,
|
|
119
|
+
"imp_mpan_core": era.imp_mpan_core,
|
|
120
|
+
"exp_mpan_core": era.exp_mpan_core,
|
|
121
|
+
"site_id": site.id,
|
|
122
|
+
"site_code": site.code,
|
|
123
|
+
"site_name": site.name,
|
|
124
|
+
"account": account,
|
|
125
|
+
"market_role_code": contract.market_role.code,
|
|
126
|
+
}
|
|
127
|
+
missing_bills[era.id] = values
|
|
128
|
+
for era_id, values in missing_bills.items():
|
|
129
|
+
if len(missing_account[values["account"]]) > 0:
|
|
130
|
+
writer.writerow(csv_make_val(values[t]) for t in titles)
|
|
131
|
+
ReportRun.w_insert_row(report_run_id, "", titles, values, {})
|
|
132
|
+
|
|
133
|
+
except BaseException:
|
|
134
|
+
msg = traceback.format_exc()
|
|
135
|
+
print(msg)
|
|
136
|
+
if writer is not None:
|
|
137
|
+
writer.writerow([msg])
|
|
138
|
+
finally:
|
|
139
|
+
ReportRun.w_update(report_run_id, "finished")
|
|
140
|
+
if f is not None:
|
|
141
|
+
f.close()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def do_get(sess):
|
|
145
|
+
contract_id = req_int("contract_id")
|
|
146
|
+
months = req_int("months")
|
|
147
|
+
finish_date = req_date("finish", resolution="month")
|
|
148
|
+
report_run = ReportRun.insert(
|
|
149
|
+
sess,
|
|
150
|
+
"missing_e_bills",
|
|
151
|
+
g.user,
|
|
152
|
+
"missing_e_bills",
|
|
153
|
+
{},
|
|
154
|
+
)
|
|
155
|
+
sess.commit()
|
|
156
|
+
args = g.user.id, report_run.id, contract_id, months, finish_date
|
|
157
|
+
threading.Thread(target=content, args=args).start()
|
|
158
|
+
return redirect(f"/report_runs/{report_run.id}", 303)
|
|
@@ -97,7 +97,18 @@
|
|
|
97
97
|
<input type="submit" value="Download">
|
|
98
98
|
</fieldset>
|
|
99
99
|
</form>
|
|
100
|
-
|
|
100
|
+
|
|
101
|
+
<form action="/reports/missing_bills">
|
|
102
|
+
<fieldset>
|
|
103
|
+
<legend>Download Missing Bills</legend>
|
|
104
|
+
<input type="hidden" name="contract_id" value="{{dc_contract.id}}">
|
|
105
|
+
|
|
106
|
+
<label>Months</label> <input name="months" value="1" maxlength="2" size="2">
|
|
107
|
+
<label>Last Month</label> {{input_date('finish', last_month_finish, 'month')}}
|
|
108
|
+
<input type="submit" value="Download">
|
|
109
|
+
</fieldset>
|
|
110
|
+
</form>
|
|
111
|
+
|
|
101
112
|
<h3>Script</h3>
|
|
102
113
|
|
|
103
114
|
<pre>{{dc_contract.charge_script}}</pre>
|
|
@@ -76,6 +76,17 @@
|
|
|
76
76
|
<input type="submit" value="Download"/>
|
|
77
77
|
</fieldset>
|
|
78
78
|
</form>
|
|
79
|
+
|
|
80
|
+
<form action="/reports/missing_bills">
|
|
81
|
+
<fieldset>
|
|
82
|
+
<legend>Download Missing Bills</legend>
|
|
83
|
+
<input type="hidden" name="contract_id" value="{{contract.id}}">
|
|
84
|
+
|
|
85
|
+
<label>Months</label> <input name="months" value="1" maxlength="2" size="2">
|
|
86
|
+
<label>Last Month</label> {{input_date('finish', last_month_finish, 'month')}}
|
|
87
|
+
<input type="submit" value="Download">
|
|
88
|
+
</fieldset>
|
|
89
|
+
</form>
|
|
79
90
|
<br>
|
|
80
91
|
<h2>Script</h2>
|
|
81
92
|
<pre>{{contract.charge_script}}</pre>
|
|
@@ -97,6 +97,17 @@
|
|
|
97
97
|
</fieldset>
|
|
98
98
|
</form>
|
|
99
99
|
|
|
100
|
+
<form action="/reports/missing_bills">
|
|
101
|
+
<fieldset>
|
|
102
|
+
<legend>Download Missing Bills</legend>
|
|
103
|
+
<input type="hidden" name="contract_id" value="{{contract.id}}">
|
|
104
|
+
|
|
105
|
+
<label>Months</label> <input name="months" value="1" maxlength="2" size="2">
|
|
106
|
+
<label>Last Month</label> {{input_date('finish', month_finish, 'month')}}
|
|
107
|
+
<input type="submit" value="Download">
|
|
108
|
+
</fieldset>
|
|
109
|
+
</form>
|
|
110
|
+
|
|
100
111
|
<h2>Script</h2>
|
|
101
112
|
|
|
102
113
|
<pre>{{contract.charge_script}}</pre>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}
|
|
4
|
+
» Fake Batch Updater
|
|
5
|
+
{% endblock %}
|
|
6
|
+
|
|
7
|
+
{% block nav %}
|
|
8
|
+
Fake Batch Updater
|
|
9
|
+
{% endblock %}
|
|
10
|
+
|
|
11
|
+
{% block content %}
|
|
12
|
+
<p>
|
|
13
|
+
{% if importer %}
|
|
14
|
+
<ul>
|
|
15
|
+
<li><a href="#controls">Controls</a></li>
|
|
16
|
+
<li><a href="#log">Log</a></li>
|
|
17
|
+
<li><a href="#batches">Fake Batches</a></li>
|
|
18
|
+
</ul>
|
|
19
|
+
|
|
20
|
+
<h4 id="controls">Controls</h4>
|
|
21
|
+
|
|
22
|
+
<table>
|
|
23
|
+
<tr>
|
|
24
|
+
<th>Is Going?</th>
|
|
25
|
+
<td>{{importer.going.is_set()}}</td>
|
|
26
|
+
</tr>
|
|
27
|
+
<tr>
|
|
28
|
+
<th>Import Now</th>
|
|
29
|
+
<td>
|
|
30
|
+
<form method="post">
|
|
31
|
+
<fieldset {% if importer.going.is_set() %}disabled{% endif %}>
|
|
32
|
+
<input type="submit" value="Import" name="import">
|
|
33
|
+
</fieldset>
|
|
34
|
+
</form>
|
|
35
|
+
</td>
|
|
36
|
+
</tr>
|
|
37
|
+
{% if importer.going.is_set() %}
|
|
38
|
+
<tr>
|
|
39
|
+
<th>Progress</th>
|
|
40
|
+
<td>{{importer.progress}}</td>
|
|
41
|
+
</tr>
|
|
42
|
+
{% endif %}
|
|
43
|
+
</table>
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
<h4 id="log">Log</h4>
|
|
47
|
+
|
|
48
|
+
<ul>
|
|
49
|
+
{% for message in importer.messages %}
|
|
50
|
+
<li>{{message}}</li>
|
|
51
|
+
{% endfor %}
|
|
52
|
+
</ul>
|
|
53
|
+
|
|
54
|
+
<h4 id="batches">Fake Batches</h4>
|
|
55
|
+
|
|
56
|
+
<table>
|
|
57
|
+
<caption>Electricity</caption>
|
|
58
|
+
<thead>
|
|
59
|
+
<tr>
|
|
60
|
+
<th>View</th>
|
|
61
|
+
<th>Reference</th>
|
|
62
|
+
<th>Contract Name</th>
|
|
63
|
+
</tr>
|
|
64
|
+
</thead>
|
|
65
|
+
<tbody>
|
|
66
|
+
{% for batch in e_fake_batches %}
|
|
67
|
+
<tr>
|
|
68
|
+
<th><a href="/e/supplier_batches/{{batch.id}}">View</a></th>
|
|
69
|
+
<td>{{ batch.reference }}</td>
|
|
70
|
+
<td>{{ batch.contract.name }}</td>
|
|
71
|
+
</tr>
|
|
72
|
+
{% endfor %}
|
|
73
|
+
</tbody>
|
|
74
|
+
</table>
|
|
75
|
+
|
|
76
|
+
<table>
|
|
77
|
+
<caption>Gas</caption>
|
|
78
|
+
<thead>
|
|
79
|
+
<tr>
|
|
80
|
+
<th>View</th>
|
|
81
|
+
<th>Reference</th>
|
|
82
|
+
<th>Contract Name</th>
|
|
83
|
+
</tr>
|
|
84
|
+
</thead>
|
|
85
|
+
<tbody>
|
|
86
|
+
{% for batch in g_fake_batches %}
|
|
87
|
+
<tr>
|
|
88
|
+
<th><a href="/g/batches/{{batch.id}}">View</a></th>
|
|
89
|
+
<td>{{ batch.reference }}</td>
|
|
90
|
+
<td>{{ batch.g_contract.name }}</td>
|
|
91
|
+
</tr>
|
|
92
|
+
{% endfor %}
|
|
93
|
+
</tbody>
|
|
94
|
+
</table>
|
|
95
|
+
|
|
96
|
+
{% else %}
|
|
97
|
+
<p>Importer not present.</p>
|
|
98
|
+
{% endif %}
|
|
99
|
+
{% endblock %}
|
chellow/templates/home.html
CHANGED
|
@@ -214,6 +214,7 @@
|
|
|
214
214
|
<li><a href="/national_grid">Automatic Importer: National Grid</a></li>
|
|
215
215
|
<li><a href="/e/lcc">Automatic Importer: Low Carbon Contracts</a></li>
|
|
216
216
|
<li><a href="/e/elexon">Automatic Importer: Elexon</a></li>
|
|
217
|
+
<li><a href="/fake_batch_updater">Automatic Fake Batch Updater</a></li>
|
|
217
218
|
</ul>
|
|
218
219
|
|
|
219
220
|
<ul>
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{% extends "base.html" %}
|
|
2
|
+
|
|
3
|
+
{% block title %}
|
|
4
|
+
» Report Runs » {{run.id}}
|
|
5
|
+
{% endblock %}
|
|
6
|
+
|
|
7
|
+
{% block nav %}
|
|
8
|
+
<a href="/report_runs">Report Runs</a> » {{run.id}}
|
|
9
|
+
{% endblock %}
|
|
10
|
+
|
|
11
|
+
{% block content %}
|
|
12
|
+
<table class="sticky">
|
|
13
|
+
<caption>Missing Electricity Bills Summary</caption>
|
|
14
|
+
<thead>
|
|
15
|
+
<tr>
|
|
16
|
+
<th>Date Created</th>
|
|
17
|
+
<th>Created By</th>
|
|
18
|
+
<th>State</th>
|
|
19
|
+
<th>Number Of Rows</th>
|
|
20
|
+
<th>Delete</th>
|
|
21
|
+
<th>Download Spreadsheet</th>
|
|
22
|
+
</tr>
|
|
23
|
+
</thead>
|
|
24
|
+
<tbody>
|
|
25
|
+
<tr>
|
|
26
|
+
<td>{{run.date_created|hh_format}}</td>
|
|
27
|
+
<td>{{run.creator}}</td>
|
|
28
|
+
<td>{{run.state}}</td>
|
|
29
|
+
<td>{{rows|length}}</td>
|
|
30
|
+
<td>
|
|
31
|
+
<form hx-delete="/report_runs/{{run.id}}/edit"
|
|
32
|
+
hx-confirm="Are you sure you want to delete this report run?">
|
|
33
|
+
<fieldset style="border: none;">
|
|
34
|
+
<input type="submit" name="delete" value="Delete">
|
|
35
|
+
</fieldset>
|
|
36
|
+
</form>
|
|
37
|
+
</td>
|
|
38
|
+
<td>
|
|
39
|
+
<a href="/report_runs/{{run.id}}/spreadsheet">Download</a>
|
|
40
|
+
</td>
|
|
41
|
+
</tr>
|
|
42
|
+
</tbody>
|
|
43
|
+
</table>
|
|
44
|
+
|
|
45
|
+
<table class="sticky">
|
|
46
|
+
<caption>
|
|
47
|
+
Missing Electricity Bills
|
|
48
|
+
</caption>
|
|
49
|
+
<thead>
|
|
50
|
+
<tr>
|
|
51
|
+
<th>Contract</th>
|
|
52
|
+
<th>Month</th>
|
|
53
|
+
<th>Supply</th>
|
|
54
|
+
<th>Import MPAN</th>
|
|
55
|
+
<th>Export MPAN</th>
|
|
56
|
+
<th>Account</th>
|
|
57
|
+
<th>Site</th>
|
|
58
|
+
</tr>
|
|
59
|
+
</thead>
|
|
60
|
+
<tbody>
|
|
61
|
+
{% for row in rows %}
|
|
62
|
+
{% set values = row.data['values'] %}
|
|
63
|
+
<tr>
|
|
64
|
+
<td>
|
|
65
|
+
{% if values['market_role_code'] == 'M' %}
|
|
66
|
+
<a href="/e/mop_contracts/{{values['contract_id']}}">{{values['contract_name']}}</a>
|
|
67
|
+
{% elif values['market_role_code'] == 'C' %}
|
|
68
|
+
<a href="/e/dc_contracts/{{values['contract_id']}}">{{values['contract_name']}}</a>
|
|
69
|
+
{% elif values['market_role_code'] == 'X' %}
|
|
70
|
+
<a href="/e/supplier_contracts/{{values['contract_id']}}">{{values['contract_name']}}</a>
|
|
71
|
+
{% endif %}
|
|
72
|
+
</td>
|
|
73
|
+
<td>{{values['month_start'][:7]}}</td>
|
|
74
|
+
<td>
|
|
75
|
+
<a href="/e/supplies/{{values['supply_id']}}">View</a>
|
|
76
|
+
</td>
|
|
77
|
+
<td>
|
|
78
|
+
{% if values['imp_mpan_core'] is not none %}
|
|
79
|
+
{{values['imp_mpan_core']}}
|
|
80
|
+
{% endif %}
|
|
81
|
+
</td>
|
|
82
|
+
<td>
|
|
83
|
+
{% if values['exp_mpan_core'] is not none %}
|
|
84
|
+
{{values['exp_mpan_core']}}
|
|
85
|
+
{% endif %}
|
|
86
|
+
</td>
|
|
87
|
+
<td>{{values['account']}}</td>
|
|
88
|
+
<td>
|
|
89
|
+
<a href="/sites/{{values['site_id']}}" title="{{values['site_name']}}">
|
|
90
|
+
{{values['site_code']}}
|
|
91
|
+
</a>
|
|
92
|
+
</td>
|
|
93
|
+
</tr>
|
|
94
|
+
{% endfor %}
|
|
95
|
+
</tbody>
|
|
96
|
+
</table>
|
|
97
|
+
{% endblock %}
|
chellow/utils.py
CHANGED
|
@@ -67,12 +67,15 @@ def req_zish(name):
|
|
|
67
67
|
def req_date(prefix, resolution="minute"):
|
|
68
68
|
year = req_int(f"{prefix}_year")
|
|
69
69
|
month = req_int(f"{prefix}_month")
|
|
70
|
-
day = req_int(f"{prefix}_day")
|
|
71
70
|
|
|
72
71
|
try:
|
|
73
|
-
if resolution == "
|
|
72
|
+
if resolution == "month":
|
|
73
|
+
d = ct_datetime(year, month)
|
|
74
|
+
elif resolution == "day":
|
|
75
|
+
day = req_int(f"{prefix}_day")
|
|
74
76
|
d = ct_datetime(year, month, day)
|
|
75
77
|
elif resolution == "minute":
|
|
78
|
+
day = req_int(f"{prefix}_day")
|
|
76
79
|
hour = req_int(f"{prefix}_hour")
|
|
77
80
|
minute = req_int(f"{prefix}_minute")
|
|
78
81
|
d = ct_datetime(year, month, day, hour, minute)
|
chellow/views.py
CHANGED
|
@@ -72,6 +72,7 @@ import chellow.national_grid
|
|
|
72
72
|
import chellow.rate_server
|
|
73
73
|
from chellow.edi_lib import SEGMENTS, parse_edi
|
|
74
74
|
from chellow.models import (
|
|
75
|
+
Batch,
|
|
75
76
|
BillType,
|
|
76
77
|
Channel,
|
|
77
78
|
Comm,
|
|
@@ -79,6 +80,7 @@ from chellow.models import (
|
|
|
79
80
|
Cop,
|
|
80
81
|
EnergisationStatus,
|
|
81
82
|
Era,
|
|
83
|
+
GBatch,
|
|
82
84
|
GContract,
|
|
83
85
|
GEra,
|
|
84
86
|
GExitZone,
|
|
@@ -156,6 +158,41 @@ def configuration():
|
|
|
156
158
|
return redirect(f"/non_core_contracts/{config.id}")
|
|
157
159
|
|
|
158
160
|
|
|
161
|
+
@home.route("/fake_batch_updater")
|
|
162
|
+
def fake_batch_updater_get():
|
|
163
|
+
importer = chellow.fake_batch_updater.importer
|
|
164
|
+
config = Contract.get_non_core_by_name(g.sess, "configuration")
|
|
165
|
+
props = config.make_properties()
|
|
166
|
+
|
|
167
|
+
e_fake_batches = g.sess.scalars(
|
|
168
|
+
select(Batch).where(
|
|
169
|
+
Batch.reference.startswith("e_fake_batch_"),
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
g_fake_batches = g.sess.scalars(
|
|
174
|
+
select(GBatch).where(
|
|
175
|
+
GBatch.reference.startswith("g_fake_batch_"),
|
|
176
|
+
)
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
return render_template(
|
|
180
|
+
"fake_batch_updater.html",
|
|
181
|
+
importer=importer,
|
|
182
|
+
config_state=config.make_state(),
|
|
183
|
+
config_properties=props.get("fake_batch_updater", {}),
|
|
184
|
+
e_fake_batches=e_fake_batches,
|
|
185
|
+
g_fake_batches=g_fake_batches,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@home.route("/fake_batch_updater", methods=["POST"])
|
|
190
|
+
def fake_batch_updater_post():
|
|
191
|
+
importer = chellow.fake_batch_updater.importer
|
|
192
|
+
importer.go()
|
|
193
|
+
return redirect("/fake_batch_updater", 303)
|
|
194
|
+
|
|
195
|
+
|
|
159
196
|
@home.route("/health")
|
|
160
197
|
def health():
|
|
161
198
|
return Response("healthy\n", mimetype="text/plain")
|
|
@@ -1501,6 +1538,19 @@ def report_run_get(run_id):
|
|
|
1501
1538
|
run=run,
|
|
1502
1539
|
rows=rows,
|
|
1503
1540
|
)
|
|
1541
|
+
elif run.name == "missing_e_bills":
|
|
1542
|
+
rows = g.sess.scalars(
|
|
1543
|
+
select(ReportRunRow)
|
|
1544
|
+
.filter(ReportRunRow.report_run == run)
|
|
1545
|
+
.order_by(
|
|
1546
|
+
ReportRunRow.data["values"]["month_start"],
|
|
1547
|
+
)
|
|
1548
|
+
).all()
|
|
1549
|
+
return render_template(
|
|
1550
|
+
"report_run_missing_e_bills.html",
|
|
1551
|
+
run=run,
|
|
1552
|
+
rows=rows,
|
|
1553
|
+
)
|
|
1504
1554
|
|
|
1505
1555
|
else:
|
|
1506
1556
|
order_by = "row.id"
|
|
@@ -1524,12 +1574,12 @@ def report_run_get(run_id):
|
|
|
1524
1574
|
)
|
|
1525
1575
|
|
|
1526
1576
|
|
|
1527
|
-
@home.route("/report_runs/<int:run_id>", methods=["
|
|
1528
|
-
def
|
|
1577
|
+
@home.route("/report_runs/<int:run_id>", methods=["DELETE"])
|
|
1578
|
+
def report_run_delete(run_id):
|
|
1529
1579
|
run = g.sess.query(ReportRun).filter(ReportRun.id == run_id).one()
|
|
1530
1580
|
run.delete(g.sess)
|
|
1531
1581
|
g.sess.commit()
|
|
1532
|
-
return
|
|
1582
|
+
return hx_redirect("/report_runs", 303)
|
|
1533
1583
|
|
|
1534
1584
|
|
|
1535
1585
|
@home.route("/report_runs/<int:run_id>/spreadsheet")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chellow
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1745313690.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,9 +1,10 @@
|
|
|
1
|
-
chellow/__init__.py,sha256=
|
|
1
|
+
chellow/__init__.py,sha256=yBjAoOWh4NlA847_j4hiI7O6YyiEysf2X1y873JS-aA,9792
|
|
2
2
|
chellow/api.py,sha256=mk17TfweR76DPFC8lX2SArTjai6y6YshASxqO1w-_-s,11036
|
|
3
3
|
chellow/bank_holidays.py,sha256=T_utYMwe_g1dz5X-aOTdIPryg49SvB7QsWM1yphlqG8,4423
|
|
4
4
|
chellow/commands.py,sha256=ESBe9ZWj1c3vdZgqMZ9gFvYAB3hRag2R1PzOwuw9yFo,1302
|
|
5
5
|
chellow/dloads.py,sha256=dixp-O0MF2_mlwrnKx3D9DH09Qu05BjTo0rZfigTjR4,5534
|
|
6
6
|
chellow/edi_lib.py,sha256=alu20x9ZX06iPfnNI9dEJzuP6RIf4We3Y_M_bl7RrcY,51789
|
|
7
|
+
chellow/fake_batch_updater.py,sha256=UdI1ygrrU5a9UZVxc9j9Lq-tIejHFGalG_4g-rPsriI,10596
|
|
7
8
|
chellow/general_import.py,sha256=y8X-FzQJzVrfvVMyErNHns2MGI511KwDC19AjIX3nTk,65325
|
|
8
9
|
chellow/models.py,sha256=XD5wl3Pa8vZFGA0aB1Pu-xJs3iBoBoeX44E8Myho_68,244648
|
|
9
10
|
chellow/national_grid.py,sha256=czwIZqzJndSGhEMQ5YzI6hRBhvjkM6VRVYXybf4_KXg,4377
|
|
@@ -11,8 +12,8 @@ chellow/proxy.py,sha256=cVXIktPlX3tQ1BYcwxq0nJXKE6r3DtFTtfFHPq55HaM,1351
|
|
|
11
12
|
chellow/rate_server.py,sha256=fg-Pf_9Hk3bXmC9riPQNGQxBvLvBa_WtNYdwDCjnCSg,5678
|
|
12
13
|
chellow/rrun.py,sha256=1Kt2q_K9UoDG_nsZz-Q6XJiMNKroWqlqFdxn2M6Q8CA,2088
|
|
13
14
|
chellow/testing.py,sha256=Dj2c1NX8lVlygueOrh2eyYawLW6qKEHxNhXVVUaNRO0,3637
|
|
14
|
-
chellow/utils.py,sha256=
|
|
15
|
-
chellow/views.py,sha256=
|
|
15
|
+
chellow/utils.py,sha256=i3GQK9MIcweosZk2gi-nX_IFq2DxURAJDyNoLBg6YwM,19421
|
|
16
|
+
chellow/views.py,sha256=GVmmChaMaFmZmcsgvR9Z5xfSh4Y1mPEBDR90kiZH5Xk,85292
|
|
16
17
|
chellow/e/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
18
|
chellow/e/aahedc.py,sha256=d2usudp7KYWpU6Pk3fal5EQ47EbvkvKeaFGylnb3NWw,606
|
|
18
19
|
chellow/e/bill_importer.py,sha256=7UcnqNlKbJc2GhW9gy8sDp9GuqambJVpZLvbafOZztA,7411
|
|
@@ -21,7 +22,7 @@ chellow/e/bsuos.py,sha256=hdP9vnOJSuZl46OAkJeUg1XJYvYIBj4J6Sqce1Hy9Vs,15542
|
|
|
21
22
|
chellow/e/ccl.py,sha256=30dh_SvlgzsTQPPAJNZWILaMvbeDsv9-P-S1JxS5_SQ,3184
|
|
22
23
|
chellow/e/cfd.py,sha256=CWLdYeNjgqT6Ro8YRf4vhwXIAJ2aV4Wi6HLNClVSeaQ,14260
|
|
23
24
|
chellow/e/computer.py,sha256=aGti5aXRTdC85M-WRiAoZkC_76sqfAiP-79ggWZF09Q,68020
|
|
24
|
-
chellow/e/dno_rate_parser.py,sha256=
|
|
25
|
+
chellow/e/dno_rate_parser.py,sha256=NOVfS9HRDsc0rO282hU-IdrcuvMlC7VE8RySqd_5eT0,21762
|
|
25
26
|
chellow/e/duos.py,sha256=RHrn93I1ASO2uYkuF18qlhG4p-jpuJhd_g3o69wtP4U,31004
|
|
26
27
|
chellow/e/elexon.py,sha256=ALhXS9Es7PV0z9ukPbIramn3cf3iLyFi-PMWPSm5iOs,5487
|
|
27
28
|
chellow/e/energy_management.py,sha256=aXC2qlGt3FAODlNl_frWzVYAQrJLP8FFOiNX3m-QE_Y,12388
|
|
@@ -58,8 +59,8 @@ chellow/e/bill_parsers/gdf_csv.py,sha256=ZfK3Oc6oP28p_P9DIevLNB_zW2WLcEJ3Lvb1gL3
|
|
|
58
59
|
chellow/e/bill_parsers/haven_csv.py,sha256=0uENq8IgVNqdxfBQMBxLTSZWCOuDHXZC0xzk52SbfyE,13652
|
|
59
60
|
chellow/e/bill_parsers/haven_edi.py,sha256=YGPHRxPOhje9s32jqPHHELni2tooOYj3cMC_qaZVPq4,16107
|
|
60
61
|
chellow/e/bill_parsers/haven_edi_tprs.py,sha256=ZVX9CCqUybsot_Z0BEOJPvl9x5kSr7fEWyuJXvZDcz4,11841
|
|
61
|
-
chellow/e/bill_parsers/mm.py,sha256=
|
|
62
|
-
chellow/e/bill_parsers/nonsettlement_dc_stark_xlsx.py,sha256=
|
|
62
|
+
chellow/e/bill_parsers/mm.py,sha256=tv7SAIbwFJYCiHz1ZwTV9QJeGkSo3-KuOp--DUimzLI,11227
|
|
63
|
+
chellow/e/bill_parsers/nonsettlement_dc_stark_xlsx.py,sha256=YvQ0Q6HlZ4XSk6Phx1UWaTSj8FREVjwQe8nrpiLVzbU,5458
|
|
63
64
|
chellow/e/bill_parsers/settlement_dc_stark_xlsx.py,sha256=osCpUYUdLcPtlo7ngXWGw0ImnxssLa1fOSejMwe51-k,6381
|
|
64
65
|
chellow/e/bill_parsers/sse_edi.py,sha256=L85DOfNkqexeEIEr8pCBn_2sHJI-zEaw6cogpE3YyYM,15204
|
|
65
66
|
chellow/e/bill_parsers/sww_xls.py,sha256=QEjiuvwvr5FuWCfqqVw8LaA_vZyAKsvRAS5fw3xtFhM,7533
|
|
@@ -106,6 +107,7 @@ chellow/reports/report_g_supplies_snapshot.py,sha256=9xB6RDrnbgxuomMcP1b1yEP4kOn
|
|
|
106
107
|
chellow/reports/report_g_supply_virtual_bill.py,sha256=EaYrB8PHJIXrUuhiZ7dwUlbNBkuyJebQHrdc308_z1o,3653
|
|
107
108
|
chellow/reports/report_g_virtual_bills.py,sha256=20vHa5LGQwOAlJlaGJaGszZrrbT0PMOZJf6hSxU2hIQ,4528
|
|
108
109
|
chellow/reports/report_g_virtual_bills_hh.py,sha256=gaiLEmKTpq6JsfZ1p0SdCDuPvzvigXp6z88gHRCA63w,3416
|
|
110
|
+
chellow/reports/report_missing_bills.py,sha256=3uWA6Wyskn30tUDOV_W6u_009flfKzua7vDh_jsBDaM,5956
|
|
109
111
|
chellow/reports/report_sscs.py,sha256=fQWyVG-gdg37DyNHgpNARpSxIwTl7mCn20fDLwx9oHg,3214
|
|
110
112
|
chellow/reports/report_supply_contacts.py,sha256=pvwlInaPYV_pa9MMK6vh854plHFwv3m5zo5xulR1g5I,3599
|
|
111
113
|
chellow/static/css/chellow.css,sha256=dnkuj9Z1BCOV_L2Y26lDd2QlTmFFhATa1YvwPVch1Oc,5375
|
|
@@ -121,9 +123,10 @@ chellow/templates/chain.html,sha256=iOnSoDQOO_-1tfcxUHQKDAUnV4QbCYu5PPNmS4RkOg4,
|
|
|
121
123
|
chellow/templates/csv_sites_monthly_duration.html,sha256=59gErG6KwA57z-qh2EJsVGvTUazqm85CSXefSfqPWcQ,486
|
|
122
124
|
chellow/templates/downloads.html,sha256=R9QPcFz-PLJOX7rDlmquIk-Hp9Iq-thzWCTfOexSpXg,937
|
|
123
125
|
chellow/templates/edi_viewer.html,sha256=szUthgHOdph6Of-7f_1LeC_zYlNJaMeS5ctn6xTAeiM,1437
|
|
126
|
+
chellow/templates/fake_batch_updater.html,sha256=aRQbxtNUlIzxwgSUy2pr-Km5NbhZkse4WSBtlqFIJMg,1885
|
|
124
127
|
chellow/templates/general_import.html,sha256=9ezzieDjaPBZ0nUJkMkzoDxWVzYtr4D-Dr2UCA5xV8U,1370
|
|
125
128
|
chellow/templates/general_imports.html,sha256=9sYN7FdzfxFypAUbUJ4VbhU3WhJrjArtqneohgX1hGE,13171
|
|
126
|
-
chellow/templates/home.html,sha256=
|
|
129
|
+
chellow/templates/home.html,sha256=EZbvIvNw0RiuIlaUTOojYSZMTd3VMbTkulABgTB_lAc,5732
|
|
127
130
|
chellow/templates/input_date.html,sha256=rpgB5n0LfN8Y5djN_ZiuSxqdskxzCoKrEqI7hyJkVQo,1248
|
|
128
131
|
chellow/templates/local_report.html,sha256=pV7_0QwyQ-D3OS9LXrly5pq3qprZnwTCoq6vCnMTkS4,1332
|
|
129
132
|
chellow/templates/local_reports.html,sha256=4wbfVkY4wUfSSfWjxqIsvCpIsa9k7H_dGAjznrG5jNM,701
|
|
@@ -143,6 +146,7 @@ chellow/templates/report_run_asset_comparison.html,sha256=VYCCUmIC7Mfe7uuaAHb6ih
|
|
|
143
146
|
chellow/templates/report_run_bill_check.html,sha256=H2ayoBL7EgKMq2Nwq5VjE_TNvcIKcqeCm0alQWLIw78,5084
|
|
144
147
|
chellow/templates/report_run_ecoes_comparison.html,sha256=VmkT5ypWLP8qZS6NbDTC4yDaG7mnUlxZz7EV8xkHGZw,4086
|
|
145
148
|
chellow/templates/report_run_g_bill_check.html,sha256=tOXl_mjR__foYKiOYflJbK-459actAtjzv8rfuL3TwM,4851
|
|
149
|
+
chellow/templates/report_run_missing_e_bills.html,sha256=l5idQhfaNhMvvzIRv-iqCpeDnYl_wgs6-mZMBOmuyR8,2447
|
|
146
150
|
chellow/templates/report_run_monthly_duration_org.html,sha256=gGNGJ4Q50q4BtIMi98rhO-7NqRHcsFUmbj2qzeOLejw,1713
|
|
147
151
|
chellow/templates/report_run_row.html,sha256=bmtcdqJaS1CXpL0i8PuqvmeF98jKNYX5-mnQu-OuDKQ,3791
|
|
148
152
|
chellow/templates/report_run_row_bill_check.html,sha256=aC2LMu_6NvmTN3ZdxHJPPPczyxPN6hg0F-PPcqIWUws,4683
|
|
@@ -198,7 +202,7 @@ chellow/templates/e/dc_bill_add.html,sha256=73Sn_MKBsUuYYnDfUMCdX1Dul6GimMC9YXk6
|
|
|
198
202
|
chellow/templates/e/dc_bill_edit.html,sha256=0GsN-ZIc4q-z_xs8igC2ZS6t4soo2SvB3qRA6iC-AuM,2707
|
|
199
203
|
chellow/templates/e/dc_bill_import.html,sha256=NHjMSoFizvFQIaPWuVE3nTCtMDTzJB0XmH8jXfV1tiA,2188
|
|
200
204
|
chellow/templates/e/dc_bill_imports.html,sha256=lCaUR47r9KPr0VrQhEvVEaKexM5R_nmkxtzgpWZ0e9s,2135
|
|
201
|
-
chellow/templates/e/dc_contract.html,sha256=
|
|
205
|
+
chellow/templates/e/dc_contract.html,sha256=MqkRck0qCgxXrHSbYTfyqjf_bAbIfMy93b9kdqH0pCQ,2746
|
|
202
206
|
chellow/templates/e/dc_contract_edit.html,sha256=IObmbHQmeZ_LSpnYgabmhoSNUR3aPmm-Jk5nJLM_u74,1706
|
|
203
207
|
chellow/templates/e/dc_contract_hh_import.html,sha256=JncR3L6cOK4jghsGyr-itEqlIemXBXt3kL09wpqnQSE,856
|
|
204
208
|
chellow/templates/e/dc_contract_hh_imports.html,sha256=eXFDGyzSgag4JRism81_p5yTzQOjCIXaVkQ8tl3dDcM,8172
|
|
@@ -255,7 +259,7 @@ chellow/templates/e/mop_bill_add.html,sha256=fjoJj6QvcCk28rPD0Z6tIXrFYZ7ygN6lIXu
|
|
|
255
259
|
chellow/templates/e/mop_bill_edit.html,sha256=Hld9b-ckLCd9N7hleugg0BuwsZrmkvDeiJ5yLW8z-Js,2748
|
|
256
260
|
chellow/templates/e/mop_bill_import.html,sha256=sSVCZnarru0GKYFW1uuP0f1Ix9iNFDtEypQ93hqeUsk,3201
|
|
257
261
|
chellow/templates/e/mop_bill_imports.html,sha256=jIhWaOuk4hYj9jhXBUxQ7C8v9HJljWfOGGeu1rZa1PI,1610
|
|
258
|
-
chellow/templates/e/mop_contract.html,sha256=
|
|
262
|
+
chellow/templates/e/mop_contract.html,sha256=M6_pZuNpjdh9EaSGA5UsqSFIAvB0ABOehJ7s5oQ3pvk,2144
|
|
259
263
|
chellow/templates/e/mop_contract_add.html,sha256=qyjDNz_pOVgfrsKaju0KwxBKJnWF9RxUa-8gUtaOc60,738
|
|
260
264
|
chellow/templates/e/mop_contract_edit.html,sha256=4DbyiKbWRdliQGPh0wlh4dEuhD-R4-3XRzXoNMLfFD4,1719
|
|
261
265
|
chellow/templates/e/mop_contracts.html,sha256=2TPVkeUefsYOVZIiiLMPJgwcrxhvjk8Io4UKvgdQ2AY,768
|
|
@@ -315,7 +319,7 @@ chellow/templates/e/supplier_bill_add.html,sha256=BsD-Zh7d9auiqJ61VPHiQrP8u8rTcw
|
|
|
315
319
|
chellow/templates/e/supplier_bill_edit.html,sha256=oxZrMcMwrvluJSPxD4yfM9mWNeugoguAwT_ai9Ynl88,2732
|
|
316
320
|
chellow/templates/e/supplier_bill_import.html,sha256=2_VvBoNE838UwuN0AiMwIrzqxQmWBo5DGY0lY833Bpk,5265
|
|
317
321
|
chellow/templates/e/supplier_bill_imports.html,sha256=9iTNGWKn9XjQTBP1Sepbo0QVNlgKh7EfizXapam7I6s,9356
|
|
318
|
-
chellow/templates/e/supplier_contract.html,sha256=
|
|
322
|
+
chellow/templates/e/supplier_contract.html,sha256=TOm5ey5b1tm0A-8g2EmZhY7WddbGDih1B4ieJknA8Ns,3254
|
|
319
323
|
chellow/templates/e/supplier_contract_add.html,sha256=gsozEtF24lzYi_Bb4LTenvh62tCt7dQ4CwaIz7rFck4,899
|
|
320
324
|
chellow/templates/e/supplier_contract_edit.html,sha256=Afwtn0l8XRbt86bMOru2AjjvctoUkieD052aY0B2mDc,1463
|
|
321
325
|
chellow/templates/e/supplier_contracts.html,sha256=VwWD4q88Fynz7vioFSAsyH6RR_1SyQQl6bQwzL-W1m0,1508
|
|
@@ -378,6 +382,6 @@ chellow/templates/g/supply_note_edit.html,sha256=b8mB6_ucBwoljp03iy6AgVaZUhGw3-1
|
|
|
378
382
|
chellow/templates/g/supply_notes.html,sha256=6epNmZ3NKdXZz27fvmRUGeffg_oc1kmwuBeyRzQe3Rg,854
|
|
379
383
|
chellow/templates/g/unit.html,sha256=KouNVU0-i84afANkLQ_heJ0uDfJ9H5A05PuLqb8iCN8,438
|
|
380
384
|
chellow/templates/g/units.html,sha256=p5Nd-lAIboKPEOO6N451hx1bcKxMg4BDODnZ-43MmJc,441
|
|
381
|
-
chellow-
|
|
382
|
-
chellow-
|
|
383
|
-
chellow-
|
|
385
|
+
chellow-1745313690.0.0.dist-info/METADATA,sha256=6L9prKczSdFxE1p_b8WJ_f16sePhQBrdB5ZLbBmbnTk,12238
|
|
386
|
+
chellow-1745313690.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
387
|
+
chellow-1745313690.0.0.dist-info/RECORD,,
|
|
File without changes
|