chellow 1706265605.0.0__py3-none-any.whl → 1706691842.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/elexon.py CHANGED
@@ -57,16 +57,17 @@ def api_search(s, resource_id, sort=None):
57
57
  return res_j
58
58
 
59
59
 
60
- def run_import(sess, log, set_progress):
60
+ def run_import(sess, log, set_progress, scripting_key):
61
61
  log("Starting to import data from Elexon")
62
62
  s = requests.Session()
63
63
  s.verify = False
64
64
 
65
65
  for mod_name in ("chellow.e.system_price", "chellow.e.tlms", "chellow.e.rcrc"):
66
66
  mod = import_module(mod_name)
67
- mod.elexon_import(sess, log, set_progress, s)
67
+ mod.elexon_import(sess, log, set_progress, s, scripting_key)
68
68
 
69
69
 
70
+ ELEXON_PORTAL_SCRIPTING_KEY_KEY = "elexonportal_scripting_key"
70
71
  LAST_RUN_KEY = "last_run"
71
72
  GLOBAL_ALERT = "There's a problem with an <a href='/e/elexon'>Elexon import</a>."
72
73
  ELEXON_STATE_KEY = "elexon"
@@ -119,6 +120,13 @@ class Elexon(threading.Thread):
119
120
  with Session() as sess:
120
121
  try:
121
122
  config = Contract.get_non_core_by_name(sess, "configuration")
123
+ props = config.make_properties()
124
+ scripting_key = props.get(ELEXON_PORTAL_SCRIPTING_KEY_KEY)
125
+ if scripting_key is None:
126
+ raise BadRequest(
127
+ f"The property {ELEXON_PORTAL_SCRIPTING_KEY_KEY} "
128
+ f"cannot be found in the configuration properties."
129
+ )
122
130
  state = config.make_state()
123
131
  try:
124
132
  elexon_state = state[ELEXON_STATE_KEY]
@@ -128,7 +136,7 @@ class Elexon(threading.Thread):
128
136
  elexon_state[LAST_RUN_KEY] = utc_datetime_now()
129
137
  config.update_state(state)
130
138
  sess.commit()
131
- run_import(sess, self.log, self.set_progress)
139
+ run_import(sess, self.log, self.set_progress, scripting_key)
132
140
  except BaseException as e:
133
141
  msg = f"{e.description} " if isinstance(e, BadRequest) else ""
134
142
  self.log(f"{msg}{traceback.format_exc()}")
chellow/e/rcrc.py CHANGED
@@ -63,14 +63,23 @@ def _find_month(lines, month_start, month_finish):
63
63
  next(parser)
64
64
  month_rcrcs = {}
65
65
  for values in parser:
66
- hh_date = to_utc(ct_datetime_parse(values[0], "%d/%m/%Y"))
66
+ if len(values) == 0:
67
+ continue
68
+ date_str = values[0]
69
+ if "/" in date_str:
70
+ pattern = "%d/%m/%Y"
71
+ elif " " in date_str:
72
+ pattern = "%d %b %Y"
73
+ else:
74
+ raise BadRequest(f"Date format {date_str} not recognized.")
75
+ hh_date = to_utc(ct_datetime_parse(date_str, pattern))
67
76
  hh_date += relativedelta(minutes=30 * int(values[2]))
68
77
  if month_start <= hh_date <= month_finish:
69
78
  month_rcrcs[key_format(hh_date)] = Decimal(values[3])
70
79
  return month_rcrcs
71
80
 
72
81
 
73
- def elexon_import(sess, log, set_progress, s):
82
+ def elexon_import(sess, log, set_progress, s, scripting_key):
74
83
  log("Starting to check RCRCs.")
75
84
  contract = Contract.get_non_core_by_name(sess, "rcrc")
76
85
  latest_rs = (
@@ -90,25 +99,15 @@ def elexon_import(sess, log, set_progress, s):
90
99
  month_start, month_finish = months[1]
91
100
  now = utc_datetime_now()
92
101
  if now > month_finish:
93
- config = Contract.get_non_core_by_name(sess, "configuration")
94
- props = config.make_properties()
95
-
96
- scripting_key = props.get(ELEXON_PORTAL_SCRIPTING_KEY_KEY)
97
- if scripting_key is None:
98
- raise BadRequest(
99
- f"The property {ELEXON_PORTAL_SCRIPTING_KEY_KEY} cannot be found in "
100
- f"the configuration properties."
101
- )
102
-
103
- contract_props = contract.make_properties()
104
- url_str = f"{contract_props['url']}file/download/RCRC_FILE?key={scripting_key}"
102
+ params = {"key": scripting_key}
103
+ url_str = "https://downloads.elexonportal.co.uk/file/download/RCRC_FILE"
105
104
  log(
106
- f"Downloading {url_str} to see if data is available from "
107
- f"{hh_format(month_start)} to {hh_format(month_finish)}."
105
+ f"Downloading {url_str}?key={scripting_key} to see if data is available "
106
+ f"from {hh_format(month_start)} to {hh_format(month_finish)}."
108
107
  )
109
108
 
110
109
  sess.rollback() # Avoid long-running transaction
111
- r = s.get(url_str, timeout=60)
110
+ r = s.get(url_str, timeout=120, params=params)
112
111
  month_rcrcs = _find_month(
113
112
  (x.decode() for x in r.iter_lines()), month_start, month_finish
114
113
  )
chellow/e/system_price.py CHANGED
@@ -2,6 +2,8 @@ import datetime
2
2
  import traceback
3
3
  from datetime import timedelta as Timedelta
4
4
 
5
+ from sqlalchemy import select
6
+
5
7
  from werkzeug.exceptions import BadRequest
6
8
 
7
9
  import xlrd
@@ -12,9 +14,6 @@ from chellow.models import Contract, RateScript
12
14
  from chellow.utils import HH, hh_format, to_ct, to_utc
13
15
 
14
16
 
15
- ELEXON_PORTAL_SCRIPTING_KEY_KEY = "elexonportal_scripting_key"
16
-
17
-
18
17
  def key_format(dt):
19
18
  return dt.strftime("%d %H:%M")
20
19
 
@@ -61,7 +60,7 @@ def hh(data_source):
61
60
  h["ssp-gbp"] = h["nbp-kwh"] * ssp
62
61
 
63
62
 
64
- def elexon_import(sess, log, set_progress, s):
63
+ def elexon_import(sess, log, set_progress, s, scripting_key):
65
64
  contract = Contract.get_non_core_by_name(sess, "system_price")
66
65
  contract_props = contract.make_properties()
67
66
 
@@ -74,9 +73,9 @@ def elexon_import(sess, log, set_progress, s):
74
73
 
75
74
  log("Starting to check System Prices.")
76
75
 
77
- for rscript in (
78
- sess.query(RateScript)
79
- .filter(RateScript.contract == contract)
76
+ for rscript in sess.scalars(
77
+ select(RateScript)
78
+ .where(RateScript.contract == contract)
80
79
  .order_by(RateScript.start_date.desc())
81
80
  ):
82
81
  ns = loads(rscript.script)
@@ -87,24 +86,16 @@ def elexon_import(sess, log, set_progress, s):
87
86
  elif rates[key_format(rscript.finish_date)]["run"] == "DF":
88
87
  fill_start = rscript.finish_date + HH
89
88
  break
89
+ params = {"key": scripting_key}
90
+ url = "https://downloads.elexonportal.co.uk/file/download/BESTVIEWPRICES_FILE"
90
91
 
91
- config = Contract.get_non_core_by_name(sess, "configuration")
92
- config_props = config.make_properties()
93
-
94
- scripting_key = config_props.get(ELEXON_PORTAL_SCRIPTING_KEY_KEY)
95
- if scripting_key is None:
96
- raise BadRequest(
97
- f"The property {ELEXON_PORTAL_SCRIPTING_KEY_KEY} cannot be found in "
98
- f"the configuration properties."
99
- )
100
- url = (
101
- f"{contract_props['url']}file/download/BESTVIEWPRICES_FILE?key={scripting_key}"
92
+ log(
93
+ f"Downloading from {url}?key={scripting_key} and extracting data from "
94
+ f"{hh_format(fill_start)}"
102
95
  )
103
96
 
104
- log(f"Downloading from {url} and extracting data from {hh_format(fill_start)}")
105
-
106
97
  sess.rollback() # Avoid long-running transactions
107
- res = s.get(url)
98
+ res = s.get(url, params=params)
108
99
  log(f"Received {res.status_code} {res.reason}")
109
100
  data = res.content
110
101
  book = xlrd.open_workbook(file_contents=data)
chellow/e/tlms.py CHANGED
@@ -4,7 +4,7 @@ from decimal import Decimal, InvalidOperation
4
4
 
5
5
  from dateutil.relativedelta import relativedelta
6
6
 
7
- from sqlalchemy import or_
7
+ from sqlalchemy import or_, select
8
8
  from sqlalchemy.sql.expression import null
9
9
 
10
10
  from werkzeug.exceptions import BadRequest
@@ -12,11 +12,9 @@ from werkzeug.exceptions import BadRequest
12
12
  from zish import dumps, loads
13
13
 
14
14
  from chellow.models import Contract, RateScript
15
- from chellow.utils import HH, hh_format, to_ct, to_utc
15
+ from chellow.utils import HH, hh_format, hh_range, to_ct, to_utc
16
16
 
17
17
 
18
- ELEXON_PORTAL_SCRIPTING_KEY_KEY = "elexonportal_scripting_key"
19
-
20
18
  RUNS = ["DF", "RF", "R3", "R2", "R1", "SF", "II"]
21
19
 
22
20
 
@@ -77,51 +75,76 @@ def hh(data_source, run="DF"):
77
75
  h["nbp-kwh"] = h["gsp-kwh"] * tlm
78
76
 
79
77
 
80
- def _save_cache(sess, cache):
81
- for yr, yr_cache in cache.items():
82
- for month, (rs, rates, rts) in tuple(yr_cache.items()):
83
- rs.script = dumps(rates)
84
- sess.commit()
85
- del yr_cache[month]
78
+ def _find_complete_date(caches, sess, contract, cache):
79
+ for rs in sess.scalars(
80
+ select(RateScript)
81
+ .where(RateScript.contract == contract, RateScript.finish_date != null())
82
+ .order_by(RateScript.start_date.desc())
83
+ ):
84
+ rates = rs.make_script()
85
+ rates["id"] = rs.id
86
+ cache["rate_scripts"].append(rates)
87
+ timestamps = cache["timestamps"]
88
+ tlms = rates["tlms"]
89
+ complete = True
90
+ for dt in hh_range(caches, rs.start_date, rs.finish_date):
91
+ timestamps[dt] = rates
92
+ key = key_format(dt)
93
+ if key in tlms:
94
+ gps = tlms[key]
95
+ for group in GSP_GROUP_LOOKUP.values():
96
+ if not (group in gps and "DF" in gps[group]):
97
+ complete = False
98
+ else:
99
+ complete = False
100
+
101
+ if complete:
102
+ return rs.finish_date
86
103
 
87
104
 
88
- def elexon_import(sess, log, set_progress, s):
89
- cache = {}
105
+ def elexon_import(sess, log, set_progress, s, scripting_key):
106
+ cache = {"rate_scripts": [], "timestamps": {}}
107
+ caches = {}
90
108
  log("Starting to check TLMs.")
91
109
  contract = Contract.get_non_core_by_name(sess, "tlms")
92
110
  contract_props = contract.make_properties()
93
111
  if contract_props.get("enabled", False):
94
- config = Contract.get_non_core_by_name(sess, "configuration")
95
- props = config.make_properties()
96
- scripting_key = props.get(ELEXON_PORTAL_SCRIPTING_KEY_KEY)
97
- if scripting_key is None:
98
- raise BadRequest(
99
- f"The property {ELEXON_PORTAL_SCRIPTING_KEY_KEY} cannot be "
100
- f"found in the configuration properties."
101
- )
102
112
 
103
- url_str = f"{contract_props['url']}file/download/TLM_FILE?key={scripting_key}"
113
+ params = {"key": scripting_key}
114
+ url_str = "https://downloads.elexonportal.co.uk/file/download/TLM_FILE"
115
+ complete_date = _find_complete_date(caches, sess, contract, cache)
116
+ log(f"Found complete up to {complete_date}")
104
117
 
105
118
  sess.rollback() # Avoid long-running transaction
106
- r = s.get(url_str)
119
+ r = s.get(url_str, params=params, timeout=120)
107
120
  parser = csv.reader(
108
121
  (x.decode() for x in r.iter_lines()), delimiter=",", quotechar='"'
109
122
  )
110
- log(f"Opened {url_str}.")
123
+ log(f"Opened {url_str}?key={scripting_key}.")
111
124
 
112
125
  next(parser, None)
113
126
  for values in parser:
114
127
  if len(values) == 0:
115
128
  continue
116
129
 
117
- elif values[3] == "":
130
+ set_progress(values[0])
131
+
132
+ if values[3] == "":
118
133
  for zone in GSP_GROUP_LOOKUP.keys():
119
134
  values[3] = zone
120
- _process_line(cache, sess, contract, log, values)
135
+ _process_line(
136
+ cache, sess, contract, log, values, complete_date, caches
137
+ )
121
138
  else:
122
- _process_line(cache, sess, contract, log, values)
139
+ _process_line(cache, sess, contract, log, values, complete_date, caches)
140
+
141
+ for rscript in cache["rate_scripts"]:
142
+ rs = sess.scalars(
143
+ select(RateScript).where(RateScript.id == rscript["id"])
144
+ ).one()
145
+ rs.script = dumps({"tlms": rscript["tlms"]})
146
+ sess.commit()
123
147
 
124
- _save_cache(sess, cache)
125
148
  else:
126
149
  log(
127
150
  "The importer is disabled. Set 'enabled' to 'true' in the "
@@ -148,10 +171,12 @@ GSP_GROUP_LOOKUP = {
148
171
  }
149
172
 
150
173
 
151
- def _process_line(cache, sess, contract, log_func, values):
174
+ def _process_line(cache, sess, contract, log_func, values, complete_date, caches):
152
175
  hh_date_ct = to_ct(Datetime.strptime(values[0], "%d/%m/%Y"))
153
176
  hh_date = to_utc(hh_date_ct)
154
177
  hh_date += relativedelta(minutes=30 * (int(values[2]) - 1))
178
+ if complete_date is not None and hh_date <= complete_date:
179
+ return
155
180
  run = values[1]
156
181
  gsp_group_code = GSP_GROUP_LOOKUP[values[3]]
157
182
  off_taking_str = values[4]
@@ -166,45 +191,13 @@ def _process_line(cache, sess, contract, log_func, values):
166
191
 
167
192
  delivering = Decimal(values[5])
168
193
 
194
+ key = key_format(hh_date)
169
195
  try:
170
- rs, rates, rts = cache[hh_date.year][hh_date.month]
196
+ cache["timestamps"][hh_date]["tlms"][key][gsp_group_code][run]
171
197
  except KeyError:
172
- _save_cache(sess, cache)
173
198
  try:
174
- yr_cache = cache[hh_date.year]
199
+ rate = cache["timestamps"][hh_date]
175
200
  except KeyError:
176
- yr_cache = cache[hh_date.year] = {}
177
-
178
- rs = (
179
- sess.query(RateScript)
180
- .filter(
181
- RateScript.contract == contract,
182
- RateScript.start_date <= hh_date,
183
- or_(
184
- RateScript.finish_date == null(), RateScript.finish_date >= hh_date
185
- ),
186
- )
187
- .first()
188
- )
189
- while rs is None:
190
- log_func(f"There's no rate script at {hh_format(hh_date)}.")
191
- latest_rs = (
192
- sess.query(RateScript)
193
- .filter(RateScript.contract == contract)
194
- .order_by(RateScript.start_date.desc())
195
- .first()
196
- )
197
- contract.update_rate_script(
198
- sess,
199
- latest_rs,
200
- latest_rs.start_date,
201
- latest_rs.start_date + relativedelta(months=2) - HH,
202
- loads(latest_rs.script),
203
- )
204
- new_rs_start = latest_rs.start_date + relativedelta(months=1)
205
- contract.insert_rate_script(sess, new_rs_start, {})
206
- sess.commit()
207
- log_func(f"Added a rate script starting at {hh_format(new_rs_start)}.")
208
201
 
209
202
  rs = (
210
203
  sess.query(RateScript)
@@ -218,29 +211,56 @@ def _process_line(cache, sess, contract, log_func, values):
218
211
  )
219
212
  .first()
220
213
  )
214
+ assert rs is None
215
+ while rs is None:
216
+ log_func(f"There's no rate script at {hh_format(hh_date)}.")
217
+ latest_rs = (
218
+ sess.query(RateScript)
219
+ .filter(RateScript.contract == contract)
220
+ .order_by(RateScript.start_date.desc())
221
+ .first()
222
+ )
223
+ contract.update_rate_script(
224
+ sess,
225
+ latest_rs,
226
+ latest_rs.start_date,
227
+ latest_rs.start_date + relativedelta(months=2) - HH,
228
+ loads(latest_rs.script),
229
+ )
230
+ new_rs_start = latest_rs.start_date + relativedelta(months=1)
231
+ new_rs = contract.insert_rate_script(sess, new_rs_start, {})
232
+ rt = {"tlms": {}, "id": new_rs.id}
233
+ cache["rate_scripts"].append(rt)
234
+ for h in hh_range(caches, new_rs.start_date, new_rs.finish_date):
235
+ cache["timestamps"][h] = rt
236
+ sess.commit()
237
+ log_func(f"Added a rate script starting at {hh_format(new_rs_start)}.")
238
+
239
+ rs = (
240
+ sess.query(RateScript)
241
+ .filter(
242
+ RateScript.contract == contract,
243
+ RateScript.start_date <= hh_date,
244
+ or_(
245
+ RateScript.finish_date == null(),
246
+ RateScript.finish_date >= hh_date,
247
+ ),
248
+ )
249
+ .first()
250
+ )
221
251
 
222
- rates = loads(rs.script)
252
+ rate = cache["timestamps"][hh_date]
223
253
 
224
254
  try:
225
- rts = rates["tlms"]
255
+ existing = rate["tlms"][key]
226
256
  except KeyError:
227
- rts = rates["tlms"] = {}
228
-
229
- yr_cache[hh_date.month] = rs, rates, rts
230
- sess.rollback()
231
-
232
- key = key_format(hh_date)
233
- try:
234
- existing = rts[key]
235
- except KeyError:
236
- existing = rts[key] = {}
257
+ existing = rate["tlms"][key] = {}
237
258
 
238
- try:
239
- group = existing[gsp_group_code]
240
- except KeyError:
241
- group = existing[gsp_group_code] = {}
259
+ try:
260
+ group = existing[gsp_group_code]
261
+ except KeyError:
262
+ group = existing[gsp_group_code] = {}
242
263
 
243
- if run not in group:
244
264
  group[run] = {"off_taking": off_taking, "delivering": delivering}
245
265
 
246
266
  log_func(
@@ -68,8 +68,8 @@ to the 'urls' list. */
68
68
  {% elif contract.name == 'tlms' %}
69
69
  <h4>Example</h4>
70
70
  <pre>{
71
- "enabled": true,
72
- "url": "https://downloads.elexonportal.co.uk/"}
71
+ "enabled": true,
72
+ }
73
73
 
74
74
  /* Requires the 'elexonportal_scripting_key' to be set in the 'configuration'
75
75
  non-core contract */
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: chellow
3
- Version: 1706265605.0.0
3
+ Version: 1706691842.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)
@@ -55,6 +55,7 @@ Chellow is a Python web application that uses the PostgreSQL database. To instal
55
55
  Chellow, follow these steps:
56
56
 
57
57
  * Install [PostgreSQL](http://www.postgresql.org/) 12
58
+ * Set the PostgreSQL time zone to 'UTC'.
58
59
  * Create a PostgreSQL database: `createdb --encoding=UTF8 chellow`
59
60
  * Set up the following environment variables to configure Chellow:
60
61
 
@@ -22,7 +22,7 @@ chellow/e/cfd.py,sha256=3yRchycC-WZwgqryGQK3B59OcMGVIWgbxrkekUOy-9k,14914
22
22
  chellow/e/computer.py,sha256=J8VvYjcSeJNrG2Ak8Zi2t2bb_8tMrkHWny4JQlgIyy8,67032
23
23
  chellow/e/dno_rate_parser.py,sha256=5MYEbcYngh7n6Szn2h9zLTwQKXH-WHKy6c80PYwNLLQ,20983
24
24
  chellow/e/duos.py,sha256=ISTcNqe9KNjVNM2Qs8IBoQxnmSXOt5W_G7tZxp4T78M,28870
25
- chellow/e/elexon.py,sha256=I0PislGIm1Kgq0TCLki7hrN2A9cWGXnpIcG-c-deaag,4946
25
+ chellow/e/elexon.py,sha256=s8QdLUQMeylLhghxvvhLyOL3-0ifUnMfmo3vcbot_DY,5487
26
26
  chellow/e/energy_management.py,sha256=aXC2qlGt3FAODlNl_frWzVYAQrJLP8FFOiNX3m-QE_Y,12388
27
27
  chellow/e/hh_importer.py,sha256=G1CK3AQQuJaNXCxGEY3UU_CjqOzfVZT-PDR67b3zpA0,20362
28
28
  chellow/e/hh_parser_bg_csv.py,sha256=IOjM-QliKzvSXyhslmxz504XWnsGkVKEaCQ48SNeNeQ,2423
@@ -31,11 +31,11 @@ chellow/e/hh_parser_simple_csv.py,sha256=lVRprIEjDpTQdeXm2xcLmRAyre2LWec8ueVwcCI
31
31
  chellow/e/laf_import.py,sha256=aqkcbjnvfBPszBLSNg6getP7iW1uWiTVHy6N5Z5x39U,5514
32
32
  chellow/e/lcc.py,sha256=q5iGbeYZdnozkIdbGuxXoJO-1KDO7AHqYOXEGBIaNHI,4963
33
33
  chellow/e/mdd_importer.py,sha256=9GN-at0DC3vOjd-0N2U856O3hInnjkMWWcRfxzQ5DjA,32042
34
- chellow/e/rcrc.py,sha256=UH9_eM-4sPuEkEyVFGubr5VCXnOJW_kMAjnyTiToxKI,4350
34
+ chellow/e/rcrc.py,sha256=92CA1uIotIHd1epQ_jEPdJKzXqDFV-AoJOJeRO6MEyA,4274
35
35
  chellow/e/ro.py,sha256=dZKZv_9wXSWuwcb3jiKavoD_9ot-PZseNVeEEe0siLo,596
36
36
  chellow/e/scenario.py,sha256=1tUxnvwTzr6cKqiw2wphdv5XDzV6JO6UVYkyQa67vHs,23263
37
- chellow/e/system_price.py,sha256=UPgUts6p7ETdclnO_MEC1mMqSWBg2bbSylDuj452OyY,6152
38
- chellow/e/tlms.py,sha256=BvHfe9bhiGvA_qeZ7iFwtOh_bmE_es3xqro1213ipDQ,7311
37
+ chellow/e/system_price.py,sha256=3cPWohHmmBI9v7fENLBzXQkpAeC3r0s5xV29Nmr6k_E,5839
38
+ chellow/e/tlms.py,sha256=kHAy2A2d1Mma7x4GdNDyrzscJd5DpJtDBYiZEg241Dw,8538
39
39
  chellow/e/tnuos.py,sha256=qybrPCVukQ2l0RIdqn8XtHEbAU0izGoqUrFLoLVUoVI,4126
40
40
  chellow/e/triad.py,sha256=S6LEMHvUKhAZe0-yfLIRciYDZ8IKMn1jh1TmmsbQD3s,13588
41
41
  chellow/e/views.py,sha256=aZecpfM1sVn3xMwlKuiw9pE2f6s4uyd98obQXToKtqA,212466
@@ -125,7 +125,7 @@ chellow/templates/macros.html,sha256=Koc8dT2pZaO2-iVm9RQiR_V3qymSZ_3iaKe2rCyrvgk
125
125
  chellow/templates/national_grid.html,sha256=8W92tsjlqPSB0J--nyFIi-wzFae9CyDr6fODXLZp8ic,991
126
126
  chellow/templates/non_core_auto_importer.html,sha256=s9SluRN1bHTHjd8GP6uFhk6LrZYG8dqBG3y94zUKe5Q,944
127
127
  chellow/templates/non_core_contract.html,sha256=BdGtxErTvw4DwXLb6B2vimuU6ode3fFA-90kBmHCwYc,1754
128
- chellow/templates/non_core_contract_edit.html,sha256=_EWwJdrez2uI2mA58Jf5WIYs7UfBw1xWV-Bpgliyurw,3005
128
+ chellow/templates/non_core_contract_edit.html,sha256=rjY174SH_wXFU345VRIfN_yjd-jc9QQjzhyrJAf3VEk,2961
129
129
  chellow/templates/non_core_contracts.html,sha256=vIrVdn4NUj58Uy17REs_fy2JBQzPkjcg63bg7q6kELg,661
130
130
  chellow/templates/non_core_rate_script.html,sha256=CqGnuWdzhk_o_2xNFcF8rgk0p7SNh0B0c3k1JzOtn98,1283
131
131
  chellow/templates/non_core_rate_script_add.html,sha256=Qx8_cYFRQZrXSyR3uf_8OxUAUz3PqyYc4V11lTU18sE,690
@@ -363,6 +363,6 @@ chellow/templates/g/supply_note_edit.html,sha256=6UQf_qbhFDys3cVsTp-c7ABWZpggW9R
363
363
  chellow/templates/g/supply_notes.html,sha256=WR3YwGh_qqTklSJ7JqWX6BKBc9rk_jMff4RiWZiF2CM,936
364
364
  chellow/templates/g/unit.html,sha256=KouNVU0-i84afANkLQ_heJ0uDfJ9H5A05PuLqb8iCN8,438
365
365
  chellow/templates/g/units.html,sha256=p5Nd-lAIboKPEOO6N451hx1bcKxMg4BDODnZ-43MmJc,441
366
- chellow-1706265605.0.0.dist-info/METADATA,sha256=4kU_QU4coCbZe3Ljngxxa-zhWlIlwGsGneoNNqrhRw0,12160
367
- chellow-1706265605.0.0.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87
368
- chellow-1706265605.0.0.dist-info/RECORD,,
366
+ chellow-1706691842.0.0.dist-info/METADATA,sha256=L3rxfmbWGPmkWihil72bLkI0vYaKsC3jxz5YPxnmbZ0,12203
367
+ chellow-1706691842.0.0.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87
368
+ chellow-1706691842.0.0.dist-info/RECORD,,