reports.virtualmeterbatch.Reporting.on_options()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 4
Code Lines 4

Duplication

Lines 4
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 4
dl 4
loc 4
rs 10
c 0
b 0
f 0
cc 1
nop 2
1
from datetime import datetime, timedelta, timezone
2
import falcon
3
import mysql.connector
4
import simplejson as json
5
from anytree import AnyNode, LevelOrderIter
6
import config
7
import excelexporters.virtualmeterbatch
8
from core.useractivity import access_control, api_key_control
9
10
11 View Code Duplication
class Reporting:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
12
    def __init__(self):
13
        """"Initializes Reporting"""
14
        pass
15
16
    @staticmethod
17
    def on_options(req, resp):
18
        _ = req
19
        resp.status = falcon.HTTP_200
20
21
    ####################################################################################################################
22
    # PROCEDURES
23
    # Step 1: valid parameters
24
    # Step 2: build a space tree
25
    # Step 3: query all virtual meters in the space tree
26
    # Step 4: query energy categories
27
    # Step 5: query reporting period energy input
28
    # Step 6: construct the report
29
    ####################################################################################################################
30
    @staticmethod
31
    def on_get(req, resp):
32
        if 'API-KEY' not in req.headers or \
33
                not isinstance(req.headers['API-KEY'], str) or \
34
                len(str.strip(req.headers['API-KEY'])) == 0:
35
            access_control(req)
36
        else:
37
            api_key_control(req)
38
        print(req.params)
39
        space_id = req.params.get('spaceid')
40
        reporting_period_start_datetime_local = req.params.get('reportingperiodstartdatetime')
41
        reporting_period_end_datetime_local = req.params.get('reportingperiodenddatetime')
42
        language = req.params.get('language')
43
        quick_mode = req.params.get('quickmode')
44
45
        ################################################################################################################
46
        # Step 1: valid parameters
47
        ################################################################################################################
48
        if space_id is None:
49
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_SPACE_ID')
50
        else:
51
            space_id = str.strip(space_id)
52
            if not space_id.isdigit() or int(space_id) <= 0:
53
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
54
                                       description='API.INVALID_SPACE_ID')
55
            else:
56
                space_id = int(space_id)
57
58
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
59
        if config.utc_offset[0] == '-':
60
            timezone_offset = -timezone_offset
61
62
        if reporting_period_start_datetime_local is None:
63
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
64
                                   description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
65
        else:
66
            reporting_period_start_datetime_local = str.strip(reporting_period_start_datetime_local)
67
            try:
68
                reporting_start_datetime_utc = datetime.strptime(reporting_period_start_datetime_local,
69
                                                                 '%Y-%m-%dT%H:%M:%S')
70
            except ValueError:
71
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
72
                                       description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
73
            reporting_start_datetime_utc = \
74
                reporting_start_datetime_utc.replace(tzinfo=timezone.utc) - timedelta(minutes=timezone_offset)
75
            # nomalize the start datetime
76
            if config.minutes_to_count == 30 and reporting_start_datetime_utc.minute >= 30:
77
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=30, second=0, microsecond=0)
78
            else:
79
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=0, second=0, microsecond=0)
80
81
        if reporting_period_end_datetime_local is None:
82
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
83
                                   description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
84
        else:
85
            reporting_period_end_datetime_local = str.strip(reporting_period_end_datetime_local)
86
            try:
87
                reporting_end_datetime_utc = datetime.strptime(reporting_period_end_datetime_local,
88
                                                               '%Y-%m-%dT%H:%M:%S')
89
            except ValueError:
90
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
91
                                       description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
92
            reporting_end_datetime_utc = reporting_end_datetime_utc.replace(tzinfo=timezone.utc) - \
93
                timedelta(minutes=timezone_offset)
94
95
        if reporting_start_datetime_utc >= reporting_end_datetime_utc:
96
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
97
                                   description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
98
99
        # if turn quick mode on, do not return parameters data and excel file
100
        is_quick_mode = False
101
        if quick_mode is not None and \
102
            len(str.strip(quick_mode)) > 0 and \
103
                str.lower(str.strip(quick_mode)) in ('true', 't', 'on', 'yes', 'y'):
104
            is_quick_mode = True
105
106
        cnx_system_db = mysql.connector.connect(**config.myems_system_db)
107
        cursor_system_db = cnx_system_db.cursor()
108
109
        cursor_system_db.execute(" SELECT name "
110
                                 " FROM tbl_spaces "
111
                                 " WHERE id = %s ", (space_id,))
112
        row = cursor_system_db.fetchone()
113
114
        if row is None:
115
            if cursor_system_db:
116
                cursor_system_db.close()
117
            if cnx_system_db:
118
                cnx_system_db.close()
119
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
120
                                   description='API.SPACE_NOT_FOUND')
121
        else:
122
            space_name = row[0]
123
124
        ################################################################################################################
125
        # Step 2: build a space tree
126
        ################################################################################################################
127
128
        query = (" SELECT id, name, parent_space_id "
129
                 " FROM tbl_spaces "
130
                 " ORDER BY id ")
131
        cursor_system_db.execute(query)
132
        rows_spaces = cursor_system_db.fetchall()
133
        node_dict = dict()
134
        if rows_spaces is not None and len(rows_spaces) > 0:
135
            for row in rows_spaces:
136
                parent_node = node_dict[row[2]] if row[2] is not None else None
137
                node_dict[row[0]] = AnyNode(id=row[0], parent=parent_node, name=row[1])
138
139
        ################################################################################################################
140
        # Step 3: query all meters in the space tree
141
        ################################################################################################################
142
        virtual_meter_dict = dict()
143
        space_dict = dict()
144
        energy_category_set = set()
145
146
        for node in LevelOrderIter(node_dict[space_id]):
147
            space_dict[node.id] = node.name
148
149
        cursor_system_db.execute(" SELECT vm.id, vm.name AS virtual_meter_name, vm.energy_category_id, "
150
                                 "        s.name AS space_name, "
151
                                 "        cc.name AS cost_center_name"
152
                                 " FROM tbl_spaces s, tbl_spaces_virtual_meters svm, "
153
                                 "      tbl_virtual_meters vm, tbl_cost_centers cc "
154
                                 " WHERE s.id IN ( " + ', '.join(map(str, space_dict.keys())) + ") "
155
                                 " AND svm.space_id = s.id AND svm.virtual_meter_id = vm.id "
156
                                 " AND vm.cost_center_id = cc.id  ", )
157
        rows_meters = cursor_system_db.fetchall()
158
        if rows_meters is not None and len(rows_meters) > 0:
159
            for row in rows_meters:
160
                virtual_meter_dict[row[0]] = {"virtual_meter_name": row[1],
161
                                              "energy_category_id": row[2],
162
                                              "space_name": row[3],
163
                                              "cost_center_name": row[4],
164
                                              "values": list(),
165
                                              "subtotal": None}
166
                energy_category_set.add(row[2])
167
168
        ################################################################################################################
169
        # Step 4: query energy categories
170
        ################################################################################################################
171
        cnx_energy_db = mysql.connector.connect(**config.myems_energy_db)
172
        cursor_energy_db = cnx_energy_db.cursor()
173
174
        # query all energy categories
175
        cursor_system_db.execute(" SELECT id, name, unit_of_measure "
176
                                 " FROM tbl_energy_categories "
177
                                 " ORDER BY id ", )
178
        rows_energy_categories = cursor_system_db.fetchall()
179
        if rows_energy_categories is None or len(rows_energy_categories) == 0:
180
            if cursor_system_db:
181
                cursor_system_db.close()
182
            if cnx_system_db:
183
                cnx_system_db.close()
184
185
            if cursor_energy_db:
186
                cursor_energy_db.close()
187
            if cnx_energy_db:
188
                cnx_energy_db.close()
189
190
            raise falcon.HTTPError(status=falcon.HTTP_404,
191
                                   title='API.NOT_FOUND',
192
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
193
        energy_category_list = list()
194
        for row_energy_category in rows_energy_categories:
195
            if row_energy_category[0] in energy_category_set:
196
                energy_category_list.append({"id": row_energy_category[0],
197
                                             "name": row_energy_category[1],
198
                                             "unit_of_measure": row_energy_category[2]})
199
200
        ################################################################################################################
201
        # Step 5: query reporting period energy input
202
        ################################################################################################################
203
        for virtual_meter_id in virtual_meter_dict:
204
205
            cursor_energy_db.execute(" SELECT SUM(actual_value) "
206
                                     " FROM tbl_virtual_meter_hourly "
207
                                     " WHERE virtual_meter_id = %s "
208
                                     "     AND start_datetime_utc >= %s "
209
                                     "     AND start_datetime_utc < %s ",
210
                                     (virtual_meter_id,
211
                                      reporting_start_datetime_utc,
212
                                      reporting_end_datetime_utc))
213
            rows_meter_energy = cursor_energy_db.fetchall()
214
            for energy_category in energy_category_list:
215
                subtotal = None
216
                for row_meter_energy in rows_meter_energy:
217
                    if energy_category['id'] == virtual_meter_dict[virtual_meter_id]['energy_category_id']:
218
                        subtotal = row_meter_energy[0]
219
                        virtual_meter_dict[virtual_meter_id]['subtotal'] = subtotal
220
                        break
221
                # append subtotal
222
                # append None if energy category is not applicable
223
                virtual_meter_dict[virtual_meter_id]['values'].append(subtotal)
224
225
        if cursor_system_db:
226
            cursor_system_db.close()
227
        if cnx_system_db:
228
            cnx_system_db.close()
229
230
        if cursor_energy_db:
231
            cursor_energy_db.close()
232
        if cnx_energy_db:
233
            cnx_energy_db.close()
234
235
        ################################################################################################################
236
        # Step 6: construct the report
237
        ################################################################################################################
238
        virtual_meter_list = list()
239
        for virtual_meter_id, virtual_meter in virtual_meter_dict.items():
240
            virtual_meter_list.append({
241
                "id": virtual_meter_id,
242
                "virtual_meter_name": virtual_meter['virtual_meter_name'],
243
                "space_name": virtual_meter['space_name'],
244
                "cost_center_name": virtual_meter['cost_center_name'],
245
                "values": virtual_meter['values'],
246
                "subtotal": virtual_meter['subtotal'],
247
            })
248
249
        result = {'virtual_meters': virtual_meter_list,
250
                  'energycategories': energy_category_list}
251
252
        # export result to Excel file and then encode the file to base64 string
253
        if not is_quick_mode:
254
            result['excel_bytes_base64'] = \
255
                excelexporters.virtualmeterbatch.export(result,
256
                                                        space_name,
257
                                                        reporting_period_start_datetime_local,
258
                                                        reporting_period_end_datetime_local,
259
                                                        language)
260
        resp.text = json.dumps(result)
261