reports.photovoltaicpowerstationitemcarbon   B
last analyzed

Complexity

Total Complexity 45

Size/Duplication

Total Lines 333
Duplicated Lines 87.09 %

Importance

Changes 0
Metric Value
wmc 45
eloc 212
dl 290
loc 333
rs 8.8
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A Reporting.__init__() 3 3 1
F Reporting.on_get() 271 271 43
A Reporting.on_options() 4 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like reports.photovoltaicpowerstationitemcarbon often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""
2
Photovoltaic Power Station Item Carbon Report API
3
4
This module provides REST API endpoints for generating photovoltaic power station item carbon emissions reports.
5
It analyzes carbon dioxide emissions from specific photovoltaic power station items, providing insights
6
into environmental impact and carbon footprint reduction opportunities.
7
8
Key Features:
9
- Photovoltaic power station item carbon emissions analysis
10
- Base period vs reporting period comparison
11
- Time-of-use carbon breakdown
12
- Carbon footprint calculations
13
- Environmental impact assessment
14
- Reduction opportunity analysis
15
16
Report Components:
17
- Photovoltaic power station item carbon emissions summary
18
- Base period comparison data
19
- Time-of-use carbon breakdown
20
- Carbon footprint metrics
21
- Environmental impact indicators
22
- Reduction opportunity analysis
23
24
The module uses Falcon framework for REST API and includes:
25
- Database queries for carbon data
26
- Carbon emission calculations
27
- Time-of-use analysis
28
- Multi-language support
29
- User authentication and authorization
30
"""
31
32
import re
33
from datetime import datetime, timedelta, timezone
34
from decimal import Decimal
35
import falcon
36
import mysql.connector
37
import simplejson as json
38
from core import utilities
39
import config
40
from core.useractivity import access_control, api_key_control
41
42
43 View Code Duplication
class Reporting:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
44
    def __init__(self):
45
        """Initializes Class"""
46
        pass
47
48
    @staticmethod
49
    def on_options(req, resp):
50
        _ = req
51
        resp.status = falcon.HTTP_200
52
53
    ####################################################################################################################
54
    # PROCEDURES
55
    # Step 1: valid parameters
56
    # Step 2: query the energy storage power station
57
    # Step 3: query generation billing data in 7 days
58
    # Step 5: query generation billing data in this month
59
    # Step 7: query generation billing data in this year
60
    # Step 9: construct the report
61
    ####################################################################################################################
62
    @staticmethod
63
    def on_get(req, resp):
64
        if 'API-KEY' not in req.headers or \
65
                not isinstance(req.headers['API-KEY'], str) or \
66
                len(str.strip(req.headers['API-KEY'])) == 0:
67
            access_control(req)
68
        else:
69
            api_key_control(req)
70
        # this procedure accepts energy storage power station id or uuid
71
        photovoltaic_power_station_id = req.params.get('id')
72
        photovoltaic_power_station_uuid = req.params.get('uuid')
73
74
        ################################################################################################################
75
        # Step 1: valid parameters
76
        ################################################################################################################
77
        if photovoltaic_power_station_id is None and photovoltaic_power_station_uuid is None:
78
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
79
                                   description='API.INVALID_photovoltaic_POWER_STATION_ID')
80
81
        if photovoltaic_power_station_id is not None:
82
            photovoltaic_power_station_id = str.strip(photovoltaic_power_station_id)
83
            if not photovoltaic_power_station_id.isdigit() or int(photovoltaic_power_station_id) <= 0:
84
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
85
                                       description='API.INVALID_photovoltaic_POWER_STATION_ID')
86
87
        if photovoltaic_power_station_uuid is not None:
88
            regex = re.compile(r'^[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}\Z', re.I)
89
            match = regex.match(str.strip(photovoltaic_power_station_uuid))
90
            if not bool(match):
91
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
92
                                       description='API.INVALID_photovoltaic_POWER_STATION_UUID')
93
94
        ################################################################################################################
95
        # Step 2: query the energy storage power station
96
        ################################################################################################################
97
        cnx_system_db = mysql.connector.connect(**config.myems_system_db)
98
        cursor_system_db = cnx_system_db.cursor()
99
        # Get Spaces associated with energy storage power stations
100
        query = (" SELECT se.photovoltaic_power_station_id, s.name "
101
                 " FROM tbl_spaces s, tbl_spaces_photovoltaic_power_stations se "
102
                 " WHERE se.space_id = s.id ")
103
        cursor_system_db.execute(query)
104
        rows_spaces = cursor_system_db.fetchall()
105
106
        space_dict = dict()
107
        if rows_spaces is not None and len(rows_spaces) > 0:
108
            for row in rows_spaces:
109
                space_dict[row[0]] = row[1]
110
        print(space_dict)
111
        # Get energy storage power station
112
        if photovoltaic_power_station_id is not None:
113
            query = (" SELECT id, name, uuid, "
114
                     "        address, latitude, longitude, rated_capacity, rated_power, "
115
                     "        contact_id, cost_center_id "
116
                     " FROM tbl_photovoltaic_power_stations "
117
                     " WHERE id = %s ")
118
            cursor_system_db.execute(query, (photovoltaic_power_station_id,))
119
            row = cursor_system_db.fetchone()
120
        elif photovoltaic_power_station_uuid is not None:
121
            query = (" SELECT id, name, uuid, "
122
                     "        address, latitude, longitude, rated_capacity, rated_power, "
123
                     "        contact_id, cost_center_id "
124
                     " FROM tbl_photovoltaic_power_stations "
125
                     " WHERE uuid = %s ")
126
            cursor_system_db.execute(query, (photovoltaic_power_station_uuid,))
127
            row = cursor_system_db.fetchone()
128
129
        if row is None:
0 ignored issues
show
introduced by
The variable row does not seem to be defined for all execution paths.
Loading history...
130
            cursor_system_db.close()
131
            cnx_system_db.close()
132
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
133
                                   description='API.photovoltaic_POWER_STATION_NOT_FOUND')
134
        else:
135
            photovoltaic_power_station_id = row[0]
136
            photovoltaic_power_station = {
137
                "id": row[0],
138
                "name": row[1],
139
                "uuid": row[2],
140
                "address": row[3],
141
                "space_name": space_dict.get(row[0]),
142
                "latitude": row[4],
143
                "longitude": row[5],
144
                "rated_capacity": row[6],
145
                "rated_power": row[7]
146
            }
147
148
        ################################################################################################################
149
        # Step 3: query generation billing data in 7 days
150
        ################################################################################################################
151
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
152
        if config.utc_offset[0] == '-':
153
            timezone_offset = -timezone_offset
154
        reporting = dict()
155
        reporting['generation_7_days'] = dict()
156
        reporting['generation_this_month'] = dict()
157
        reporting['generation_this_year'] = dict()
158
159
        end_datetime_utc = datetime.utcnow()
160
        end_datetime_local = datetime.utcnow() + timedelta(minutes=timezone_offset)
161
        period_type = 'daily'
162
        start_datetime_local = end_datetime_local.replace(hour=0, minute=0, second=0, microsecond=0) - timedelta(days=6)
163
        start_datetime_utc = start_datetime_local - timedelta(minutes=timezone_offset)
164
        print('start_datetime_local:' + start_datetime_local.isoformat())
165
        print('end_datetime_local:' + end_datetime_local.isoformat())
166
        print('start_datetime_utc:' + start_datetime_utc.isoformat())
167
        print('end_datetime_utc:' + end_datetime_utc.isoformat())
168
169
        cnx_billing_db = mysql.connector.connect(**config.myems_billing_db)
170
        cursor_billing_db = cnx_billing_db.cursor()
171
172
        reporting['generation_7_days'] = dict()
173
        reporting['generation_7_days']['timestamps_array'] = list()
174
        reporting['generation_7_days']['values_array'] = list()
175
176
        timestamps = list()
177
        values = list()
178
        query = (" SELECT start_datetime_utc, actual_value "
179
                 " FROM tbl_photovoltaic_power_station_generation_hourly "
180
                 " WHERE photovoltaic_power_station_id = %s "
181
                 " AND start_datetime_utc >= %s "
182
                 " AND start_datetime_utc < %s "
183
                 " ORDER BY start_datetime_utc ")
184
        cursor_billing_db.execute(query, (photovoltaic_power_station_id, start_datetime_utc, end_datetime_utc))
185
        rows_generation_hourly = cursor_billing_db.fetchall()
186
187
        rows_generation_periodically = utilities.aggregate_hourly_data_by_period(rows_generation_hourly,
188
                                                                                 start_datetime_utc,
189
                                                                                 end_datetime_utc,
190
                                                                                 period_type)
191
        for row_generation_periodically in rows_generation_periodically:
192
            current_datetime_local = row_generation_periodically[0].replace(tzinfo=timezone.utc) + \
193
                                     timedelta(minutes=timezone_offset)
194
            if period_type == 'hourly':
195
                current_datetime = current_datetime_local.isoformat()[0:19]
196
            elif period_type == 'daily':
197
                current_datetime = current_datetime_local.isoformat()[0:10]
198
            elif period_type == 'weekly':
199
                current_datetime = current_datetime_local.isoformat()[0:10]
200
            elif period_type == 'monthly':
201
                current_datetime = current_datetime_local.isoformat()[0:7]
202
            elif period_type == 'yearly':
203
                current_datetime = current_datetime_local.isoformat()[0:4]
204
205
            actual_value = Decimal(0.0) if row_generation_periodically[1] is None else row_generation_periodically[1]
206
            timestamps.append(current_datetime)
0 ignored issues
show
introduced by
The variable current_datetime does not seem to be defined for all execution paths.
Loading history...
207
            values.append(actual_value)
208
        reporting['generation_7_days']['timestamps_array'].append(timestamps)
209
        reporting['generation_7_days']['values_array'].append(values)
210
211
        ################################################################################################################
212
        # Step 5: query generation billing data in this month
213
        ################################################################################################################
214
        end_datetime_utc = datetime.utcnow()
215
        end_datetime_local = datetime.utcnow() + timedelta(minutes=timezone_offset)
216
        period_type = 'daily'
217
        start_datetime_local = end_datetime_local.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
218
        start_datetime_utc = start_datetime_local - timedelta(minutes=timezone_offset)
219
        print('start_datetime_local:' + start_datetime_local.isoformat())
220
        print('end_datetime_local:' + end_datetime_local.isoformat())
221
        print('start_datetime_utc:' + start_datetime_utc.isoformat())
222
        print('end_datetime_utc:' + end_datetime_utc.isoformat())
223
224
        reporting['generation_this_month'] = dict()
225
        reporting['generation_this_month']['timestamps_array'] = list()
226
        reporting['generation_this_month']['values_array'] = list()
227
228
        timestamps = list()
229
        values = list()
230
        query = (" SELECT start_datetime_utc, actual_value "
231
                 " FROM tbl_photovoltaic_power_station_generation_hourly "
232
                 " WHERE photovoltaic_power_station_id = %s "
233
                 " AND start_datetime_utc >= %s "
234
                 " AND start_datetime_utc < %s "
235
                 " ORDER BY start_datetime_utc ")
236
        cursor_billing_db.execute(query, (photovoltaic_power_station['id'], start_datetime_utc, end_datetime_utc))
237
        rows_generation_hourly = cursor_billing_db.fetchall()
238
239
        rows_generation_periodically = utilities.aggregate_hourly_data_by_period(rows_generation_hourly,
240
                                                                                 start_datetime_utc,
241
                                                                                 end_datetime_utc,
242
                                                                                 period_type)
243
244
        for row_generation_periodically in rows_generation_periodically:
245
            current_datetime_local = row_generation_periodically[0].replace(tzinfo=timezone.utc) + \
246
                                     timedelta(minutes=timezone_offset)
247
            if period_type == 'hourly':
248
                current_datetime = current_datetime_local.isoformat()[0:19]
249
            elif period_type == 'daily':
250
                current_datetime = current_datetime_local.isoformat()[0:10]
251
            elif period_type == 'weekly':
252
                current_datetime = current_datetime_local.isoformat()[0:10]
253
            elif period_type == 'monthly':
254
                current_datetime = current_datetime_local.isoformat()[0:7]
255
            elif period_type == 'yearly':
256
                current_datetime = current_datetime_local.isoformat()[0:4]
257
258
            actual_value = Decimal(0.0) if row_generation_periodically[1] is None else row_generation_periodically[1]
259
            timestamps.append(current_datetime)
260
            values.append(actual_value)
261
        reporting['generation_this_month']['timestamps_array'].append(timestamps)
262
        reporting['generation_this_month']['values_array'].append(values)
263
264
        ################################################################################################################
265
        # Step 7: query generation billing data in this year
266
        ################################################################################################################
267
        end_datetime_utc = datetime.utcnow()
268
        end_datetime_local = datetime.utcnow() + timedelta(minutes=timezone_offset)
269
        period_type = 'monthly'
270
        start_datetime_local = end_datetime_local.replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0)
271
        start_datetime_utc = start_datetime_local - timedelta(minutes=timezone_offset)
272
        print('start_datetime_local:' + start_datetime_local.isoformat())
273
        print('end_datetime_local:' + end_datetime_local.isoformat())
274
        print('start_datetime_utc:' + start_datetime_utc.isoformat())
275
        print('end_datetime_utc:' + end_datetime_utc.isoformat())
276
277
        reporting['generation_this_year'] = dict()
278
        reporting['generation_this_year']['timestamps_array'] = list()
279
        reporting['generation_this_year']['values_array'] = list()
280
281
        timestamps = list()
282
        values = list()
283
        query = (" SELECT start_datetime_utc, actual_value "
284
                 " FROM tbl_photovoltaic_power_station_generation_hourly "
285
                 " WHERE photovoltaic_power_station_id = %s "
286
                 " AND start_datetime_utc >= %s "
287
                 " AND start_datetime_utc < %s "
288
                 " ORDER BY start_datetime_utc ")
289
        cursor_billing_db.execute(query, (photovoltaic_power_station['id'], start_datetime_utc, end_datetime_utc))
290
        rows_generation_hourly = cursor_billing_db.fetchall()
291
292
        rows_generation_periodically = utilities.aggregate_hourly_data_by_period(rows_generation_hourly,
293
                                                                                 start_datetime_utc,
294
                                                                                 end_datetime_utc,
295
                                                                                 period_type)
296
        for row_generation_periodically in rows_generation_periodically:
297
            current_datetime_local = row_generation_periodically[0].replace(tzinfo=timezone.utc) + \
298
                                     timedelta(minutes=timezone_offset)
299
            if period_type == 'hourly':
300
                current_datetime = current_datetime_local.isoformat()[0:19]
301
            elif period_type == 'daily':
302
                current_datetime = current_datetime_local.isoformat()[0:10]
303
            elif period_type == 'weekly':
304
                current_datetime = current_datetime_local.isoformat()[0:10]
305
            elif period_type == 'monthly':
306
                current_datetime = current_datetime_local.isoformat()[0:7]
307
            elif period_type == 'yearly':
308
                current_datetime = current_datetime_local.isoformat()[0:4]
309
310
            actual_value = Decimal(0.0) if row_generation_periodically[1] is None else row_generation_periodically[1]
311
            timestamps.append(current_datetime)
312
            values.append(actual_value)
313
        reporting['generation_this_year']['timestamps_array'].append(timestamps)
314
        reporting['generation_this_year']['values_array'].append(values)
315
316
        ################################################################################################################
317
        # Step 9: construct the report
318
        ################################################################################################################
319
        if cursor_system_db:
320
            cursor_system_db.close()
321
        if cnx_system_db:
322
            cnx_system_db.close()
323
324
        if cursor_billing_db:
325
            cursor_billing_db.close()
326
        if cnx_billing_db:
327
            cnx_billing_db.close()
328
329
        result = dict()
330
        result['photovoltaic_power_station'] = photovoltaic_power_station
331
        result['reporting'] = reporting
332
        resp.text = json.dumps(result)
333