core.virtualmeter   F
last analyzed

Complexity

Total Complexity 294

Size/Duplication

Total Lines 1420
Duplicated Lines 27.39 %

Importance

Changes 0
Metric Value
wmc 294
eloc 1086
dl 389
loc 1420
rs 1.256
c 0
b 0
f 0

18 Methods

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