chellow 1710934022.0.0__py3-none-any.whl → 1712589951.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
@@ -63,8 +63,8 @@ def get_importer_modules():
63
63
  )
64
64
 
65
65
 
66
- def create_app(testing=False):
67
- app = Flask("chellow", instance_relative_config=True)
66
+ def create_app(testing=False, instance_path=None):
67
+ app = Flask("chellow", instance_relative_config=True, instance_path=instance_path)
68
68
  app.wsgi_app = MsProxy(app.wsgi_app)
69
69
  app.secret_key = os.urandom(24)
70
70
  start_sqlalchemy()
@@ -79,7 +79,6 @@ def create_app(testing=False):
79
79
 
80
80
  if not testing:
81
81
  db_upgrade(app.root_path)
82
- chellow.dloads.startup(Path(app.instance_path))
83
82
  chellow.e.hh_importer.startup()
84
83
 
85
84
  with Session() as sess:
@@ -90,6 +89,8 @@ def create_app(testing=False):
90
89
  api_props = props.get("api", {})
91
90
  api.description = api_props.get("description", "Access Chellow data")
92
91
 
92
+ chellow.dloads.startup(Path(app.instance_path), run_deleter=(not testing))
93
+
93
94
  for module in get_importer_modules():
94
95
  if not testing:
95
96
  module.startup()
chellow/dloads.py CHANGED
@@ -20,12 +20,14 @@ download_path = None
20
20
  SERIAL_DIGITS = 5
21
21
 
22
22
 
23
- def startup(instance_path):
23
+ def startup(instance_path, run_deleter=True):
24
24
  global file_deleter
25
25
  global download_id
26
26
  global download_path
27
- file_deleter = FileDeleter()
28
- file_deleter.start()
27
+
28
+ if run_deleter:
29
+ file_deleter = FileDeleter()
30
+ file_deleter.start()
29
31
 
30
32
  download_path = instance_path / "downloads"
31
33
  download_path.mkdir(parents=True, exist_ok=True)
chellow/e/views.py CHANGED
@@ -1569,7 +1569,6 @@ def era_edit_form_get(era_id):
1569
1569
  try:
1570
1570
  era = Era.get_by_id(g.sess, era_id)
1571
1571
  ct_now = ct_datetime_now()
1572
- cops = g.sess.scalars(select(Cop).order_by(Cop.code))
1573
1572
  comms = g.sess.scalars(select(Comm).order_by(Comm.code))
1574
1573
  energisation_statuses = g.sess.scalars(
1575
1574
  select(EnergisationStatus).order_by(EnergisationStatus.code)
@@ -1781,6 +1780,11 @@ def era_edit_form_get(era_id):
1781
1780
  else:
1782
1781
  mtc_participant = None
1783
1782
 
1783
+ if mtc_participant is None:
1784
+ cops = g.sess.scalars(select(Cop).order_by(Cop.code))
1785
+ else:
1786
+ cops = Cop.get_valid(g.sess, mtc_participant.meter_type)
1787
+
1784
1788
  if pc.code == "00":
1785
1789
  imp_llfcs_q = (
1786
1790
  select(Llfc)
chellow/models.py CHANGED
@@ -510,6 +510,17 @@ class Cop(Base, PersistentClass):
510
510
  raise BadRequest(f"The CoP with code {code} can't be found.")
511
511
  return typ
512
512
 
513
+ @staticmethod
514
+ def get_valid(sess, meter_type):
515
+
516
+ if meter_type.code == "C5":
517
+ q = select(Cop).where(Cop.code.in_(["1", "2", "3", "4", "5"]))
518
+ elif meter_type.code in ["6A", "6B", "6C", "6D"]:
519
+ q = select(Cop).where(Cop.code == meter_type.code.lower())
520
+ else:
521
+ q = select(Cop)
522
+ return sess.scalars(q.order_by(Cop.code))
523
+
513
524
  __tablename__ = "cop"
514
525
  id = Column(Integer, primary_key=True)
515
526
  code = Column(String, unique=True, nullable=False)
@@ -3700,12 +3711,9 @@ class Era(Base, PersistentClass):
3700
3711
  f"{hh_format(finish_date)}."
3701
3712
  )
3702
3713
 
3703
- if (
3704
- self.mtc_participant.meter_type.code == "C5"
3705
- and cop.code not in ["1", "2", "3", "4", "5"]
3706
- or self.mtc_participant.meter_type.code in ["6A", "6B", "6C", "6D"]
3707
- and cop.code.upper() != self.mtc_participant.meter_type.code
3708
- ):
3714
+ if cop.code not in [
3715
+ c.code for c in Cop.get_valid(sess, self.mtc_participant.meter_type)
3716
+ ]:
3709
3717
  raise BadRequest(
3710
3718
  f"The CoP of {cop.code} is not compatible with the meter type code "
3711
3719
  f"of {self.mtc_participant.meter_type.code}."
@@ -1,5 +1,4 @@
1
1
  import csv
2
- import os
3
2
  import threading
4
3
  import traceback
5
4
 
@@ -10,11 +9,11 @@ from sqlalchemy.sql.expression import null
10
9
 
11
10
  from werkzeug.exceptions import BadRequest
12
11
 
13
- import chellow.dloads
14
12
  import chellow.e.computer
15
13
  import chellow.e.duos
16
14
  import chellow.e.tnuos
17
- from chellow.models import Era, Pc, Session, Site, SiteEra, Source, Supply
15
+ from chellow.dloads import open_file
16
+ from chellow.models import Era, Pc, Session, Site, SiteEra, Source, Supply, User
18
17
  from chellow.utils import (
19
18
  HH,
20
19
  c_months_u,
@@ -144,28 +143,30 @@ def _write_sites(sess, caches, writer, year, site_id):
144
143
  sess.rollback()
145
144
 
146
145
 
147
- def content(year, site_id, user):
146
+ def content(year, site_id, user_id):
148
147
  caches = {}
149
148
  f = writer = None
150
149
  try:
151
150
  with Session() as sess:
152
- running_name, finished_name = chellow.dloads.make_names("output.csv", user)
153
- f = open(running_name, mode="w", newline="")
151
+ user = User.get_by_id(sess, user_id)
152
+ f = open_file("output.csv", user, mode="w", newline="")
154
153
  writer = csv.writer(f, lineterminator="\n")
155
154
  _write_sites(sess, caches, writer, year, site_id)
156
155
 
157
156
  except BadRequest as e:
158
157
  writer.writerow([e.description])
159
158
  except BaseException:
160
- writer.writerow([traceback.format_exc()])
159
+ msg = traceback.format_exc()
160
+ print(msg)
161
+ if writer is not None:
162
+ writer.writerow([msg])
161
163
  finally:
162
164
  if f is not None:
163
165
  f.close()
164
- os.rename(running_name, finished_name)
165
166
 
166
167
 
167
168
  def do_get(sess):
168
169
  site_id = req_int("site_id") if "site_id" in request.values else None
169
170
  year = req_int("year")
170
- threading.Thread(target=content, args=(year, site_id, g.user)).start()
171
+ threading.Thread(target=content, args=(year, site_id, g.user.id)).start()
171
172
  return chellow_redirect("/downloads", 303)
@@ -1,5 +1,4 @@
1
1
  import csv
2
- import os
3
2
  import threading
4
3
  import traceback
5
4
  from datetime import datetime as Datetime
@@ -14,8 +13,9 @@ from sqlalchemy.sql.expression import null, or_, true
14
13
 
15
14
  from werkzeug.exceptions import BadRequest
16
15
 
17
- import chellow.computer
18
- from chellow.models import Era, Session, Site, SiteEra, Supply
16
+ from chellow.dloads import open_file
17
+ from chellow.e.computer import SupplySource, contract_func, forecast_date
18
+ from chellow.models import Era, Session, Site, SiteEra, Supply, User
19
19
  from chellow.utils import HH, csv_make_val, hh_format, hh_max, hh_min, req_bool, req_int
20
20
  from chellow.views import chellow_redirect
21
21
 
@@ -29,15 +29,13 @@ def content(
29
29
  finish_day,
30
30
  is_import,
31
31
  supply_id,
32
- user,
32
+ user_id,
33
33
  ):
34
34
  caches = {}
35
35
  try:
36
36
  with Session() as sess:
37
- running_name, finished_name = chellow.dloads.make_names(
38
- "daily_supplier_virtual_bill.csv", user
39
- )
40
- f = open(running_name, mode="w", newline="")
37
+ user = User.get_by_id(sess, user_id)
38
+ f = open_file("daily_supplier_virtual_bill.csv", user, mode="w", newline="")
41
39
  writer = csv.writer(f, lineterminator="\n")
42
40
  start_date = Datetime(start_year, start_month, start_day, tzinfo=pytz.utc)
43
41
  finish_date = (
@@ -47,7 +45,7 @@ def content(
47
45
  )
48
46
 
49
47
  supply = Supply.get_by_id(sess, supply_id)
50
- forecast_date = chellow.computer.forecast_date()
48
+ fd = forecast_date()
51
49
  day_start = start_date
52
50
  header_titles = [
53
51
  "MPAN Core",
@@ -71,9 +69,7 @@ def content(
71
69
  else:
72
70
  cont = era.exp_supplier_contract
73
71
 
74
- for title in chellow.computer.contract_func(
75
- caches, cont, "virtual_bill_titles"
76
- )():
72
+ for title in contract_func(caches, cont, "virtual_bill_titles")():
77
73
  if title not in bill_titles:
78
74
  bill_titles.append(title)
79
75
 
@@ -94,11 +90,11 @@ def content(
94
90
  chunk_start = hh_max(era.start_date, day_start)
95
91
  chunk_finish = hh_min(era.finish_date, day_finish)
96
92
 
97
- ss = chellow.computer.SupplySource(
93
+ ss = SupplySource(
98
94
  sess,
99
95
  chunk_start,
100
96
  chunk_finish,
101
- forecast_date,
97
+ fd,
102
98
  era,
103
99
  is_import,
104
100
  caches,
@@ -120,9 +116,7 @@ def content(
120
116
  ss.years_back > 0,
121
117
  ]
122
118
 
123
- chellow.computer.contract_func(
124
- caches, ss.supplier_contract, "virtual_bill"
125
- )(ss)
119
+ contract_func(caches, ss.supplier_contract, "virtual_bill")(ss)
126
120
  bill = ss.supplier_bill
127
121
  for title in bill_titles:
128
122
  if title in bill:
@@ -144,7 +138,6 @@ def content(
144
138
  finally:
145
139
  if f is not None:
146
140
  f.close()
147
- os.rename(running_name, finished_name)
148
141
 
149
142
 
150
143
  def do_get(sess):
@@ -159,18 +152,16 @@ def do_get(sess):
159
152
  is_import = req_bool("is_import")
160
153
  supply_id = req_int("supply_id")
161
154
 
162
- threading.Thread(
163
- target=content,
164
- args=(
165
- start_year,
166
- start_month,
167
- start_day,
168
- finish_year,
169
- finish_month,
170
- finish_day,
171
- is_import,
172
- supply_id,
173
- g.user,
174
- ),
175
- ).start()
155
+ args = (
156
+ start_year,
157
+ start_month,
158
+ start_day,
159
+ finish_year,
160
+ finish_month,
161
+ finish_day,
162
+ is_import,
163
+ supply_id,
164
+ g.user.id,
165
+ )
166
+ threading.Thread(target=content, args=args).start()
176
167
  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
@@ -13,8 +12,8 @@ from sqlalchemy.sql.expression import null
13
12
 
14
13
  from werkzeug.exceptions import BadRequest
15
14
 
16
- import chellow.dloads
17
- from chellow.models import Contract, Era, ReportRun, Session, Site, SiteEra
15
+ from chellow.dloads import open_file
16
+ from chellow.models import Contract, Era, ReportRun, Session, Site, SiteEra, User
18
17
  from chellow.views import chellow_redirect
19
18
 
20
19
  STATUSES_ACTIVE = ("IN USE / IN SERVICE", "STORED SPARE")
@@ -147,13 +146,12 @@ def _process_sites(sess, file_like, writer, props, report_run):
147
146
  FNAME = "asset_comparison"
148
147
 
149
148
 
150
- def content(user, file_like, report_run_id):
149
+ def content(user_id, file_like, report_run_id):
150
+ f = report_run = writer = None
151
151
  try:
152
152
  with Session() as sess:
153
- running_name, finished_name = chellow.dloads.make_names(
154
- f"{FNAME}.csv", user
155
- )
156
- f = open(running_name, mode="w", newline="")
153
+ user = User.get_by_id(sess, user_id)
154
+ f = open_file(f"{FNAME}.csv", user, mode="w", newline="")
157
155
  writer = csv.writer(f, lineterminator="\n")
158
156
  report_run = ReportRun.get_by_id(sess, report_run_id)
159
157
 
@@ -171,11 +169,11 @@ def content(user, file_like, report_run_id):
171
169
  report_run.insert_row(sess, "", ["error"], {"error": msg}, {})
172
170
  sess.commit()
173
171
  sys.stderr.write(msg)
174
- writer.writerow([msg])
172
+ if writer is not None:
173
+ writer.writerow([msg])
175
174
  finally:
176
175
  if f is not None:
177
176
  f.close()
178
- os.rename(running_name, finished_name)
179
177
 
180
178
 
181
179
  def do_post(sess):
@@ -194,6 +192,6 @@ def do_post(sess):
194
192
  },
195
193
  )
196
194
  sess.commit()
197
- args = user, StringIO(file_item.read().decode("utf8")), report_run.id
195
+ args = user.id, StringIO(file_item.read().decode("utf8")), report_run.id
198
196
  threading.Thread(target=content, args=args).start()
199
197
  return chellow_redirect(f"/report_runs/{report_run.id}", 303)
@@ -1,5 +1,4 @@
1
1
  import csv
2
- import os
3
2
  import threading
4
3
  import traceback
5
4
 
@@ -7,18 +6,18 @@ from flask import g
7
6
 
8
7
  from sqlalchemy.orm import joinedload
9
8
 
10
- import chellow.dloads
11
- from chellow.models import Llfc, Session
9
+ from chellow.dloads import open_file
10
+ from chellow.models import Llfc, Session, User
12
11
  from chellow.utils import hh_format
13
12
  from chellow.views import chellow_redirect
14
13
 
15
14
 
16
- def content(user):
15
+ def content(user_id):
17
16
  f = writer = None
18
17
  try:
19
18
  with Session() as sess:
20
- running_name, finished_name = chellow.dloads.make_names("llfcs.csv", user)
21
- f = open(running_name, mode="w", newline="")
19
+ user = User.get_by_id(sess, user_id)
20
+ f = open_file("llfcs.csv", user, mode="w", newline="")
22
21
  writer = csv.writer(f, lineterminator="\n")
23
22
  writer.writerow(
24
23
  (
@@ -53,14 +52,16 @@ def content(user):
53
52
  )
54
53
  )
55
54
  except BaseException:
56
- writer.writerow([traceback.format_exc()])
55
+ msg = traceback.format_exc()
56
+ print(msg)
57
+ if writer is not None:
58
+ writer.writerow([msg])
57
59
  finally:
58
60
  if f is not None:
59
61
  f.close()
60
- os.rename(running_name, finished_name)
61
62
 
62
63
 
63
64
  def do_get(sess):
64
- args = (g.user,)
65
+ args = (g.user.id,)
65
66
  threading.Thread(target=content, args=args).start()
66
67
  return chellow_redirect("/downloads", 303)
@@ -1,6 +1,4 @@
1
1
  import csv
2
- import os
3
- import sys
4
2
  import threading
5
3
  import traceback
6
4
  from datetime import datetime as Datetime, timedelta as Timedelta
@@ -12,20 +10,18 @@ import pytz
12
10
  from sqlalchemy.orm import joinedload
13
11
  from sqlalchemy.sql.expression import null
14
12
 
15
- import chellow.dloads
16
- from chellow.models import Session, Site, Snag
13
+ from chellow.dloads import open_file
14
+ from chellow.models import Session, Site, Snag, User
17
15
  from chellow.utils import hh_format
18
16
  from chellow.views import chellow_redirect
19
17
 
20
18
 
21
- def content(user):
19
+ def content(user_id):
22
20
  f = writer = None
23
21
  try:
24
22
  with Session() as sess:
25
- running_name, finished_name = chellow.dloads.make_names(
26
- "site_snags.csv", user
27
- )
28
- f = open(running_name, mode="w", newline="")
23
+ user = User.get_by_id(sess, user_id)
24
+ f = open_file("site_snags.csv", user, mode="w", newline="")
29
25
  writer = csv.writer(f, lineterminator="\n")
30
26
  writer.writerow(
31
27
  (
@@ -74,15 +70,15 @@ def content(user):
74
70
  )
75
71
  except BaseException:
76
72
  msg = traceback.format_exc()
77
- sys.stderr.write(msg)
78
- writer.writerow([msg])
73
+ print(msg)
74
+ if writer is not None:
75
+ writer.writerow([msg])
79
76
  finally:
80
77
  if f is not None:
81
78
  f.close()
82
- os.rename(running_name, finished_name)
83
79
 
84
80
 
85
81
  def do_get(sess):
86
- args = (g.user,)
82
+ args = (g.user.id,)
87
83
  threading.Thread(target=content, args=args).start()
88
84
  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,10 +11,10 @@ from sqlalchemy.sql.expression import null, true
12
11
 
13
12
  from werkzeug.exceptions import BadRequest
14
13
 
15
- import chellow.dloads
14
+ from chellow.dloads import open_file
16
15
  from chellow.e.computer import contract_func, forecast_date
17
16
  from chellow.gas.engine import GDataSource
18
- from chellow.models import GContract, GEra, Session, Site, SiteGEra
17
+ from chellow.models import GContract, GEra, Session, Site, SiteGEra, User
19
18
  from chellow.utils import (
20
19
  c_months_u,
21
20
  hh_format,
@@ -29,14 +28,13 @@ from chellow.utils import (
29
28
  from chellow.views import chellow_redirect
30
29
 
31
30
 
32
- def content(start_date, finish_date, g_contract_id, user):
31
+ def content(start_date, finish_date, g_contract_id, user_id):
33
32
  report_context = {}
33
+ f = writer = None
34
34
  try:
35
35
  with Session() as sess:
36
- running_name, finished_name = chellow.dloads.make_names(
37
- "gas_virtual_bills.csv", user
38
- )
39
- f = open(running_name, mode="w", newline="")
36
+ user = User.get_by_id(sess, user_id)
37
+ f = open_file("gas_virtual_bills.csv", user, mode="w", newline="")
40
38
  writer = csv.writer(f, lineterminator="\n")
41
39
 
42
40
  g_contract = GContract.get_by_id(sess, g_contract_id)
@@ -125,11 +123,11 @@ def content(start_date, finish_date, g_contract_id, user):
125
123
  except BaseException:
126
124
  msg = traceback.format_exc()
127
125
  sys.stderr.write(msg)
128
- writer.writerow([msg])
126
+ if writer is not None:
127
+ writer.writerow([msg])
129
128
  finally:
130
129
  if f is not None:
131
130
  f.close()
132
- os.rename(running_name, finished_name)
133
131
 
134
132
 
135
133
  def do_get(sess):
@@ -137,7 +135,6 @@ def do_get(sess):
137
135
  finish_date = req_date("finish")
138
136
  g_contract_id = req_int("g_contract_id")
139
137
 
140
- threading.Thread(
141
- target=content, args=(start_date, finish_date, g_contract_id, g.user)
142
- ).start()
138
+ args = start_date, finish_date, g_contract_id, g.user.id
139
+ threading.Thread(target=content, args=args).start()
143
140
  return chellow_redirect("/downloads", 303)
@@ -1,4 +1,3 @@
1
- import os
2
1
  import sys
3
2
  import threading
4
3
  import traceback
@@ -13,7 +12,7 @@ from sqlalchemy.orm import joinedload
13
12
 
14
13
  from werkzeug.exceptions import BadRequest
15
14
 
16
- import chellow.dloads
15
+ from chellow.dloads import open_file
17
16
  from chellow.models import MeasurementRequirement, Session, Ssc, Tpr, User
18
17
  from chellow.utils import req_bool
19
18
  from chellow.views import chellow_redirect
@@ -54,9 +53,7 @@ def content(
54
53
  with Session() as sess:
55
54
  user = User.get_by_id(sess, user_id)
56
55
 
57
- running_name, finished_name = chellow.dloads.make_names("sscs.ods", user)
58
-
59
- rf = open(running_name, "wb")
56
+ rf = open_file("sscs.ods", user, "wb")
60
57
 
61
58
  for ssc in sess.scalars(select(Ssc).order_by(Ssc.code)):
62
59
  ssc_rows.append(
@@ -100,8 +97,7 @@ def content(
100
97
  ssc_rows.append(["Problem " + msg])
101
98
  if rf is None:
102
99
  msg = traceback.format_exc()
103
- r_name, f_name = chellow.dloads.make_names("error.txt", None)
104
- ef = open(r_name, "w")
100
+ ef = open_file("error.txt", None, "w")
105
101
  ef.write(msg + "\n")
106
102
  ef.close()
107
103
  else:
@@ -109,7 +105,6 @@ def content(
109
105
  finally:
110
106
  if rf is not None:
111
107
  rf.close()
112
- os.rename(running_name, finished_name)
113
108
 
114
109
 
115
110
  def do_get(sess):
@@ -1,5 +1,4 @@
1
1
  import csv
2
- import os
3
2
  import sys
4
3
  import threading
5
4
  import traceback
@@ -10,7 +9,7 @@ from sqlalchemy import select
10
9
  from sqlalchemy.orm import joinedload
11
10
  from sqlalchemy.sql.expression import null
12
11
 
13
- import chellow.dloads
12
+ from chellow.dloads import open_file
14
13
  from chellow.models import (
15
14
  Contract,
16
15
  Era,
@@ -33,10 +32,7 @@ def content(user_id, report_run_id):
33
32
  try:
34
33
  with Session() as sess:
35
34
  user = User.get_by_id(sess, user_id)
36
- running_name, finished_name = chellow.dloads.make_names(
37
- f"{FNAME}.csv", user
38
- )
39
- f = open(running_name, mode="w", newline="")
35
+ f = open_file(f"{FNAME}.csv", user, mode="w", newline="")
40
36
  report_run = ReportRun.get_by_id(sess, report_run_id)
41
37
 
42
38
  _process(sess, f, report_run)
@@ -56,7 +52,6 @@ def content(user_id, report_run_id):
56
52
  finally:
57
53
  if f is not None:
58
54
  f.close()
59
- os.rename(running_name, finished_name)
60
55
 
61
56
 
62
57
  def _process(sess, f, report_run):
@@ -129,13 +124,7 @@ def _process(sess, f, report_run):
129
124
 
130
125
 
131
126
  def do_get(sess):
132
- report_run = ReportRun.insert(
133
- sess,
134
- FNAME,
135
- g.user,
136
- FNAME,
137
- {},
138
- )
127
+ report_run = ReportRun.insert(sess, FNAME, g.user, FNAME, {})
139
128
  sess.commit()
140
129
  threading.Thread(target=content, args=(g.user.id, report_run.id)).start()
141
130
  return chellow_redirect(f"/report_runs/{report_run.id}", 303)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: chellow
3
- Version: 1710934022.0.0
3
+ Version: 1712589951.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,11 +1,11 @@
1
- chellow/__init__.py,sha256=_9hq4MlC2YB7kUh4iH9hwmx7BqMV6JeyvGVB8uWk8Cs,10127
1
+ chellow/__init__.py,sha256=A6gh-jVJBhuCFmfquLEa8uf-kQhsRSFficE1SlnHnAg,10200
2
2
  chellow/api.py,sha256=mk17TfweR76DPFC8lX2SArTjai6y6YshASxqO1w-_-s,11036
3
3
  chellow/bank_holidays.py,sha256=T_utYMwe_g1dz5X-aOTdIPryg49SvB7QsWM1yphlqG8,4423
4
4
  chellow/commands.py,sha256=ESBe9ZWj1c3vdZgqMZ9gFvYAB3hRag2R1PzOwuw9yFo,1302
5
- chellow/dloads.py,sha256=vtebrKchBt_oANDO_c-aL2jO-w1KKwBcYaG_VgzUlJI,5209
5
+ chellow/dloads.py,sha256=5SmP-0QPK6xCkd_wjIWh_8FAzN5OxNHCzyEQ1Xb-Y-M,5256
6
6
  chellow/edi_lib.py,sha256=het3R0DBGtXEo-Sy1zvXQuX9EWQ1X6Y33G4l1hYYuds,51859
7
7
  chellow/general_import.py,sha256=4LduwzNipxOiHYHQLb0x96_m3RPuDnQMbJzOFDbOqTg,65039
8
- chellow/models.py,sha256=5ZWG1IexWNfwdq_ZlAjTyl0BlG2w682ydF09pEl31CY,237109
8
+ chellow/models.py,sha256=j26jxohyZ09tWVOAnWRFl0k0QUVRX0nDthIIDJpo7cE,237327
9
9
  chellow/national_grid.py,sha256=uxcv0qisHPyzw9AVIYPzsRqwt2XPAcZL-SBR12qcrS0,4364
10
10
  chellow/proxy.py,sha256=cVXIktPlX3tQ1BYcwxq0nJXKE6r3DtFTtfFHPq55HaM,1351
11
11
  chellow/rate_server.py,sha256=vNuKzlr0lkAzZ4zG7zboStoo92_6iLKAxbw1yZ-ucII,5626
@@ -38,7 +38,7 @@ chellow/e/system_price.py,sha256=3cPWohHmmBI9v7fENLBzXQkpAeC3r0s5xV29Nmr6k_E,583
38
38
  chellow/e/tlms.py,sha256=kHAy2A2d1Mma7x4GdNDyrzscJd5DpJtDBYiZEg241Dw,8538
39
39
  chellow/e/tnuos.py,sha256=KoMPJDIXfE4zwhSDuywGF1ooxjTYLVjtF7BkSFm6X24,4158
40
40
  chellow/e/triad.py,sha256=S6LEMHvUKhAZe0-yfLIRciYDZ8IKMn1jh1TmmsbQD3s,13588
41
- chellow/e/views.py,sha256=aZecpfM1sVn3xMwlKuiw9pE2f6s4uyd98obQXToKtqA,212466
41
+ chellow/e/views.py,sha256=p-zQa24HtNVLTHILqfiZ8UrixuljHSC163SrJuLQ3Ik,212590
42
42
  chellow/e/bill_parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  chellow/e/bill_parsers/activity_mop_stark_xlsx.py,sha256=UgWXDPzQkQghyj_lfgBqoSJpHB-t-qOdSaB8qY6GLog,4071
44
44
  chellow/e/bill_parsers/annual_mop_stark_xlsx.py,sha256=-HMoIfa_utXYKA44RuC0Xqv3vd2HLeQU_4P0iBUd3WA,4219
@@ -69,15 +69,13 @@ chellow/reports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
69
  chellow/reports/report_109.py,sha256=S8fsOwh-jVWGL0RsgyYLdqn00BAvlkBbi4VfTSGI-Mc,11181
70
70
  chellow/reports/report_111.py,sha256=qU5MYPQzZ_cEvuERE8bxq93D-dN1yd4oML6FIqd0Jdk,28278
71
71
  chellow/reports/report_169.py,sha256=tnTiHmhigcgZ7E8rUSfgzsVYUCW947xBZZq04-KqfK8,9287
72
- chellow/reports/report_177.py,sha256=JPfmdJMxRqzV_jpXjH-ziuRMwdK5UIjBUhRETJ2PnvA,10505
73
- chellow/reports/report_181.py,sha256=Fm2kgqjZvTVU31H6KVBVg3bqcGPHomJG82KXa7I7k4E,4989
72
+ chellow/reports/report_181.py,sha256=mCL6vXfrj8r0kL-LilDS0SnSu5tJOzCq7eMJVx9zpEw,4999
74
73
  chellow/reports/report_183.py,sha256=DZX-hHJPl_Tbgkt31C_cuLZg_5L2b6iCPQ5foOZizR0,8817
75
74
  chellow/reports/report_187.py,sha256=UvpaYHjyJFNV5puYq8_KxfzoBtVrwFgIGUOmC5oGA9A,9956
76
- chellow/reports/report_215.py,sha256=_HZO_MhL51ttzmG6IrT8YVG6WmJMkRNWsXOW7AL7GWs,6766
77
75
  chellow/reports/report_219.py,sha256=o2eEg3mX9_raP2b4gjc2Gu4vqnMqcvvJBYQ1oQjxvpE,6637
78
76
  chellow/reports/report_231.py,sha256=gOb1AkXZQvwVpRg5cIenO7iR7Se1_zsWnJp9l2BlgpA,5008
79
77
  chellow/reports/report_233.py,sha256=DLAmkoHFKH-N8OEIoX4jeCxAaCfJK0e7ssjFJJpPqzw,4334
80
- chellow/reports/report_241.py,sha256=se2V1tb5scvH9-RRuGG3U-IbqUYd-BZcNFwvTRjkbsw,5625
78
+ chellow/reports/report_241.py,sha256=AlFmSHnfG2HWv_ICmWX7fNpPwLHjq7mo1QtOTjSKO3k,5384
81
79
  chellow/reports/report_247.py,sha256=ozgCcee8XeqYbOpZCyU2STJKaz6h2x7TYQogagTaYLw,46626
82
80
  chellow/reports/report_29.py,sha256=KDFjgrLBv4WbG9efCdu_geMR7gT_QV9N97Wfdt7aDc4,2736
83
81
  chellow/reports/report_291.py,sha256=rqBXy6s7hMeYWw-yNX6w_L5t2yz6rNEeos_4xO7wf90,7519
@@ -88,20 +86,20 @@ chellow/reports/report_429.py,sha256=0GCc0rQnOSG0fusw69yMMOCxnWApBd3P2sGWIg1py9M
88
86
  chellow/reports/report_59.py,sha256=21uNlcuYQCOAvrRir241HR-6SsuL9bWEPVCcU_jOxfE,44731
89
87
  chellow/reports/report_81.py,sha256=UYr0Dm4TmCCYUbIWw3hWNI0GdQRhH27vwVk3Ytdsrcg,5288
90
88
  chellow/reports/report_87.py,sha256=ZadBo40rUORN0Fi926-dDHeTFn6L-fBzp3b4k7v5MdY,6802
91
- chellow/reports/report_asset_comparison.py,sha256=6kHiPOL5fiZXtPyKXQ0fHvi1zYX51Frb4NMMDwLdWvc,6140
89
+ chellow/reports/report_asset_comparison.py,sha256=UN298MHuzyUDUiiZr7F_Ua6SrdVOlFLjgKjnIbrA-14,6118
92
90
  chellow/reports/report_batches.py,sha256=7O5TcB9W1sPKS0LQMsC6sUDzx73X5iyoZE0O3zK91zs,4798
93
91
  chellow/reports/report_bills.py,sha256=AHW6tiZAOE0gXDfencPvemE4zqK6eTqfN8_bWQ4RT5o,3323
94
- chellow/reports/report_csv_llfcs.py,sha256=XbVGcTbjLSrJL7zdgqIPX_FvN4Pa_lUSMDtkuFpPMBw,1934
92
+ chellow/reports/report_csv_llfcs.py,sha256=OHSbP64lQ6dlAMcQYgvdANlA4lQyF0iBlwk7V9c9nuo,1944
95
93
  chellow/reports/report_csv_site_hh_data.py,sha256=T-clGDmYdn0ej7zZfL3kDp4Vyd82WzptxEzxx9KqAZg,4270
96
- chellow/reports/report_csv_site_snags.py,sha256=XrJmIW0G4SyhpMJFZw1rqLom84wzLVqid44EBS0UT9s,2747
94
+ chellow/reports/report_csv_site_snags.py,sha256=gG2sYQrLoIBwCoMUC8rhmAL7Kffh_rvNb9UOX7cYDko,2668
97
95
  chellow/reports/report_ecoes_comparison.py,sha256=bTkbcvNB__UcNQtHncC41_Ql222vvPTJTnyVJbgjylM,21313
98
96
  chellow/reports/report_g_monthly_duration.py,sha256=vI5FKAU8_oThjR5oflPZont7Z7sVAunr0qlMfJsaPJI,12004
99
97
  chellow/reports/report_g_supplies_snapshot.py,sha256=0M0x_0o0985Hu45gUJJJwzfx0DDOAuXBJ1UVUDbQ36g,4718
100
98
  chellow/reports/report_g_supply_virtual_bill.py,sha256=x_KtQ02dwgmXvAEUXJ1poK0BRwqxa-GcbJ5pddEina0,3694
101
- chellow/reports/report_g_virtual_bills.py,sha256=17jKVjSrvQ91qE924InSzdxSebLo_QkvR2M1ugnRglo,4604
99
+ chellow/reports/report_g_virtual_bills.py,sha256=t3hmTiURk1E_mPucIboCdPBlSLapDIUdHYRpVTFtJgw,4569
102
100
  chellow/reports/report_g_virtual_bills_hh.py,sha256=5crgu7oUrOSR9GAA1uhb8lJNn4gCUSBAaXci1gOuohE,3457
103
- chellow/reports/report_sscs.py,sha256=0LQAe_Madtomvij8MgP8y6isVjFhu8zBDZQSN4j10Xs,3437
104
- chellow/reports/report_supply_contacts.py,sha256=v3gwCBfc_9k10T4ytasFOtRIPZVjKEDgrUwXW75ZPKs,3840
101
+ chellow/reports/report_sscs.py,sha256=beCzKpaQaeeiyMGpX6o-gbl1tCS6ArdF1o4GeIQE67s,3255
102
+ chellow/reports/report_supply_contacts.py,sha256=vyA3mLWqYISwOuRhzRIYQIWmvWXS1Vv_rJClq2IFBiw,3640
105
103
  chellow/static/css/chellow.css,sha256=mcLjqKMo0qtdQWY7AnXEL8Bvx2B-Pu8kcGO58bUXOpY,5372
106
104
  chellow/static/images/favicon.svg,sha256=ySFHoVJYmr-xU93QrE-jLYn-ZNythh2vsemnR8dkvg0,2339
107
105
  chellow/static/images/favicon_test.svg,sha256=HnLS_BjNt8M0Ikko5Z-f_E2aed7y6RRU6j3K6XADciE,2346
@@ -363,6 +361,6 @@ chellow/templates/g/supply_note_edit.html,sha256=6UQf_qbhFDys3cVsTp-c7ABWZpggW9R
363
361
  chellow/templates/g/supply_notes.html,sha256=WR3YwGh_qqTklSJ7JqWX6BKBc9rk_jMff4RiWZiF2CM,936
364
362
  chellow/templates/g/unit.html,sha256=KouNVU0-i84afANkLQ_heJ0uDfJ9H5A05PuLqb8iCN8,438
365
363
  chellow/templates/g/units.html,sha256=p5Nd-lAIboKPEOO6N451hx1bcKxMg4BDODnZ-43MmJc,441
366
- chellow-1710934022.0.0.dist-info/METADATA,sha256=sFNcimdKN1-J3ReZZu4ppgCiIflLbWFfK3GbXyojil4,12203
367
- chellow-1710934022.0.0.dist-info/WHEEL,sha256=bq9SyP5NxIRA9EpQgMCd-9RmPHWvbH-4lTDGwxgIR64,87
368
- chellow-1710934022.0.0.dist-info/RECORD,,
364
+ chellow-1712589951.0.0.dist-info/METADATA,sha256=7i4xrwuQ86GNArcYRVjMgd8c2k9pbTy5yW8XWSKvrTY,12203
365
+ chellow-1712589951.0.0.dist-info/WHEEL,sha256=as-1oFTWSeWBgyzh0O_qF439xqBe6AbBgt4MfYe5zwY,87
366
+ chellow-1712589951.0.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.22.3
2
+ Generator: hatchling 1.22.5
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,285 +0,0 @@
1
- import os
2
- import threading
3
- import time
4
- import traceback
5
- from datetime import datetime as Datetime
6
-
7
- from dateutil.relativedelta import relativedelta
8
-
9
- from flask import g, request
10
-
11
- import pytz
12
-
13
- from sqlalchemy import Float, cast, func, or_
14
- from sqlalchemy.orm import joinedload
15
- from sqlalchemy.sql.expression import null, true
16
-
17
- import chellow.computer
18
- import chellow.dloads
19
- from chellow.models import Bill, Channel, Era, HhDatum, Session, Site, SiteEra, Supply
20
- from chellow.utils import HH, hh_format, hh_max, hh_min, req_int
21
- from chellow.views import chellow_redirect
22
-
23
-
24
- def _content(sess, tmp_file, year, month, months, supply_id, user):
25
- supplies = (
26
- sess.query(Supply)
27
- .join(Era)
28
- .distinct()
29
- .options(joinedload(Supply.generator_type))
30
- )
31
-
32
- if supply_id is not None:
33
- supply = Supply.get_by_id(sess, supply_id)
34
- supplies = supplies.filter(Supply.id == supply.id)
35
-
36
- caches = {}
37
-
38
- start_date = Datetime(year, month, 1, tzinfo=pytz.utc) - relativedelta(
39
- months=months - 1
40
- )
41
-
42
- field_names = (
43
- "supply-name",
44
- "source-code",
45
- "generator-type",
46
- "month",
47
- "pc-code",
48
- "msn",
49
- "site-code",
50
- "site-name",
51
- "metering-type",
52
- "import-mpan-core",
53
- "metered-import-kwh",
54
- "metered-import-net-gbp",
55
- "metered-import-estimated-kwh",
56
- "billed-import-kwh",
57
- "billed-import-net-gbp",
58
- "export-mpan-core",
59
- "metered-export-kwh",
60
- "metered-export-estimated-kwh",
61
- "billed-export-kwh",
62
- "billed-export-net-gbp",
63
- "problem",
64
- "timestamp",
65
- )
66
-
67
- tmp_file.write("supply-id," + ",".join(field_names) + "\n")
68
-
69
- forecast_date = chellow.computer.forecast_date()
70
-
71
- for i in range(months):
72
- month_start = start_date + relativedelta(months=i)
73
- month_finish = month_start + relativedelta(months=1) - HH
74
-
75
- for supply in supplies.filter(
76
- Era.start_date <= month_finish,
77
- or_(Era.finish_date == null(), Era.finish_date >= month_start),
78
- ):
79
- generator_type = supply.generator_type
80
- if generator_type is None:
81
- generator_type = ""
82
- else:
83
- generator_type = generator_type.code
84
-
85
- source_code = supply.source.code
86
- eras = supply.find_eras(sess, month_start, month_finish)
87
- era = eras[-1]
88
- metering_type = era.meter_category
89
-
90
- site = (
91
- sess.query(Site)
92
- .join(SiteEra)
93
- .filter(SiteEra.era == era, SiteEra.is_physical == true())
94
- .one()
95
- )
96
-
97
- values = {
98
- "supply-name": supply.name,
99
- "source-code": source_code,
100
- "generator-type": generator_type,
101
- "month": hh_format(month_finish),
102
- "pc-code": era.pc.code,
103
- "msn": era.msn,
104
- "site-code": site.code,
105
- "site-name": site.name,
106
- "metering-type": metering_type,
107
- "problem": "",
108
- }
109
-
110
- tmp_file.write(str(supply.id) + ",")
111
-
112
- for is_import, pol_name in [(True, "import"), (False, "export")]:
113
- if is_import:
114
- mpan_core = era.imp_mpan_core
115
- else:
116
- mpan_core = era.exp_mpan_core
117
-
118
- values[pol_name + "-mpan-core"] = mpan_core
119
- kwh = 0
120
- est_kwh = 0
121
-
122
- if metering_type in ["hh", "amr"]:
123
- est_kwh = (
124
- sess.query(HhDatum.value)
125
- .join(Channel)
126
- .join(Era)
127
- .filter(
128
- HhDatum.status == "E",
129
- Era.supply_id == supply.id,
130
- Channel.channel_type == "ACTIVE",
131
- Channel.imp_related == is_import,
132
- HhDatum.start_date >= month_start,
133
- HhDatum.start_date <= month_finish,
134
- )
135
- .first()
136
- )
137
- if est_kwh is None:
138
- est_kwh = 0
139
- else:
140
- est_kwh = est_kwh[0]
141
-
142
- if not (is_import and source_code in ("net", "gen-net")):
143
- kwh_sum = (
144
- sess.query(cast(func.sum(HhDatum.value), Float))
145
- .join(Channel)
146
- .join(Era)
147
- .filter(
148
- Era.supply_id == supply.id,
149
- Channel.channel_type == "ACTIVE",
150
- Channel.imp_related == is_import,
151
- HhDatum.start_date >= month_start,
152
- HhDatum.start_date <= month_finish,
153
- )
154
- .one()[0]
155
- )
156
- if kwh_sum is not None:
157
- kwh += kwh_sum
158
-
159
- values["metered-" + pol_name + "-estimated-kwh"] = est_kwh
160
- values["metered-" + pol_name + "-kwh"] = kwh
161
- values["metered-" + pol_name + "-net-gbp"] = 0
162
- values["billed-" + pol_name + "-kwh"] = 0
163
- values["billed-" + pol_name + "-net-gbp"] = 0
164
- values["billed-" + pol_name + "-apportioned-kwh"] = 0
165
- values["billed-" + pol_name + "-apportioned-net-gbp"] = 0
166
- values["billed-" + pol_name + "-raw-kwh"] = 0
167
- values["billed-" + pol_name + "-raw-net-gbp"] = 0
168
-
169
- for bill in sess.query(Bill).filter(
170
- Bill.supply == supply,
171
- Bill.start_date <= month_finish,
172
- Bill.finish_date >= month_start,
173
- ):
174
- bill_start = bill.start_date
175
- bill_finish = bill.finish_date
176
- bill_duration = (bill_finish - bill_start).total_seconds() + 30 * 60
177
- overlap_duration = (
178
- min(bill_finish, month_finish) - max(bill_start, month_start)
179
- ).total_seconds() + 30 * 60
180
- overlap_proportion = float(overlap_duration) / float(bill_duration)
181
- values["billed-import-net-gbp"] += overlap_proportion * float(bill.net)
182
- values["billed-import-kwh"] += overlap_proportion * float(bill.kwh)
183
-
184
- for era in eras:
185
- chunk_start = hh_max(era.start_date, month_start)
186
- chunk_finish = hh_min(era.finish_date, month_finish)
187
-
188
- import_mpan_core = era.imp_mpan_core
189
- if import_mpan_core is None:
190
- continue
191
-
192
- supplier_contract = era.imp_supplier_contract
193
-
194
- if source_code in ["net", "gen-net", "3rd-party"]:
195
- supply_source = chellow.computer.SupplySource(
196
- sess,
197
- chunk_start,
198
- chunk_finish,
199
- forecast_date,
200
- era,
201
- True,
202
- caches,
203
- )
204
-
205
- values["metered-import-kwh"] += sum(
206
- datum["msp-kwh"] for datum in supply_source.hh_data
207
- )
208
-
209
- import_vb_function = supply_source.contract_func(
210
- supplier_contract, "virtual_bill"
211
- )
212
- if import_vb_function is None:
213
- values["problem"] += (
214
- "Can't find the "
215
- "virtual_bill function in the supplier "
216
- "contract. "
217
- )
218
- else:
219
- import_vb_function(supply_source)
220
- values["metered-import-net-gbp"] += supply_source.supplier_bill[
221
- "net-gbp"
222
- ]
223
-
224
- supply_source.contract_func(era.dc_contract, "virtual_bill")(
225
- supply_source
226
- )
227
- values["metered-import-net-gbp"] += supply_source.dc_bill["net-gbp"]
228
-
229
- mop_func = supply_source.contract_func(
230
- era.mop_contract, "virtual_bill"
231
- )
232
- if mop_func is None:
233
- values["problem"] += (
234
- " MOP virtual_bill " "function can't be found."
235
- )
236
- else:
237
- mop_func(supply_source)
238
- mop_bill = supply_source.mop_bill
239
- values["metered-import-net-gbp"] += mop_bill["net-gbp"]
240
- if len(mop_bill["problem"]) > 0:
241
- values["problem"] += (
242
- " MOP virtual bill problem: " + mop_bill["problem"]
243
- )
244
-
245
- values["timestamp"] = int(time.time() * 1000)
246
- tmp_file.write(
247
- ",".join('"' + str(values[name]) + '"' for name in field_names) + "\n"
248
- )
249
-
250
-
251
- def content(year, month, months, supply_id, user):
252
- tmp_file = None
253
- try:
254
- with Session() as sess:
255
- if supply_id is None:
256
- base_name = (
257
- f"supplies_monthly_duration_for_all_supplies_for_{months}_to_"
258
- f"{year}_{month}.csv"
259
- )
260
- else:
261
- base_name = (
262
- f"supplies_monthly_duration_for_{supply_id}_{months}_to_{year}_"
263
- f"{month}.csv"
264
- )
265
- running_name, finished_name = chellow.dloads.make_names(base_name, user)
266
-
267
- tmp_file = open(running_name, "w")
268
- _content(sess, tmp_file, year, month, months, supply_id, user)
269
- except BaseException:
270
- tmp_file.write(traceback.format_exc())
271
- finally:
272
- tmp_file.close()
273
- os.rename(running_name, finished_name)
274
-
275
-
276
- def do_get(sess):
277
- year = req_int("end_year")
278
- month = req_int("end_month")
279
- months = req_int("months")
280
- supply_id = req_int("supply_id") if "supply_id" in request.values else None
281
- user = g.user
282
- threading.Thread(
283
- target=content, args=(year, month, months, supply_id, user)
284
- ).start()
285
- return chellow_redirect("/downloads", 303)
@@ -1,176 +0,0 @@
1
- import csv
2
- import os
3
- import sys
4
- import threading
5
- import traceback
6
- from datetime import datetime
7
-
8
- from flask import g, request
9
-
10
- import pytz
11
-
12
- from sqlalchemy import or_
13
- from sqlalchemy.sql.expression import null, true
14
-
15
- import chellow.dloads
16
- from chellow.models import Era, Session, Site, SiteEra, Source, Supply
17
- from chellow.utils import hh_after, hh_before, prev_hh, req_int
18
- from chellow.views import chellow_redirect
19
-
20
-
21
- def content(year, supply_id, user):
22
- f = writer = None
23
- try:
24
- with Session() as sess:
25
- running_name, finished_name = chellow.dloads.make_names(
26
- "crc_special_events.csv", user
27
- )
28
- f = open(running_name, mode="w", newline="")
29
- writer = csv.writer(f, lineterminator="\n")
30
- writer.writerow(("MPAN Core", "Site Id", "Site Name", "Date", "Event"))
31
-
32
- year_start = datetime(year, 4, 1, tzinfo=pytz.utc)
33
- year_finish = prev_hh(datetime(year + 1, 4, 1, tzinfo=pytz.utc))
34
-
35
- def add_event(events, date, code, era=None, mpan_core=None):
36
- if era is None:
37
- mpan_cores = [mpan_core]
38
- else:
39
- mpan_cores = []
40
- if era.imp_mpan_core is not None:
41
- mpan_cores.append(era.imp_mpan_core)
42
- if era.exp_mpan_core is not None:
43
- mpan_cores.append(era.exp_mpan_core)
44
-
45
- for mpan_core in mpan_cores:
46
- events.append({"date": date, "code": code, "mpan-core": mpan_core})
47
-
48
- if supply_id is None:
49
- supplies = (
50
- sess.query(Supply)
51
- .join(Source)
52
- .join(Era)
53
- .filter(
54
- Source.code.in_(("net", "gen-net", "gen")),
55
- Era.start_date <= year_finish,
56
- or_(Era.finish_date == null(), Era.finish_date >= year_start),
57
- )
58
- .distinct()
59
- )
60
- else:
61
- supply = Supply.get_by_id(supply_id)
62
- supplies = sess.query(Supply).filter(Supply.id == supply.id)
63
-
64
- for supply in supplies:
65
- eras = (
66
- sess.query(Era)
67
- .filter(
68
- Era.supply == supply,
69
- Era.start_date <= year_finish,
70
- or_(Era.finish_date == null(), Era.finish_date >= year_start),
71
- )
72
- .order_by(Era.start_date)
73
- .all()
74
- )
75
- events = []
76
- first_era = eras[0]
77
- first_era_start = first_era.start_date
78
- if hh_after(first_era_start, year_start):
79
- add_event(events, first_era_start, "New Supply", first_era)
80
-
81
- last_era = eras[-1]
82
- last_era_finish = last_era.finish_date
83
- if hh_before(last_era_finish, year_finish):
84
- add_event(events, last_era_finish, "Disconnection", last_era)
85
-
86
- prev_era = first_era
87
- for era in eras[1:]:
88
- if era.msn != prev_era.msn:
89
- add_event(events, era.start_date, "Meter Change", era)
90
- if era.pc.code != prev_era.pc.code:
91
- add_event(
92
- events, era.start_date, "Change Of Profile Class", era
93
- )
94
-
95
- if era.mop_contract_id != prev_era.mop_contract_id:
96
- add_event(events, era.start_date, "Change Of MOP", era)
97
-
98
- if era.dc_contract_id != prev_era.dc_contract_id:
99
- add_event(events, era.start_date, "Change Of DC", era)
100
-
101
- for is_import in [True, False]:
102
- if era.imp_mpan_core is None:
103
- mpan_core = era.exp_mpan_core
104
- else:
105
- mpan_core = era.imp_mpan_core
106
-
107
- if is_import:
108
- cur_sup = era.imp_supplier_contract
109
- prev_sup = prev_era.imp_supplier_contract
110
- else:
111
- cur_sup = era.exp_supplier_contract
112
- prev_sup = prev_era.exp_supplier_contract
113
-
114
- if cur_sup is None and prev_sup is not None:
115
- add_event(
116
- events, era.start_date, "End of supply", mpan_core
117
- )
118
- elif cur_sup is not None and prev_sup is None:
119
- add_event(
120
- events,
121
- era.start_date,
122
- "Start of supply",
123
- None,
124
- mpan_core,
125
- )
126
- elif (
127
- cur_sup is not None
128
- and prev_sup is not None
129
- and cur_sup != prev_sup
130
- ):
131
- add_event(
132
- events,
133
- era.start_date,
134
- "Change Of Supplier",
135
- None,
136
- mpan_core,
137
- )
138
-
139
- prev_era = era
140
-
141
- if len(events) > 0:
142
- site = (
143
- sess.query(Site)
144
- .join(SiteEra)
145
- .filter(SiteEra.is_physical == true(), SiteEra.era == last_era)
146
- .one()
147
- )
148
-
149
- for event in events:
150
- vals = [
151
- event["mpan-core"],
152
- site.code,
153
- site.name,
154
- event["date"].strftime("%Y-%m-%d %H:%M"),
155
- event["code"],
156
- ]
157
- writer.writerow(vals)
158
-
159
- # Avoid a long-running transaction
160
- sess.rollback()
161
- except BaseException:
162
- msg = traceback.format_exc()
163
- sys.stderr.write(msg)
164
- writer.writerow([msg])
165
- finally:
166
- if f is not None:
167
- f.close()
168
- os.rename(running_name, finished_name)
169
-
170
-
171
- def do_get(sess):
172
- year = req_int("year")
173
- supply_id = req_int("supply_id") if "supply_id" in request.values else None
174
-
175
- threading.Thread(target=content, args=(year, supply_id, g.user)).start()
176
- return chellow_redirect("/downloads", 303)