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 CHANGED
@@ -54,6 +54,7 @@ def get_importer_modules():
54
54
  chellow.e.bsuos,
55
55
  chellow.e.system_price,
56
56
  chellow.e.hh_importer,
57
+ chellow.e.lcc,
57
58
  chellow.testing,
58
59
  chellow.e.tlms,
59
60
  chellow.bank_holidays,
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
+ &raquo; 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 %}
@@ -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
@@ -61,6 +61,7 @@ import chellow.e.bsuos
61
61
  import chellow.e.computer
62
62
  import chellow.e.hh_importer
63
63
  import chellow.e.laf_import
64
+ import chellow.e.lcc
64
65
  import chellow.e.mdd_importer
65
66
  import chellow.e.rcrc
66
67
  import chellow.e.system_price
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: chellow
3
- Version: 1704969359.0.0
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=lt3x_UQpzbq_MtbAYwwfaxXuL1k9njPJ1AV-ZD9J5Bc,10328
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=-TzZiARb5xJveAziXt5WWfMGVcey8qWs9VF9EEq24cE,78491
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=VuWVbHT5z7WttVMxLixynjbpjRxbviLElRD7DbGfKdQ,211828
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=5GXzbgFeceQAnr8nQEg911v0xT4hJN1H0-5Npo9GbAE,5516
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-1704969359.0.0.dist-info/METADATA,sha256=lAzBfUjbt2ZXbQy-svdbUprs3NJAipKQDhqmeb3F4F8,12160
362
- chellow-1704969359.0.0.dist-info/WHEEL,sha256=mRYSEL3Ih6g5a_CVMIcwiF__0Ae4_gLYh01YFNwiq1k,87
363
- chellow-1704969359.0.0.dist-info/RECORD,,
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,,