core.virtualmeter   F
last analyzed

Complexity

Total Complexity 324

Size/Duplication

Total Lines 1594
Duplicated Lines 22.77 %

Importance

Changes 0
Metric Value
wmc 324
eloc 1211
dl 363
loc 1594
rs 0.8
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
F VirtualMeterItem.on_get() 47 164 32
F VirtualMeterImport.on_post() 58 216 53
A VirtualMeterImport.on_options() 0 4 1
F VirtualMeterItem.on_delete() 0 166 26
A VirtualMeterExport.__init__() 0 2 1
F VirtualMeterClone.on_post() 47 157 26
A VirtualMeterCollection.__init__() 0 3 1
A VirtualMeterCollection.on_options() 0 5 1
F VirtualMeterItem.on_put() 30 263 64
A VirtualMeterClone.__init__() 0 2 1
A VirtualMeterClone.on_options() 0 5 1
F VirtualMeterCollection.on_post() 47 208 47
F VirtualMeterCollection.on_get() 47 172 34
F VirtualMeterExport.on_get() 47 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

1 Function

Rating   Name   Duplication   Size   Complexity  
B clear_virtualmeter_cache() 40 40 6

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