chellow 1738661883.0.0__py3-none-any.whl → 1739889885.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/e/views.py CHANGED
@@ -4251,7 +4251,10 @@ def site_add_e_supply_form_get(site_id):
4251
4251
  try:
4252
4252
  ct_now = ct_datetime_now()
4253
4253
  cops = g.sess.query(Cop).order_by(Cop.code)
4254
- comms = g.sess.execute(select(Comm).order_by(Comm.code)).scalars()
4254
+ comms = g.sess.scalars(select(Comm).order_by(Comm.code))
4255
+ dtc_meter_types = g.sess.scalars(
4256
+ select(DtcMeterType).order_by(DtcMeterType.code)
4257
+ )
4255
4258
  energisation_statuses = g.sess.query(EnergisationStatus).order_by(
4256
4259
  EnergisationStatus.code
4257
4260
  )
@@ -4515,6 +4518,7 @@ def site_add_e_supply_form_get(site_id):
4515
4518
  start_date=start_date,
4516
4519
  imp_llfcs=imp_llfcs,
4517
4520
  exp_llfcs=exp_llfcs,
4521
+ dtc_meter_types=dtc_meter_types,
4518
4522
  )
4519
4523
  except BadRequest as e:
4520
4524
  g.sess.rollback()
chellow/gas/views.py CHANGED
@@ -170,20 +170,20 @@ def supply_get(g_supply_id):
170
170
  )
171
171
 
172
172
  g_bills = (
173
- g.sess.query(GBill)
174
- .filter(GBill.g_supply == g_supply)
173
+ select(GBill)
174
+ .where(GBill.g_supply == g_supply)
175
175
  .order_by(
176
176
  GBill.start_date.desc(),
177
177
  GBill.issue_date.desc(),
178
178
  GBill.reference.desc(),
179
179
  )
180
180
  )
181
- if g_era.finish_date is not None:
182
- g_bills = g_bills.filter(GBill.start_date <= g_era.finish_date)
181
+ if g_era != g_eras[0]:
182
+ g_bills = g_bills.where(GBill.start_date < g_era.finish_date)
183
183
  if g_era != g_eras[-1]:
184
- g_bills = g_bills.filter(GBill.start_date >= g_era.start_date)
184
+ g_bills = g_bills.where(GBill.start_date >= g_era.start_date)
185
185
 
186
- for g_bill in g_bills:
186
+ for g_bill in g.sess.scalars(g_bills):
187
187
  g_reads = (
188
188
  g.sess.query(GRegisterRead)
189
189
  .filter(GRegisterRead.g_bill == g_bill)
@@ -220,7 +220,7 @@ def supply_get(g_supply_id):
220
220
 
221
221
  RELATIVE_YEAR = relativedelta(years=1)
222
222
 
223
- now = Datetime.utcnow()
223
+ now = utc_datetime_now()
224
224
  triad_year = (now - RELATIVE_YEAR).year if now.month < 3 else now.year
225
225
  this_month_start = Datetime(now.year, now.month, 1)
226
226
  last_month_start = this_month_start - relativedelta(months=1)
chellow/models.py CHANGED
@@ -6416,6 +6416,13 @@ class ReportRun(Base, PersistentClass):
6416
6416
  report_run.update(state)
6417
6417
  wsess.commit()
6418
6418
 
6419
+ @staticmethod
6420
+ def w_update_data(report_run_id, data):
6421
+ with Session() as wsess:
6422
+ report_run = ReportRun.get_by_id(wsess, report_run_id)
6423
+ report_run.update_data(data)
6424
+ wsess.commit()
6425
+
6419
6426
  @staticmethod
6420
6427
  def w_insert_row(report_run_id, tab, titles, values, properties):
6421
6428
  with Session() as wsess:
@@ -16,16 +16,34 @@ from werkzeug.exceptions import BadRequest
16
16
 
17
17
  import chellow.gas.engine
18
18
  from chellow.dloads import open_file
19
- from chellow.models import GBatch, GBill, GContract, GEra, Session, Site, SiteGEra, User
19
+ from chellow.models import (
20
+ GBatch,
21
+ GBill,
22
+ GContract,
23
+ GEra,
24
+ RSession,
25
+ ReportRun,
26
+ Site,
27
+ SiteGEra,
28
+ User,
29
+ )
20
30
  from chellow.utils import csv_make_val, hh_max, hh_min, req_date, req_int, to_utc
21
31
 
22
32
 
23
- def content(g_batch_id, g_bill_id, g_contract_id, start_date, finish_date, user_id):
33
+ def content(
34
+ g_batch_id,
35
+ g_bill_id,
36
+ g_contract_id,
37
+ start_date,
38
+ finish_date,
39
+ user_id,
40
+ report_run_id,
41
+ ):
24
42
  forecast_date = to_utc(Datetime.max)
25
43
  report_context = {}
26
44
  sess = tmp_file = None
27
45
  try:
28
- with Session() as sess:
46
+ with RSession() as sess:
29
47
  user = User.get_by_id(sess, user_id)
30
48
 
31
49
  tmp_file = open_file("g_bill_check.csv", user, mode="w")
@@ -108,13 +126,17 @@ def content(g_batch_id, g_bill_id, g_contract_id, start_date, finish_date, user_
108
126
  vbf,
109
127
  titles,
110
128
  csv_writer,
129
+ report_run_id,
111
130
  )
131
+ ReportRun.w_update(report_run_id, "finished")
112
132
  except BadRequest as e:
113
133
  tmp_file.write(f"Problem: {e.description}")
134
+ ReportRun.w_update(report_run_id, "problem")
114
135
  except BaseException:
115
136
  msg = traceback.format_exc()
116
137
  sys.stderr.write(msg + "\n")
117
138
  tmp_file.write(f"Problem {msg}")
139
+ ReportRun.w_update(report_run_id, "interrupted")
118
140
  finally:
119
141
  tmp_file.close()
120
142
 
@@ -129,6 +151,7 @@ def _process_g_bill_ids(
129
151
  vbf,
130
152
  titles,
131
153
  csv_writer,
154
+ report_run_id,
132
155
  ):
133
156
  g_bill_id = list(sorted(g_bill_ids))[0]
134
157
  g_bill_ids.remove(g_bill_id)
@@ -144,6 +167,7 @@ def _process_g_bill_ids(
144
167
  "covered_bill_ids": [],
145
168
  "virtual_problem": "",
146
169
  }
170
+ site_id = None
147
171
  read_dict = defaultdict(set)
148
172
  for g_read in g_bill.g_reads:
149
173
  if not all(
@@ -264,7 +288,7 @@ def _process_g_bill_ids(
264
288
  else:
265
289
  v = getattr(g_read, title)
266
290
  vals[k].add(v)
267
-
291
+ elements = set()
268
292
  for g_era in sess.execute(
269
293
  select(GEra).where(
270
294
  GEra.g_supply == g_supply,
@@ -283,6 +307,7 @@ def _process_g_bill_ids(
283
307
  .filter(SiteGEra.is_physical == true(), SiteGEra.g_era == g_era)
284
308
  .one()
285
309
  )
310
+ site_id = site.id
286
311
  vals["site_code"] = site.code
287
312
  vals["site_name"] = site.name
288
313
 
@@ -313,6 +338,9 @@ def _process_g_bill_ids(
313
338
  except TypeError as detail:
314
339
  raise BadRequest(f"For key {vk} and value {v}. {detail}")
315
340
 
341
+ if k.endswith("_gbp"):
342
+ elements.add(k[:-4])
343
+
316
344
  if g_bill.id not in covered_bills.keys():
317
345
  g_bill = covered_bills[sorted(covered_bills.keys())[0]]
318
346
 
@@ -324,22 +352,36 @@ def _process_g_bill_ids(
324
352
  vals["mprn"] = g_supply.mprn
325
353
  vals["supply_name"] = g_supply.name
326
354
 
327
- for k, v in vals.items():
328
- if k == "covered_bill_ids":
329
- vals[k] = " | ".join(str(b) for b in v)
330
- else:
331
- vals[k] = csv_make_val(v)
332
-
333
355
  for i, title in enumerate(titles):
334
356
  if title.startswith("difference_"):
335
- try:
336
- covered_val = float(vals[titles[i - 2]])
337
- virtual_val = float(vals[titles[i - 1]])
338
- vals[title] = covered_val - virtual_val
339
- except KeyError:
340
- vals[title] = None
357
+ covered_val = float(vals.get(titles[i - 2], 0))
358
+ virtual_val = float(vals.get(titles[i - 1], 0))
359
+ vals[title] = covered_val - virtual_val
341
360
 
342
361
  csv_writer.writerow([csv_make_val(vals.get(k)) for k in titles])
362
+ vals["g_bill_id"] = g_bill.id
363
+ vals["g_batch_id"] = g_bill.g_batch.id
364
+ vals["g_supply_id"] = g_supply.id
365
+ vals["site_id"] = site_id
366
+ for element in sorted(elements):
367
+ rate_name = f"difference_{element}_rate"
368
+ covered_rates = vals.get(f"covered_{element}_rate", [0])
369
+ virtual_rates = vals.get(f"virtual_{element}_rate", [0])
370
+ if len(covered_rates) == 1 and len(virtual_rates) == 1:
371
+ vals[rate_name] = (
372
+ float(covered_rates.pop()) * 100 - float(virtual_rates.pop()) * 100
373
+ )
374
+ else:
375
+ vals[rate_name] = 0
376
+
377
+ try:
378
+ covered_kwh = float(vals["covered_kwh"])
379
+ virtual_kwh = float(vals["virtual_kwh"])
380
+ vals["difference_kwh"] = covered_kwh - virtual_kwh
381
+ except KeyError:
382
+ vals["difference_kwh"] = None
383
+
384
+ ReportRun.w_insert_row(report_run_id, "", titles, vals, {"is_checked": False})
343
385
 
344
386
 
345
387
  def do_get(sess):
@@ -347,17 +389,39 @@ def do_get(sess):
347
389
 
348
390
  if "g_batch_id" in request.values:
349
391
  g_batch_id = req_int("g_batch_id")
392
+ g_batch = GBatch.get_by_id(g.sess, g_batch_id)
393
+ run_g_contract_id = g_batch.g_contract.id
350
394
  elif "g_bill_id" in request.values:
351
395
  g_bill_id = req_int("g_bill_id")
396
+ g_bill = GBill.get_by_id(g.sess, g_bill_id)
397
+ run_g_contract_id = g_bill.g_batch.g_contract.id
352
398
  elif "g_contract_id" in request.values:
353
399
  g_contract_id = req_int("g_contract_id")
354
400
  start_date = req_date("start_date")
355
401
  finish_date = req_date("finish_date")
402
+ run_g_contract_id = g_contract_id
356
403
  else:
357
404
  raise BadRequest(
358
405
  "The bill check needs a g_batch_id, g_bill_id or g_contract_id."
359
406
  )
360
407
 
361
- args = g_batch_id, g_bill_id, g_contract_id, start_date, finish_date, g.user.id
408
+ report_run = ReportRun.insert(
409
+ sess,
410
+ "g_bill_check",
411
+ g.user,
412
+ "g_bill_check",
413
+ {"g_contract_id": run_g_contract_id},
414
+ )
415
+ sess.commit()
416
+
417
+ args = (
418
+ g_batch_id,
419
+ g_bill_id,
420
+ g_contract_id,
421
+ start_date,
422
+ finish_date,
423
+ g.user.id,
424
+ report_run.id,
425
+ )
362
426
  threading.Thread(target=content, args=args).start()
363
- return redirect("/downloads", 303)
427
+ return redirect(f"/report_runs/{report_run.id}", 303)
@@ -230,7 +230,7 @@ table.sticky {
230
230
  position: relative;
231
231
  }
232
232
 
233
- table.sticky th {
233
+ table.sticky thead {
234
234
  position: sticky;
235
235
  top: 0;
236
236
  background: white;
@@ -0,0 +1,182 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}
4
+ &raquo; Report Runs &raquo; {{run.id}}
5
+ {% endblock %}
6
+
7
+ {% block nav %}
8
+ <a href="/report_runs">Report Runs</a> &raquo; {{run.id}}
9
+ {% endblock %}
10
+
11
+ {% block content %}
12
+ {% if request.method == "GET" and request.values.delete %}
13
+ <form method="post" action="/report_runs/{{run.id}}">
14
+ <fieldset>
15
+ <legend>Are you sure you want to delete this report run?</legend>
16
+ <input type="submit" name="delete" value="delete">
17
+ <a href="/report_runs/{{run.id}}">Cancel</a>
18
+ </fieldset>
19
+ </form>
20
+ {% else %}
21
+ <table>
22
+ <caption>Bill Check</caption>
23
+ <thead>
24
+ <tr>
25
+ <th>Date Created</th>
26
+ <th>Created By</th>
27
+ <th>Title</th>
28
+ <th>State</th>
29
+ <th>Number Of Rows</th>
30
+ <th>Sum Of Difference GBP</th>
31
+ <th>Delete</th>
32
+ <th>Download Spreadsheet</th>
33
+ </tr>
34
+ </thead>
35
+ <tbody>
36
+ <tr>
37
+ <td>{{run.date_created|hh_format}}</td>
38
+ <td>{{run.creator}}</td>
39
+ <td>{{run.title}}</td>
40
+ <td>{{run.state}}</td>
41
+ <td>{{rows|length}} (truncated at {{ROW_LIMIT}})</td>
42
+ <td>
43
+ {% if 'sum_difference' in summary and summary.sum_difference is not none %}
44
+ {{"%.2f"|format(summary.sum_difference)}}</td>
45
+ {% endif %}
46
+ <td>
47
+ <form action="/report_runs/{{run.id}}">
48
+ <fieldset style="border: none;">
49
+ <input type="submit" name="delete" value="Delete">
50
+ </fieldset>
51
+ </form>
52
+ </td>
53
+ <td><a href="/report_runs/{{run.id}}/spreadsheet">Download</a></td>
54
+ </tr>
55
+ </tbody>
56
+ </table>
57
+
58
+ {% if summary.missing_bills|length > 0 %}
59
+ <table class="sticky">
60
+ <caption>
61
+ Some Missing Bills (<a href="/report_runs/{{report_run}}">x</a>)
62
+ </caption>
63
+ <thead>
64
+ <tr>
65
+ <th>View</th>
66
+ <th>Import MPAN Core</th>
67
+ <th>Export MPAN Core</th>
68
+ <th>Start</th>
69
+ <th>Finish</th>
70
+ <th>Problem</th>
71
+ <th>Estimated Difference GBP</th>
72
+ </tr>
73
+ </thead>
74
+ <tbody>
75
+ {% for row in rows %}
76
+ <tr>
77
+ <td><a href="/report_run_rows/{{row.id}}">View</a></td>
78
+ {% for title in row.data.titles %}
79
+ <td>{{row.data['values'][title]}}</td>
80
+ {% endfor %}
81
+ </tr>
82
+ {% endfor %}
83
+ </tbody>
84
+ </table>
85
+ {% endif %}
86
+
87
+ {% if show_elements %}
88
+ <table class="sticky">
89
+ <caption>Elements</caption>
90
+ <thead>
91
+ <tr>
92
+ <th>Element</th>
93
+ {% for elname, diff in elements %}
94
+ <th>{{elname}}</th>
95
+ {% endfor %}
96
+ </tr>
97
+ </thead>
98
+ <tbody>
99
+ <tr>
100
+ <td>Sum of differences (GBP)</td>
101
+ {% for elname, diff in elements %}
102
+ {% if diff is not none %}
103
+ <td>{{"%.2f"|format(diff)}}</td>
104
+ {% endif %}
105
+ {% endfor %}
106
+ </tr>
107
+ </tbody>
108
+ </table>
109
+ {% endif %}
110
+
111
+ <table class="sticky">
112
+ <caption>
113
+ Rows - Ordered By
114
+ {% if sort_absolute %}
115
+ Absolute
116
+ {% endif %}
117
+ {{order_by}}
118
+ </caption>
119
+ <thead>
120
+ <tr>
121
+ <th rowspan="2">View</th>
122
+ <th rowspan="2">Batch</th>
123
+ <th rowspan="2">Site</th>
124
+ <th rowspan="2">MPRN</th>
125
+ <th rowspan="2">Covered Bills</th>
126
+ <th rowspan="2">Covered Start</th>
127
+ <th rowspan="2">Covered Finish</th>
128
+ <th colspan="{{columns|length}}">Difference</th>
129
+ <th rowspan="2">Problem</th>
130
+ </tr>
131
+ <tr>
132
+ {% for column in columns %}
133
+ <th>
134
+ {{' '.join(column.split('_')[1:])}}
135
+ {% if order_by == column %}
136
+
137
+ {% else %}
138
+ <a href="/report_runs/{{run.id}}?order_by={{column}}">↑</a>
139
+ {% endif %}
140
+ </th>
141
+ {% endfor %}
142
+ </tr>
143
+ </thead>
144
+ <tbody>
145
+ {% for row in rows %}
146
+ {% set values = row.data['values'] %}
147
+ <tr>
148
+ <td><a href="/report_run_rows/{{row.id}}">View</a></td>
149
+ <td><a href="/g/batches/{{values.g_batch_id}}">{{values.batch}}</a></td>
150
+ <td>
151
+ <a href="/sites/{{values.site_id}}" title="{{values.site_name}}">{{values.site_code}}</a>
152
+ </td>
153
+ <td>
154
+ <a href="/g/supplies/{{values.g_supply_id}}">{{values.mprn}}</a>
155
+ </td>
156
+ <td>
157
+ {% if values['covered_bill_ids'] is not none %}
158
+ {% for bill_id in values['covered_bill_ids'] %}
159
+ <a href="/g/bills/{{bill_id}}">{{bill_id}}</a>
160
+ {% if bill_id == values.g_bill_id %}
161
+ {% endif %}
162
+ {% endfor %}
163
+ {% endif %}
164
+ </td>
165
+ <td><span title="{{values.covered_start}}">{{values.covered_start[:10]}}</span></td>
166
+ <td><span title="{{values.covered_finish}}">{{values.covered_finish[:10]}}</span></td>
167
+ {% for title in columns %}
168
+ <td>
169
+ {% if title in values and values[title] is not none %}
170
+ {{"%.2f"|format(values[title])}}
171
+ {% endif %}
172
+ </td>
173
+ {% endfor %}
174
+ <td>
175
+ {{ values.covered_problem }} {{ values.virtual_problem }}
176
+ </td>
177
+ </tr>
178
+ {% endfor %}
179
+ </tbody>
180
+ </table>
181
+ {% endif %}
182
+ {% endblock %}
@@ -0,0 +1,191 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}
4
+ &raquo; Report Runs &raquo; {{row.report_run.id}} &raquo; Row {{row.id}}
5
+ {% endblock %}
6
+
7
+ {% block nav %}
8
+ <a href="/report_runs">Report Runs</a> &raquo;
9
+ <a href="/report_runs/{{row.report_run.id}}">{{row.report_run.id}}</a>
10
+ &raquo; Row {{row.id}}
11
+ {% endblock %}
12
+
13
+ {% block content %}
14
+ {% set values = row.data['values'] %}
15
+ {% set properties = row.data.get('properties', {}) %}
16
+ {% if row.report_run.name == 'bill_check' %}
17
+ <table>
18
+ <caption>Bill Check</caption>
19
+ <thead>
20
+ <tr>
21
+ <th rowspan="2">Batch</th>
22
+ <th colspan="8">Bill</th>
23
+ <th colspan="2">MPAN Core</th>
24
+ <th colspan="2">Site</th>
25
+ <th rowspan="2">Checking</th>
26
+ </tr>
27
+ <tr>
28
+ <th>Reference</th>
29
+ <th>Type</th>
30
+ <th>kWh</th>
31
+ <th>Net GBP</th>
32
+ <th>VAT GBP</th>
33
+ <th>Gross GBP</th>
34
+ <th>Start</th>
35
+ <th>Finish</th>
36
+ <th>Import</th>
37
+ <th>Export</th>
38
+ <th>Code</th>
39
+ <th>Name</th>
40
+ </tr>
41
+ </thead>
42
+ <tbody>
43
+ <tr>
44
+ <td>
45
+ {% if values.batch_id != None %}
46
+ <a href="/e/supplier_batches/{{values.batch_id}}">{{values['batch']}}</a>
47
+ {% endif %}
48
+ </td>
49
+ <td>
50
+ {% if values.bill_id != None %}
51
+ <a href="/e/supplier_bills/{{values.bill_id}}">{{
52
+ values['bill-reference']}}</a>
53
+ {% endif %}
54
+ </td>
55
+ <td>{{values['bill-type']}}</td>
56
+ <td>{{values['bill-kwh']}}</td>
57
+ <td>{{values['bill-net-gbp']}}</td>
58
+ <td>{{values['bill-vat-gbp']}}</td>
59
+ <td>{{values['bill-gross-gbp']}}</td>
60
+ <td>{{values['bill-start-date']}}</td>
61
+ <td>{{values['bill-finish-date']}}</td>
62
+ <td>
63
+ {% if values['imp-mpan-core'] != None %}
64
+ <a href="/e/supplies/{{values.supply_id}}">{{
65
+ values['imp-mpan-core']}}</a>
66
+ {% endif %}
67
+ </td>
68
+ <td>
69
+ {% if values['exp-mpan-core'] != None %}
70
+ <a href="/e/supplies/{{values.supply_id}}">{{
71
+ values['exp-mpan-core']}}</a>
72
+ {% endif %}
73
+ </td>
74
+ <td>
75
+ {% if values.site_id != None %}
76
+ <a href="/sites/{{values.site_id}}">{{
77
+ values['site-code']}}</a>
78
+ {% endif %}
79
+ </td>
80
+ <td>{{values['site-name']}}</td>
81
+ <td>
82
+ <form action="/report_run_rows/{{row.id}}" method="post">
83
+ <fieldset>
84
+ {{input_textarea(
85
+ 'note', properties.note, 5, 40,
86
+ placeholder='Notes on checking go here...')}}
87
+ <br>
88
+ <br>
89
+ <label>Checked?
90
+ {{input_checkbox('is_checked', properties.is_checked)}}
91
+ </label>
92
+ <input type="submit" name="update" value="Update">
93
+ </fieldset>
94
+ </form>
95
+ </td>
96
+ </tr>
97
+ </tbody>
98
+ </table>
99
+
100
+ <table>
101
+ <caption>Covered</caption>
102
+ <thead>
103
+ <tr>
104
+ <th>From</th>
105
+ <th>To</th>
106
+ <th>Bills</th>
107
+ <th>Metered kWh</th>
108
+ <th>Net GBP</th>
109
+ <th>Virtual Net GBP</th>
110
+ <th>Difference GBP</th>
111
+ <th>Problem</th>
112
+ <th>Virtual Problem</th>
113
+ </tr>
114
+ </thead>
115
+ <tbody>
116
+ <tr>
117
+ <td>{{values['covered-from']}}</td>
118
+ <td>{{values['covered-to']}}</td>
119
+ <td>
120
+ {% if values['covered-bills'] != None %}
121
+ <ul>
122
+ {% for bill_id in values['covered-bills'] %}
123
+ <li>
124
+ <a href="/e/supplier_bills/{{bill_id}}">{{bill_id}}</a>
125
+ {% if bill_id == values.bill_id %}
126
+ (This bill)
127
+ {% endif %}
128
+ </li>
129
+ {% endfor %}
130
+ </ul>
131
+ {% endif %}
132
+ </td>
133
+ <td>
134
+ {% if values['metered-kwh'] != None %}
135
+ {{"%.0f"|format(values['metered-kwh'])}}
136
+ {% endif %}
137
+ </td>
138
+ <td>{{values['covered-net-gbp']}}</td>
139
+ <td>
140
+ {% if values['virtual-net-gbp'] != None %}
141
+ {{"%.2f"|format(values['virtual-net-gbp'])}}
142
+ {% endif %}
143
+ </td>
144
+ <td>
145
+ {% if values['difference-net-gbp'] != None %}
146
+ {{"%.2f"|format(values['difference-net-gbp'])}}
147
+ {% endif %}
148
+ </td>
149
+ <td>{{values['covered-problem']}}</td>
150
+ <td>{{values['virtual-problem']}}</td>
151
+ </tr>
152
+ </tbody>
153
+ </table>
154
+
155
+ {% for table in tables %}
156
+ <table>
157
+ <caption>{{table.name}}</caption>
158
+ <thead>
159
+ <tr>
160
+ {% for title in table.titles %}
161
+ <th>{{title}}</th>
162
+ {% endfor %}
163
+ </tr>
164
+ </thead>
165
+ <tbody>
166
+ <tr>
167
+ {% for v in table['values'] %}
168
+ <td>{{v}}</td>
169
+ {% endfor %}
170
+ </tr>
171
+ </tbody>
172
+ </table>
173
+ {% endfor %}
174
+
175
+ <h2>Raw</h2>
176
+ <pre>{{raw_data}}</pre>
177
+
178
+ {% else %}
179
+ <table>
180
+ <caption>Report Run Row</caption>
181
+ <tbody>
182
+ {% for title in row.data.titles %}
183
+ <tr>
184
+ <th>{{title}}</th>
185
+ <td>{{row.data['values'][title]}}</td>
186
+ </tr>
187
+ {% endfor %}
188
+ </tbody>
189
+ </table>
190
+ {% endif %}
191
+ {% endblock %}
chellow/views.py CHANGED
@@ -1365,6 +1365,74 @@ def report_run_get(run_id):
1365
1365
  hide_checked=hide_checked,
1366
1366
  ROW_LIMIT=ROW_LIMIT,
1367
1367
  )
1368
+ elif run.name == "g_bill_check":
1369
+ row = (
1370
+ g.sess.query(ReportRunRow)
1371
+ .filter(ReportRunRow.report_run == run)
1372
+ .order_by(ReportRunRow.id)
1373
+ .first()
1374
+ )
1375
+ elements = []
1376
+ summary = {}
1377
+ if row is None:
1378
+ pass
1379
+
1380
+ else:
1381
+ titles = row.data["titles"]
1382
+ diff_titles = [
1383
+ t for t in titles if t.startswith("difference_") and t.endswith("_gbp")
1384
+ ]
1385
+ diff_selects = [
1386
+ func.sum(ReportRunRow.data["values"][t].as_float()) for t in diff_titles
1387
+ ]
1388
+ sum_diffs = (
1389
+ g.sess.query(*diff_selects).filter(ReportRunRow.report_run == run).one()
1390
+ )
1391
+
1392
+ for t, sum_diff in zip(diff_titles, sum_diffs):
1393
+ elem = t[11:-4]
1394
+ sdiff = 0 if sum_diff is None else sum_diff
1395
+ if elem == "net":
1396
+ summary["sum_difference"] = sdiff
1397
+ else:
1398
+ elements.append((elem, sdiff))
1399
+
1400
+ elements.sort(key=lambda x: abs(x[1]), reverse=True)
1401
+ elements.insert(0, ("net", summary["sum_difference"]))
1402
+
1403
+ if "order_by" in request.values:
1404
+ order_by = req_str("order_by")
1405
+ else:
1406
+ order_by = "difference_net_gbp"
1407
+
1408
+ g_contract = GContract.get_by_id(g.sess, run.data["g_contract_id"])
1409
+ g_contract_props = g_contract.make_properties()
1410
+ props = g_contract_props.get("report_run", {})
1411
+ sort_absolute = props.get("sort_absolute", True)
1412
+ show_elements = props.get("show_elements", True)
1413
+ columns = props.get("columns", [f"difference_{el[0]}_gbp" for el in elements])
1414
+
1415
+ ROW_LIMIT = 200
1416
+ q = select(ReportRunRow).where(ReportRunRow.report_run == run).limit(ROW_LIMIT)
1417
+ if sort_absolute:
1418
+ q = q.order_by(
1419
+ func.abs(ReportRunRow.data["values"][order_by].as_float()).desc()
1420
+ )
1421
+ else:
1422
+ q = q.order_by(ReportRunRow.data["values"][order_by].as_float().desc())
1423
+ rows = g.sess.scalars(q).all()
1424
+ return render_template(
1425
+ "report_run_g_bill_check.html",
1426
+ run=run,
1427
+ rows=rows,
1428
+ summary=summary,
1429
+ elements=elements,
1430
+ order_by=order_by,
1431
+ ROW_LIMIT=ROW_LIMIT,
1432
+ sort_absolute=sort_absolute,
1433
+ show_elements=show_elements,
1434
+ columns=columns,
1435
+ )
1368
1436
 
1369
1437
  elif run.name == "asset_comparison":
1370
1438
  rows = (
@@ -1548,6 +1616,51 @@ def report_run_row_get(row_id):
1548
1616
  return render_template(
1549
1617
  "report_run_row_bill_check.html", row=row, raw_data=raw_data, tables=tables
1550
1618
  )
1619
+ elif row.report_run.name == "g_bill_check":
1620
+ values = row.data["values"]
1621
+ elements = {}
1622
+ for t in row.data["values"].keys():
1623
+
1624
+ if (
1625
+ t.startswith("covered_")
1626
+ or t.startswith("virtual_")
1627
+ or t.startswith("difference_")
1628
+ ) and t not in (
1629
+ "covered_from",
1630
+ "covered_to",
1631
+ "covered_bills",
1632
+ "covered_problem",
1633
+ "virtual_problem",
1634
+ ):
1635
+ toks = t.split("_")
1636
+ name = "_".join(toks[1:-1])
1637
+ try:
1638
+ table = elements[name]
1639
+ except KeyError:
1640
+ table = elements[name] = {"order": 0}
1641
+
1642
+ if "titles" not in table:
1643
+ table["titles"] = []
1644
+ table["titles"].append(toks[0] + "_" + "_".join(toks[2:]))
1645
+ if "values" not in table:
1646
+ table["values"] = []
1647
+ table["values"].append(values[t])
1648
+ if t.startswith("difference_") and t.endswith("-gbp"):
1649
+ table["order"] = abs(values[t])
1650
+
1651
+ for k, v in elements.items():
1652
+ if k == "net":
1653
+ continue
1654
+ v["name"] = k
1655
+ tables.append(v)
1656
+
1657
+ tables.sort(key=lambda t: t["order"], reverse=True)
1658
+ return render_template(
1659
+ "report_run_row_g_bill_check.html",
1660
+ row=row,
1661
+ raw_data=raw_data,
1662
+ tables=tables,
1663
+ )
1551
1664
 
1552
1665
  else:
1553
1666
  return render_template(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chellow
3
- Version: 1738661883.0.0
3
+ Version: 1739889885.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)
@@ -5,14 +5,14 @@ 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
7
  chellow/general_import.py,sha256=tcYZK2N7HOFJSkYyy3MMxVBEvMFjfB9pjF0ksujtkFA,65113
8
- chellow/models.py,sha256=9LLROzS6gXZGICk0ITXhCyTtKaIY7HW4bMqWg4Mc-xw,244221
8
+ chellow/models.py,sha256=GVuPnDUGJqPbdD8Z4Ccoiw4ZLeuEXFgAkrTLS_znTQw,244452
9
9
  chellow/national_grid.py,sha256=czwIZqzJndSGhEMQ5YzI6hRBhvjkM6VRVYXybf4_KXg,4377
10
10
  chellow/proxy.py,sha256=cVXIktPlX3tQ1BYcwxq0nJXKE6r3DtFTtfFHPq55HaM,1351
11
11
  chellow/rate_server.py,sha256=fg-Pf_9Hk3bXmC9riPQNGQxBvLvBa_WtNYdwDCjnCSg,5678
12
12
  chellow/rrun.py,sha256=1Kt2q_K9UoDG_nsZz-Q6XJiMNKroWqlqFdxn2M6Q8CA,2088
13
13
  chellow/testing.py,sha256=Od4HHH6pZrhJ_De118_F55RJEKmAvhUH2S24QE9qFQk,3635
14
14
  chellow/utils.py,sha256=Ej7dsbQ6Ee8X2aZ7B2Vs-hUFCsMABioAdOV1DJjwY-0,19293
15
- chellow/views.py,sha256=OlNGtFB9OatQPMzHiXTjBoGOSnFT7cC38GA2x7rtcFg,79490
15
+ chellow/views.py,sha256=82V1OtdscrAmbiT3B5MDZoCe50fDSJwfBF-PpbB5tm8,83513
16
16
  chellow/e/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  chellow/e/aahedc.py,sha256=d2usudp7KYWpU6Pk3fal5EQ47EbvkvKeaFGylnb3NWw,606
18
18
  chellow/e/bill_importer.py,sha256=7UcnqNlKbJc2GhW9gy8sDp9GuqambJVpZLvbafOZztA,7411
@@ -43,7 +43,7 @@ chellow/e/system_price.py,sha256=6w5J7bzwFAZubE2zdOFRiS8IIrVP8hkoIOaG2yCt-Ic,623
43
43
  chellow/e/tlms.py,sha256=M33D6YpMixu2KkwSCzDRM3kThLgShg8exp63Obo75l8,8905
44
44
  chellow/e/tnuos.py,sha256=NBmc-f3oezrl4gviAKobljHfICTpBKxxxEGBGJi_lRk,4927
45
45
  chellow/e/triad.py,sha256=lIQj7EdUrcFwEqleuHZXYU_bfzIwNOqUVVxB3NPQt4A,13710
46
- chellow/e/views.py,sha256=VkoB3-jgydp-2RMdlYVvSL4UVUmt7wMnCJpHSBdf1jM,220753
46
+ chellow/e/views.py,sha256=ZDVUirf0fMLO8aS5gF3xpPnRDmNqYxxGHkGjxm0_AY4,220901
47
47
  chellow/e/bill_parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
48
  chellow/e/bill_parsers/activity_mop_stark_xlsx.py,sha256=UgWXDPzQkQghyj_lfgBqoSJpHB-t-qOdSaB8qY6GLog,4071
49
49
  chellow/e/bill_parsers/annual_mop_stark_xlsx.py,sha256=-HMoIfa_utXYKA44RuC0Xqv3vd2HLeQU_4P0iBUd3WA,4219
@@ -72,7 +72,7 @@ chellow/gas/cv.py,sha256=4cdYYQ8Qak6NeYdBCB4YaQ0jX8-UkaydIIdibCQuXxM,7344
72
72
  chellow/gas/dn_rate_parser.py,sha256=Mq8rAcUEUxIQOks59bsCKl8GrefvoHbrTCHqon9N0z0,11340
73
73
  chellow/gas/engine.py,sha256=d2rR1y8b3u2QhmfqyFwwLu_loeZxY_3WwwtDyGJfam0,25282
74
74
  chellow/gas/transportation.py,sha256=Bkg8TWOs-v0ES-4qqwbleiOhqbE_t2KauUx9JYMZELM,5300
75
- chellow/gas/views.py,sha256=7ObrqTR-z6cRGpBcilUroSKL9C0hGfOo4GcCnGhBuYU,59737
75
+ chellow/gas/views.py,sha256=GeCvi6BGTUN7bu7sVkypNckwG3Crl6AbUcRob9qMi0E,59733
76
76
  chellow/reports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
77
  chellow/reports/report_109.py,sha256=Exb-FQ5f70-ier_h15CgHGysQ7vJ7k3gFZ1001zM3iM,11171
78
78
  chellow/reports/report_111.py,sha256=o_wWyrtmxpTnmT9DVlsG5h5wdm3JqojirRlx_7W5_kQ,28787
@@ -90,7 +90,7 @@ chellow/reports/report_291.py,sha256=BnWtxe0eWN2QKKWpwjs5-RI5LbReBKL119QbkrkNhV8
90
90
  chellow/reports/report_33.py,sha256=lt1EN_LNx6u-AgdaS3YRkPMZA33JgMcELolHF4oJUMw,16689
91
91
  chellow/reports/report_387.py,sha256=bBqtnGGDWIXzXe2zVm9yeDRZKNgkUaOS3XTX6k09f18,5631
92
92
  chellow/reports/report_41.py,sha256=8xUIN9D5dtT0Dn1RmbnWsLRmM-QbGA5xGhWCK3ljAzA,7406
93
- chellow/reports/report_429.py,sha256=bpnz_SAiWFoHN7UF3rS0-LupbDyE_AnGI-XOphGXyPc,12627
93
+ chellow/reports/report_429.py,sha256=SbFlBsHXWMWRlMbY_e0zco2R8tMG9XVulr56E3mRalU,14287
94
94
  chellow/reports/report_59.py,sha256=PkdRA6ctvDGWTQd5vb9cQH6MG920TxcD6aHh268zrj8,42221
95
95
  chellow/reports/report_81.py,sha256=bBpV6MtvKGtoLiqzZoK2h21KDs4vfDwy-etpfL9oiEI,5570
96
96
  chellow/reports/report_87.py,sha256=udzbCuXcckWD-OHmfJCT6bwg_paYhm4vfDWlL8WM-jA,6933
@@ -108,7 +108,7 @@ chellow/reports/report_g_virtual_bills.py,sha256=20vHa5LGQwOAlJlaGJaGszZrrbT0PMO
108
108
  chellow/reports/report_g_virtual_bills_hh.py,sha256=gaiLEmKTpq6JsfZ1p0SdCDuPvzvigXp6z88gHRCA63w,3416
109
109
  chellow/reports/report_sscs.py,sha256=fQWyVG-gdg37DyNHgpNARpSxIwTl7mCn20fDLwx9oHg,3214
110
110
  chellow/reports/report_supply_contacts.py,sha256=pvwlInaPYV_pa9MMK6vh854plHFwv3m5zo5xulR1g5I,3599
111
- chellow/static/css/chellow.css,sha256=mcLjqKMo0qtdQWY7AnXEL8Bvx2B-Pu8kcGO58bUXOpY,5372
111
+ chellow/static/css/chellow.css,sha256=dnkuj9Z1BCOV_L2Y26lDd2QlTmFFhATa1YvwPVch1Oc,5375
112
112
  chellow/static/images/favicon.svg,sha256=ySFHoVJYmr-xU93QrE-jLYn-ZNythh2vsemnR8dkvg0,2339
113
113
  chellow/static/images/favicon_test.svg,sha256=HnLS_BjNt8M0Ikko5Z-f_E2aed7y6RRU6j3K6XADciE,2346
114
114
  chellow/static/images/logo.png,sha256=XMW2XwukTicKVJ46E2SnbHJYh77uFLVYlwR4xN43YKg,1569
@@ -142,9 +142,11 @@ chellow/templates/report_run.html,sha256=O_wjIu43S-mKVyZyku3dJJdvyck3rAgEdhw59Ts
142
142
  chellow/templates/report_run_asset_comparison.html,sha256=VYCCUmIC7Mfe7uuaAHb6ihiK6zsqeTlQbzgtzLqR3zg,2305
143
143
  chellow/templates/report_run_bill_check.html,sha256=H2ayoBL7EgKMq2Nwq5VjE_TNvcIKcqeCm0alQWLIw78,5084
144
144
  chellow/templates/report_run_ecoes_comparison.html,sha256=VmkT5ypWLP8qZS6NbDTC4yDaG7mnUlxZz7EV8xkHGZw,4086
145
+ chellow/templates/report_run_g_bill_check.html,sha256=tOXl_mjR__foYKiOYflJbK-459actAtjzv8rfuL3TwM,4851
145
146
  chellow/templates/report_run_monthly_duration_org.html,sha256=gGNGJ4Q50q4BtIMi98rhO-7NqRHcsFUmbj2qzeOLejw,1713
146
147
  chellow/templates/report_run_row.html,sha256=bmtcdqJaS1CXpL0i8PuqvmeF98jKNYX5-mnQu-OuDKQ,3791
147
148
  chellow/templates/report_run_row_bill_check.html,sha256=aC2LMu_6NvmTN3ZdxHJPPPczyxPN6hg0F-PPcqIWUws,4683
149
+ chellow/templates/report_run_row_g_bill_check.html,sha256=aC2LMu_6NvmTN3ZdxHJPPPczyxPN6hg0F-PPcqIWUws,4683
148
150
  chellow/templates/report_run_supply_contacts.html,sha256=JNzwz9M6qbLRDMkCzFCxxANapUer5klxo7t5a48nAzg,2117
149
151
  chellow/templates/report_runs.html,sha256=ecoIkl2WtfYtifiTxnslmpMGYYGVQW-CVSBpqhXyiE4,1131
150
152
  chellow/templates/scenario.html,sha256=tCoq1wBq4l9PRS-zFtPcCWXlxD_SSFvFFkERf4FWVNU,2055
@@ -376,6 +378,6 @@ chellow/templates/g/supply_note_edit.html,sha256=b8mB6_ucBwoljp03iy6AgVaZUhGw3-1
376
378
  chellow/templates/g/supply_notes.html,sha256=6epNmZ3NKdXZz27fvmRUGeffg_oc1kmwuBeyRzQe3Rg,854
377
379
  chellow/templates/g/unit.html,sha256=KouNVU0-i84afANkLQ_heJ0uDfJ9H5A05PuLqb8iCN8,438
378
380
  chellow/templates/g/units.html,sha256=p5Nd-lAIboKPEOO6N451hx1bcKxMg4BDODnZ-43MmJc,441
379
- chellow-1738661883.0.0.dist-info/METADATA,sha256=5g40keAGNfvyLS-2x60_y2NxBT429rX9_52Hzj44FiY,12204
380
- chellow-1738661883.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
381
- chellow-1738661883.0.0.dist-info/RECORD,,
381
+ chellow-1739889885.0.0.dist-info/METADATA,sha256=iQJxsDUYm0NRHUc8O70ShLjSedd-0j5cjZh_5ojdJuw,12204
382
+ chellow-1739889885.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
383
+ chellow-1739889885.0.0.dist-info/RECORD,,