core.virtualmeter   F
last analyzed

Complexity

Total Complexity 306

Size/Duplication

Total Lines 1462
Duplicated Lines 26.61 %

Importance

Changes 0
Metric Value
wmc 306
eloc 1128
dl 389
loc 1462
rs 1.0879
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A VirtualMeterCollection.on_options() 0 5 1
A VirtualMeterCollection.__init__() 0 3 1
F VirtualMeterCollection.on_get() 0 135 28
F VirtualMeterItem.on_get() 127 127 26
F VirtualMeterImport.on_post() 58 213 53
A VirtualMeterImport.on_options() 0 4 1
F VirtualMeterItem.on_delete() 0 163 26
A VirtualMeterExport.__init__() 0 2 1
F VirtualMeterClone.on_post() 0 154 26
F VirtualMeterItem.on_put() 30 260 64
A VirtualMeterClone.__init__() 0 2 1
A VirtualMeterClone.on_options() 0 5 1
F VirtualMeterCollection.on_post() 47 205 47
F VirtualMeterExport.on_get() 127 127 26
A VirtualMeterItem.on_options() 0 5 1
A VirtualMeterExport.on_options() 0 5 1
A VirtualMeterImport.__init__() 0 2 1
A VirtualMeterItem.__init__() 0 2 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 core.virtualmeter 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
import uuid
2
from datetime import datetime, timedelta
3
import falcon
4
import mysql.connector
5
import simplejson as json
6
from core.useractivity import user_logger, admin_control, access_control, api_key_control
7
import config
8
9
10
class VirtualMeterCollection:
11
    """
12
    Virtual Meter Collection Resource
13
14
    This class handles CRUD operations for virtual meter collection.
15
    It provides endpoints for listing all virtual meters and creating new virtual meters.
16
    Virtual meters are calculated meters that derive their values from other meters.
17
    """
18
    def __init__(self):
19
        """Initialize VirtualMeterCollection"""
20
        pass
21
22
    @staticmethod
23
    def on_options(req, resp):
24
        """Handle OPTIONS requests for CORS preflight"""
25
        _ = req
26
        resp.status = falcon.HTTP_200
27
28
    @staticmethod
29
    def on_get(req, resp):
30
        if 'API-KEY' not in req.headers or \
31
                not isinstance(req.headers['API-KEY'], str) or \
32
                len(str.strip(req.headers['API-KEY'])) == 0:
33
            access_control(req)
34
        else:
35
            api_key_control(req)
36
        search_query = req.get_param('q', default=None)
37
        if search_query is not None:
38
            search_query = search_query.strip()
39
        else:
40
            search_query = ''
41
42
        cnx = mysql.connector.connect(**config.myems_system_db)
43
        cursor = cnx.cursor()
44
45
        query = (" SELECT id, name, uuid "
46
                 " FROM tbl_energy_categories ")
47
        cursor.execute(query)
48
        rows_energy_categories = cursor.fetchall()
49
50
        energy_category_dict = dict()
51
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
52
            for row in rows_energy_categories:
53
                energy_category_dict[row[0]] = {"id": row[0],
54
                                                "name": row[1],
55
                                                "uuid": row[2]}
56
57
        query = (" SELECT id, name, uuid "
58
                 " FROM tbl_energy_items ")
59
        cursor.execute(query)
60
        rows_energy_items = cursor.fetchall()
61
62
        energy_item_dict = dict()
63
        if rows_energy_items is not None and len(rows_energy_items) > 0:
64
            for row in rows_energy_items:
65
                energy_item_dict[row[0]] = {"id": row[0],
66
                                            "name": row[1],
67
                                            "uuid": row[2]}
68
69
        query = (" SELECT id, name, uuid "
70
                 " FROM tbl_cost_centers ")
71
        cursor.execute(query)
72
        rows_cost_centers = cursor.fetchall()
73
74
        cost_center_dict = dict()
75
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
76
            for row in rows_cost_centers:
77
                cost_center_dict[row[0]] = {"id": row[0],
78
                                            "name": row[1],
79
                                            "uuid": row[2]}
80
81
        query = (" SELECT id, name, uuid, equation, energy_category_id, is_counted, cost_center_id, "
82
                 "        energy_item_id, description "
83
                 " FROM tbl_virtual_meters " )
84
        params = []
85
        if search_query:
86
            query += " WHERE name LIKE %s OR description LIKE %s "
87
            params = [f'%{search_query}%', f'%{search_query}%']
88
89
        query += " ORDER BY id "
90
        cursor.execute(query,params)
91
        rows_virtual_meters = cursor.fetchall()
92
93
        result = list()
94
        if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
95
            for row in rows_virtual_meters:
96
                meta_result = {"id": row[0],
97
                               "name": row[1],
98
                               "uuid": row[2],
99
                               "equation": row[3],
100
                               "energy_category": energy_category_dict.get(row[4], None),
101
                               "is_counted": True if row[5] else False,
102
                               "cost_center": cost_center_dict.get(row[6], None),
103
                               "energy_item": energy_item_dict.get(row[7], None),
104
                               "description": row[8],
105
                               "expression": {}}
106
107
                expression = dict()
108
109
                if meta_result["equation"] is not None:
110
                    expression = {'equation': meta_result["equation"], 'variables': []}
111
112
                    query_variables = (" SELECT v.id, v.name, v.meter_type, v.meter_id "
113
                                       " FROM tbl_virtual_meters vm, tbl_variables v "
114
                                       " WHERE vm.id = %s AND v.virtual_meter_id = vm.id "
115
                                       " ORDER BY SUBSTRING(v.name, 2, 10) + 0 ")
116
                    cursor.execute(query_variables, (meta_result['id'],))
117
                    rows_variables = cursor.fetchall()
118
                    if rows_variables is not None:
119
                        for row_variable in rows_variables:
120
                            if row_variable[2].lower() == 'meter':
121
                                query_meter = (" SELECT m.name "
122
                                               " FROM tbl_meters m "
123
                                               " WHERE m.id = %s ")
124
                                cursor.execute(query_meter, (row_variable[3],))
125
                                row_meter = cursor.fetchone()
126
                                if row_meter is not None:
127
                                    expression['variables'].append({'id': row_variable[0],
128
                                                                    'name': row_variable[1],
129
                                                                    'meter_type': row_variable[2],
130
                                                                    'meter_id': row_variable[3],
131
                                                                    'meter_name': row_meter[0]})
132
                            elif row_variable[2].lower() == 'offline_meter':
133
                                query_meter = (" SELECT m.name "
134
                                               " FROM tbl_offline_meters m "
135
                                               " WHERE m.id = %s ")
136
                                cursor.execute(query_meter, (row_variable[3],))
137
                                row_meter = cursor.fetchone()
138
                                if row_meter is not None:
139
                                    expression['variables'].append({'id': row_variable[0],
140
                                                                    'name': row_variable[1],
141
                                                                    'meter_type': row_variable[2],
142
                                                                    'meter_id': row_variable[3],
143
                                                                    'meter_name': row_meter[0]})
144
                            elif row_variable[2].lower() == 'virtual_meter':
145
                                query_meter = (" SELECT m.name "
146
                                               " FROM tbl_virtual_meters m "
147
                                               " WHERE m.id = %s ")
148
                                cursor.execute(query_meter, (row_variable[3],))
149
                                row_meter = cursor.fetchone()
150
                                if row_meter is not None:
151
                                    expression['variables'].append({'id': row_variable[0],
152
                                                                    'name': row_variable[1],
153
                                                                    'meter_type': row_variable[2],
154
                                                                    'meter_id': row_variable[3],
155
                                                                    'meter_name': row_meter[0]})
156
157
                meta_result['expression'] = expression
158
                result.append(meta_result)
159
160
        cursor.close()
161
        cnx.close()
162
        resp.text = json.dumps(result)
163
164
    @staticmethod
165
    @user_logger
166
    def on_post(req, resp):
167
        """Handles POST requests"""
168
        admin_control(req)
169
        try:
170
            raw_json = req.stream.read().decode('utf-8')
171
        except UnicodeDecodeError as ex:
172
            print("Failed to decode request")
173
            raise falcon.HTTPError(status=falcon.HTTP_400,
174
                                   title='API.BAD_REQUEST',
175
                                   description='API.INVALID_ENCODING')
176
        except Exception as ex:
177
            print("Unexpected error reading request stream")
178
            raise falcon.HTTPError(status=falcon.HTTP_400,
179
                                   title='API.BAD_REQUEST',
180
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
181
182
        new_values = json.loads(raw_json)
183
184
        if 'name' not in new_values['data'].keys() or \
185
                not isinstance(new_values['data']['name'], str) or \
186
                len(str.strip(new_values['data']['name'])) == 0:
187
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
188
                                   description='API.INVALID_VIRTUAL_METER_NAME')
189
        name = str.strip(new_values['data']['name'])
190
191
        if 'energy_category_id' not in new_values['data'].keys() or new_values['data']['energy_category_id'] <= 0:
192
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
193
                                   description='API.INVALID_ENERGY_CATEGORY_ID')
194
        energy_category_id = new_values['data']['energy_category_id']
195
196
        if 'is_counted' not in new_values['data'].keys() or not isinstance(new_values['data']['is_counted'], bool):
197
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
198
                                   description='API.INVALID_IS_COUNTED_VALUE')
199
        is_counted = new_values['data']['is_counted']
200
201
        if 'cost_center_id' not in new_values['data'].keys() or \
202
                not isinstance(new_values['data']['cost_center_id'], int) or \
203
                new_values['data']['cost_center_id'] <= 0:
204
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
205
                                   description='API.INVALID_COST_CENTER_ID')
206
207
        cost_center_id = new_values['data']['cost_center_id']
208
209
        if 'energy_item_id' in new_values['data'].keys() and \
210
                new_values['data']['energy_item_id'] is not None:
211
            if not isinstance(new_values['data']['energy_item_id'], int) or \
212
                    new_values['data']['energy_item_id'] <= 0:
213
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
214
                                       description='API.INVALID_ENERGY_ITEM_ID')
215
            energy_item_id = new_values['data']['energy_item_id']
216
        else:
217
            energy_item_id = None
218
219
        if 'description' in new_values['data'].keys() and \
220
                new_values['data']['description'] is not None and \
221
                len(str(new_values['data']['description'])) > 0:
222
            description = str.strip(new_values['data']['description'])
223
        else:
224
            description = None
225
226
        if 'expression' not in new_values['data'].keys():
227
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
228
                                   description='API.INVALID_EXPRESSION_OBJECT')
229
230
        if 'equation' not in new_values['data']['expression'].keys() \
231
                or len(new_values['data']['expression']['equation']) == 0:
232
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
233
                                   description='API.INVALID_EQUATION_IN_EXPRESSION')
234
        # todo: validate equation with more rules
235
236
        if 'variables' not in new_values['data']['expression'].keys() \
237
                or len(new_values['data']['expression']['variables']) == 0:
238
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
239
                                   description='API.EMPTY_VARIABLES_ARRAY')
240
241
        for variable in new_values['data']['expression']['variables']:
242
            if 'name' not in variable.keys() or \
243
                    len(variable['name']) == 0:
244
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
245
                                       description='API.INVALID_VARIABLE_NAME')
246
            if 'meter_type' not in variable.keys() or \
247
                    len(variable['meter_type']) == 0 or \
248
                    variable['meter_type'].lower() not in ['meter', 'offline_meter', 'virtual_meter']:
249
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
250
                                       description='API.INVALID_VARIABLE_METER_TYPE')
251
            if 'meter_id' not in variable.keys() or \
252
                    variable['meter_id'] <= 0:
253
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
254
                                       description='API.INVALID_VARIABLE_METER_ID')
255
256
        cnx = mysql.connector.connect(**config.myems_system_db)
257
        cursor = cnx.cursor()
258
259
        cursor.execute(" SELECT name "
260
                       " FROM tbl_virtual_meters "
261
                       " WHERE name = %s ", (name,))
262
        if cursor.fetchone() is not None:
263
            cursor.close()
264
            cnx.close()
265
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.NOT_FOUND',
266
                                   description='API.VIRTUAL_METER_NAME_IS_ALREADY_IN_USE')
267
268
        cursor.execute(" SELECT name "
269
                       " FROM tbl_energy_categories "
270
                       " WHERE id = %s ",
271
                       (energy_category_id,))
272
        if cursor.fetchone() is None:
273
            cursor.close()
274
            cnx.close()
275
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
276
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
277
278
        cursor.execute(" SELECT name "
279
                       " FROM tbl_cost_centers "
280
                       " WHERE id = %s ",
281
                       (new_values['data']['cost_center_id'],))
282
        row = cursor.fetchone()
283
        if row is None:
284
            cursor.close()
285
            cnx.close()
286
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
287
                                   description='API.COST_CENTER_NOT_FOUND')
288
289 View Code Duplication
        if energy_item_id is not None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
290
            cursor.execute(" SELECT name, energy_category_id "
291
                           " FROM tbl_energy_items "
292
                           " WHERE id = %s ",
293
                           (new_values['data']['energy_item_id'],))
294
            row = cursor.fetchone()
295
            if row is None:
296
                cursor.close()
297
                cnx.close()
298
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
299
                                       description='API.ENERGY_ITEM_NOT_FOUND')
300
            else:
301
                if row[1] != energy_category_id:
302
                    cursor.close()
303
                    cnx.close()
304
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.BAD_REQUEST',
305
                                           description='API.ENERGY_ITEM_IS_NOT_BELONG_TO_ENERGY_CATEGORY')
306
307
        for variable in new_values['data']['expression']['variables']:
308 View Code Duplication
            if variable['meter_type'].lower() == 'meter':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
309
                cursor.execute(" SELECT name "
310
                               " FROM tbl_meters "
311
                               " WHERE id = %s ", (variable['meter_id'],))
312
                if cursor.fetchone() is None:
313
                    cursor.close()
314
                    cnx.close()
315
                    raise falcon.HTTPError(status=falcon.HTTP_404,
316
                                           title='API.NOT_FOUND',
317
                                           description='API.METER_OF_VARIABLE_NOT_FOUND')
318
            elif variable['meter_type'].lower() == 'offline_meter':
319
                cursor.execute(" SELECT name "
320
                               " FROM tbl_offline_meters "
321
                               " WHERE id = %s ", (variable['meter_id'],))
322
                if cursor.fetchone() is None:
323
                    cursor.close()
324
                    cnx.close()
325
                    raise falcon.HTTPError(status=falcon.HTTP_404,
326
                                           title='API.NOT_FOUND',
327
                                           description='API.OFFLINE_METER_OF_VARIABLE_NOT_FOUND')
328
            elif variable['meter_type'].lower() == 'virtual_meter':
329
                cursor.execute(" SELECT name "
330
                               " FROM tbl_virtual_meters "
331
                               " WHERE id = %s ", (variable['meter_id'],))
332
                if cursor.fetchone() is None:
333
                    cursor.close()
334
                    cnx.close()
335
                    raise falcon.HTTPError(status=falcon.HTTP_404,
336
                                           title='API.NOT_FOUND',
337
                                           description='API.VIRTUAL_METER_OF_VARIABLE_NOT_FOUND')
338
339
        add_values = (" INSERT INTO tbl_virtual_meters "
340
                      "     (name, uuid, equation, energy_category_id, is_counted, "
341
                      "      cost_center_id, energy_item_id, description) "
342
                      " VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ")
343
        cursor.execute(add_values, (name,
344
                                    str(uuid.uuid4()),
345
                                    new_values['data']['expression']['equation'].lower(),
346
                                    energy_category_id,
347
                                    is_counted,
348
                                    cost_center_id,
349
                                    energy_item_id,
350
                                    description))
351
        new_id = cursor.lastrowid
352
        cnx.commit()
353
354
        # add variables
355
        for variable in new_values['data']['expression']['variables']:
356
            add_values = (" INSERT INTO tbl_variables (name, virtual_meter_id, meter_type, meter_id) "
357
                          " VALUES (%s, %s, %s, %s) ")
358
            cursor.execute(add_values, (variable['name'].lower(),
359
                                        new_id,
360
                                        variable['meter_type'],
361
                                        variable['meter_id'],))
362
            cnx.commit()
363
364
        cursor.close()
365
        cnx.close()
366
367
        resp.status = falcon.HTTP_201
368
        resp.location = '/virtualmeters/' + str(new_id)
369
370
371
class VirtualMeterItem:
372
    def __init__(self):
373
        pass
374
375
    @staticmethod
376
    def on_options(req, resp, id_):
377
        _ = req
378
        resp.status = falcon.HTTP_200
379
        _ = id_
380
381 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
382
    def on_get(req, resp, id_):
383
        if 'API-KEY' not in req.headers or \
384
                not isinstance(req.headers['API-KEY'], str) or \
385
                len(str.strip(req.headers['API-KEY'])) == 0:
386
            access_control(req)
387
        else:
388
            api_key_control(req)
389
        if not id_.isdigit() or int(id_) <= 0:
390
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
391
                                   description='API.INVALID_VIRTUAL_METER_ID')
392
393
        cnx = mysql.connector.connect(**config.myems_system_db)
394
        cursor = cnx.cursor()
395
396
        query = (" SELECT id, name, uuid "
397
                 " FROM tbl_energy_categories ")
398
        cursor.execute(query)
399
        rows_energy_categories = cursor.fetchall()
400
401
        energy_category_dict = dict()
402
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
403
            for row in rows_energy_categories:
404
                energy_category_dict[row[0]] = {"id": row[0],
405
                                                "name": row[1],
406
                                                "uuid": row[2]}
407
408
        query = (" SELECT id, name, uuid, energy_category_id "
409
                 " FROM tbl_energy_items ")
410
        cursor.execute(query)
411
        rows_energy_items = cursor.fetchall()
412
413
        energy_item_dict = dict()
414
        if rows_energy_items is not None and len(rows_energy_items) > 0:
415
            for row in rows_energy_items:
416
                energy_item_dict[row[0]] = {"id": row[0],
417
                                            "name": row[1],
418
                                            "uuid": row[2]}
419
420
        query = (" SELECT id, name, uuid "
421
                 " FROM tbl_cost_centers ")
422
        cursor.execute(query)
423
        rows_cost_centers = cursor.fetchall()
424
425
        cost_center_dict = dict()
426
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
427
            for row in rows_cost_centers:
428
                cost_center_dict[row[0]] = {"id": row[0],
429
                                            "name": row[1],
430
                                            "uuid": row[2]}
431
432
        query = (" SELECT id, name, uuid, equation, energy_category_id, is_counted, cost_center_id, "
433
                 "        energy_item_id, description "
434
                 " FROM tbl_virtual_meters "
435
                 " WHERE id = %s ")
436
        cursor.execute(query, (id_,))
437
        row = cursor.fetchone()
438
        if row is None:
439
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
440
                                   description='API.VIRTUAL_METER_NOT_FOUND')
441
        else:
442
            meta_result = {"id": row[0],
443
                           "name": row[1],
444
                           "uuid": row[2],
445
                           "equation": row[3],
446
                           "energy_category": energy_category_dict.get(row[4], None),
447
                           "is_counted": True if row[5] else False,
448
                           "cost_center": cost_center_dict.get(row[6], None),
449
                           "energy_item": energy_item_dict.get(row[7], None),
450
                           "description": row[8],
451
                           "expression": {}}
452
453
        expression = dict()
454
455
        if meta_result["equation"] is not None:
456
            expression = {'equation': meta_result["equation"], 'variables': []}
457
458
            query_variables = (" SELECT v.id, v.name, v.meter_type, v.meter_id "
459
                               " FROM tbl_virtual_meters  vm, tbl_variables v "
460
                               " WHERE vm.id = %s AND v.virtual_meter_id = vm.id "
461
                               " ORDER BY v.name ")
462
            cursor.execute(query_variables, (meta_result["id"],))
463
            rows_variables = cursor.fetchall()
464
            if rows_variables is not None:
465
                for row_variable in rows_variables:
466
                    if row_variable[2].lower() == 'meter':
467
                        query_meter = (" SELECT m.name "
468
                                       " FROM tbl_meters m "
469
                                       " WHERE m.id = %s ")
470
                        cursor.execute(query_meter, (row_variable[3],))
471
                        row_meter = cursor.fetchone()
472
                        if row_meter is not None:
473
                            expression['variables'].append({'id': row_variable[0],
474
                                                            'name': row_variable[1],
475
                                                            'meter_type': row_variable[2],
476
                                                            'meter_id': row_variable[3],
477
                                                            'meter_name': row_meter[0]})
478
                    elif row_variable[2].lower() == 'offline_meter':
479
                        query_meter = (" SELECT m.name "
480
                                       " FROM tbl_offline_meters m "
481
                                       " WHERE m.id = %s ")
482
                        cursor.execute(query_meter, (row_variable[3],))
483
                        row_meter = cursor.fetchone()
484
                        if row_meter is not None:
485
                            expression['variables'].append({'id': row_variable[0],
486
                                                            'name': row_variable[1],
487
                                                            'meter_type': row_variable[2],
488
                                                            'meter_id': row_variable[3],
489
                                                            'meter_name': row_meter[0]})
490
                    elif row_variable[2].lower() == 'virtual_meter':
491
                        query_meter = (" SELECT m.name "
492
                                       " FROM tbl_virtual_meters m "
493
                                       " WHERE m.id = %s ")
494
                        cursor.execute(query_meter, (row_variable[3],))
495
                        row_meter = cursor.fetchone()
496
                        if row_meter is not None:
497
                            expression['variables'].append({'id': row_variable[0],
498
                                                            'name': row_variable[1],
499
                                                            'meter_type': row_variable[2],
500
                                                            'meter_id': row_variable[3],
501
                                                            'meter_name': row_meter[0]})
502
503
        meta_result['expression'] = expression
504
505
        cursor.close()
506
        cnx.close()
507
        resp.text = json.dumps(meta_result)
508
509
    @staticmethod
510
    @user_logger
511
    def on_delete(req, resp, id_):
512
        admin_control(req)
513
        if not id_.isdigit() or int(id_) <= 0:
514
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
515
                                   description='API.INVALID_VIRTUAL_METER_ID')
516
517
        cnx = mysql.connector.connect(**config.myems_system_db)
518
        cursor = cnx.cursor()
519
520
        cursor.execute(" SELECT uuid "
521
                       " FROM tbl_virtual_meters "
522
                       " WHERE id = %s ", (id_,))
523
        row = cursor.fetchone()
524
        if row is None:
525
            cursor.close()
526
            cnx.close()
527
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
528
                                   description='API.VIRTUAL_METER_NOT_FOUND')
529
        else:
530
            virtual_meter_uuid = row[0]
531
532
        # check relations with other virtual meters
533
        cursor.execute(" SELECT vm.name "
534
                       " FROM tbl_variables va, tbl_virtual_meters vm "
535
                       " WHERE va.meter_id = %s AND va.meter_type = 'virtual_meter' AND va.virtual_meter_id = vm.id ",
536
                       (id_,))
537
        rows_virtual_meters = cursor.fetchall()
538
        if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
539
            cursor.close()
540
            cnx.close()
541
            raise falcon.HTTPError(status=falcon.HTTP_400,
542
                                   title='API.BAD_REQUEST',
543
                                   description='API.THERE_IS_RELATION_WITH_OTHER_VIRTUAL_METERS')
544
545
        # check relation with spaces
546
        cursor.execute(" SELECT id "
547
                       " FROM tbl_spaces_virtual_meters "
548
                       " WHERE virtual_meter_id = %s ", (id_,))
549
        rows_spaces = cursor.fetchall()
550
        if rows_spaces is not None and len(rows_spaces) > 0:
551
            cursor.close()
552
            cnx.close()
553
            raise falcon.HTTPError(status=falcon.HTTP_400,
554
                                   title='API.BAD_REQUEST',
555
                                   description='API.THERE_IS_RELATION_WITH_SPACES')
556
557
        # check relation with combined equipments
558
        cursor.execute(" SELECT combined_equipment_id "
559
                       " FROM tbl_combined_equipments_virtual_meters "
560
                       " WHERE virtual_meter_id = %s ",
561
                       (id_,))
562
        rows_combined_equipments = cursor.fetchall()
563
        if rows_combined_equipments is not None and len(rows_combined_equipments) > 0:
564
            cursor.close()
565
            cnx.close()
566
            raise falcon.HTTPError(status=falcon.HTTP_400,
567
                                   title='API.BAD_REQUEST',
568
                                   description='API.THERE_IS_RELATION_WITH_COMBINED_EQUIPMENTS')
569
570
        # check relation with combined equipment parameters
571
        cursor.execute(" SELECT combined_equipment_id "
572
                       " FROM tbl_combined_equipments_parameters "
573
                       " WHERE numerator_meter_uuid = %s OR denominator_meter_uuid = %s",
574
                       (virtual_meter_uuid, virtual_meter_uuid,))
575
        rows_combined_equipments = cursor.fetchall()
576
        if rows_combined_equipments is not None and len(rows_combined_equipments) > 0:
577
            cursor.close()
578
            cnx.close()
579
            raise falcon.HTTPError(status=falcon.HTTP_400,
580
                                   title='API.BAD_REQUEST',
581
                                   description='API.THERE_IS_RELATION_WITH_COMBINED_EQUIPMENT_PARAMETERS')
582
583
        # check relation with equipments
584
        cursor.execute(" SELECT equipment_id "
585
                       " FROM tbl_equipments_virtual_meters "
586
                       " WHERE virtual_meter_id = %s ", (id_,))
587
        rows_equipments = cursor.fetchall()
588
        if rows_equipments is not None and len(rows_equipments) > 0:
589
            cursor.close()
590
            cnx.close()
591
            raise falcon.HTTPError(status=falcon.HTTP_400,
592
                                   title='API.BAD_REQUEST',
593
                                   description='API.THERE_IS_RELATION_WITH_EQUIPMENTS')
594
595
        # check relation with equipment parameters
596
        cursor.execute(" SELECT equipment_id "
597
                       " FROM tbl_equipments_parameters "
598
                       " WHERE numerator_meter_uuid = %s OR denominator_meter_uuid = %s",
599
                       (virtual_meter_uuid, virtual_meter_uuid,))
600
        rows_equipments = cursor.fetchall()
601
        if rows_equipments is not None and len(rows_equipments) > 0:
602
            cursor.close()
603
            cnx.close()
604
            raise falcon.HTTPError(status=falcon.HTTP_400,
605
                                   title='API.BAD_REQUEST',
606
                                   description='API.THERE_IS_RELATION_WITH_EQUIPMENT_PARAMETERS')
607
608
        # check relations with tenants
609
        cursor.execute(" SELECT tenant_id "
610
                       " FROM tbl_tenants_virtual_meters "
611
                       " WHERE virtual_meter_id = %s ", (id_,))
612
        rows_tenants = cursor.fetchall()
613
        if rows_tenants is not None and len(rows_tenants) > 0:
614
            cursor.close()
615
            cnx.close()
616
            raise falcon.HTTPError(status=falcon.HTTP_400,
617
                                   title='API.BAD_REQUEST',
618
                                   description='API.THERE_IS_RELATION_WITH_TENANTS')
619
620
        # check relations with stores
621
        cursor.execute(" SELECT store_id "
622
                       " FROM tbl_stores_virtual_meters "
623
                       " WHERE virtual_meter_id = %s ", (id_,))
624
        rows_stores = cursor.fetchall()
625
        if rows_stores is not None and len(rows_stores) > 0:
626
            cursor.close()
627
            cnx.close()
628
            raise falcon.HTTPError(status=falcon.HTTP_400,
629
                                   title='API.BAD_REQUEST',
630
                                   description='API.THERE_IS_RELATION_WITH_STORES')
631
632
        # check relations with shopfloors
633
        cursor.execute(" SELECT shopfloor_id "
634
                       " FROM tbl_shopfloors_virtual_meters "
635
                       " WHERE virtual_meter_id = %s ", (id_,))
636
        rows_shopfloors = cursor.fetchall()
637
        if rows_shopfloors is not None and len(rows_shopfloors) > 0:
638
            cursor.close()
639
            cnx.close()
640
            raise falcon.HTTPError(status=falcon.HTTP_400,
641
                                   title='API.BAD_REQUEST',
642
                                   description='API.THERE_IS_RELATION_WITH_SHOPFLOORS')
643
644
        # check relation with energy flow diagram links
645
        cursor.execute(" SELECT id "
646
                       " FROM tbl_energy_flow_diagrams_links "
647
                       " WHERE meter_uuid = %s ", (virtual_meter_uuid,))
648
        rows_links = cursor.fetchall()
649
        if rows_links is not None and len(rows_links) > 0:
650
            cursor.close()
651
            cnx.close()
652
            raise falcon.HTTPError(status=falcon.HTTP_400,
653
                                   title='API.BAD_REQUEST',
654
                                   description='API.THERE_IS_RELATION_WITH_ENERGY_FLOW_DIAGRAM_LINKS')
655
656
        cursor.execute(" SELECT id "
657
                       " FROM tbl_variables "
658
                       " WHERE virtual_meter_id = %s ", (id_,))
659
        row_variables = cursor.fetchall()
660
        if row_variables is not None and len(row_variables) > 0:
661
            # delete variables
662
            cursor.execute(" DELETE FROM tbl_variables WHERE virtual_meter_id = %s ", (id_,))
663
            cnx.commit()
664
665
        cursor.execute(" DELETE FROM tbl_virtual_meters WHERE id = %s ", (id_,))
666
        cnx.commit()
667
668
        cursor.close()
669
        cnx.close()
670
671
        resp.status = falcon.HTTP_204
672
673
    @staticmethod
674
    @user_logger
675
    def on_put(req, resp, id_):
676
        """Handles PUT requests"""
677
        admin_control(req)
678
        try:
679
            raw_json = req.stream.read().decode('utf-8')
680
        except UnicodeDecodeError as ex:
681
            print("Failed to decode request")
682
            raise falcon.HTTPError(status=falcon.HTTP_400,
683
                                   title='API.BAD_REQUEST',
684
                                   description='API.INVALID_ENCODING')
685
        except Exception as ex:
686
            print("Unexpected error reading request stream")
687
            raise falcon.HTTPError(status=falcon.HTTP_400,
688
                                   title='API.BAD_REQUEST',
689
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
690
691
        if not id_.isdigit() or int(id_) <= 0:
692
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
693
                                   description='API.INVALID_VIRTUAL_METER_ID')
694
695
        new_values = json.loads(raw_json)
696
697
        if 'name' not in new_values['data'].keys() or \
698
                not isinstance(new_values['data']['name'], str) or \
699
                len(str.strip(new_values['data']['name'])) == 0:
700
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
701
                                   description='API.INVALID_VIRTUAL_METER_NAME')
702
        name = str.strip(new_values['data']['name'])
703
704
        if 'energy_category_id' not in new_values['data'].keys() or new_values['data']['energy_category_id'] <= 0:
705
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
706
                                   description='API.INVALID_ENERGY_CATEGORY_ID')
707
        energy_category_id = new_values['data']['energy_category_id']
708
709
        if 'is_counted' not in new_values['data'].keys() or not isinstance(new_values['data']['is_counted'], bool):
710
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
711
                                   description='API.INVALID_IS_COUNTED_VALUE')
712
        is_counted = new_values['data']['is_counted']
713
714
        if 'cost_center_id' not in new_values['data'].keys() or \
715
                not isinstance(new_values['data']['cost_center_id'], int) or \
716
                new_values['data']['cost_center_id'] <= 0:
717
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
718
                                   description='API.INVALID_COST_CENTER_ID')
719
720
        cost_center_id = new_values['data']['cost_center_id']
721
722
        if 'energy_item_id' in new_values['data'].keys() and \
723
                new_values['data']['energy_item_id'] is not None:
724
            if not isinstance(new_values['data']['energy_item_id'], int) or \
725
                    new_values['data']['energy_item_id'] <= 0:
726
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
727
                                       description='API.INVALID_ENERGY_ITEM_ID')
728
            energy_item_id = new_values['data']['energy_item_id']
729
        else:
730
            energy_item_id = None
731
732
        if 'description' in new_values['data'].keys() and \
733
                new_values['data']['description'] is not None and \
734
                len(str(new_values['data']['description'])) > 0:
735
            description = str.strip(new_values['data']['description'])
736
        else:
737
            description = None
738
739
        if 'expression' not in new_values['data'].keys():
740
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
741
                                   description='API.INVALID_EXPRESSION_OBJECT')
742
743
        if 'equation' not in new_values['data']['expression'].keys() \
744
                or len(new_values['data']['expression']['equation']) == 0:
745
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
746
                                   description='API.INVALID_EQUATION_IN_EXPRESSION')
747
        # todo: validate equation with more rules
748
749
        if 'variables' not in new_values['data']['expression'].keys() \
750
                or len(new_values['data']['expression']['variables']) == 0:
751
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
752
                                   description='API.EMPTY_VARIABLES_ARRAY')
753
754
        for variable in new_values['data']['expression']['variables']:
755
            if 'name' not in variable.keys() or \
756
                    len(variable['name']) == 0:
757
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
758
                                       description='API.INVALID_VARIABLE_NAME')
759
            if 'meter_type' not in variable.keys() or \
760
                len(variable['meter_type']) == 0 or \
761
                    variable['meter_type'].lower() not in ['meter', 'offline_meter', 'virtual_meter']:
762
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
763
                                       description='API.INVALID_VARIABLE_METER_TYPE')
764
            if 'meter_id' not in variable.keys() or \
765
                    variable['meter_id'] <= 0:
766
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
767
                                       description='API.INVALID_VARIABLE_METER_ID')
768
769
        cnx = mysql.connector.connect(**config.myems_system_db)
770
        cursor = cnx.cursor()
771
772
        cursor.execute(" SELECT name "
773
                       " FROM tbl_virtual_meters "
774
                       " WHERE id = %s ", (id_,))
775
        if cursor.fetchone() is None:
776
            cursor.close()
777
            cnx.close()
778
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
779
                                   description='API.VIRTUAL_METER_NOT_FOUND')
780
781
        cursor.execute(" SELECT name "
782
                       " FROM tbl_virtual_meters "
783
                       " WHERE name = %s AND id != %s ", (name, id_))
784
        if cursor.fetchone() is not None:
785
            cursor.close()
786
            cnx.close()
787
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
788
                                   description='API.VIRTUAL_METER_NAME_IS_ALREADY_IN_USE')
789
790
        cursor.execute(" SELECT name "
791
                       " FROM tbl_energy_categories "
792
                       " WHERE id = %s ",
793
                       (energy_category_id,))
794
        if cursor.fetchone() is None:
795
            cursor.close()
796
            cnx.close()
797
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
798
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
799
800
        cursor.execute(" SELECT name "
801
                       " FROM tbl_cost_centers "
802
                       " WHERE id = %s ",
803
                       (new_values['data']['cost_center_id'],))
804
        row = cursor.fetchone()
805
        if row is None:
806
            cursor.close()
807
            cnx.close()
808
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
809
                                   description='API.COST_CENTER_NOT_FOUND')
810
811
        if energy_item_id is not None:
812
            cursor.execute(" SELECT name, energy_category_id "
813
                           " FROM tbl_energy_items "
814
                           " WHERE id = %s ",
815
                           (new_values['data']['energy_item_id'],))
816
            row = cursor.fetchone()
817
            if row is None:
818
                cursor.close()
819
                cnx.close()
820
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
821
                                       description='API.ENERGY_ITEM_NOT_FOUND')
822
            else:
823
                if row[1] != energy_category_id:
824
                    cursor.close()
825
                    cnx.close()
826
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
827
                                           description='API.ENERGY_ITEM_IS_NOT_BELONG_TO_ENERGY_CATEGORY')
828
829
        for variable in new_values['data']['expression']['variables']:
830 View Code Duplication
            if variable['meter_type'].lower() == 'meter':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
831
                cursor.execute(" SELECT name "
832
                               " FROM tbl_meters "
833
                               " WHERE id = %s ", (variable['meter_id'],))
834
                if cursor.fetchone() is None:
835
                    cursor.close()
836
                    cnx.close()
837
                    raise falcon.HTTPError(status=falcon.HTTP_404,
838
                                           title='API.NOT_FOUND',
839
                                           description='API.METER_OF_VARIABLE_NOT_FOUND')
840
            elif variable['meter_type'].lower() == 'offline_meter':
841
                cursor.execute(" SELECT name "
842
                               " FROM tbl_offline_meters "
843
                               " WHERE id = %s ", (variable['meter_id'],))
844
                if cursor.fetchone() is None:
845
                    cursor.close()
846
                    cnx.close()
847
                    raise falcon.HTTPError(status=falcon.HTTP_404,
848
                                           title='API.NOT_FOUND',
849
                                           description='API.OFFLINE_METER_OF_VARIABLE_NOT_FOUND')
850
            elif variable['meter_type'].lower() == 'virtual_meter':
851
                cursor.execute(" SELECT name "
852
                               " FROM tbl_virtual_meters "
853
                               " WHERE id = %s ", (variable['meter_id'],))
854
                if cursor.fetchone() is None:
855
                    cursor.close()
856
                    cnx.close()
857
                    raise falcon.HTTPError(status=falcon.HTTP_404,
858
                                           title='API.NOT_FOUND',
859
                                           description='API.VIRTUAL_METER_OF_VARIABLE_NOT_FOUND')
860
        try:
861
            update_row = (" UPDATE tbl_virtual_meters "
862
                          " SET name = %s, equation = %s, energy_category_id = %s, is_counted = %s, "
863
                          "     cost_center_id = %s, energy_item_id = %s, description = %s "
864
                          " WHERE id = %s ")
865
            cursor.execute(update_row, (name,
866
                                        new_values['data']['expression']['equation'].lower(),
867
                                        energy_category_id,
868
                                        is_counted,
869
                                        cost_center_id,
870
                                        energy_item_id,
871
                                        description,
872
                                        id_,))
873
            cnx.commit()
874
        except OperationalError as ex:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable OperationalError does not seem to be defined.
Loading history...
875
            print("Failed to SQL operate request")
876
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
877
        except ProgrammingError as ex:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ProgrammingError does not seem to be defined.
Loading history...
878
            print("Failed to SQL request")
879
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
880
        except DataError as ex:
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable DataError does not seem to be defined.
Loading history...
881
            print("Failed to SQL Data request")
882
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
883
        except Exception as ex:
884
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
885
886
        try:
887
            cursor.execute(" SELECT id "
888
                           " FROM tbl_variables "
889
                           " WHERE virtual_meter_id = %s ", (id_,))
890
            row_variables = cursor.fetchall()
891
            if row_variables is not None and len(row_variables) > 0:
892
                # delete variables
893
                cursor.execute(" DELETE FROM tbl_variables WHERE virtual_meter_id = %s ", (id_,))
894
                cnx.commit()
895
        except OperationalError as ex:
896
            print("Failed to SQL operate request")
897
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
898
        except ProgrammingError as ex:
899
            print("Failed to SQL request")
900
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
901
        except DataError as ex:
902
            print("Failed to SQL Data request")
903
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
904
        except Exception as ex:
905
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
906
907
        # add variables
908
        for variable in new_values['data']['expression']['variables']:
909
            try:
910
                add_values = (" INSERT INTO tbl_variables (name, virtual_meter_id, meter_type, meter_id) "
911
                              " VALUES (%s, %s, %s, %s) ")
912
                cursor.execute(add_values, (variable['name'].lower(),
913
                                            id_,
914
                                            variable['meter_type'],
915
                                            variable['meter_id'],))
916
                cnx.commit()
917
            except OperationalError as ex:
918
                print("Failed to SQL operate request")
919
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
920
            except ProgrammingError as ex:
921
                print("Failed to SQL request")
922
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
923
            except DataError as ex:
924
                print("Failed to SQL Data request")
925
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
926
            except Exception as ex:
927
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR', description=str(ex))
928
929
        cursor.close()
930
        cnx.close()
931
932
        resp.status = falcon.HTTP_200
933
934
935
class VirtualMeterExport:
936
    def __init__(self):
937
        pass
938
939
    @staticmethod
940
    def on_options(req, resp, id_):
941
        _ = req
942
        resp.status = falcon.HTTP_200
943
        _ = id_
944
945 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
946
    def on_get(req, resp, id_):
947
        if 'API-KEY' not in req.headers or \
948
                not isinstance(req.headers['API-KEY'], str) or \
949
                len(str.strip(req.headers['API-KEY'])) == 0:
950
            access_control(req)
951
        else:
952
            api_key_control(req)
953
        if not id_.isdigit() or int(id_) <= 0:
954
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
955
                                   description='API.INVALID_VIRTUAL_METER_ID')
956
957
        cnx = mysql.connector.connect(**config.myems_system_db)
958
        cursor = cnx.cursor()
959
960
        query = (" SELECT id, name, uuid "
961
                 " FROM tbl_energy_categories ")
962
        cursor.execute(query)
963
        rows_energy_categories = cursor.fetchall()
964
965
        energy_category_dict = dict()
966
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
967
            for row in rows_energy_categories:
968
                energy_category_dict[row[0]] = {"id": row[0],
969
                                                "name": row[1],
970
                                                "uuid": row[2]}
971
972
        query = (" SELECT id, name, uuid, energy_category_id "
973
                 " FROM tbl_energy_items ")
974
        cursor.execute(query)
975
        rows_energy_items = cursor.fetchall()
976
977
        energy_item_dict = dict()
978
        if rows_energy_items is not None and len(rows_energy_items) > 0:
979
            for row in rows_energy_items:
980
                energy_item_dict[row[0]] = {"id": row[0],
981
                                            "name": row[1],
982
                                            "uuid": row[2]}
983
984
        query = (" SELECT id, name, uuid "
985
                 " FROM tbl_cost_centers ")
986
        cursor.execute(query)
987
        rows_cost_centers = cursor.fetchall()
988
989
        cost_center_dict = dict()
990
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
991
            for row in rows_cost_centers:
992
                cost_center_dict[row[0]] = {"id": row[0],
993
                                            "name": row[1],
994
                                            "uuid": row[2]}
995
996
        query = (" SELECT id, name, uuid, equation, energy_category_id, is_counted, cost_center_id, "
997
                 "        energy_item_id, description "
998
                 " FROM tbl_virtual_meters "
999
                 " WHERE id = %s ")
1000
        cursor.execute(query, (id_,))
1001
        row = cursor.fetchone()
1002
        if row is None:
1003
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1004
                                   description='API.VIRTUAL_METER_NOT_FOUND')
1005
        else:
1006
            meta_result = {"id": row[0],
1007
                           "name": row[1],
1008
                           "uuid": row[2],
1009
                           "equation": row[3],
1010
                           "energy_category": energy_category_dict.get(row[4], None),
1011
                           "is_counted": True if row[5] else False,
1012
                           "cost_center": cost_center_dict.get(row[6], None),
1013
                           "energy_item": energy_item_dict.get(row[7], None),
1014
                           "description": row[8],
1015
                           "expression": {}}
1016
1017
        expression = dict()
1018
1019
        if meta_result["equation"] is not None:
1020
            expression = {'equation': meta_result["equation"], 'variables': []}
1021
1022
            query_variables = (" SELECT v.id, v.name, v.meter_type, v.meter_id "
1023
                               " FROM tbl_virtual_meters  vm, tbl_variables v "
1024
                               " WHERE vm.id = %s AND v.virtual_meter_id = vm.id "
1025
                               " ORDER BY v.name ")
1026
            cursor.execute(query_variables, (meta_result["id"],))
1027
            rows_variables = cursor.fetchall()
1028
            if rows_variables is not None:
1029
                for row_variable in rows_variables:
1030
                    if row_variable[2].lower() == 'meter':
1031
                        query_meter = (" SELECT m.name "
1032
                                       " FROM tbl_meters m "
1033
                                       " WHERE m.id = %s ")
1034
                        cursor.execute(query_meter, (row_variable[3],))
1035
                        row_meter = cursor.fetchone()
1036
                        if row_meter is not None:
1037
                            expression['variables'].append({'id': row_variable[0],
1038
                                                            'name': row_variable[1],
1039
                                                            'meter_type': row_variable[2],
1040
                                                            'meter_id': row_variable[3],
1041
                                                            'meter_name': row_meter[0]})
1042
                    elif row_variable[2].lower() == 'offline_meter':
1043
                        query_meter = (" SELECT m.name "
1044
                                       " FROM tbl_offline_meters m "
1045
                                       " WHERE m.id = %s ")
1046
                        cursor.execute(query_meter, (row_variable[3],))
1047
                        row_meter = cursor.fetchone()
1048
                        if row_meter is not None:
1049
                            expression['variables'].append({'id': row_variable[0],
1050
                                                            'name': row_variable[1],
1051
                                                            'meter_type': row_variable[2],
1052
                                                            'meter_id': row_variable[3],
1053
                                                            'meter_name': row_meter[0]})
1054
                    elif row_variable[2].lower() == 'virtual_meter':
1055
                        query_meter = (" SELECT m.name "
1056
                                       " FROM tbl_virtual_meters m "
1057
                                       " WHERE m.id = %s ")
1058
                        cursor.execute(query_meter, (row_variable[3],))
1059
                        row_meter = cursor.fetchone()
1060
                        if row_meter is not None:
1061
                            expression['variables'].append({'id': row_variable[0],
1062
                                                            'name': row_variable[1],
1063
                                                            'meter_type': row_variable[2],
1064
                                                            'meter_id': row_variable[3],
1065
                                                            'meter_name': row_meter[0]})
1066
1067
        meta_result['expression'] = expression
1068
1069
        cursor.close()
1070
        cnx.close()
1071
        resp.text = json.dumps(meta_result)
1072
1073
1074
class VirtualMeterImport:
1075
    def __init__(self):
1076
        pass
1077
1078
    @staticmethod
1079
    def on_options(req, resp):
1080
        _ = req
1081
        resp.status = falcon.HTTP_200
1082
1083
    @staticmethod
1084
    @user_logger
1085
    def on_post(req, resp):
1086
        """Handles POST requests"""
1087
        admin_control(req)
1088
        try:
1089
            raw_json = req.stream.read().decode('utf-8')
1090
        except UnicodeDecodeError as ex:
1091
            print("Failed to decode request")
1092
            raise falcon.HTTPError(status=falcon.HTTP_400,
1093
                                   title='API.BAD_REQUEST',
1094
                                   description='API.INVALID_ENCODING')
1095
        except Exception as ex:
1096
            print("Unexpected error reading request stream")
1097
            raise falcon.HTTPError(status=falcon.HTTP_400,
1098
                                   title='API.BAD_REQUEST',
1099
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1100
1101
        new_values = json.loads(raw_json)
1102
1103
        if 'name' not in new_values.keys() or \
1104
                not isinstance(new_values['name'], str) or \
1105
                len(str.strip(new_values['name'])) == 0:
1106
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1107
                                   description='API.INVALID_VIRTUAL_METER_NAME')
1108
1109
        name = str.strip(new_values['name'])
1110
1111
        if 'energy_category' not in new_values.keys() or \
1112
            'id' not in new_values['energy_category'].keys() or \
1113
            not isinstance(new_values['energy_category']['id'], int) or \
1114
                new_values['energy_category']['id'] <= 0:
1115
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1116
                                   description='API.INVALID_ENERGY_CATEGORY_ID')
1117
        energy_category_id = new_values['energy_category']['id']
1118
1119
        if 'is_counted' not in new_values.keys() or not isinstance(new_values['is_counted'], bool):
1120
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1121
                                   description='API.INVALID_IS_COUNTED_VALUE')
1122
        is_counted = new_values['is_counted']
1123
1124
        if 'cost_center' not in new_values.keys() or \
1125
            new_values['cost_center'] is None or \
1126
            'id' not in new_values['cost_center'].keys() or \
1127
                not isinstance(new_values['cost_center']['id'], int) or \
1128
                new_values['cost_center']['id'] <= 0:
1129
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1130
                                   description='API.INVALID_COST_CENTER_ID')
1131
1132
        cost_center_id = new_values['cost_center']['id']
1133
1134 View Code Duplication
        if 'energy_item' in new_values.keys() and \
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1135
            new_values['energy_item'] is not None and \
1136
                'id' in new_values['energy_item'].keys() and \
1137
                new_values['energy_item']['id'] is not None:
1138
            if not isinstance(new_values['energy_item']['id'], int) or \
1139
                    new_values['energy_item']['id'] <= 0:
1140
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1141
                                       description='API.INVALID_ENERGY_ITEM_ID')
1142
            energy_item_id = new_values['energy_item']['id']
1143
        else:
1144
            energy_item_id = None
1145
1146
        if 'description' in new_values.keys() and \
1147
                new_values['description'] is not None and \
1148
                len(str(new_values['description'])) > 0:
1149
            description = str.strip(new_values['description'])
1150
        else:
1151
            description = None
1152
1153
        if 'expression' not in new_values.keys():
1154
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1155
                                   description='API.INVALID_EXPRESSION_OBJECT')
1156
1157
        if 'equation' not in new_values['expression'].keys() \
1158
                or len(new_values['expression']['equation']) == 0:
1159
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1160
                                   description='API.INVALID_EQUATION_IN_EXPRESSION')
1161
        # todo: validate equation with more rules
1162
1163
        if 'variables' not in new_values['expression'].keys() \
1164
                or len(new_values['expression']['variables']) == 0:
1165
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1166
                                   description='API.EMPTY_VARIABLES_ARRAY')
1167
1168
        for variable in new_values['expression']['variables']:
1169
            if 'name' not in variable.keys() or \
1170
                    len(variable['name']) == 0:
1171
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1172
                                       description='API.INVALID_VARIABLE_NAME')
1173
            if 'meter_type' not in variable.keys() or \
1174
                    len(variable['meter_type']) == 0 or \
1175
                    variable['meter_type'].lower() not in ['meter', 'offline_meter', 'virtual_meter']:
1176
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1177
                                       description='API.INVALID_VARIABLE_METER_TYPE')
1178
            if 'meter_id' not in variable.keys() or \
1179
                    variable['meter_id'] <= 0:
1180
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1181
                                       description='API.INVALID_VARIABLE_METER_ID')
1182
1183
        cnx = mysql.connector.connect(**config.myems_system_db)
1184
        cursor = cnx.cursor()
1185
1186
        cursor.execute(" SELECT name "
1187
                       " FROM tbl_virtual_meters "
1188
                       " WHERE name = %s ", (name,))
1189
        if cursor.fetchone() is not None:
1190
            cursor.close()
1191
            cnx.close()
1192
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.NOT_FOUND',
1193
                                   description='API.VIRTUAL_METER_NAME_IS_ALREADY_IN_USE')
1194
1195
        cursor.execute(" SELECT name "
1196
                       " FROM tbl_energy_categories "
1197
                       " WHERE id = %s ",
1198
                       (energy_category_id,))
1199
        if cursor.fetchone() is None:
1200
            cursor.close()
1201
            cnx.close()
1202
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1203
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
1204
1205
        cursor.execute(" SELECT name "
1206
                       " FROM tbl_cost_centers "
1207
                       " WHERE id = %s ",
1208
                       (new_values['cost_center']['id'],))
1209
        row = cursor.fetchone()
1210
        if row is None:
1211
            cursor.close()
1212
            cnx.close()
1213
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1214
                                   description='API.COST_CENTER_NOT_FOUND')
1215
1216 View Code Duplication
        if energy_item_id is not None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1217
            cursor.execute(" SELECT name, energy_category_id "
1218
                           " FROM tbl_energy_items "
1219
                           " WHERE id = %s ",
1220
                           (new_values['energy_item']['id'],))
1221
            row = cursor.fetchone()
1222
            if row is None:
1223
                cursor.close()
1224
                cnx.close()
1225
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1226
                                       description='API.ENERGY_ITEM_NOT_FOUND')
1227
            else:
1228
                if row[1] != energy_category_id:
1229
                    cursor.close()
1230
                    cnx.close()
1231
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.BAD_REQUEST',
1232
                                           description='API.ENERGY_ITEM_IS_NOT_BELONG_TO_ENERGY_CATEGORY')
1233
1234
        for variable in new_values['expression']['variables']:
1235 View Code Duplication
            if variable['meter_type'].lower() == 'meter':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1236
                cursor.execute(" SELECT name "
1237
                               " FROM tbl_meters "
1238
                               " WHERE id = %s ", (variable['meter_id'],))
1239
                if cursor.fetchone() is None:
1240
                    cursor.close()
1241
                    cnx.close()
1242
                    raise falcon.HTTPError(status=falcon.HTTP_404,
1243
                                           title='API.NOT_FOUND',
1244
                                           description='API.METER_OF_VARIABLE_NOT_FOUND')
1245
            elif variable['meter_type'].lower() == 'offline_meter':
1246
                cursor.execute(" SELECT name "
1247
                               " FROM tbl_offline_meters "
1248
                               " WHERE id = %s ", (variable['meter_id'],))
1249
                if cursor.fetchone() is None:
1250
                    cursor.close()
1251
                    cnx.close()
1252
                    raise falcon.HTTPError(status=falcon.HTTP_404,
1253
                                           title='API.NOT_FOUND',
1254
                                           description='API.OFFLINE_METER_OF_VARIABLE_NOT_FOUND')
1255
            elif variable['meter_type'].lower() == 'virtual_meter':
1256
                cursor.execute(" SELECT name "
1257
                               " FROM tbl_virtual_meters "
1258
                               " WHERE id = %s ", (variable['meter_id'],))
1259
                if cursor.fetchone() is None:
1260
                    cursor.close()
1261
                    cnx.close()
1262
                    raise falcon.HTTPError(status=falcon.HTTP_404,
1263
                                           title='API.NOT_FOUND',
1264
                                           description='API.VIRTUAL_METER_OF_VARIABLE_NOT_FOUND')
1265
1266
        add_values = (" INSERT INTO tbl_virtual_meters "
1267
                      "     (name, uuid, equation, energy_category_id, is_counted, "
1268
                      "      cost_center_id, energy_item_id, description) "
1269
                      " VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ")
1270
        cursor.execute(add_values, (name,
1271
                                    str(uuid.uuid4()),
1272
                                    new_values['expression']['equation'].lower(),
1273
                                    energy_category_id,
1274
                                    is_counted,
1275
                                    cost_center_id,
1276
                                    energy_item_id,
1277
                                    description))
1278
        new_id = cursor.lastrowid
1279
        cnx.commit()
1280
1281
        # add variables
1282
        for variable in new_values['expression']['variables']:
1283
            add_values = (" INSERT INTO tbl_variables (name, virtual_meter_id, meter_type, meter_id) "
1284
                          " VALUES (%s, %s, %s, %s) ")
1285
            cursor.execute(add_values, (variable['name'].lower(),
1286
                                        new_id,
1287
                                        variable['meter_type'],
1288
                                        variable['meter_id'],))
1289
            cnx.commit()
1290
1291
        cursor.close()
1292
        cnx.close()
1293
1294
        resp.status = falcon.HTTP_201
1295
        resp.location = '/virtualmeters/' + str(new_id)
1296
1297
1298
class VirtualMeterClone:
1299
    def __init__(self):
1300
        pass
1301
1302
    @staticmethod
1303
    def on_options(req, resp, id_):
1304
        _ = req
1305
        resp.status = falcon.HTTP_200
1306
        _ = id_
1307
1308
    @staticmethod
1309
    @user_logger
1310
    def on_post(req, resp, id_):
1311
        admin_control(req)
1312
        if not id_.isdigit() or int(id_) <= 0:
1313
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1314
                                   description='API.INVALID_VIRTUAL_METER_ID')
1315
1316
        cnx = mysql.connector.connect(**config.myems_system_db)
1317
        cursor = cnx.cursor()
1318
1319
        query = (" SELECT id, name, uuid "
1320
                 " FROM tbl_energy_categories ")
1321
        cursor.execute(query)
1322
        rows_energy_categories = cursor.fetchall()
1323
1324
        energy_category_dict = dict()
1325
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
1326
            for row in rows_energy_categories:
1327
                energy_category_dict[row[0]] = {"id": row[0],
1328
                                                "name": row[1],
1329
                                                "uuid": row[2]}
1330
1331
        query = (" SELECT id, name, uuid, energy_category_id "
1332
                 " FROM tbl_energy_items ")
1333
        cursor.execute(query)
1334
        rows_energy_items = cursor.fetchall()
1335
1336
        energy_item_dict = dict()
1337
        if rows_energy_items is not None and len(rows_energy_items) > 0:
1338
            for row in rows_energy_items:
1339
                energy_item_dict[row[0]] = {"id": row[0],
1340
                                            "name": row[1],
1341
                                            "uuid": row[2]}
1342
1343
        query = (" SELECT id, name, uuid "
1344
                 " FROM tbl_cost_centers ")
1345
        cursor.execute(query)
1346
        rows_cost_centers = cursor.fetchall()
1347
1348
        cost_center_dict = dict()
1349
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
1350
            for row in rows_cost_centers:
1351
                cost_center_dict[row[0]] = {"id": row[0],
1352
                                            "name": row[1],
1353
                                            "uuid": row[2]}
1354
        query = (" SELECT id, name, uuid, equation, energy_category_id, is_counted, cost_center_id, "
1355
                 "        energy_item_id, description "
1356
                 " FROM tbl_virtual_meters "
1357
                 " WHERE id = %s ")
1358
        cursor.execute(query, (id_,))
1359
        row = cursor.fetchone()
1360
        if row is None:
1361
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1362
                                   description='API.VIRTUAL_METER_NOT_FOUND')
1363
        else:
1364
            meta_result = {"id": row[0],
1365
                           "name": row[1],
1366
                           "uuid": row[2],
1367
                           "equation": row[3],
1368
                           "energy_category": energy_category_dict.get(row[4], None),
1369
                           "is_counted": True if row[5] else False,
1370
                           "cost_center": cost_center_dict.get(row[6], None),
1371
                           "energy_item": energy_item_dict.get(row[7], None),
1372
                           "description": row[8],
1373
                           "expression": {}}
1374
1375
        expression = dict()
1376
1377
        if meta_result["equation"] is not None:
1378
            expression = {'equation': meta_result["equation"], 'variables': []}
1379
1380
            query_variables = (" SELECT v.id, v.name, v.meter_type, v.meter_id "
1381
                               " FROM tbl_virtual_meters  vm, tbl_variables v "
1382
                               " WHERE vm.id = %s AND v.virtual_meter_id = vm.id "
1383
                               " ORDER BY v.name ")
1384
            cursor.execute(query_variables, (meta_result["id"],))
1385
            rows_variables = cursor.fetchall()
1386
            if rows_variables is not None:
1387
                for row_variable in rows_variables:
1388
                    if row_variable[2].lower() == 'meter':
1389
                        query_meter = (" SELECT m.name "
1390
                                       " FROM tbl_meters m "
1391
                                       " WHERE m.id = %s ")
1392
                        cursor.execute(query_meter, (row_variable[3],))
1393
                        row_meter = cursor.fetchone()
1394
                        if row_meter is not None:
1395
                            expression['variables'].append({'id': row_variable[0],
1396
                                                            'name': row_variable[1],
1397
                                                            'meter_type': row_variable[2],
1398
                                                            'meter_id': row_variable[3],
1399
                                                            'meter_name': row_meter[0]})
1400
                    elif row_variable[2].lower() == 'offline_meter':
1401
                        query_meter = (" SELECT m.name "
1402
                                       " FROM tbl_offline_meters m "
1403
                                       " WHERE m.id = %s ")
1404
                        cursor.execute(query_meter, (row_variable[3],))
1405
                        row_meter = cursor.fetchone()
1406
                        if row_meter is not None:
1407
                            expression['variables'].append({'id': row_variable[0],
1408
                                                            'name': row_variable[1],
1409
                                                            'meter_type': row_variable[2],
1410
                                                            'meter_id': row_variable[3],
1411
                                                            'meter_name': row_meter[0]})
1412
                    elif row_variable[2].lower() == 'virtual_meter':
1413
                        query_meter = (" SELECT m.name "
1414
                                       " FROM tbl_virtual_meters m "
1415
                                       " WHERE m.id = %s ")
1416
                        cursor.execute(query_meter, (row_variable[3],))
1417
                        row_meter = cursor.fetchone()
1418
                        if row_meter is not None:
1419
                            expression['variables'].append({'id': row_variable[0],
1420
                                                            'name': row_variable[1],
1421
                                                            'meter_type': row_variable[2],
1422
                                                            'meter_id': row_variable[3],
1423
                                                            'meter_name': row_meter[0]})
1424
1425
        meta_result['expression'] = expression
1426
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
1427
        if config.utc_offset[0] == '-':
1428
            timezone_offset = -timezone_offset
1429
        new_name = (str.strip(meta_result['name']) +
1430
                    (datetime.utcnow() + timedelta(minutes=timezone_offset)).isoformat(sep='-', timespec='seconds'))
1431
        energy_item_id = meta_result['energy_item']['id'] if meta_result['energy_item'] is not None else None
1432
        add_values = (" INSERT INTO tbl_virtual_meters "
1433
                      "     (name, uuid, equation, energy_category_id, is_counted, "
1434
                      "      cost_center_id, energy_item_id, description) "
1435
                      " VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ")
1436
        cursor.execute(add_values, (new_name,
1437
                                    str(uuid.uuid4()),
1438
                                    meta_result['expression']['equation'].lower(),
1439
                                    meta_result['energy_category']['id'],
1440
                                    meta_result['is_counted'],
1441
                                    meta_result['cost_center']['id'],
1442
                                    energy_item_id,
1443
                                    meta_result['description']))
1444
        new_id = cursor.lastrowid
1445
        cnx.commit()
1446
1447
        # add variables
1448
        for variable in meta_result['expression']['variables']:
1449
            add_values = (" INSERT INTO tbl_variables (name, virtual_meter_id, meter_type, meter_id) "
1450
                          " VALUES (%s, %s, %s, %s) ")
1451
            cursor.execute(add_values, (variable['name'].lower(),
1452
                                        new_id,
1453
                                        variable['meter_type'],
1454
                                        variable['meter_id'],))
1455
            cnx.commit()
1456
1457
        cursor.close()
1458
        cnx.close()
1459
1460
        resp.status = falcon.HTTP_201
1461
        resp.location = '/virtualmeters/' + str(new_id)
1462
1463