chellow 1707396281.0.0__py3-none-any.whl → 1708358861.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/hh_importer.py CHANGED
@@ -48,6 +48,7 @@ class HhDataImportProcess(threading.Thread):
48
48
  super().__init__(name=f"HH Manual Import: contract {dc_contract_id}")
49
49
 
50
50
  self.messages = []
51
+ self.error = None
51
52
  self.istream = istream
52
53
  self.dc_contract_id = dc_contract_id
53
54
  self.id = process_id
@@ -74,14 +75,16 @@ class HhDataImportProcess(threading.Thread):
74
75
  parser_module = importlib.import_module(
75
76
  f"chellow.e.hh_parser_{mod_name}"
76
77
  )
77
- self.converter = parser_module.create_parser(self.istream, mpan_map)
78
+ self.converter = parser_module.create_parser(
79
+ self.istream, mpan_map, self.messages
80
+ )
78
81
  sess.rollback()
79
82
  HhDatum.insert(sess, self.converter, contract)
80
83
  sess.commit()
81
84
  except BadRequest as e:
82
- self.messages.append(e.description)
85
+ self.error = e.description
83
86
  except BaseException:
84
- self.messages.append(f"Outer problem {traceback.format_exc()}")
87
+ self.error = f"Outer problem {traceback.format_exc()}"
85
88
 
86
89
  def get_status(self):
87
90
  return (
@@ -281,8 +284,8 @@ class HhImportTask(threading.Thread):
281
284
  for message in self.importer.messages:
282
285
  self.log(message)
283
286
 
284
- if len(self.importer.messages) > 0:
285
- raise BadRequest("Problem loading file.")
287
+ if self.importer.error is not None:
288
+ raise BadRequest(f"Problem loading file. {self.importer.error}")
286
289
 
287
290
  contract = Contract.get_dc_by_id(sess, self.contract_id)
288
291
  contract.update_state(state)
@@ -384,12 +387,12 @@ class HhImportTask(threading.Thread):
384
387
 
385
388
  self.importer.run()
386
389
  messages = self.importer.messages
387
- self.importer = None
388
390
  for message in messages:
389
391
  self.log(message)
390
392
 
391
- if len(messages) > 0:
392
- raise BadRequest("Problem loading file.")
393
+ if self.importer.error is not None:
394
+ raise BadRequest(f"Problem loading file. {self.importer.error}")
395
+ self.importer = None
393
396
 
394
397
  contract = Contract.get_dc_by_id(sess, self.contract_id)
395
398
  contract.update_state(state)
@@ -8,12 +8,12 @@ from werkzeug.exceptions import BadRequest
8
8
  from chellow.utils import parse_mpan_core, utc_datetime
9
9
 
10
10
 
11
- def create_parser(reader, mpan_map):
12
- return HhParserBglobal(reader, mpan_map)
11
+ def create_parser(reader, mpan_map, messages):
12
+ return HhParserBglobal(reader, mpan_map, messages)
13
13
 
14
14
 
15
15
  class HhParserBglobal:
16
- def __init__(self, reader, mpan_map):
16
+ def __init__(self, reader, mpan_map, messages):
17
17
  self.shredder = zip(itertools.count(1), csv.reader(reader))
18
18
  self.line_number, self.values = next(self.shredder)
19
19
  self.mpan_map = mpan_map
@@ -6,12 +6,12 @@ from werkzeug.exceptions import BadRequest
6
6
  from chellow.utils import HH, parse_mpan_core, utc_datetime
7
7
 
8
8
 
9
- def create_parser(reader, mpan_map):
10
- return StarkDf2HhParser(reader, mpan_map)
9
+ def create_parser(reader, mpan_map, messages):
10
+ return StarkDf2HhParser(reader, mpan_map, messages)
11
11
 
12
12
 
13
13
  class StarkDf2HhParser:
14
- def __init__(self, reader, mpan_map):
14
+ def __init__(self, reader, mpan_map, messages):
15
15
  self.line = self.line_number = None
16
16
  self.reader = zip(itertools.count(1), reader)
17
17
 
@@ -25,6 +25,9 @@ class StarkDf2HhParser:
25
25
  4: "REACTIVE_EXP",
26
26
  }
27
27
 
28
+ self.mpan_map = mpan_map
29
+ self.messages = messages
30
+
28
31
  def __iter__(self):
29
32
  return self
30
33
 
@@ -36,6 +39,15 @@ class StarkDf2HhParser:
36
39
  lline = self.line.strip().upper()
37
40
  if lline.startswith("#O"):
38
41
  self.core = parse_mpan_core(lline[2:])
42
+ if self.core in self.mpan_map and self.mpan_map[self.core] is None:
43
+ msg = f"The MPAN core {self.core} has been ignored"
44
+ if len(self.messages) == 0 or self.messages[-1] != msg:
45
+ self.messages.append(
46
+ f"The MPAN core {self.core} has been ignored"
47
+ )
48
+ self.core = None
49
+ continue
50
+
39
51
  elif lline.startswith("#S"):
40
52
  sensor = int(lline[2:].strip())
41
53
  try:
@@ -72,6 +84,10 @@ class StarkDf2HhParser:
72
84
  except ValueError:
73
85
  raise BadRequest("Problem parsing the value: " + fields[2])
74
86
  status = fields[3][-1]
87
+
88
+ if self.core is None:
89
+ continue
90
+
75
91
  local_datum = {
76
92
  "mpan_core": self.core,
77
93
  "channel_type": self.channel_type,
@@ -8,12 +8,12 @@ from werkzeug.exceptions import BadRequest
8
8
  from chellow.utils import parse_channel_type, parse_mpan_core, to_utc, validate_hh_start
9
9
 
10
10
 
11
- def create_parser(reader, mpan_map):
12
- return HhParserCsvSimple(reader, mpan_map)
11
+ def create_parser(reader, mpan_map, messages):
12
+ return HhParserCsvSimple(reader, mpan_map, messages)
13
13
 
14
14
 
15
15
  class HhParserCsvSimple:
16
- def __init__(self, reader, mpan_map):
16
+ def __init__(self, reader, mpan_map, messages):
17
17
  self.shredder = zip(itertools.count(1), csv.reader(reader))
18
18
  next(self.shredder) # skip the title line
19
19
  self.values = None
@@ -1,5 +1,4 @@
1
1
  import csv
2
- import os
3
2
  import sys
4
3
  import threading
5
4
  import traceback
@@ -10,20 +9,28 @@ from flask import g
10
9
 
11
10
  from sqlalchemy.sql.expression import true
12
11
 
13
- import chellow.dloads
14
- from chellow.models import Channel, Contract, Era, Session, Site, SiteEra, Snag, Supply
12
+ from chellow.dloads import open_file
13
+ from chellow.models import (
14
+ Channel,
15
+ Contract,
16
+ Era,
17
+ Session,
18
+ Site,
19
+ SiteEra,
20
+ Snag,
21
+ Supply,
22
+ User,
23
+ )
15
24
  from chellow.utils import csv_make_val, hh_before, req_int, utc_datetime_now
16
25
  from chellow.views import chellow_redirect
17
26
 
18
27
 
19
- def content(contract_id, days_hidden, user):
28
+ def content(contract_id, days_hidden, user_id):
20
29
  f = writer = None
21
30
  try:
22
31
  with Session() as sess:
23
- running_name, finished_name = chellow.dloads.make_names(
24
- "channel_snags.csv", user
25
- )
26
- f = open(running_name, mode="w", newline="")
32
+ user = User.get_by_id(sess, user_id)
33
+ f = open_file("channel_snags.csv", user, mode="w", newline="")
27
34
  writer = csv.writer(f, lineterminator="\n")
28
35
  titles = (
29
36
  "Hidden Days",
@@ -111,13 +118,12 @@ def content(contract_id, days_hidden, user):
111
118
  finally:
112
119
  if f is not None:
113
120
  f.close()
114
- os.rename(running_name, finished_name)
115
121
 
116
122
 
117
123
  def do_get(sess):
118
124
  contract_id = req_int("dc_contract_id")
119
125
  days_hidden = req_int("days_hidden")
120
126
 
121
- args = (contract_id, days_hidden, g.user)
127
+ args = contract_id, days_hidden, g.user.id
122
128
  threading.Thread(target=content, args=args).start()
123
129
  return chellow_redirect("/downloads", 303)
@@ -1,5 +1,4 @@
1
1
  import csv
2
- import os
3
2
  import sys
4
3
  import threading
5
4
  import traceback
@@ -12,7 +11,12 @@ from sqlalchemy import or_, text
12
11
  from sqlalchemy.orm import joinedload
13
12
  from sqlalchemy.sql.expression import func, null
14
13
 
15
- import chellow.dloads
14
+ from werkzeug.exceptions import BadRequest
15
+
16
+ import chellow.e.duos
17
+ import chellow.e.tnuos
18
+ from chellow.dloads import open_file
19
+ from chellow.e.computer import SupplySource
16
20
  from chellow.models import (
17
21
  Batch,
18
22
  Bill,
@@ -25,6 +29,7 @@ from chellow.models import (
25
29
  Session,
26
30
  SiteEra,
27
31
  Supply,
32
+ User,
28
33
  )
29
34
  from chellow.utils import (
30
35
  CHANNEL_TYPES,
@@ -40,10 +45,11 @@ from chellow.utils import (
40
45
  from chellow.views import chellow_redirect
41
46
 
42
47
 
43
- def content(running_name, finished_name, date, supply_id, mpan_cores):
48
+ def content(user_id, date, supply_id, mpan_cores):
44
49
  try:
45
50
  with Session() as sess:
46
- f = open(running_name, mode="w", newline="")
51
+ user = User.get_by_id(sess, user_id)
52
+ f = open_file("supplies_snapshot.csv", user, mode="w", newline="")
47
53
  _process(sess, f, date, supply_id, mpan_cores)
48
54
  except BaseException:
49
55
  msg = traceback.format_exc()
@@ -52,7 +58,354 @@ def content(running_name, finished_name, date, supply_id, mpan_cores):
52
58
  finally:
53
59
  if f is not None:
54
60
  f.close()
55
- os.rename(running_name, finished_name)
61
+
62
+
63
+ def _process_era(caches, sess, era_id, date, year_start):
64
+ NORMAL_READ_TYPES = ("N", "C", "N3")
65
+
66
+ era, supply, generator_type = (
67
+ sess.query(Era, Supply, GeneratorType)
68
+ .join(Supply, Era.supply_id == Supply.id)
69
+ .outerjoin(GeneratorType, Supply.generator_type_id == GeneratorType.id)
70
+ .filter(Era.id == era_id)
71
+ .options(
72
+ joinedload(Era.channels),
73
+ joinedload(Era.cop),
74
+ joinedload(Era.comm),
75
+ joinedload(Era.dc_contract),
76
+ joinedload(Era.exp_llfc),
77
+ joinedload(Era.exp_supplier_contract),
78
+ joinedload(Era.imp_llfc),
79
+ joinedload(Era.imp_supplier_contract),
80
+ joinedload(Era.mop_contract),
81
+ joinedload(Era.mtc_participant).joinedload(MtcParticipant.mtc),
82
+ joinedload(Era.mtc_participant).joinedload(MtcParticipant.meter_type),
83
+ joinedload(Era.pc),
84
+ joinedload(Era.site_eras).joinedload(SiteEra.site),
85
+ joinedload(Era.ssc),
86
+ joinedload(Era.energisation_status),
87
+ joinedload(Era.supply).joinedload(Supply.source),
88
+ joinedload(Era.supply).joinedload(Supply.gsp_group),
89
+ joinedload(Era.supply).joinedload(Supply.dno),
90
+ )
91
+ .one()
92
+ )
93
+
94
+ site_codes = []
95
+ site_names = []
96
+ for site_era in era.site_eras:
97
+ if site_era.is_physical:
98
+ physical_site = site_era.site
99
+ else:
100
+ site = site_era.site
101
+ site_codes.append(site.code)
102
+ site_names.append(site.name)
103
+
104
+ sup_eras = (
105
+ sess.query(Era).filter(Era.supply == supply).order_by(Era.start_date).all()
106
+ )
107
+ supply_start_date = sup_eras[0].start_date
108
+ supply_finish_date = sup_eras[-1].finish_date
109
+
110
+ if era.imp_mpan_core is None:
111
+ voltage_level_code = era.exp_llfc.voltage_level.code
112
+ is_substation = era.exp_llfc.is_substation
113
+ else:
114
+ voltage_level_code = era.imp_llfc.voltage_level.code
115
+ is_substation = era.imp_llfc.is_substation
116
+
117
+ if generator_type is None:
118
+ generator_type_str = ""
119
+ else:
120
+ generator_type_str = generator_type.code
121
+
122
+ metering_type = era.meter_category
123
+
124
+ if metering_type in ("nhh", "amr"):
125
+ latest_prev_normal_read = (
126
+ sess.query(RegisterRead)
127
+ .join(Bill)
128
+ .join(RegisterRead.previous_type)
129
+ .filter(
130
+ ReadType.code.in_(NORMAL_READ_TYPES),
131
+ RegisterRead.previous_date <= date,
132
+ Bill.supply_id == supply.id,
133
+ )
134
+ .order_by(RegisterRead.previous_date.desc())
135
+ .options(joinedload(RegisterRead.previous_type))
136
+ .first()
137
+ )
138
+
139
+ latest_pres_normal_read = (
140
+ sess.query(RegisterRead)
141
+ .join(Bill)
142
+ .join(RegisterRead.present_type)
143
+ .filter(
144
+ ReadType.code.in_(NORMAL_READ_TYPES),
145
+ RegisterRead.present_date <= date,
146
+ Bill.supply == supply,
147
+ )
148
+ .order_by(RegisterRead.present_date.desc())
149
+ .options(joinedload(RegisterRead.present_type))
150
+ .first()
151
+ )
152
+
153
+ if latest_prev_normal_read is None and latest_pres_normal_read is None:
154
+ latest_normal_read_date = None
155
+ latest_normal_read_type = None
156
+ elif latest_pres_normal_read is not None and latest_prev_normal_read is None:
157
+ latest_normal_read_date = latest_pres_normal_read.present_date
158
+ latest_normal_read_type = latest_pres_normal_read.present_type.code
159
+ elif latest_pres_normal_read is None and latest_prev_normal_read is not None:
160
+ latest_normal_read_date = latest_prev_normal_read.previous_date
161
+ latest_normal_read_type = latest_prev_normal_read.previous_type.code
162
+ elif (
163
+ latest_pres_normal_read.present_date > latest_prev_normal_read.previous_date
164
+ ):
165
+ latest_normal_read_date = latest_pres_normal_read.present_date
166
+ latest_normal_read_type = latest_pres_normal_read.present_type.code
167
+ else:
168
+ latest_normal_read_date = latest_prev_normal_read.previous_date
169
+ latest_normal_read_type = latest_prev_normal_read.previous_type.code
170
+ if latest_normal_read_date is not None:
171
+ latest_normal_read_date = hh_format(latest_normal_read_date)
172
+
173
+ else:
174
+ latest_normal_read_date = metering_type
175
+ latest_normal_read_type = None
176
+
177
+ mop_contract = era.mop_contract
178
+ mop_contract_name = mop_contract.name
179
+ mop_account = era.mop_account
180
+ latest_mop_bill_date = (
181
+ sess.query(Bill.finish_date)
182
+ .join(Batch)
183
+ .filter(
184
+ Bill.start_date <= date,
185
+ Bill.supply == supply,
186
+ Batch.contract == mop_contract,
187
+ )
188
+ .order_by(Bill.finish_date.desc())
189
+ .first()
190
+ )
191
+
192
+ if latest_mop_bill_date is not None:
193
+ latest_mop_bill_date = hh_format(latest_mop_bill_date[0])
194
+
195
+ dc_contract = era.dc_contract
196
+ dc_contract_name = dc_contract.name
197
+ dc_account = era.dc_account
198
+ latest_dc_bill_date = (
199
+ sess.query(Bill.finish_date)
200
+ .join(Batch)
201
+ .filter(
202
+ Bill.start_date <= date,
203
+ Bill.supply == supply,
204
+ Batch.contract == dc_contract,
205
+ )
206
+ .order_by(Bill.finish_date.desc())
207
+ .first()
208
+ )
209
+
210
+ if latest_dc_bill_date is not None:
211
+ latest_dc_bill_date = hh_format(latest_dc_bill_date[0])
212
+
213
+ channel_values = []
214
+ for imp_related in [True, False]:
215
+ for channel_type in CHANNEL_TYPES:
216
+ if era.find_channel(sess, imp_related, channel_type) is None:
217
+ channel_values.append("false")
218
+ else:
219
+ channel_values.append("true")
220
+
221
+ imp_avg_months = None
222
+ exp_avg_months = None
223
+ for is_import in [True, False]:
224
+ if metering_type == "nhh":
225
+ continue
226
+
227
+ params = {
228
+ "supply_id": supply.id,
229
+ "year_start": year_start,
230
+ "year_finish": date,
231
+ "is_import": is_import,
232
+ }
233
+ month_mds = tuple(
234
+ md[0] * 2
235
+ for md in sess.execute(
236
+ text(
237
+ """
238
+
239
+ select max(hh_datum.value) as md
240
+ from hh_datum join channel on (hh_datum.channel_id = channel.id)
241
+ join era on (channel.era_id = era.id)
242
+ where era.supply_id = :supply_id and hh_datum.start_date >= :year_start
243
+ and hh_datum.start_date <= :year_finish
244
+ and channel.channel_type = 'ACTIVE'
245
+ and channel.imp_related = :is_import
246
+ group by extract(month from (hh_datum.start_date at time zone 'utc'))
247
+ order by md desc
248
+ limit 3
249
+
250
+ """
251
+ ),
252
+ params=params,
253
+ )
254
+ )
255
+
256
+ avg_months = sum(month_mds)
257
+ if len(month_mds) > 0:
258
+ avg_months /= len(month_mds)
259
+ if is_import:
260
+ imp_avg_months = avg_months
261
+ else:
262
+ exp_avg_months = avg_months
263
+
264
+ if (imp_avg_months is not None and imp_avg_months > 100) or (
265
+ exp_avg_months is not None and exp_avg_months > 100
266
+ ):
267
+ mandatory_hh = "yes"
268
+ else:
269
+ mandatory_hh = "no"
270
+
271
+ imp_latest_supplier_bill_date = None
272
+ exp_latest_supplier_bill_date = None
273
+ imp_tnuos_band = None
274
+ for is_import in (True, False):
275
+ for er in (
276
+ sess.query(Era)
277
+ .filter(Era.supply == era.supply, Era.start_date <= date)
278
+ .order_by(Era.start_date.desc())
279
+ ):
280
+ if is_import:
281
+ if er.imp_mpan_core is None:
282
+ break
283
+ else:
284
+ supplier_contract = er.imp_supplier_contract
285
+ if era.energisation_status.code == "E":
286
+ imp_ss = SupplySource(
287
+ sess, date, date, date + HH, era, True, caches
288
+ )
289
+ chellow.e.duos.duos_vb(imp_ss)
290
+ hh = imp_ss.hh_data[0]
291
+ if hh["start-date"] >= chellow.e.tnuos.BANDED_START:
292
+ imp_tnuos_band = chellow.e.tnuos.BAND_LOOKUP[
293
+ hh["duos-description"]
294
+ ]
295
+ else:
296
+ if er.exp_mpan_core is None:
297
+ break
298
+ else:
299
+ supplier_contract = er.exp_supplier_contract
300
+
301
+ latest_bill_date = (
302
+ sess.query(Bill.finish_date)
303
+ .join(Batch)
304
+ .filter(
305
+ Bill.finish_date >= er.start_date,
306
+ Bill.finish_date <= hh_min(er.finish_date, date),
307
+ Bill.supply == supply,
308
+ Batch.contract == supplier_contract,
309
+ )
310
+ .order_by(Bill.finish_date.desc())
311
+ .first()
312
+ )
313
+
314
+ if latest_bill_date is not None:
315
+ latest_bill_date = hh_format(latest_bill_date[0])
316
+
317
+ if is_import:
318
+ imp_latest_supplier_bill_date = latest_bill_date
319
+ else:
320
+ exp_latest_supplier_bill_date = latest_bill_date
321
+ break
322
+
323
+ meter_installation_date = (
324
+ sess.query(func.min(Era.start_date))
325
+ .filter(Era.supply == era.supply, Era.msn == era.msn)
326
+ .one()[0]
327
+ )
328
+
329
+ ssc = era.ssc
330
+ if ssc is None:
331
+ ssc_code = ssc_description = num_registers = None
332
+ else:
333
+ ssc_code, ssc_description = ssc.code, ssc.description
334
+ num_registers = (
335
+ sess.query(MeasurementRequirement)
336
+ .filter(MeasurementRequirement.ssc == ssc)
337
+ .count()
338
+ )
339
+
340
+ return (
341
+ [
342
+ date,
343
+ era.imp_mpan_core,
344
+ era.exp_mpan_core,
345
+ physical_site.code,
346
+ physical_site.name,
347
+ ", ".join(site_codes),
348
+ ", ".join(site_names),
349
+ supply.id,
350
+ supply.source.code,
351
+ generator_type_str,
352
+ supply.gsp_group.code,
353
+ supply.dno.dno_code,
354
+ voltage_level_code,
355
+ is_substation,
356
+ metering_type,
357
+ mandatory_hh,
358
+ era.pc.code,
359
+ era.mtc_participant.mtc.code,
360
+ era.cop.code,
361
+ era.comm.code,
362
+ ssc_code,
363
+ ssc_description,
364
+ era.energisation_status.code,
365
+ num_registers,
366
+ mop_contract_name,
367
+ mop_account,
368
+ dc_contract_name,
369
+ dc_account,
370
+ era.msn,
371
+ meter_installation_date,
372
+ latest_normal_read_date,
373
+ latest_normal_read_type,
374
+ latest_dc_bill_date,
375
+ latest_mop_bill_date,
376
+ supply_start_date,
377
+ supply_finish_date,
378
+ era.properties,
379
+ imp_tnuos_band,
380
+ ]
381
+ + channel_values
382
+ + [
383
+ era.imp_sc,
384
+ None if era.imp_llfc is None else era.imp_llfc.code,
385
+ None if era.imp_llfc is None else era.imp_llfc.description,
386
+ (
387
+ None
388
+ if era.imp_supplier_contract is None
389
+ else era.imp_supplier_contract.name
390
+ ),
391
+ era.imp_supplier_account,
392
+ imp_avg_months,
393
+ imp_latest_supplier_bill_date,
394
+ ]
395
+ + [
396
+ era.exp_sc,
397
+ None if era.exp_llfc is None else era.exp_llfc.code,
398
+ None if era.exp_llfc is None else era.exp_llfc.description,
399
+ (
400
+ None
401
+ if era.exp_supplier_contract is None
402
+ else era.exp_supplier_contract.name
403
+ ),
404
+ era.exp_supplier_account,
405
+ exp_avg_months,
406
+ exp_latest_supplier_bill_date,
407
+ ]
408
+ )
56
409
 
57
410
 
58
411
  def _process(sess, f, date, supply_id, mpan_cores):
@@ -95,6 +448,7 @@ def _process(sess, f, date, supply_id, mpan_cores):
95
448
  "Supply Start Date",
96
449
  "Supply Finish Date",
97
450
  "Properties",
451
+ "tnuos_band",
98
452
  "Import ACTIVE?",
99
453
  "Import REACTIVE_IMPORT?",
100
454
  "Import REACTIVE_EXPORT?",
@@ -118,8 +472,8 @@ def _process(sess, f, date, supply_id, mpan_cores):
118
472
  )
119
473
  writer.writerow(titles)
120
474
 
121
- NORMAL_READ_TYPES = ("N", "C", "N3")
122
475
  year_start = date + HH - relativedelta(years=1)
476
+ caches = {}
123
477
 
124
478
  era_ids = (
125
479
  sess.query(Era.id)
@@ -141,342 +495,12 @@ def _process(sess, f, date, supply_id, mpan_cores):
141
495
  )
142
496
 
143
497
  for (era_id,) in era_ids:
144
- era, supply, generator_type = (
145
- sess.query(Era, Supply, GeneratorType)
146
- .join(Supply, Era.supply_id == Supply.id)
147
- .outerjoin(GeneratorType, Supply.generator_type_id == GeneratorType.id)
148
- .filter(Era.id == era_id)
149
- .options(
150
- joinedload(Era.channels),
151
- joinedload(Era.cop),
152
- joinedload(Era.comm),
153
- joinedload(Era.dc_contract),
154
- joinedload(Era.exp_llfc),
155
- joinedload(Era.exp_supplier_contract),
156
- joinedload(Era.imp_llfc),
157
- joinedload(Era.imp_supplier_contract),
158
- joinedload(Era.mop_contract),
159
- joinedload(Era.mtc_participant).joinedload(MtcParticipant.mtc),
160
- joinedload(Era.mtc_participant).joinedload(MtcParticipant.meter_type),
161
- joinedload(Era.pc),
162
- joinedload(Era.site_eras).joinedload(SiteEra.site),
163
- joinedload(Era.ssc),
164
- joinedload(Era.energisation_status),
165
- joinedload(Era.supply).joinedload(Supply.source),
166
- joinedload(Era.supply).joinedload(Supply.gsp_group),
167
- joinedload(Era.supply).joinedload(Supply.dno),
168
- )
169
- .one()
170
- )
171
-
172
- site_codes = []
173
- site_names = []
174
- for site_era in era.site_eras:
175
- if site_era.is_physical:
176
- physical_site = site_era.site
177
- else:
178
- site = site_era.site
179
- site_codes.append(site.code)
180
- site_names.append(site.name)
181
-
182
- sup_eras = (
183
- sess.query(Era).filter(Era.supply == supply).order_by(Era.start_date).all()
184
- )
185
- supply_start_date = sup_eras[0].start_date
186
- supply_finish_date = sup_eras[-1].finish_date
187
-
188
- if era.imp_mpan_core is None:
189
- voltage_level_code = era.exp_llfc.voltage_level.code
190
- is_substation = era.exp_llfc.is_substation
191
- else:
192
- voltage_level_code = era.imp_llfc.voltage_level.code
193
- is_substation = era.imp_llfc.is_substation
194
-
195
- if generator_type is None:
196
- generator_type_str = ""
197
- else:
198
- generator_type_str = generator_type.code
199
-
200
- metering_type = era.meter_category
201
-
202
- if metering_type in ("nhh", "amr"):
203
- latest_prev_normal_read = (
204
- sess.query(RegisterRead)
205
- .join(Bill)
206
- .join(RegisterRead.previous_type)
207
- .filter(
208
- ReadType.code.in_(NORMAL_READ_TYPES),
209
- RegisterRead.previous_date <= date,
210
- Bill.supply_id == supply.id,
211
- )
212
- .order_by(RegisterRead.previous_date.desc())
213
- .options(joinedload(RegisterRead.previous_type))
214
- .first()
215
- )
216
-
217
- latest_pres_normal_read = (
218
- sess.query(RegisterRead)
219
- .join(Bill)
220
- .join(RegisterRead.present_type)
221
- .filter(
222
- ReadType.code.in_(NORMAL_READ_TYPES),
223
- RegisterRead.present_date <= date,
224
- Bill.supply == supply,
225
- )
226
- .order_by(RegisterRead.present_date.desc())
227
- .options(joinedload(RegisterRead.present_type))
228
- .first()
229
- )
230
-
231
- if latest_prev_normal_read is None and latest_pres_normal_read is None:
232
- latest_normal_read_date = None
233
- latest_normal_read_type = None
234
- elif (
235
- latest_pres_normal_read is not None and latest_prev_normal_read is None
236
- ):
237
- latest_normal_read_date = latest_pres_normal_read.present_date
238
- latest_normal_read_type = latest_pres_normal_read.present_type.code
239
- elif (
240
- latest_pres_normal_read is None and latest_prev_normal_read is not None
241
- ):
242
- latest_normal_read_date = latest_prev_normal_read.previous_date
243
- latest_normal_read_type = latest_prev_normal_read.previous_type.code
244
- elif (
245
- latest_pres_normal_read.present_date
246
- > latest_prev_normal_read.previous_date
247
- ):
248
- latest_normal_read_date = latest_pres_normal_read.present_date
249
- latest_normal_read_type = latest_pres_normal_read.present_type.code
250
- else:
251
- latest_normal_read_date = latest_prev_normal_read.previous_date
252
- latest_normal_read_type = latest_prev_normal_read.previous_type.code
253
- if latest_normal_read_date is not None:
254
- latest_normal_read_date = hh_format(latest_normal_read_date)
255
-
256
- else:
257
- latest_normal_read_date = metering_type
258
- latest_normal_read_type = None
259
-
260
- mop_contract = era.mop_contract
261
- mop_contract_name = mop_contract.name
262
- mop_account = era.mop_account
263
- latest_mop_bill_date = (
264
- sess.query(Bill.finish_date)
265
- .join(Batch)
266
- .filter(
267
- Bill.start_date <= date,
268
- Bill.supply == supply,
269
- Batch.contract == mop_contract,
270
- )
271
- .order_by(Bill.finish_date.desc())
272
- .first()
273
- )
274
-
275
- if latest_mop_bill_date is not None:
276
- latest_mop_bill_date = hh_format(latest_mop_bill_date[0])
277
-
278
- dc_contract = era.dc_contract
279
- dc_contract_name = dc_contract.name
280
- dc_account = era.dc_account
281
- latest_dc_bill_date = (
282
- sess.query(Bill.finish_date)
283
- .join(Batch)
284
- .filter(
285
- Bill.start_date <= date,
286
- Bill.supply == supply,
287
- Batch.contract == dc_contract,
288
- )
289
- .order_by(Bill.finish_date.desc())
290
- .first()
291
- )
292
-
293
- if latest_dc_bill_date is not None:
294
- latest_dc_bill_date = hh_format(latest_dc_bill_date[0])
295
-
296
- channel_values = []
297
- for imp_related in [True, False]:
298
- for channel_type in CHANNEL_TYPES:
299
- if era.find_channel(sess, imp_related, channel_type) is None:
300
- channel_values.append("false")
301
- else:
302
- channel_values.append("true")
303
-
304
- imp_avg_months = None
305
- exp_avg_months = None
306
- for is_import in [True, False]:
307
- if metering_type == "nhh":
308
- continue
309
-
310
- params = {
311
- "supply_id": supply.id,
312
- "year_start": year_start,
313
- "year_finish": date,
314
- "is_import": is_import,
315
- }
316
- month_mds = tuple(
317
- md[0] * 2
318
- for md in sess.execute(
319
- text(
320
- """
321
-
322
- select max(hh_datum.value) as md
323
- from hh_datum join channel on (hh_datum.channel_id = channel.id)
324
- join era on (channel.era_id = era.id)
325
- where era.supply_id = :supply_id and hh_datum.start_date >= :year_start
326
- and hh_datum.start_date <= :year_finish
327
- and channel.channel_type = 'ACTIVE'
328
- and channel.imp_related = :is_import
329
- group by extract(month from (hh_datum.start_date at time zone 'utc'))
330
- order by md desc
331
- limit 3
332
-
333
- """
334
- ),
335
- params=params,
336
- )
337
- )
338
-
339
- avg_months = sum(month_mds)
340
- if len(month_mds) > 0:
341
- avg_months /= len(month_mds)
342
- if is_import:
343
- imp_avg_months = avg_months
344
- else:
345
- exp_avg_months = avg_months
346
-
347
- if (imp_avg_months is not None and imp_avg_months > 100) or (
348
- exp_avg_months is not None and exp_avg_months > 100
349
- ):
350
- mandatory_hh = "yes"
351
- else:
352
- mandatory_hh = "no"
353
-
354
- imp_latest_supplier_bill_date = None
355
- exp_latest_supplier_bill_date = None
356
- for is_import in (True, False):
357
- for er in (
358
- sess.query(Era)
359
- .filter(Era.supply == era.supply, Era.start_date <= date)
360
- .order_by(Era.start_date.desc())
361
- ):
362
- if is_import:
363
- if er.imp_mpan_core is None:
364
- break
365
- else:
366
- supplier_contract = er.imp_supplier_contract
367
- else:
368
- if er.exp_mpan_core is None:
369
- break
370
- else:
371
- supplier_contract = er.exp_supplier_contract
372
-
373
- latest_bill_date = (
374
- sess.query(Bill.finish_date)
375
- .join(Batch)
376
- .filter(
377
- Bill.finish_date >= er.start_date,
378
- Bill.finish_date <= hh_min(er.finish_date, date),
379
- Bill.supply == supply,
380
- Batch.contract == supplier_contract,
381
- )
382
- .order_by(Bill.finish_date.desc())
383
- .first()
384
- )
385
-
386
- if latest_bill_date is not None:
387
- latest_bill_date = hh_format(latest_bill_date[0])
388
-
389
- if is_import:
390
- imp_latest_supplier_bill_date = latest_bill_date
391
- else:
392
- exp_latest_supplier_bill_date = latest_bill_date
393
- break
394
-
395
- meter_installation_date = (
396
- sess.query(func.min(Era.start_date))
397
- .filter(Era.supply == era.supply, Era.msn == era.msn)
398
- .one()[0]
399
- )
400
-
401
- ssc = era.ssc
402
- if ssc is None:
403
- ssc_code = ssc_description = num_registers = None
404
- else:
405
- ssc_code, ssc_description = ssc.code, ssc.description
406
- num_registers = (
407
- sess.query(MeasurementRequirement)
408
- .filter(MeasurementRequirement.ssc == ssc)
409
- .count()
410
- )
411
-
412
- vals = (
413
- [
414
- date,
415
- era.imp_mpan_core,
416
- era.exp_mpan_core,
417
- physical_site.code,
418
- physical_site.name,
419
- ", ".join(site_codes),
420
- ", ".join(site_names),
421
- supply.id,
422
- supply.source.code,
423
- generator_type_str,
424
- supply.gsp_group.code,
425
- supply.dno.dno_code,
426
- voltage_level_code,
427
- is_substation,
428
- metering_type,
429
- mandatory_hh,
430
- era.pc.code,
431
- era.mtc_participant.mtc.code,
432
- era.cop.code,
433
- era.comm.code,
434
- ssc_code,
435
- ssc_description,
436
- era.energisation_status.code,
437
- num_registers,
438
- mop_contract_name,
439
- mop_account,
440
- dc_contract_name,
441
- dc_account,
442
- era.msn,
443
- meter_installation_date,
444
- latest_normal_read_date,
445
- latest_normal_read_type,
446
- latest_dc_bill_date,
447
- latest_mop_bill_date,
448
- supply_start_date,
449
- supply_finish_date,
450
- era.properties,
451
- ]
452
- + channel_values
453
- + [
454
- era.imp_sc,
455
- None if era.imp_llfc is None else era.imp_llfc.code,
456
- None if era.imp_llfc is None else era.imp_llfc.description,
457
- (
458
- None
459
- if era.imp_supplier_contract is None
460
- else era.imp_supplier_contract.name
461
- ),
462
- era.imp_supplier_account,
463
- imp_avg_months,
464
- imp_latest_supplier_bill_date,
465
- ]
466
- + [
467
- era.exp_sc,
468
- None if era.exp_llfc is None else era.exp_llfc.code,
469
- None if era.exp_llfc is None else era.exp_llfc.description,
470
- (
471
- None
472
- if era.exp_supplier_contract is None
473
- else era.exp_supplier_contract.name
474
- ),
475
- era.exp_supplier_account,
476
- exp_avg_months,
477
- exp_latest_supplier_bill_date,
478
- ]
479
- )
498
+ try:
499
+ vals = _process_era(caches, sess, era_id, date, year_start)
500
+ except BaseException as e:
501
+ raise BadRequest(f"Problem with era {era_id}: {e}") from e
502
+ except BadRequest as e:
503
+ raise BadRequest(f"Problem with era {era_id}: {e.description}")
480
504
  writer.writerow([csv_make_val(v) for v in vals])
481
505
 
482
506
  # Avoid a long-running transaction
@@ -502,10 +526,6 @@ def do_get(session):
502
526
  else:
503
527
  mpan_cores = None
504
528
 
505
- running_name, finished_name = chellow.dloads.make_names(
506
- "supplies_snapshot.csv", user
507
- )
508
-
509
- args = (running_name, finished_name, date, supply_id, mpan_cores)
529
+ args = user.id, date, supply_id, mpan_cores
510
530
  threading.Thread(target=content, args=args).start()
511
531
  return chellow_redirect("/downloads", 303)
@@ -62,7 +62,11 @@
62
62
  "hostname": "example.com",
63
63
  "username": "username",
64
64
  "password": "password",
65
- "directories": ["downloads1", "downloads2"]}
65
+ "directories": ["downloads1", "downloads2"]
66
+ "mpan_map": { \\ Optional
67
+ "99 0993 2821 985": null, \\ Ignore MPAN
68
+ },
69
+ }
66
70
  </pre>
67
71
  </code>
68
72
  <p>For the HTTPS protocol:</p>
@@ -81,6 +85,9 @@
81
85
  "name2": val2,
82
86
  }
83
87
  }
88
+ "mpan_map": { \\ Optional
89
+ "99 0993 2821 985": null, \\ Ignore MPAN
90
+ },
84
91
  }
85
92
 
86
93
  {% endraw %}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: chellow
3
- Version: 1707396281.0.0
3
+ Version: 1708358861.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)
@@ -24,10 +24,10 @@ chellow/e/dno_rate_parser.py,sha256=5MYEbcYngh7n6Szn2h9zLTwQKXH-WHKy6c80PYwNLLQ,
24
24
  chellow/e/duos.py,sha256=ISTcNqe9KNjVNM2Qs8IBoQxnmSXOt5W_G7tZxp4T78M,28870
25
25
  chellow/e/elexon.py,sha256=s8QdLUQMeylLhghxvvhLyOL3-0ifUnMfmo3vcbot_DY,5487
26
26
  chellow/e/energy_management.py,sha256=aXC2qlGt3FAODlNl_frWzVYAQrJLP8FFOiNX3m-QE_Y,12388
27
- chellow/e/hh_importer.py,sha256=G1CK3AQQuJaNXCxGEY3UU_CjqOzfVZT-PDR67b3zpA0,20362
28
- chellow/e/hh_parser_bg_csv.py,sha256=IOjM-QliKzvSXyhslmxz504XWnsGkVKEaCQ48SNeNeQ,2423
29
- chellow/e/hh_parser_df2.py,sha256=G4kt4N4mNjUa1Z7B1Ov6rAyZmWDyGEVFYscdghToMRo,3558
30
- chellow/e/hh_parser_simple_csv.py,sha256=lVRprIEjDpTQdeXm2xcLmRAyre2LWec8ueVwcCISZPo,2082
27
+ chellow/e/hh_importer.py,sha256=nhmlWDNBm8U4salBud8ivdaDGF1lkY82NqQCrGuPYRI,20483
28
+ chellow/e/hh_parser_bg_csv.py,sha256=W5SU2MSpa8BGA0VJw1JXF-IwbCNLFy8fe35yxLZ7gEw,2453
29
+ chellow/e/hh_parser_df2.py,sha256=ynNNRMpLm9qs23tJjfm0vGSaAduXfPMw_7-WfQHXliQ,4209
30
+ chellow/e/hh_parser_simple_csv.py,sha256=RN4QOLvTQeoPrpvXvQ9hkOBZnR5piybLfjCiSJdjpjs,2112
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
@@ -76,12 +76,12 @@ chellow/reports/report_187.py,sha256=UvpaYHjyJFNV5puYq8_KxfzoBtVrwFgIGUOmC5oGA9A
76
76
  chellow/reports/report_215.py,sha256=_HZO_MhL51ttzmG6IrT8YVG6WmJMkRNWsXOW7AL7GWs,6766
77
77
  chellow/reports/report_219.py,sha256=o2eEg3mX9_raP2b4gjc2Gu4vqnMqcvvJBYQ1oQjxvpE,6637
78
78
  chellow/reports/report_231.py,sha256=E70sOYFS_Zc52LUfMY7SniQ1c6K3vPvDWvCvwaMUFrA,4210
79
- chellow/reports/report_233.py,sha256=KL9Qd3f-284w1Jj_ksUJ_OrE9Z_3RCz1zhRwZ9gdtLQ,4387
79
+ chellow/reports/report_233.py,sha256=DLAmkoHFKH-N8OEIoX4jeCxAaCfJK0e7ssjFJJpPqzw,4334
80
80
  chellow/reports/report_241.py,sha256=se2V1tb5scvH9-RRuGG3U-IbqUYd-BZcNFwvTRjkbsw,5625
81
81
  chellow/reports/report_247.py,sha256=m2YgZY1YkAkSI2Qn3cNlFe2Gv8ToXfFs1zHiQd9RwbM,41556
82
82
  chellow/reports/report_29.py,sha256=KDFjgrLBv4WbG9efCdu_geMR7gT_QV9N97Wfdt7aDc4,2736
83
83
  chellow/reports/report_291.py,sha256=q04Y9EfMhQuEX4ObpmUs8vJTL76QwdG1A2-2HDVWhHQ,7587
84
- chellow/reports/report_33.py,sha256=LXLpsjfUTbCDuSKEE8o-LVpBLqRADpiGnyeilBgLGA0,16925
84
+ chellow/reports/report_33.py,sha256=3WXPN0xV6PGMKY_Oc5bKR84JuiXirYlYdrghqIgYY_s,16675
85
85
  chellow/reports/report_387.py,sha256=KcClrEOK5IRvePp0Jgbzs1Gwf-_CqYmEtFFlXrr_Z3E,5670
86
86
  chellow/reports/report_41.py,sha256=QQeTshA1Og7N3wPaoZ8ynJzwsvZ1mgSFc7DDkVqIZoM,7447
87
87
  chellow/reports/report_429.py,sha256=0GCc0rQnOSG0fusw69yMMOCxnWApBd3P2sGWIg1py9M,12359
@@ -186,7 +186,7 @@ chellow/templates/e/dc_bill_edit.html,sha256=0GsN-ZIc4q-z_xs8igC2ZS6t4soo2SvB3qR
186
186
  chellow/templates/e/dc_bill_import.html,sha256=NHjMSoFizvFQIaPWuVE3nTCtMDTzJB0XmH8jXfV1tiA,2188
187
187
  chellow/templates/e/dc_bill_imports.html,sha256=lCaUR47r9KPr0VrQhEvVEaKexM5R_nmkxtzgpWZ0e9s,2135
188
188
  chellow/templates/e/dc_contract.html,sha256=_fq_s69kmbrGlScYngRYm4cPdRgh2roMvSfvG0J4xzA,2326
189
- chellow/templates/e/dc_contract_edit.html,sha256=BQPhcFqlBGaxBqinqxoeGZQPA6nmgfNAo9EcaOtycjM,2960
189
+ chellow/templates/e/dc_contract_edit.html,sha256=wKK2pXN78VfSJ6gjfNRkikRFkYTiKtGL3UeVFIYSRwc,3115
190
190
  chellow/templates/e/dc_contract_hh_import.html,sha256=UODiBFNohb60MjH1w-9JW1JE0O9GR2uKPGw-lD7Da5g,848
191
191
  chellow/templates/e/dc_contract_hh_imports.html,sha256=eXFDGyzSgag4JRism81_p5yTzQOjCIXaVkQ8tl3dDcM,8172
192
192
  chellow/templates/e/dc_contracts.html,sha256=v8czpEcMzndngfWNXIKXib6Xrz9OwJjQAmNQNrQ6Y08,1705
@@ -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-1707396281.0.0.dist-info/METADATA,sha256=znQdcRupraUVzaIpiElWdAt0Zm8UtIuzJ1Vo7Ne4Mwk,12203
367
- chellow-1707396281.0.0.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87
368
- chellow-1707396281.0.0.dist-info/RECORD,,
366
+ chellow-1708358861.0.0.dist-info/METADATA,sha256=_bInYkXY1vjjvC7miNTD-mnFkrdyaxQ9nx3uOCNzYSE,12203
367
+ chellow-1708358861.0.0.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87
368
+ chellow-1708358861.0.0.dist-info/RECORD,,