reports.offlinemeterdaily.Reporting.on_get()   F
last analyzed

Complexity

Conditions 32

Size

Total Lines 158
Code Lines 116

Duplication

Lines 28
Ratio 17.72 %

Importance

Changes 0
Metric Value
eloc 116
dl 28
loc 158
rs 0
c 0
b 0
f 0
cc 32
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like reports.offlinemeterdaily.Reporting.on_get() 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
Offline Meter Daily Report API
3
4
This module provides REST API endpoints for generating offline meter daily reports.
5
It analyzes daily energy consumption data from offline meters to provide
6
insights into daily usage patterns and performance trends.
7
8
Key Features:
9
- Offline meter daily energy consumption analysis
10
- Daily consumption pattern identification
11
- Daily performance metrics calculation
12
- Daily trend analysis
13
- Performance monitoring
14
- Daily optimization insights
15
16
Report Components:
17
- Offline meter daily consumption summary
18
- Daily consumption patterns
19
- Daily performance metrics
20
- Daily trend analysis
21
- Performance indicators
22
- Daily optimization recommendations
23
24
The module uses Falcon framework for REST API and includes:
25
- Database queries for daily data
26
- Daily analysis algorithms
27
- Performance monitoring tools
28
- Multi-language support
29
- User authentication and authorization
30
"""
31
32
import re
33
import falcon
34
import simplejson as json
35
import mysql.connector
36
import config
37
from datetime import datetime, timedelta, timezone
38
from core.useractivity import access_control, api_key_control
39
from decimal import Decimal
40
41
42
class Reporting:
43
    def __init__(self):
44
        """"Initializes Reporting"""
45
        pass
46
47
    @staticmethod
48
    def on_options(req, resp):
49
        _ = req
50
        resp.status = falcon.HTTP_200
51
52
    ####################################################################################################################
53
    # PROCEDURES
54
    # Step 1: valid parameters
55
    # Step 2: query the offline meter
56
    # Step 3: query associated points
57
    # Step 4: query reporting period points trends
58
    # Step 5: query tariff data
59
    # Step 6: construct the report
60
    ####################################################################################################################
61
    @staticmethod
62
    def on_get(req, resp):
63
        if 'API-KEY' not in req.headers or \
64
                not isinstance(req.headers['API-KEY'], str) or \
65
                len(str.strip(req.headers['API-KEY'])) == 0:
66
            access_control(req)
67
        else:
68
            api_key_control(req)
69
        print(req.params)
70
        offline_meter_id = req.params.get('offlinemeterid')
71
        offline_meter_uuid = req.params.get('offlinemeteruuid')
72
        reporting_period_start_datetime_local = req.params.get('reportingperiodstartdatetime')
73
        reporting_period_end_datetime_local = req.params.get('reportingperiodenddatetime')
74
75
        ################################################################################################################
76
        # Step 1: valid parameters
77
        ################################################################################################################
78
        if offline_meter_id is None and offline_meter_uuid is None:
79
            raise falcon.HTTPError(status=falcon.HTTP_400,
80
                                   title='API.BAD_REQUEST',
81
                                   description='API.INVALID_OFFLINE_METER_ID')
82
83
        if offline_meter_id is not None:
84
            offline_meter_id = str.strip(offline_meter_id)
85
            if not offline_meter_id.isdigit() or int(offline_meter_id) <= 0:
86
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
87
                                       description='API.INVALID_OFFLINE_METER_ID')
88
89
        if offline_meter_uuid is not None:
90
            regex = re.compile('^[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)
91
            match = regex.match(str.strip(offline_meter_uuid))
92
            if not bool(match):
93
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
94
                                       description='API.INVALID_OFFLINE_METER_UUID')
95
96
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
97
        if config.utc_offset[0] == '-':
98
            timezone_offset = -timezone_offset
99
100 View Code Duplication
        if reporting_period_start_datetime_local is None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
101
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
102
                                   description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
103
        else:
104
            reporting_period_start_datetime_local = str.strip(reporting_period_start_datetime_local)
105
            try:
106
                reporting_start_datetime_utc = datetime.strptime(reporting_period_start_datetime_local,
107
                                                                 '%Y-%m-%dT%H:%M:%S')
108
            except ValueError:
109
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
110
                                       description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
111
            reporting_start_datetime_utc = reporting_start_datetime_utc.replace(tzinfo=timezone.utc) - \
112
                timedelta(minutes=timezone_offset)
113
114
        if reporting_period_end_datetime_local is None:
115
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
116
                                   description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
117
        else:
118
            reporting_period_end_datetime_local = str.strip(reporting_period_end_datetime_local)
119
            try:
120
                reporting_end_datetime_utc = datetime.strptime(reporting_period_end_datetime_local,
121
                                                               '%Y-%m-%dT%H:%M:%S')
122
            except ValueError:
123
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
124
                                       description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
125
            reporting_end_datetime_utc = reporting_end_datetime_utc.replace(tzinfo=timezone.utc) - \
126
                timedelta(minutes=timezone_offset)
127
128
        if reporting_start_datetime_utc >= reporting_end_datetime_utc:
129
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
130
                                   description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
131
132
        ################################################################################################################
133
        # Step 2: query the offline meter
134
        ################################################################################################################
135
        cnx_system = mysql.connector.connect(**config.myems_system_db)
136
        cursor_system = cnx_system.cursor()
137
138
        cnx_energy = mysql.connector.connect(**config.myems_energy_db)
139
        cursor_historical = cnx_energy.cursor()
140
        if offline_meter_id is not None:
141
            cursor_system.execute(" SELECT id, name   "
142
                                  " FROM  tbl_offline_meters  "
143
                                  " WHERE id = %s ", (offline_meter_id,))
144
            row_offline_meter = cursor_system.fetchone()
145
        if row_offline_meter is None:
0 ignored issues
show
introduced by
The variable row_offline_meter does not seem to be defined in case offline_meter_id is not None on line 140 is False. Are you sure this can never be the case?
Loading history...
146
            if cursor_system:
147
                cursor_system.close()
148
            if cnx_system:
149
                cnx_system.disconnect()
150
151
            if cursor_historical:
152
                cursor_historical.close()
153
            if cnx_energy:
154
                cnx_energy.disconnect()
155
            raise falcon.HTTPError(status=falcon.HTTP_404,
156
                                   title='API.NOT_FOUND',
157
                                   description='API.OFFLINE_METER_NOT_FOUND')
158
159
        #######################################################
160
        # Step 4: query reporting period points trends
161
        #######################################################
162
        reporting_date_list = list()
163
        reporting_daily_values = list()
164
165
        query = (" SELECT start_datetime_utc, actual_value "
166
                 " FROM tbl_offline_meter_hourly "
167
                 " WHERE offline_meter_id = %s "
168
                 " AND start_datetime_utc >= %s "
169
                 " AND start_datetime_utc < %s "
170
                 " ORDER BY start_datetime_utc ")
171
        cursor_historical.execute(query, (row_offline_meter[0],
172
                                          reporting_start_datetime_utc,
173
                                          reporting_end_datetime_utc))
174
        rows_offline_meter_hourly = cursor_historical.fetchall()
175
176
        start_datetime_utc = reporting_start_datetime_utc.replace(tzinfo=None)
177
        end_datetime_utc = reporting_end_datetime_utc.replace(tzinfo=None)
178
179
        start_datetime_local = start_datetime_utc + timedelta(hours=int(config.utc_offset[1:3]))
180
        current_datetime_utc = start_datetime_local.replace(hour=0) - timedelta(hours=int(config.utc_offset[1:3]))
181
182 View Code Duplication
        while current_datetime_utc <= end_datetime_utc:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
183
            flag = True
184
            subtotal = Decimal(0.0)
185
            for row in rows_offline_meter_hourly:
186
                if current_datetime_utc <= row[0] < current_datetime_utc + timedelta(days=1):
187
                    flag = False
188
                    subtotal += row[1]
189
            if flag:
190
                subtotal = None
191
            current_datetime = start_datetime_local.isoformat()[0:10]
192
193
            reporting_date_list.append(current_datetime)
194
            reporting_daily_values.append(subtotal)
195
            current_datetime_utc += timedelta(days=1)
196
            start_datetime_local += timedelta(days=1)
197
198
        ################################################################################################################
199
        # Step 6: construct the report
200
        ################################################################################################################
201
        if cursor_system:
202
            cursor_system.close()
203
        if cnx_system:
204
            cnx_system.disconnect()
205
206
        if cursor_historical:
207
            cursor_historical.close()
208
        if cnx_energy:
209
            cnx_energy.disconnect()
210
211
        result_values = []
212
        for date, daily_value in zip(reporting_date_list, reporting_daily_values):
213
            result_values.append({
214
                "monthdate": date,
215
                "daily_value": daily_value
216
            })
217
218
        resp.text = json.dumps(result_values)
219