Code Duplication    Length = 250-250 lines in 2 locations

myems-api/reports/offlinemeterbatch.py 1 location

@@ 43-292 (lines=250) @@
40
from core.useractivity import access_control, api_key_control
41
42
43
class Reporting:
44
    def __init__(self):
45
        """"Initializes Reporting"""
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: build a space tree
57
    # Step 3: query all offline meters in the space tree
58
    # Step 4: query energy categories
59
    # Step 5: query reporting period energy input
60
    # Step 6: 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
        print(req.params)
71
        space_id = req.params.get('spaceid')
72
        reporting_period_start_datetime_local = req.params.get('reportingperiodstartdatetime')
73
        reporting_period_end_datetime_local = req.params.get('reportingperiodenddatetime')
74
        language = req.params.get('language')
75
        quick_mode = req.params.get('quickmode')
76
77
        ################################################################################################################
78
        # Step 1: valid parameters
79
        ################################################################################################################
80
        if space_id is None:
81
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_SPACE_ID')
82
        else:
83
            space_id = str.strip(space_id)
84
            if not space_id.isdigit() or int(space_id) <= 0:
85
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
86
                                       description='API.INVALID_SPACE_ID')
87
            else:
88
                space_id = int(space_id)
89
90
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
91
        if config.utc_offset[0] == '-':
92
            timezone_offset = -timezone_offset
93
94
        if reporting_period_start_datetime_local is None:
95
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
96
                                   description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
97
        else:
98
            reporting_period_start_datetime_local = str.strip(reporting_period_start_datetime_local)
99
            try:
100
                reporting_start_datetime_utc = datetime.strptime(reporting_period_start_datetime_local,
101
                                                                 '%Y-%m-%dT%H:%M:%S')
102
            except ValueError:
103
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
104
                                       description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
105
            reporting_start_datetime_utc = \
106
                reporting_start_datetime_utc.replace(tzinfo=timezone.utc) - timedelta(minutes=timezone_offset)
107
            # nomalize the start datetime
108
            if config.minutes_to_count == 30 and reporting_start_datetime_utc.minute >= 30:
109
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=30, second=0, microsecond=0)
110
            else:
111
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=0, second=0, microsecond=0)
112
113
        if reporting_period_end_datetime_local is None:
114
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
115
                                   description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
116
        else:
117
            reporting_period_end_datetime_local = str.strip(reporting_period_end_datetime_local)
118
            try:
119
                reporting_end_datetime_utc = datetime.strptime(reporting_period_end_datetime_local,
120
                                                               '%Y-%m-%dT%H:%M:%S')
121
            except ValueError:
122
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
123
                                       description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
124
            reporting_end_datetime_utc = reporting_end_datetime_utc.replace(tzinfo=timezone.utc) - \
125
                timedelta(minutes=timezone_offset)
126
127
        if reporting_start_datetime_utc >= reporting_end_datetime_utc:
128
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
129
                                   description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
130
131
        cnx_system_db = mysql.connector.connect(**config.myems_system_db)
132
        cursor_system_db = cnx_system_db.cursor()
133
134
        # if turn quick mode on, do not return parameters data and excel file
135
        is_quick_mode = False
136
        if quick_mode is not None and \
137
            len(str.strip(quick_mode)) > 0 and \
138
                str.lower(str.strip(quick_mode)) in ('true', 't', 'on', 'yes', 'y'):
139
            is_quick_mode = True
140
141
        cursor_system_db.execute(" SELECT name "
142
                                 " FROM tbl_spaces "
143
                                 " WHERE id = %s ", (space_id,))
144
        row = cursor_system_db.fetchone()
145
146
        if row is None:
147
            if cursor_system_db:
148
                cursor_system_db.close()
149
            if cnx_system_db:
150
                cnx_system_db.close()
151
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
152
                                   description='API.SPACE_NOT_FOUND')
153
        else:
154
            space_name = row[0]
155
156
        ################################################################################################################
157
        # Step 2: build a space tree
158
        ################################################################################################################
159
160
        query = (" SELECT id, name, parent_space_id "
161
                 " FROM tbl_spaces "
162
                 " ORDER BY id ")
163
        cursor_system_db.execute(query)
164
        rows_spaces = cursor_system_db.fetchall()
165
        node_dict = dict()
166
        if rows_spaces is not None and len(rows_spaces) > 0:
167
            for row in rows_spaces:
168
                parent_node = node_dict[row[2]] if row[2] is not None else None
169
                node_dict[row[0]] = AnyNode(id=row[0], parent=parent_node, name=row[1])
170
171
        ################################################################################################################
172
        # Step 3: query all offline meters in the space tree
173
        ################################################################################################################
174
        offline_meter_dict = dict()
175
        space_dict = dict()
176
        energy_category_set = set()
177
178
        for node in LevelOrderIter(node_dict[space_id]):
179
            space_dict[node.id] = node.name
180
181
        cursor_system_db.execute(" SELECT om.id, om.name AS offline_meter_name, om.energy_category_id, "
182
                                 "        s.name AS space_name, "
183
                                 "        cc.name AS cost_center_name"
184
                                 " FROM tbl_spaces s, tbl_spaces_offline_meters som, "
185
                                 "      tbl_offline_meters om, tbl_cost_centers cc "
186
                                 " WHERE s.id IN ( " + ', '.join(map(str, space_dict.keys())) + ") "
187
                                 " AND som.space_id = s.id AND som.offline_meter_id = om.id "
188
                                 " AND om.cost_center_id = cc.id  ", )
189
        rows_offline_meters = cursor_system_db.fetchall()
190
        if rows_offline_meters is not None and len(rows_offline_meters) > 0:
191
            for row in rows_offline_meters:
192
                offline_meter_dict[row[0]] = {"offline_meter_name": row[1],
193
                                              "energy_category_id": row[2],
194
                                              "space_name": row[3],
195
                                              "cost_center_name": row[4],
196
                                              "values": list(),
197
                                              "subtotal": None}
198
                energy_category_set.add(row[2])
199
200
        ################################################################################################################
201
        # Step 4: query energy categories
202
        ################################################################################################################
203
        cnx_energy_db = mysql.connector.connect(**config.myems_energy_db)
204
        cursor_energy_db = cnx_energy_db.cursor()
205
206
        # query all energy categories
207
        cursor_system_db.execute(" SELECT id, name, unit_of_measure "
208
                                 " FROM tbl_energy_categories "
209
                                 " ORDER BY id ", )
210
        rows_energy_categories = cursor_system_db.fetchall()
211
        if rows_energy_categories is None or len(rows_energy_categories) == 0:
212
            if cursor_system_db:
213
                cursor_system_db.close()
214
            if cnx_system_db:
215
                cnx_system_db.close()
216
217
            if cursor_energy_db:
218
                cursor_energy_db.close()
219
            if cnx_energy_db:
220
                cnx_energy_db.close()
221
222
            raise falcon.HTTPError(status=falcon.HTTP_404,
223
                                   title='API.NOT_FOUND',
224
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
225
        energy_category_list = list()
226
        for row_energy_category in rows_energy_categories:
227
            if row_energy_category[0] in energy_category_set:
228
                energy_category_list.append({"id": row_energy_category[0],
229
                                             "name": row_energy_category[1],
230
                                             "unit_of_measure": row_energy_category[2]})
231
232
        ################################################################################################################
233
        # Step 5: query reporting period energy input
234
        ################################################################################################################
235
        for offline_meter_id in offline_meter_dict:
236
237
            cursor_energy_db.execute(" SELECT SUM(actual_value) "
238
                                     " FROM tbl_offline_meter_hourly "
239
                                     " WHERE offline_meter_id = %s "
240
                                     "     AND start_datetime_utc >= %s "
241
                                     "     AND start_datetime_utc < %s ",
242
                                     (offline_meter_id,
243
                                      reporting_start_datetime_utc,
244
                                      reporting_end_datetime_utc))
245
            rows_offline_meter_energy = cursor_energy_db.fetchall()
246
            for energy_category in energy_category_list:
247
                subtotal = None
248
                for row_offline_meter_energy in rows_offline_meter_energy:
249
                    if energy_category['id'] == offline_meter_dict[offline_meter_id]['energy_category_id']:
250
                        subtotal = row_offline_meter_energy[0]
251
                        offline_meter_dict[offline_meter_id]['subtotal'] = subtotal
252
                        break
253
                # append subtotal
254
                # append None if energy category is not applicable
255
                offline_meter_dict[offline_meter_id]['values'].append(subtotal)
256
257
        if cursor_system_db:
258
            cursor_system_db.close()
259
        if cnx_system_db:
260
            cnx_system_db.close()
261
262
        if cursor_energy_db:
263
            cursor_energy_db.close()
264
        if cnx_energy_db:
265
            cnx_energy_db.close()
266
267
        ################################################################################################################
268
        # Step 6: construct the report
269
        ################################################################################################################
270
        offline_meter_list = list()
271
        for offline_meter_id, offline_meter in offline_meter_dict.items():
272
            offline_meter_list.append({
273
                "id": offline_meter_id,
274
                "offline_meter_name": offline_meter['offline_meter_name'],
275
                "space_name": offline_meter['space_name'],
276
                "cost_center_name": offline_meter['cost_center_name'],
277
                "values": offline_meter['values'],
278
                "subtotal": offline_meter['subtotal'],
279
            })
280
281
        result = {'offline_meters': offline_meter_list, 'energycategories': energy_category_list,
282
                  'excel_bytes_base64': None}
283
284
        # export result to Excel file and then encode the file to base64 string
285
        if not is_quick_mode:
286
            result['excel_bytes_base64'] = \
287
                excelexporters.offlinemeterbatch.export(result,
288
                                                        space_name,
289
                                                        reporting_period_start_datetime_local,
290
                                                        reporting_period_end_datetime_local,
291
                                                        language)
292
        resp.text = json.dumps(result)
293

myems-api/reports/virtualmeterbatch.py 1 location

@@ 43-292 (lines=250) @@
40
from core.useractivity import access_control, api_key_control
41
42
43
class Reporting:
44
    def __init__(self):
45
        """"Initializes Reporting"""
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: build a space tree
57
    # Step 3: query all virtual meters in the space tree
58
    # Step 4: query energy categories
59
    # Step 5: query reporting period energy input
60
    # Step 6: 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
        print(req.params)
71
        space_id = req.params.get('spaceid')
72
        reporting_period_start_datetime_local = req.params.get('reportingperiodstartdatetime')
73
        reporting_period_end_datetime_local = req.params.get('reportingperiodenddatetime')
74
        language = req.params.get('language')
75
        quick_mode = req.params.get('quickmode')
76
77
        ################################################################################################################
78
        # Step 1: valid parameters
79
        ################################################################################################################
80
        if space_id is None:
81
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST', description='API.INVALID_SPACE_ID')
82
        else:
83
            space_id = str.strip(space_id)
84
            if not space_id.isdigit() or int(space_id) <= 0:
85
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
86
                                       description='API.INVALID_SPACE_ID')
87
            else:
88
                space_id = int(space_id)
89
90
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
91
        if config.utc_offset[0] == '-':
92
            timezone_offset = -timezone_offset
93
94
        if reporting_period_start_datetime_local is None:
95
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
96
                                   description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
97
        else:
98
            reporting_period_start_datetime_local = str.strip(reporting_period_start_datetime_local)
99
            try:
100
                reporting_start_datetime_utc = datetime.strptime(reporting_period_start_datetime_local,
101
                                                                 '%Y-%m-%dT%H:%M:%S')
102
            except ValueError:
103
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
104
                                       description="API.INVALID_REPORTING_PERIOD_START_DATETIME")
105
            reporting_start_datetime_utc = \
106
                reporting_start_datetime_utc.replace(tzinfo=timezone.utc) - timedelta(minutes=timezone_offset)
107
            # nomalize the start datetime
108
            if config.minutes_to_count == 30 and reporting_start_datetime_utc.minute >= 30:
109
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=30, second=0, microsecond=0)
110
            else:
111
                reporting_start_datetime_utc = reporting_start_datetime_utc.replace(minute=0, second=0, microsecond=0)
112
113
        if reporting_period_end_datetime_local is None:
114
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
115
                                   description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
116
        else:
117
            reporting_period_end_datetime_local = str.strip(reporting_period_end_datetime_local)
118
            try:
119
                reporting_end_datetime_utc = datetime.strptime(reporting_period_end_datetime_local,
120
                                                               '%Y-%m-%dT%H:%M:%S')
121
            except ValueError:
122
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
123
                                       description="API.INVALID_REPORTING_PERIOD_END_DATETIME")
124
            reporting_end_datetime_utc = reporting_end_datetime_utc.replace(tzinfo=timezone.utc) - \
125
                timedelta(minutes=timezone_offset)
126
127
        if reporting_start_datetime_utc >= reporting_end_datetime_utc:
128
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
129
                                   description='API.INVALID_REPORTING_PERIOD_END_DATETIME')
130
131
        # if turn quick mode on, do not return parameters data and excel file
132
        is_quick_mode = False
133
        if quick_mode is not None and \
134
            len(str.strip(quick_mode)) > 0 and \
135
                str.lower(str.strip(quick_mode)) in ('true', 't', 'on', 'yes', 'y'):
136
            is_quick_mode = True
137
138
        cnx_system_db = mysql.connector.connect(**config.myems_system_db)
139
        cursor_system_db = cnx_system_db.cursor()
140
141
        cursor_system_db.execute(" SELECT name "
142
                                 " FROM tbl_spaces "
143
                                 " WHERE id = %s ", (space_id,))
144
        row = cursor_system_db.fetchone()
145
146
        if row is None:
147
            if cursor_system_db:
148
                cursor_system_db.close()
149
            if cnx_system_db:
150
                cnx_system_db.close()
151
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
152
                                   description='API.SPACE_NOT_FOUND')
153
        else:
154
            space_name = row[0]
155
156
        ################################################################################################################
157
        # Step 2: build a space tree
158
        ################################################################################################################
159
160
        query = (" SELECT id, name, parent_space_id "
161
                 " FROM tbl_spaces "
162
                 " ORDER BY id ")
163
        cursor_system_db.execute(query)
164
        rows_spaces = cursor_system_db.fetchall()
165
        node_dict = dict()
166
        if rows_spaces is not None and len(rows_spaces) > 0:
167
            for row in rows_spaces:
168
                parent_node = node_dict[row[2]] if row[2] is not None else None
169
                node_dict[row[0]] = AnyNode(id=row[0], parent=parent_node, name=row[1])
170
171
        ################################################################################################################
172
        # Step 3: query all meters in the space tree
173
        ################################################################################################################
174
        virtual_meter_dict = dict()
175
        space_dict = dict()
176
        energy_category_set = set()
177
178
        for node in LevelOrderIter(node_dict[space_id]):
179
            space_dict[node.id] = node.name
180
181
        cursor_system_db.execute(" SELECT vm.id, vm.name AS virtual_meter_name, vm.energy_category_id, "
182
                                 "        s.name AS space_name, "
183
                                 "        cc.name AS cost_center_name"
184
                                 " FROM tbl_spaces s, tbl_spaces_virtual_meters svm, "
185
                                 "      tbl_virtual_meters vm, tbl_cost_centers cc "
186
                                 " WHERE s.id IN ( " + ', '.join(map(str, space_dict.keys())) + ") "
187
                                 " AND svm.space_id = s.id AND svm.virtual_meter_id = vm.id "
188
                                 " AND vm.cost_center_id = cc.id  ", )
189
        rows_meters = cursor_system_db.fetchall()
190
        if rows_meters is not None and len(rows_meters) > 0:
191
            for row in rows_meters:
192
                virtual_meter_dict[row[0]] = {"virtual_meter_name": row[1],
193
                                              "energy_category_id": row[2],
194
                                              "space_name": row[3],
195
                                              "cost_center_name": row[4],
196
                                              "values": list(),
197
                                              "subtotal": None}
198
                energy_category_set.add(row[2])
199
200
        ################################################################################################################
201
        # Step 4: query energy categories
202
        ################################################################################################################
203
        cnx_energy_db = mysql.connector.connect(**config.myems_energy_db)
204
        cursor_energy_db = cnx_energy_db.cursor()
205
206
        # query all energy categories
207
        cursor_system_db.execute(" SELECT id, name, unit_of_measure "
208
                                 " FROM tbl_energy_categories "
209
                                 " ORDER BY id ", )
210
        rows_energy_categories = cursor_system_db.fetchall()
211
        if rows_energy_categories is None or len(rows_energy_categories) == 0:
212
            if cursor_system_db:
213
                cursor_system_db.close()
214
            if cnx_system_db:
215
                cnx_system_db.close()
216
217
            if cursor_energy_db:
218
                cursor_energy_db.close()
219
            if cnx_energy_db:
220
                cnx_energy_db.close()
221
222
            raise falcon.HTTPError(status=falcon.HTTP_404,
223
                                   title='API.NOT_FOUND',
224
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
225
        energy_category_list = list()
226
        for row_energy_category in rows_energy_categories:
227
            if row_energy_category[0] in energy_category_set:
228
                energy_category_list.append({"id": row_energy_category[0],
229
                                             "name": row_energy_category[1],
230
                                             "unit_of_measure": row_energy_category[2]})
231
232
        ################################################################################################################
233
        # Step 5: query reporting period energy input
234
        ################################################################################################################
235
        for virtual_meter_id in virtual_meter_dict:
236
237
            cursor_energy_db.execute(" SELECT SUM(actual_value) "
238
                                     " FROM tbl_virtual_meter_hourly "
239
                                     " WHERE virtual_meter_id = %s "
240
                                     "     AND start_datetime_utc >= %s "
241
                                     "     AND start_datetime_utc < %s ",
242
                                     (virtual_meter_id,
243
                                      reporting_start_datetime_utc,
244
                                      reporting_end_datetime_utc))
245
            rows_meter_energy = cursor_energy_db.fetchall()
246
            for energy_category in energy_category_list:
247
                subtotal = None
248
                for row_meter_energy in rows_meter_energy:
249
                    if energy_category['id'] == virtual_meter_dict[virtual_meter_id]['energy_category_id']:
250
                        subtotal = row_meter_energy[0]
251
                        virtual_meter_dict[virtual_meter_id]['subtotal'] = subtotal
252
                        break
253
                # append subtotal
254
                # append None if energy category is not applicable
255
                virtual_meter_dict[virtual_meter_id]['values'].append(subtotal)
256
257
        if cursor_system_db:
258
            cursor_system_db.close()
259
        if cnx_system_db:
260
            cnx_system_db.close()
261
262
        if cursor_energy_db:
263
            cursor_energy_db.close()
264
        if cnx_energy_db:
265
            cnx_energy_db.close()
266
267
        ################################################################################################################
268
        # Step 6: construct the report
269
        ################################################################################################################
270
        virtual_meter_list = list()
271
        for virtual_meter_id, virtual_meter in virtual_meter_dict.items():
272
            virtual_meter_list.append({
273
                "id": virtual_meter_id,
274
                "virtual_meter_name": virtual_meter['virtual_meter_name'],
275
                "space_name": virtual_meter['space_name'],
276
                "cost_center_name": virtual_meter['cost_center_name'],
277
                "values": virtual_meter['values'],
278
                "subtotal": virtual_meter['subtotal'],
279
            })
280
281
        result = {'virtual_meters': virtual_meter_list,
282
                  'energycategories': energy_category_list}
283
284
        # export result to Excel file and then encode the file to base64 string
285
        if not is_quick_mode:
286
            result['excel_bytes_base64'] = \
287
                excelexporters.virtualmeterbatch.export(result,
288
                                                        space_name,
289
                                                        reporting_period_start_datetime_local,
290
                                                        reporting_period_end_datetime_local,
291
                                                        language)
292
        resp.text = json.dumps(result)
293