Passed
Push — master ( f8b1cd...9ee003 )
by
unknown
10:23 queued 16s
created

core.meter.clear_meter_cache()   B

Complexity

Conditions 6

Size

Total Lines 46
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 31
dl 0
loc 46
rs 8.2026
c 0
b 0
f 0
cc 6
nop 1
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
def clear_meter_cache(meter_id=None):
12
    """
13
    Clear meter-related cache after data modification
14
15
    Args:
16
        meter_id: Meter ID (optional, for specific 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 meter list cache (all search query variations)
36
        list_cache_key_pattern = 'meter: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 meter item cache if meter_id is provided
42
        if meter_id:
43
            item_cache_key = f'meter:item:{meter_id}'
44
            redis_client.delete(item_cache_key)
45
            submeter_cache_key = f'meter:submeter:{meter_id}'
46
            redis_client.delete(submeter_cache_key)
47
            point_cache_key = f'meter:point:{meter_id}'
48
            redis_client.delete(point_cache_key)
49
            command_cache_key = f'meter:command:{meter_id}'
50
            redis_client.delete(command_cache_key)
51
            export_cache_key = f'meter:export:{meter_id}'
52
            redis_client.delete(export_cache_key)
53
54
    except Exception:
55
        # If cache clear fails, ignore and continue
56
        pass
57
58
59
class MeterCollection:
60
    """
61
    Meter Collection Resource
62
63
    This class handles CRUD operations for meter collection.
64
    It provides endpoints for listing all meters and creating new meters.
65
    Meters are used for measuring energy consumption and production.
66
    """
67
    def __init__(self):
68
        """Initialize MeterCollection"""
69
        pass
70
71
    @staticmethod
72
    def on_options(req, resp):
73
        """Handle OPTIONS requests for CORS preflight"""
74
        _ = req
75
        resp.status = falcon.HTTP_200
76
77
    @staticmethod
78
    def on_get(req, resp):
79
        if 'API-KEY' not in req.headers or \
80
                not isinstance(req.headers['API-KEY'], str) or \
81
                len(str.strip(req.headers['API-KEY'])) == 0:
82
            access_control(req)
83
        else:
84
            api_key_control(req)
85
86
        search_query = req.get_param('q', default=None)
87
        if search_query is not None:
88
            search_query = search_query.strip()
89
        else:
90
            search_query = ''
91
92
        # Redis cache key
93
        cache_key = f'meter:list:{search_query}'
94
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
95
96
        # Try to get from Redis cache (only if Redis is enabled)
97
        redis_client = None
98
        if config.redis.get('is_enabled', False):
99
            try:
100
                redis_client = redis.Redis(
101
                    host=config.redis['host'],
102
                    port=config.redis['port'],
103
                    password=config.redis['password'] if config.redis['password'] else None,
104
                    db=config.redis['db'],
105
                    decode_responses=True,
106
                    socket_connect_timeout=2,
107
                    socket_timeout=2
108
                )
109
                redis_client.ping()
110
                cached_result = redis_client.get(cache_key)
111
                if cached_result:
112
                    resp.text = cached_result
113
                    return
114
            except Exception:
115
                # If Redis connection fails, continue to database query
116
                pass
117
118
        # Cache miss or Redis error - query database
119
        cnx = mysql.connector.connect(**config.myems_system_db)
120
        cursor = cnx.cursor()
121
122
        query = (" SELECT id, name, uuid "
123
                 " FROM tbl_energy_categories ")
124
        cursor.execute(query)
125
        rows_energy_categories = cursor.fetchall()
126
127
        energy_category_dict = dict()
128
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
129
            for row in rows_energy_categories:
130
                energy_category_dict[row[0]] = {"id": row[0],
131
                                                "name": row[1],
132
                                                "uuid": row[2]}
133
134
        query = (" SELECT id, name, uuid "
135
                 " FROM tbl_cost_centers ")
136
        cursor.execute(query)
137
        rows_cost_centers = cursor.fetchall()
138
139
        cost_center_dict = dict()
140
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
141
            for row in rows_cost_centers:
142
                cost_center_dict[row[0]] = {"id": row[0],
143
                                            "name": row[1],
144
                                            "uuid": row[2]}
145
146
        query = (" SELECT id, name, uuid "
147
                 " FROM tbl_energy_items ")
148
        cursor.execute(query)
149
        rows_energy_items = cursor.fetchall()
150
151
        energy_item_dict = dict()
152
        if rows_energy_items is not None and len(rows_energy_items) > 0:
153
            for row in rows_energy_items:
154
                energy_item_dict[row[0]] = {"id": row[0],
155
                                            "name": row[1],
156
                                            "uuid": row[2]}
157
158
        query = (" SELECT id, name, uuid "
159
                 " FROM tbl_meters ")
160
        cursor.execute(query)
161
        rows_master_meters = cursor.fetchall()
162
163
        master_meter_dict = dict()
164
        if rows_master_meters is not None and len(rows_master_meters) > 0:
165
            for row in rows_master_meters:
166
                master_meter_dict[row[0]] = {"id": row[0],
167
                                             "name": row[1],
168
                                             "uuid": row[2]}
169
170
        query_base = (" SELECT id, name, uuid, energy_category_id, "
171
                      "        is_counted, hourly_low_limit, hourly_high_limit, "
172
                      "        cost_center_id, energy_item_id, master_meter_id, description "
173
                      " FROM tbl_meters ")
174
        params = []
175
        if search_query:
176
            query_base += " WHERE name LIKE %s OR description LIKE %s "
177
            params = [f'%{search_query}%', f'%{search_query}%']
178
179
        query_base += " ORDER BY id "
180
        cursor.execute(query_base, params)
181
        rows_meters = cursor.fetchall()
182
183
        result = list()
184
        if rows_meters is not None and len(rows_meters) > 0:
185
            for row in rows_meters:
186
                meta_result = {"id": row[0],
187
                               "name": row[1],
188
                               "uuid": row[2],
189
                               "energy_category": energy_category_dict.get(row[3], None),
190
                               "is_counted": True if row[4] else False,
191
                               "hourly_low_limit": row[5],
192
                               "hourly_high_limit": row[6],
193
                               "cost_center": cost_center_dict.get(row[7], None),
194
                               "energy_item": energy_item_dict.get(row[8], None),
195
                               "master_meter": master_meter_dict.get(row[9], None),
196
                               "description": row[10],
197
                               "qrcode": "meter:" + row[2]}
198
                result.append(meta_result)
199
200
        cursor.close()
201
        cnx.close()
202
203
        # Store result in Redis cache
204
        result_json = json.dumps(result)
205
        if redis_client:
206
            try:
207
                redis_client.setex(cache_key, cache_expire, result_json)
208
            except Exception:
209
                # If cache set fails, ignore and continue
210
                pass
211
212
        resp.text = result_json
213
214
    @staticmethod
215
    @user_logger
216
    def on_post(req, resp):
217
        """Handles POST requests"""
218
        admin_control(req)
219
        try:
220
            raw_json = req.stream.read().decode('utf-8')
221
        except UnicodeDecodeError as ex:
222
            print("Failed to decode request")
223
            raise falcon.HTTPError(status=falcon.HTTP_400,
224
                                   title='API.BAD_REQUEST',
225
                                   description='API.INVALID_ENCODING')
226
        except Exception as ex:
227
            print("Unexpected error reading request stream")
228
            raise falcon.HTTPError(status=falcon.HTTP_400,
229
                                   title='API.BAD_REQUEST',
230
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
231
232
        new_values = json.loads(raw_json)
233
234
        if 'name' not in new_values['data'].keys() or \
235
                not isinstance(new_values['data']['name'], str) or \
236
                len(str.strip(new_values['data']['name'])) == 0:
237
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
238
                                   description='API.INVALID_METER_NAME')
239
        name = str.strip(new_values['data']['name'])
240
241
        if 'energy_category_id' not in new_values['data'].keys() or \
242
                not isinstance(new_values['data']['energy_category_id'], int) or \
243
                new_values['data']['energy_category_id'] <= 0:
244
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
245
                                   description='API.INVALID_ENERGY_CATEGORY_ID')
246
        energy_category_id = new_values['data']['energy_category_id']
247
248
        if 'is_counted' not in new_values['data'].keys() or \
249
                not isinstance(new_values['data']['is_counted'], bool):
250
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
251
                                   description='API.INVALID_IS_COUNTED_VALUE')
252
        is_counted = new_values['data']['is_counted']
253
254
        if 'hourly_low_limit' not in new_values['data'].keys() or \
255
                not (isinstance(new_values['data']['hourly_low_limit'], float) or
256
                     isinstance(new_values['data']['hourly_low_limit'], int)):
257
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
258
                                   description='API.INVALID_HOURLY_LOW_LIMIT_VALUE')
259
        hourly_low_limit = new_values['data']['hourly_low_limit']
260
261
        if 'hourly_high_limit' not in new_values['data'].keys() or \
262
                not (isinstance(new_values['data']['hourly_high_limit'], float) or
263
                     isinstance(new_values['data']['hourly_high_limit'], int)):
264
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
265
                                   description='API.INVALID_HOURLY_HIGH_LIMIT_VALUE')
266
        hourly_high_limit = new_values['data']['hourly_high_limit']
267
268
        if 'cost_center_id' not in new_values['data'].keys() or \
269
                not isinstance(new_values['data']['cost_center_id'], int) or \
270
                new_values['data']['cost_center_id'] <= 0:
271
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
272
                                   description='API.INVALID_COST_CENTER_ID')
273
        cost_center_id = new_values['data']['cost_center_id']
274
275
        if 'energy_item_id' in new_values['data'].keys() and \
276
                new_values['data']['energy_item_id'] is not None:
277
            if not isinstance(new_values['data']['energy_item_id'], int) or \
278
                    new_values['data']['energy_item_id'] <= 0:
279
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
280
                                       description='API.INVALID_ENERGY_ITEM_ID')
281
            energy_item_id = new_values['data']['energy_item_id']
282
        else:
283
            energy_item_id = None
284
285
        if 'master_meter_id' in new_values['data'].keys():
286
            if not isinstance(new_values['data']['master_meter_id'], int) or \
287
                    new_values['data']['master_meter_id'] <= 0:
288
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
289
                                       description='API.INVALID_MASTER_METER_ID')
290
            master_meter_id = new_values['data']['master_meter_id']
291
        else:
292
            master_meter_id = None
293
294
        if 'description' in new_values['data'].keys() and \
295
                new_values['data']['description'] is not None and \
296
                len(str(new_values['data']['description'])) > 0:
297
            description = str.strip(new_values['data']['description'])
298
        else:
299
            description = None
300
301
        cnx = mysql.connector.connect(**config.myems_system_db)
302
        cursor = cnx.cursor()
303
304
        cursor.execute(" SELECT name "
305
                       " FROM tbl_meters "
306
                       " WHERE name = %s ", (name,))
307
        if cursor.fetchone() is not None:
308
            cursor.close()
309
            cnx.close()
310
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
311
                                   description='API.METER_NAME_IS_ALREADY_IN_USE')
312
313
        cursor.execute(" SELECT name "
314
                       " FROM tbl_energy_categories "
315
                       " WHERE id = %s ",
316
                       (new_values['data']['energy_category_id'],))
317
        if cursor.fetchone() is None:
318
            cursor.close()
319
            cnx.close()
320
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
321
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
322
323
        cursor.execute(" SELECT name "
324
                       " FROM tbl_cost_centers "
325
                       " WHERE id = %s ",
326
                       (new_values['data']['cost_center_id'],))
327
        row = cursor.fetchone()
328
        if row is None:
329
            cursor.close()
330
            cnx.close()
331
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
332
                                   description='API.COST_CENTER_NOT_FOUND')
333
334 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...
335
            cursor.execute(" SELECT name, energy_category_id "
336
                           " FROM tbl_energy_items "
337
                           " WHERE id = %s ",
338
                           (new_values['data']['energy_item_id'],))
339
            row = cursor.fetchone()
340
            if row is None:
341
                cursor.close()
342
                cnx.close()
343
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
344
                                       description='API.ENERGY_ITEM_NOT_FOUND')
345
            else:
346
                if row[1] != energy_category_id:
347
                    cursor.close()
348
                    cnx.close()
349
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.BAD_REQUEST',
350
                                           description='API.ENERGY_ITEM_DOES_NOT_BELONG_TO_ENERGY_CATEGORY')
351
352 View Code Duplication
        if master_meter_id is not None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
353
            cursor.execute(" SELECT name, energy_category_id "
354
                           " FROM tbl_meters "
355
                           " WHERE id = %s ",
356
                           (new_values['data']['master_meter_id'],))
357
            row = cursor.fetchone()
358
            if row is None:
359
                cursor.close()
360
                cnx.close()
361
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
362
                                       description='API.MASTER_METER_NOT_FOUND')
363
            else:
364
                if row[1] != energy_category_id:
365
                    cursor.close()
366
                    cnx.close()
367
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.BAD_REQUEST',
368
                                           description='API.MASTER_METER_DOES_NOT_BELONG_TO_SAME_ENERGY_CATEGORY')
369
370
        add_values = (" INSERT INTO tbl_meters "
371
                      "    (name, uuid, energy_category_id, is_counted, hourly_low_limit, hourly_high_limit,"
372
                      "     cost_center_id, energy_item_id, master_meter_id, description) "
373
                      " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
374
        cursor.execute(add_values, (name,
375
                                    str(uuid.uuid4()),
376
                                    energy_category_id,
377
                                    is_counted,
378
                                    hourly_low_limit,
379
                                    hourly_high_limit,
380
                                    cost_center_id,
381
                                    energy_item_id,
382
                                    master_meter_id,
383
                                    description))
384
        new_id = cursor.lastrowid
385
        cnx.commit()
386
        cursor.close()
387
        cnx.close()
388
389
        # Clear cache after creating new meter
390
        clear_meter_cache()
391
392
        resp.status = falcon.HTTP_201
393
        resp.location = '/meters/' + str(new_id)
394
395
396
class MeterItem:
397
    def __init__(self):
398
        pass
399
400
    @staticmethod
401
    def on_options(req, resp, id_):
402
        _ = req
403
        _ = id_
404
        resp.status = falcon.HTTP_200
405
406
    @staticmethod
407
    def on_get(req, resp, id_):
408
        if 'API-KEY' not in req.headers or \
409
                not isinstance(req.headers['API-KEY'], str) or \
410
                len(str.strip(req.headers['API-KEY'])) == 0:
411
            access_control(req)
412
        else:
413
            api_key_control(req)
414
        if not id_.isdigit() or int(id_) <= 0:
415
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
416
                                   description='API.INVALID_METER_ID')
417
418
        # Redis cache key
419
        cache_key = f'meter:item:{id_}'
420
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
421
422
        # Try to get from Redis cache (only if Redis is enabled)
423
        redis_client = None
424
        if config.redis.get('is_enabled', False):
425
            try:
426
                redis_client = redis.Redis(
427
                    host=config.redis['host'],
428
                    port=config.redis['port'],
429
                    password=config.redis['password'] if config.redis['password'] else None,
430
                    db=config.redis['db'],
431
                    decode_responses=True,
432
                    socket_connect_timeout=2,
433
                    socket_timeout=2
434
                )
435
                redis_client.ping()
436
                cached_result = redis_client.get(cache_key)
437
                if cached_result:
438
                    resp.text = cached_result
439
                    return
440
            except Exception:
441
                # If Redis connection fails, continue to database query
442
                pass
443
444
        # Cache miss or Redis error - query database
445
        cnx = mysql.connector.connect(**config.myems_system_db)
446
        cursor = cnx.cursor()
447
448
        query = (" SELECT id, name, uuid "
449
                 " FROM tbl_energy_categories ")
450
        cursor.execute(query)
451
        rows_energy_categories = cursor.fetchall()
452
453
        energy_category_dict = dict()
454
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
455
            for row in rows_energy_categories:
456
                energy_category_dict[row[0]] = {"id": row[0],
457
                                                "name": row[1],
458
                                                "uuid": row[2]}
459
460
        query = (" SELECT id, name, uuid "
461
                 " FROM tbl_cost_centers ")
462
        cursor.execute(query)
463
        rows_cost_centers = cursor.fetchall()
464
465
        cost_center_dict = dict()
466
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
467
            for row in rows_cost_centers:
468
                cost_center_dict[row[0]] = {"id": row[0],
469
                                            "name": row[1],
470
                                            "uuid": row[2]}
471
472
        query = (" SELECT id, name, uuid "
473
                 " FROM tbl_energy_items ")
474
        cursor.execute(query)
475
        rows_energy_items = cursor.fetchall()
476
477
        energy_item_dict = dict()
478
        if rows_energy_items is not None and len(rows_energy_items) > 0:
479
            for row in rows_energy_items:
480
                energy_item_dict[row[0]] = {"id": row[0],
481
                                            "name": row[1],
482
                                            "uuid": row[2]}
483
484
        query = (" SELECT id, name, uuid "
485
                 " FROM tbl_meters ")
486
        cursor.execute(query)
487
        rows_master_meters = cursor.fetchall()
488
489
        master_meter_dict = dict()
490
        if rows_master_meters is not None and len(rows_master_meters) > 0:
491
            for row in rows_master_meters:
492
                master_meter_dict[row[0]] = {"id": row[0],
493
                                             "name": row[1],
494
                                             "uuid": row[2]}
495
496
        query = (" SELECT id, name, uuid, energy_category_id, "
497
                 "        is_counted, hourly_low_limit, hourly_high_limit, "
498
                 "        cost_center_id, energy_item_id, master_meter_id, description "
499
                 " FROM tbl_meters "
500
                 " WHERE id = %s ")
501
        cursor.execute(query, (id_,))
502
        row = cursor.fetchone()
503
        cursor.close()
504
        cnx.close()
505
506
        if row is None:
507
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
508
                                   description='API.METER_NOT_FOUND')
509
        else:
510
            meta_result = {"id": row[0],
511
                           "name": row[1],
512
                           "uuid": row[2],
513
                           "energy_category": energy_category_dict.get(row[3], None),
514
                           "is_counted": True if row[4] else False,
515
                           "hourly_low_limit": row[5],
516
                           "hourly_high_limit": row[6],
517
                           "cost_center": cost_center_dict.get(row[7], None),
518
                           "energy_item": energy_item_dict.get(row[8], None),
519
                           "master_meter": master_meter_dict.get(row[9], None),
520
                           "description": row[10],
521
                           "qrcode": "meter:"+row[2]}
522
523
        # Store result in Redis cache
524
        result_json = json.dumps(meta_result)
525
        if redis_client:
526
            try:
527
                redis_client.setex(cache_key, cache_expire, result_json)
528
            except Exception:
529
                # If cache set fails, ignore and continue
530
                pass
531
532
        resp.text = result_json
533
534
    @staticmethod
535
    @user_logger
536
    def on_delete(req, resp, id_):
537
        admin_control(req)
538
        if not id_.isdigit() or int(id_) <= 0:
539
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
540
                                   description='API.INVALID_METER_ID')
541
542
        cnx = mysql.connector.connect(**config.myems_system_db)
543
        cursor = cnx.cursor()
544
545
        cursor.execute(" SELECT uuid "
546
                       " FROM tbl_meters "
547
                       " WHERE id = %s ", (id_,))
548
        row = cursor.fetchone()
549
        if row is None:
550
            cursor.close()
551
            cnx.close()
552
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
553
                                   description='API.METER_NOT_FOUND')
554
        else:
555
            meter_uuid = row[0]
556
557
        # check relation with tbl_energy_storage_containers_batteries
558
        cursor.execute("SELECT name "
559
                       "FROM tbl_energy_storage_containers_batteries "
560
                       "WHERE charge_meter_id = %s "
561
                       "   OR discharge_meter_id = %s "
562
                       "LIMIT 1",
563
                       (id_, id_))
564
        if cursor.fetchone() is not None:
565
            cursor.close()
566
            cnx.close()
567
            raise falcon.HTTPError(status=falcon.HTTP_400,
568
                                   title='API.BAD_REQUEST',
569
                                   description='API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_BATTERIES')
570
571
        # check relation with tbl_energy_storage_containers_grids
572
        cursor.execute("SELECT name "
573
                       "FROM tbl_energy_storage_containers_grids "
574
                       "WHERE buy_meter_id = %s "
575
                       "   OR sell_meter_id = %s "
576
                       "LIMIT 1",
577
                       (id_, id_))
578
        if cursor.fetchone() is not None:
579
            cursor.close()
580
            cnx.close()
581
            raise falcon.HTTPError(status=falcon.HTTP_400,
582
                                   title='API.BAD_REQUEST',
583
                                   description='API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_GRIDS')
584
585
        # check relation with tbl_energy_storage_containers_loads
586
        cursor.execute("SELECT name "
587
                       "FROM tbl_energy_storage_containers_loads "
588
                       "WHERE meter_id = %s "
589
                       "LIMIT 1",
590
                       (id_,))
591
        if cursor.fetchone() is not None:
592
            cursor.close()
593
            cnx.close()
594
            raise falcon.HTTPError(status=falcon.HTTP_400,
595
                                   title='API.BAD_REQUEST',
596
                                   description='API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_LOADS')
597
598
        # check relation with tbl_meters
599
        cursor.execute("SELECT name "
600
                       "FROM tbl_meters "
601
                       "WHERE master_meter_id = %s "
602
                       "LIMIT 1",
603
                       (id_,))
604
        if cursor.fetchone() is not None:
605
            cursor.close()
606
            cnx.close()
607
            raise falcon.HTTPError(status=falcon.HTTP_400,
608
                                   title='API.BAD_REQUEST',
609
                                   description='API.THERE_IS_RELATION_WITH_METERS')
610
611
        # check relation with tbl_photovoltaic_power_stations_grids
612
        cursor.execute("SELECT name "
613
                       "FROM tbl_photovoltaic_power_stations_grids "
614
                       "WHERE buy_meter_id = %s "
615
                       "   OR sell_meter_id = %s "
616
                       "LIMIT 1",
617
                       (id_, id_))
618
        if cursor.fetchone() is not None:
619
            cursor.close()
620
            cnx.close()
621
            raise falcon.HTTPError(status=falcon.HTTP_400,
622
                                   title='API.BAD_REQUEST',
623
                                   description='API.THERE_IS_RELATION_WITH_PHOTOVOLTAIC_POWER_STATIONS_GRIDS')
624
625
        # check relation with tbl_photovoltaic_power_stations_invertors
626
        cursor.execute("SELECT name "
627
                       "FROM tbl_photovoltaic_power_stations_invertors "
628
                       "WHERE generation_meter_id = %s "
629
                       "LIMIT 1",
630
                       (id_,))
631
        if cursor.fetchone() is not None:
632
            cursor.close()
633
            cnx.close()
634
            raise falcon.HTTPError(status=falcon.HTTP_400,
635
                                   title='API.BAD_REQUEST',
636
                                   description='API.THERE_IS_RELATION_WITH_PHOTOVOLTAIC_POWER_STATIONS_INVERTORS')
637
638
        # check relation with tbl_photovoltaic_power_stations_loads
639
        cursor.execute("SELECT name "
640
                       "FROM tbl_photovoltaic_power_stations_loads "
641
                       "WHERE meter_id = %s "
642
                       "LIMIT 1",
643
                       (id_,))
644
        if cursor.fetchone() is not None:
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_PHOTOVOLTAIC_POWER_STATIONS_LOADS')
650
651
        # check if this meter is being used by virtual meters
652
        cursor.execute(" SELECT vm.name "
653
                       " FROM tbl_variables va, tbl_virtual_meters vm "
654
                       " WHERE va.meter_id = %s AND va.meter_type = 'meter' AND va.virtual_meter_id = vm.id ",
655
                       (id_,))
656
        rows_virtual_meters = cursor.fetchall()
657
        if rows_virtual_meters is not None and len(rows_virtual_meters)>0:
658
            cursor.close()
659
            cnx.close()
660
            raise falcon.HTTPError(status=falcon.HTTP_400,
661
                                   title='API.BAD_REQUEST',
662
                                   description='API.THIS_METER_IS_BEING_USED_BY_A_VIRTUAL_METER')
663
664
        # check relation with child meters
665
        cursor.execute(" SELECT id "
666
                       " FROM tbl_meters "
667
                       " WHERE master_meter_id = %s ", (id_,))
668
        rows_child_meters = cursor.fetchall()
669
        if rows_child_meters is not None and len(rows_child_meters) > 0:
670
            cursor.close()
671
            cnx.close()
672
            raise falcon.HTTPError(status=falcon.HTTP_400,
673
                                   title='API.BAD_REQUEST',
674
                                   description='API.THERE_IS_RELATION_WITH_CHILD_METERS')
675
676
        # check relation with spaces
677
        cursor.execute(" SELECT id "
678
                       " FROM tbl_spaces_meters "
679
                       " WHERE meter_id = %s ", (id_,))
680
        rows_spaces = cursor.fetchall()
681
        if rows_spaces is not None and len(rows_spaces) > 0:
682
            cursor.close()
683
            cnx.close()
684
            raise falcon.HTTPError(status=falcon.HTTP_400,
685
                                   title='API.BAD_REQUEST',
686
                                   description='API.THERE_IS_RELATION_WITH_SPACES')
687
688
        # check relation with tenants
689
        cursor.execute(" SELECT tenant_id "
690
                       " FROM tbl_tenants_meters "
691
                       " WHERE meter_id = %s ", (id_,))
692
        rows_tenants = cursor.fetchall()
693
        if rows_tenants is not None and len(rows_tenants) > 0:
694
            cursor.close()
695
            cnx.close()
696
            raise falcon.HTTPError(status=falcon.HTTP_400,
697
                                   title='API.BAD_REQUEST',
698
                                   description='API.THERE_IS_RELATION_WITH_TENANTS')
699
700
        # check relation with stores
701
        cursor.execute(" SELECT store_id "
702
                       " FROM tbl_stores_meters "
703
                       " WHERE meter_id = %s ", (id_,))
704
        rows_stores = cursor.fetchall()
705
        if rows_stores is not None and len(rows_stores) > 0:
706
            cursor.close()
707
            cnx.close()
708
            raise falcon.HTTPError(status=falcon.HTTP_400,
709
                                   title='API.BAD_REQUEST',
710
                                   description='API.THERE_IS_RELATION_WITH_STORES')
711
712
        # check relation with shopfloors
713
        cursor.execute(" SELECT shopfloor_id "
714
                       " FROM tbl_shopfloors_meters "
715
                       " WHERE meter_id = %s ", (id_,))
716
        rows_shopfloors = cursor.fetchall()
717
        if rows_shopfloors is not None and len(rows_shopfloors) > 0:
718
            cursor.close()
719
            cnx.close()
720
            raise falcon.HTTPError(status=falcon.HTTP_400,
721
                                   title='API.BAD_REQUEST',
722
                                   description='API.THERE_IS_RELATION_WITH_SHOPFLOORS')
723
724
        # check relation with combined equipments
725
        cursor.execute(" SELECT combined_equipment_id "
726
                       " FROM tbl_combined_equipments_meters "
727
                       " WHERE meter_id = %s ",
728
                       (id_,))
729
        rows_combined_equipments = cursor.fetchall()
730
        if rows_combined_equipments is not None and len(rows_combined_equipments) > 0:
731
            cursor.close()
732
            cnx.close()
733
            raise falcon.HTTPError(status=falcon.HTTP_400,
734
                                   title='API.BAD_REQUEST',
735
                                   description='API.THERE_IS_RELATION_WITH_COMBINED_EQUIPMENTS')
736
737
        # check relation with combined equipment parameters
738
        cursor.execute(" SELECT combined_equipment_id "
739
                       " FROM tbl_combined_equipments_parameters "
740
                       " WHERE numerator_meter_uuid = %s OR denominator_meter_uuid = %s", (meter_uuid, meter_uuid,))
741
        rows_combined_equipments = cursor.fetchall()
742
        if rows_combined_equipments is not None and len(rows_combined_equipments) > 0:
743
            cursor.close()
744
            cnx.close()
745
            raise falcon.HTTPError(status=falcon.HTTP_400,
746
                                   title='API.BAD_REQUEST',
747
                                   description='API.THERE_IS_RELATION_WITH_COMBINED_EQUIPMENT_PARAMETERS')
748
749
        # check relation with equipments
750
        cursor.execute(" SELECT equipment_id "
751
                       " FROM tbl_equipments_meters "
752
                       " WHERE meter_id = %s ", (id_,))
753
        rows_equipments = cursor.fetchall()
754
        if rows_equipments is not None and len(rows_equipments) > 0:
755
            cursor.close()
756
            cnx.close()
757
            raise falcon.HTTPError(status=falcon.HTTP_400,
758
                                   title='API.BAD_REQUEST',
759
                                   description='API.THERE_IS_RELATION_WITH_EQUIPMENTS')
760
761
        # check relation with equipment parameters
762
        cursor.execute(" SELECT equipment_id "
763
                       " FROM tbl_equipments_parameters "
764
                       " WHERE numerator_meter_uuid = %s OR denominator_meter_uuid = %s", (meter_uuid, meter_uuid, ))
765
        rows_equipments = cursor.fetchall()
766
        if rows_equipments is not None and len(rows_equipments) > 0:
767
            cursor.close()
768
            cnx.close()
769
            raise falcon.HTTPError(status=falcon.HTTP_400,
770
                                   title='API.BAD_REQUEST',
771
                                   description='API.THERE_IS_RELATION_WITH_EQUIPMENT_PARAMETERS')
772
773
        # check relation with energy flow diagram links
774
        cursor.execute(" SELECT id "
775
                       " FROM tbl_energy_flow_diagrams_links "
776
                       " WHERE meter_uuid = %s ", (meter_uuid,))
777
        rows_links = cursor.fetchall()
778
        if rows_links is not None and len(rows_links) > 0:
779
            cursor.close()
780
            cnx.close()
781
            raise falcon.HTTPError(status=falcon.HTTP_400,
782
                                   title='API.BAD_REQUEST',
783
                                   description='API.THERE_IS_RELATION_WITH_ENERGY_FLOW_DIAGRAM_LINKS')
784
785
        # check relation with microgrids batteries
786
        cursor.execute("SELECT name "
787
                       "FROM tbl_microgrids_batteries "
788
                       "WHERE charge_meter_id = %s "
789
                       "   OR discharge_meter_id = %s "
790
                       "LIMIT 1",
791
                       (id_, id_))
792
        if cursor.fetchone() is not None:
793
            cursor.close()
794
            cnx.close()
795
            raise falcon.HTTPError(status=falcon.HTTP_400,
796
                                   title='API.BAD_REQUEST',
797
                                   description='API.THERE_IS_RELATION_WITH_MICROGRIDS_BATTERIES')
798
799
        # check relation with microgrids evchargers
800
        cursor.execute("SELECT name "
801
                       "FROM tbl_microgrids_evchargers "
802
                       "WHERE meter_id = %s "
803
                       "LIMIT 1",
804
                       (id_,))
805
        if cursor.fetchone() is not None:
806
            cursor.close()
807
            cnx.close()
808
            raise falcon.HTTPError(status=falcon.HTTP_400,
809
                                   title='API.BAD_REQUEST',
810
                                   description='API.THERE_IS_RELATION_WITH_MICROGRIDS_EVCHARGERS')
811
812
        # check relation with microgrids generators
813
        cursor.execute("SELECT name "
814
                       "FROM tbl_microgrids_generators "
815
                       "WHERE meter_id = %s "
816
                       "LIMIT 1",
817
                       (id_,))
818
        if cursor.fetchone() is not None:
819
            cursor.close()
820
            cnx.close()
821
            raise falcon.HTTPError(status=falcon.HTTP_400,
822
                                   title='API.BAD_REQUEST',
823
                                   description='API.THERE_IS_RELATION_WITH_MICROGRIDS_GENERATORS')
824
825
        # check relation with microgrids grids
826
        cursor.execute("SELECT name "
827
                       "FROM tbl_microgrids_grids "
828
                       "WHERE buy_meter_id = %s "
829
                       "   OR sell_meter_id = %s "
830
                       "LIMIT 1",
831
                       (id_, id_))
832
        if cursor.fetchone() is not None:
833
            cursor.close()
834
            cnx.close()
835
            raise falcon.HTTPError(status=falcon.HTTP_400,
836
                                   title='API.BAD_REQUEST',
837
                                   description='API.THERE_IS_RELATION_WITH_MICROGRIDS_GRIDS')
838
839
        # check relation with microgrids heatpumps
840
        cursor.execute("SELECT name "
841
                       "FROM tbl_microgrids_heatpumps "
842
                       "WHERE electricity_meter_id = %s "
843
                       "   OR heat_meter_id = %s "
844
                       "   OR cooling_meter_id = %s "
845
                       "LIMIT 1",
846
                       (id_, id_, id_))
847
        if cursor.fetchone() is not None:
848
            cursor.close()
849
            cnx.close()
850
            raise falcon.HTTPError(status=falcon.HTTP_400,
851
                                   title='API.BAD_REQUEST',
852
                                   description='API.THERE_IS_RELATION_WITH_MICROGRIDS_HEATPUMPS')
853
854
        # check relation with microgrids loads
855
        cursor.execute("SELECT name "
856
                       "FROM tbl_microgrids_loads "
857
                       "WHERE meter_id = %s "
858
                       "LIMIT 1",
859
                       (id_,))
860
        if cursor.fetchone() is not None:
861
            cursor.close()
862
            cnx.close()
863
            raise falcon.HTTPError(status=falcon.HTTP_400,
864
                                   title='API.BAD_REQUEST',
865
                                   description='API.THERE_IS_RELATION_WITH_MICROGRIDS_LOADS')
866
867
        # check relation with microgrids photovoltaics
868
        cursor.execute("SELECT name "
869
                       "FROM tbl_microgrids_photovoltaics "
870
                       "WHERE meter_id = %s "
871
                       "LIMIT 1",
872
                       (id_,))
873
        if cursor.fetchone() is not None:
874
            cursor.close()
875
            cnx.close()
876
            raise falcon.HTTPError(status=falcon.HTTP_400,
877
                                   title='API.BAD_REQUEST',
878
                                   description='API.THERE_IS_RELATION_WITH_MICROGRIDS_PHOTOVOLTAICS')
879
880
        # delete relation with commands
881
        cursor.execute(" DELETE FROM tbl_meters_commands WHERE meter_id = %s ", (id_,))
882
883
        # delete relation with points
884
        cursor.execute(" DELETE FROM tbl_meters_points WHERE meter_id = %s ", (id_,))
885
886
        cursor.execute(" DELETE FROM tbl_meters WHERE id = %s ", (id_,))
887
        cnx.commit()
888
889
        cursor.close()
890
        cnx.close()
891
892
        # Clear cache after deleting meter
893
        clear_meter_cache(meter_id=id_)
894
895
        resp.status = falcon.HTTP_204
896
897
    @staticmethod
898
    @user_logger
899
    def on_put(req, resp, id_):
900
        """Handles PUT requests"""
901
        admin_control(req)
902
        try:
903
            raw_json = req.stream.read().decode('utf-8')
904
        except UnicodeDecodeError as ex:
905
            print("Failed to decode request")
906
            raise falcon.HTTPError(status=falcon.HTTP_400,
907
                                   title='API.BAD_REQUEST',
908
                                   description='API.INVALID_ENCODING')
909
        except Exception as ex:
910
            print("Unexpected error reading request stream")
911
            raise falcon.HTTPError(status=falcon.HTTP_400,
912
                                   title='API.BAD_REQUEST',
913
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
914
915
        if not id_.isdigit() or int(id_) <= 0:
916
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
917
                                   description='API.INVALID_METER_ID')
918
919
        new_values = json.loads(raw_json)
920
921
        if 'name' not in new_values['data'].keys() or \
922
                not isinstance(new_values['data']['name'], str) or \
923
                len(str.strip(new_values['data']['name'])) == 0:
924
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
925
                                   description='API.INVALID_METER_NAME')
926
        name = str.strip(new_values['data']['name'])
927
928
        if 'energy_category_id' not in new_values['data'].keys() or \
929
                not isinstance(new_values['data']['energy_category_id'], int) or \
930
                new_values['data']['energy_category_id'] <= 0:
931
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
932
                                   description='API.INVALID_ENERGY_CATEGORY_ID')
933
        energy_category_id = new_values['data']['energy_category_id']
934
935
        if 'is_counted' not in new_values['data'].keys() or \
936
                not isinstance(new_values['data']['is_counted'], bool):
937
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
938
                                   description='API.INVALID_IS_COUNTED_VALUE')
939
        is_counted = new_values['data']['is_counted']
940
941
        if 'hourly_low_limit' not in new_values['data'].keys() or \
942
                not (isinstance(new_values['data']['hourly_low_limit'], float) or
943
                     isinstance(new_values['data']['hourly_low_limit'], int)):
944
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
945
                                   description='API.INVALID_HOURLY_LOW_LIMIT_VALUE')
946
        hourly_low_limit = new_values['data']['hourly_low_limit']
947
948
        if 'hourly_high_limit' not in new_values['data'].keys() or \
949
                not (isinstance(new_values['data']['hourly_high_limit'], float) or
950
                     isinstance(new_values['data']['hourly_high_limit'], int)):
951
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
952
                                   description='API.INVALID_HOURLY_HIGH_LIMIT_VALUE')
953
        hourly_high_limit = new_values['data']['hourly_high_limit']
954
955
        if 'cost_center_id' not in new_values['data'].keys() or \
956
                not isinstance(new_values['data']['cost_center_id'], int) or \
957
                new_values['data']['cost_center_id'] <= 0:
958
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
959
                                   description='API.INVALID_COST_CENTER_ID')
960
961
        cost_center_id = new_values['data']['cost_center_id']
962
963
        if 'energy_item_id' in new_values['data'].keys() and \
964
                new_values['data']['energy_item_id'] is not None:
965
            if not isinstance(new_values['data']['energy_item_id'], int) or \
966
                    new_values['data']['energy_item_id'] <= 0:
967
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
968
                                       description='API.INVALID_ENERGY_ITEM_ID')
969
            energy_item_id = new_values['data']['energy_item_id']
970
        else:
971
            energy_item_id = None
972
973
        if 'master_meter_id' in new_values['data'].keys():
974
            if not isinstance(new_values['data']['master_meter_id'], int) or \
975
                    new_values['data']['master_meter_id'] <= 0 or \
976
                    new_values['data']['master_meter_id'] == int(id_):
977
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
978
                                       description='API.INVALID_MASTER_METER_ID')
979
            master_meter_id = new_values['data']['master_meter_id']
980
        else:
981
            master_meter_id = None
982
983
        if 'description' in new_values['data'].keys() and \
984
                new_values['data']['description'] is not None and \
985
                len(str(new_values['data']['description'])) > 0:
986
            description = str.strip(new_values['data']['description'])
987
        else:
988
            description = None
989
990
        cnx = mysql.connector.connect(**config.myems_system_db)
991
        cursor = cnx.cursor()
992
993
        cursor.execute(" SELECT name "
994
                       " FROM tbl_meters "
995
                       " WHERE id = %s ", (id_,))
996
        if cursor.fetchone() is None:
997
            cursor.close()
998
            cnx.close()
999
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1000
                                   description='API.METER_NOT_FOUND')
1001
1002
        cursor.execute(" SELECT name "
1003
                       " FROM tbl_meters "
1004
                       " WHERE name = %s AND id != %s ", (name, id_))
1005
        if cursor.fetchone() is not None:
1006
            cursor.close()
1007
            cnx.close()
1008
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1009
                                   description='API.METER_NAME_IS_ALREADY_IN_USE')
1010
1011
        cursor.execute(" SELECT name "
1012
                       " FROM tbl_energy_categories "
1013
                       " WHERE id = %s ",
1014
                       (new_values['data']['energy_category_id'],))
1015
        if cursor.fetchone() is None:
1016
            cursor.close()
1017
            cnx.close()
1018
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1019
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
1020
1021
        cursor.execute(" SELECT name "
1022
                       " FROM tbl_cost_centers "
1023
                       " WHERE id = %s ",
1024
                       (new_values['data']['cost_center_id'],))
1025
        row = cursor.fetchone()
1026
        if row is None:
1027
            cursor.close()
1028
            cnx.close()
1029
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1030
                                   description='API.COST_CENTER_NOT_FOUND')
1031
1032 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...
1033
            cursor.execute(" SELECT name, energy_category_id "
1034
                           " FROM tbl_energy_items "
1035
                           " WHERE id = %s ",
1036
                           (new_values['data']['energy_item_id'],))
1037
            row = cursor.fetchone()
1038
            if row is None:
1039
                cursor.close()
1040
                cnx.close()
1041
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1042
                                       description='API.ENERGY_ITEM_NOT_FOUND')
1043
            else:
1044
                if row[1] != energy_category_id:
1045
                    cursor.close()
1046
                    cnx.close()
1047
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.BAD_REQUEST',
1048
                                           description='API.ENERGY_ITEM_DOES_NOT_BELONG_TO_ENERGY_CATEGORY')
1049
1050 View Code Duplication
        if master_meter_id is not None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1051
            cursor.execute(" SELECT name, energy_category_id "
1052
                           " FROM tbl_meters "
1053
                           " WHERE id = %s ",
1054
                           (new_values['data']['master_meter_id'],))
1055
            row = cursor.fetchone()
1056
            if row is None:
1057
                cursor.close()
1058
                cnx.close()
1059
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1060
                                       description='API.MASTER_METER_NOT_FOUND')
1061
            else:
1062
                if row[1] != energy_category_id:
1063
                    cursor.close()
1064
                    cnx.close()
1065
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.BAD_REQUEST',
1066
                                           description='API.MASTER_METER_DOES_NOT_BELONG_TO_SAME_ENERGY_CATEGORY')
1067
1068
        # todo: check all descendants against new_values['data']['master_meter_id']
1069
        if master_meter_id is not None:
1070
            cursor.execute(" SELECT name "
1071
                           " FROM tbl_meters "
1072
                           " WHERE id = %s AND master_meter_id = %s ",
1073
                           (new_values['data']['master_meter_id'], id_))
1074
            row = cursor.fetchone()
1075
            if row is not None:
1076
                cursor.close()
1077
                cnx.close()
1078
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1079
                                       description='API.CANNOT_SET_EXISTING_SUBMETER_AS_MASTER_METER')
1080
1081
        update_row = (" UPDATE tbl_meters "
1082
                      " SET name = %s, energy_category_id = %s, is_counted = %s, "
1083
                      "     hourly_low_limit = %s, hourly_high_limit = %s, "
1084
                      "     cost_center_id = %s, energy_item_id = %s, master_meter_id = %s, description = %s "
1085
                      " WHERE id = %s ")
1086
        cursor.execute(update_row, (name,
1087
                                    energy_category_id,
1088
                                    is_counted,
1089
                                    hourly_low_limit,
1090
                                    hourly_high_limit,
1091
                                    cost_center_id,
1092
                                    energy_item_id,
1093
                                    master_meter_id,
1094
                                    description,
1095
                                    id_,))
1096
        cnx.commit()
1097
1098
        cursor.close()
1099
        cnx.close()
1100
1101
        # Clear cache after updating meter
1102
        clear_meter_cache(meter_id=id_)
1103
1104
        resp.status = falcon.HTTP_200
1105
1106
1107
class MeterSubmeterCollection:
1108
    def __init__(self):
1109
        pass
1110
1111
    @staticmethod
1112
    def on_options(req, resp, id_):
1113
        _ = req
1114
        _ = id_
1115
        resp.status = falcon.HTTP_200
1116
1117
    @staticmethod
1118
    def on_get(req, resp, id_):
1119
        if 'API-KEY' not in req.headers or \
1120
                not isinstance(req.headers['API-KEY'], str) or \
1121
                len(str.strip(req.headers['API-KEY'])) == 0:
1122
            access_control(req)
1123
        else:
1124
            api_key_control(req)
1125
        if not id_.isdigit() or int(id_) <= 0:
1126
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1127
                                   description='API.INVALID_METER_ID')
1128
1129
        # Redis cache key
1130
        cache_key = f'meter:submeter:{id_}'
1131
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
1132
1133
        # Try to get from Redis cache (only if Redis is enabled)
1134
        redis_client = None
1135
        if config.redis.get('is_enabled', False):
1136
            try:
1137
                redis_client = redis.Redis(
1138
                    host=config.redis['host'],
1139
                    port=config.redis['port'],
1140
                    password=config.redis['password'] if config.redis['password'] else None,
1141
                    db=config.redis['db'],
1142
                    decode_responses=True,
1143
                    socket_connect_timeout=2,
1144
                    socket_timeout=2
1145
                )
1146
                redis_client.ping()
1147
                cached_result = redis_client.get(cache_key)
1148
                if cached_result:
1149
                    resp.text = cached_result
1150
                    return
1151
            except Exception:
1152
                # If Redis connection fails, continue to database query
1153
                pass
1154
1155
        # Cache miss or Redis error - query database
1156
        cnx = mysql.connector.connect(**config.myems_system_db)
1157
        cursor = cnx.cursor()
1158
1159
        cursor.execute(" SELECT name, uuid "
1160
                       " FROM tbl_meters "
1161
                       " WHERE id = %s ", (id_,))
1162
        row = cursor.fetchone()
1163
        if row is None:
1164
            cursor.close()
1165
            cnx.close()
1166
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1167
                                   description='API.METER_NOT_FOUND')
1168
        else:
1169
            master_meter = {"id": id_,
1170
                            "name": row[0],
1171
                            "uuid": row[1]}
1172
1173
        query = (" SELECT id, name, uuid "
1174
                 " FROM tbl_energy_categories ")
1175
        cursor.execute(query)
1176
        rows_energy_categories = cursor.fetchall()
1177
1178
        energy_category_dict = dict()
1179
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
1180
            for row in rows_energy_categories:
1181
                energy_category_dict[row[0]] = {"id": row[0],
1182
                                                "name": row[1],
1183
                                                "uuid": row[2]}
1184
1185
        query = (" SELECT id, name, uuid "
1186
                 " FROM tbl_cost_centers ")
1187
        cursor.execute(query)
1188
        rows_cost_centers = cursor.fetchall()
1189
1190
        cost_center_dict = dict()
1191
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
1192
            for row in rows_cost_centers:
1193
                cost_center_dict[row[0]] = {"id": row[0],
1194
                                            "name": row[1],
1195
                                            "uuid": row[2]}
1196
1197
        query = (" SELECT id, name, uuid "
1198
                 " FROM tbl_energy_items ")
1199
        cursor.execute(query)
1200
        rows_energy_items = cursor.fetchall()
1201
1202
        energy_item_dict = dict()
1203
        if rows_energy_items is not None and len(rows_energy_items) > 0:
1204
            for row in rows_energy_items:
1205
                energy_item_dict[row[0]] = {"id": row[0],
1206
                                            "name": row[1],
1207
                                            "uuid": row[2]}
1208
1209
        query = (" SELECT id, name, uuid, energy_category_id, "
1210
                 "        is_counted, hourly_low_limit, hourly_high_limit, "
1211
                 "        cost_center_id, energy_item_id, master_meter_id, description "
1212
                 " FROM tbl_meters "
1213
                 " WHERE master_meter_id = %s "
1214
                 " ORDER BY id ")
1215
        cursor.execute(query, (id_, ))
1216
        rows_meters = cursor.fetchall()
1217
1218
        result = list()
1219 View Code Duplication
        if rows_meters is not None and len(rows_meters) > 0:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1220
            for row in rows_meters:
1221
                meta_result = {"id": row[0],
1222
                               "name": row[1],
1223
                               "uuid": row[2],
1224
                               "energy_category": energy_category_dict.get(row[3], None),
1225
                               "is_counted": True if row[4] else False,
1226
                               "hourly_low_limit": row[5],
1227
                               "hourly_high_limit": row[6],
1228
                               "cost_center": cost_center_dict.get(row[7], None),
1229
                               "energy_item": energy_item_dict.get(row[8], None),
1230
                               "master_meter": master_meter,
1231
                               "description": row[10]}
1232
                result.append(meta_result)
1233
1234
        cursor.close()
1235
        cnx.close()
1236
1237
        # Store result in Redis cache
1238
        result_json = json.dumps(result)
1239
        if redis_client:
1240
            try:
1241
                redis_client.setex(cache_key, cache_expire, result_json)
1242
            except Exception:
1243
                # If cache set fails, ignore and continue
1244
                pass
1245
1246
        resp.text = result_json
1247
1248
1249
class MeterPointCollection:
1250
    def __init__(self):
1251
        pass
1252
1253
    @staticmethod
1254
    def on_options(req, resp, id_):
1255
        _ = req
1256
        _ = id_
1257
        resp.status = falcon.HTTP_200
1258
1259 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1260
    def on_get(req, resp, id_):
1261
        if 'API-KEY' not in req.headers or \
1262
                not isinstance(req.headers['API-KEY'], str) or \
1263
                len(str.strip(req.headers['API-KEY'])) == 0:
1264
            access_control(req)
1265
        else:
1266
            api_key_control(req)
1267
        if not id_.isdigit() or int(id_) <= 0:
1268
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1269
                                   description='API.INVALID_METER_ID')
1270
1271
        # Redis cache key
1272
        cache_key = f'meter:point:{id_}'
1273
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
1274
1275
        # Try to get from Redis cache (only if Redis is enabled)
1276
        redis_client = None
1277
        if config.redis.get('is_enabled', False):
1278
            try:
1279
                redis_client = redis.Redis(
1280
                    host=config.redis['host'],
1281
                    port=config.redis['port'],
1282
                    password=config.redis['password'] if config.redis['password'] else None,
1283
                    db=config.redis['db'],
1284
                    decode_responses=True,
1285
                    socket_connect_timeout=2,
1286
                    socket_timeout=2
1287
                )
1288
                redis_client.ping()
1289
                cached_result = redis_client.get(cache_key)
1290
                if cached_result:
1291
                    resp.text = cached_result
1292
                    return
1293
            except Exception:
1294
                # If Redis connection fails, continue to database query
1295
                pass
1296
1297
        # Cache miss or Redis error - query database
1298
        cnx = mysql.connector.connect(**config.myems_system_db)
1299
        cursor = cnx.cursor()
1300
1301
        cursor.execute(" SELECT name "
1302
                       " FROM tbl_meters "
1303
                       " WHERE id = %s ", (id_,))
1304
        if cursor.fetchone() is None:
1305
            cursor.close()
1306
            cnx.close()
1307
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1308
                                   description='API.METER_NOT_FOUND')
1309
1310
        query = (" SELECT p.id, p.name, "
1311
                 "        ds.id, ds.name, ds.uuid, "
1312
                 "        p.address "
1313
                 " FROM tbl_points p, tbl_meters_points mp, tbl_data_sources ds "
1314
                 " WHERE mp.meter_id = %s AND p.id = mp.point_id AND p.data_source_id = ds.id "
1315
                 " ORDER BY p.name ")
1316
        cursor.execute(query, (id_,))
1317
        rows = cursor.fetchall()
1318
1319
        result = list()
1320
        if rows is not None and len(rows) > 0:
1321
            for row in rows:
1322
                meta_result = {"id": row[0], "name": row[1],
1323
                               "data_source": {"id": row[2], "name": row[3], "uuid": row[4]},
1324
                               "address": row[5]}
1325
                result.append(meta_result)
1326
1327
        # Store result in Redis cache
1328
        result_json = json.dumps(result)
1329
        if redis_client:
1330
            try:
1331
                redis_client.setex(cache_key, cache_expire, result_json)
1332
            except Exception:
1333
                # If cache set fails, ignore and continue
1334
                pass
1335
1336
        resp.text = result_json
1337
1338
    @staticmethod
1339
    @user_logger
1340
    def on_post(req, resp, id_):
1341
        """Handles POST requests"""
1342
        admin_control(req)
1343
        try:
1344
            raw_json = req.stream.read().decode('utf-8')
1345
        except UnicodeDecodeError as ex:
1346
            print("Failed to decode request")
1347
            raise falcon.HTTPError(status=falcon.HTTP_400,
1348
                                   title='API.BAD_REQUEST',
1349
                                   description='API.INVALID_ENCODING')
1350
        except Exception as ex:
1351
            print("Unexpected error reading request stream")
1352
            raise falcon.HTTPError(status=falcon.HTTP_400,
1353
                                   title='API.BAD_REQUEST',
1354
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1355
1356
        if not id_.isdigit() or int(id_) <= 0:
1357
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1358
                                   description='API.INVALID_METER_ID')
1359
1360
        new_values = json.loads(raw_json)
1361
        cnx = mysql.connector.connect(**config.myems_system_db)
1362
        cursor = cnx.cursor()
1363
1364
        cursor.execute(" SELECT name "
1365
                       " from tbl_meters "
1366
                       " WHERE id = %s ", (id_,))
1367
        if cursor.fetchone() is None:
1368
            cursor.close()
1369
            cnx.close()
1370
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1371
                                   description='API.METER_NOT_FOUND')
1372
        cursor.execute(" SELECT name, object_type "
1373
                       " FROM tbl_points "
1374
                       " WHERE id = %s ", (new_values['data']['point_id'],))
1375
        row = cursor.fetchone()
1376 View Code Duplication
        if row is None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1377
            cursor.close()
1378
            cnx.close()
1379
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1380
                                   description='API.POINT_NOT_FOUND')
1381
        elif row[1] == 'ENERGY_VALUE':
1382
            query = (" SELECT p.id "
1383
                     " FROM tbl_meters_points mp, tbl_points p "
1384
                     " WHERE mp.meter_id = %s AND mp.point_id = p.id AND p.object_type = 'ENERGY_VALUE' ")
1385
            cursor.execute(query, (id_,))
1386
            rows_points = cursor.fetchall()
1387
            if rows_points is not None and len(rows_points) > 0:
1388
                cursor.close()
1389
                cnx.close()
1390
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
1391
                                       description='API.METER_CANNOT_HAVE_MORE_THAN_ONE_ENERGY_VALUE_POINTS')
1392
1393
        query = (" SELECT id "
1394
                 " FROM tbl_meters_points "
1395
                 " WHERE meter_id = %s AND point_id = %s")
1396
        cursor.execute(query, (id_, new_values['data']['point_id'],))
1397
        if cursor.fetchone() is not None:
1398
            cursor.close()
1399
            cnx.close()
1400
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
1401
                                   description='API.METER_POINT_RELATION_EXISTS')
1402
1403
        add_row = (" INSERT INTO tbl_meters_points (meter_id, point_id) "
1404
                   " VALUES (%s, %s) ")
1405
        cursor.execute(add_row, (id_, new_values['data']['point_id'],))
1406
        cnx.commit()
1407
        cursor.close()
1408
        cnx.close()
1409
1410
        # Clear cache after adding point to meter
1411
        clear_meter_cache(meter_id=id_)
1412
1413
        resp.status = falcon.HTTP_201
1414
        resp.location = '/meters/' + str(id_) + '/points/' + str(new_values['data']['point_id'])
1415
1416
1417 View Code Duplication
class MeterPointItem:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1418
    def __init__(self):
1419
        pass
1420
1421
    @staticmethod
1422
    def on_options(req, resp, id_, pid):
1423
        _ = req
1424
        _ = id_
1425
        _ = pid
1426
        resp.status = falcon.HTTP_200
1427
1428
    @staticmethod
1429
    @user_logger
1430
    def on_delete(req, resp, id_, pid):
1431
        """Handles DELETE requests"""
1432
        admin_control(req)
1433
        if not id_.isdigit() or int(id_) <= 0:
1434
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1435
                                   description='API.INVALID_METER_ID')
1436
1437
        if not pid.isdigit() or int(pid) <= 0:
1438
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1439
                                   description='API.INVALID_POINT_ID')
1440
1441
        cnx = mysql.connector.connect(**config.myems_system_db)
1442
        cursor = cnx.cursor()
1443
1444
        cursor.execute(" SELECT name "
1445
                       " FROM tbl_meters "
1446
                       " WHERE id = %s ", (id_,))
1447
        if cursor.fetchone() is None:
1448
            cursor.close()
1449
            cnx.close()
1450
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1451
                                   description='API.METER_NOT_FOUND')
1452
1453
        cursor.execute(" SELECT name "
1454
                       " FROM tbl_points "
1455
                       " WHERE id = %s ", (pid,))
1456
        if cursor.fetchone() is None:
1457
            cursor.close()
1458
            cnx.close()
1459
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1460
                                   description='API.POINT_NOT_FOUND')
1461
1462
        cursor.execute(" SELECT id "
1463
                       " FROM tbl_meters_points "
1464
                       " WHERE meter_id = %s AND point_id = %s ", (id_, pid))
1465
        if cursor.fetchone() is None:
1466
            cursor.close()
1467
            cnx.close()
1468
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1469
                                   description='API.METER_POINT_RELATION_NOT_FOUND')
1470
1471
        cursor.execute(" DELETE FROM tbl_meters_points WHERE meter_id = %s AND point_id = %s ", (id_, pid))
1472
        cnx.commit()
1473
1474
        cursor.close()
1475
        cnx.close()
1476
1477
        # Clear cache after removing point from meter
1478
        clear_meter_cache(meter_id=id_)
1479
1480
        resp.status = falcon.HTTP_204
1481
1482
1483 View Code Duplication
class MeterCommandCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1484
    def __init__(self):
1485
        pass
1486
1487
    @staticmethod
1488
    def on_options(req, resp, id_):
1489
        _ = req
1490
        _ = id_
1491
        resp.status = falcon.HTTP_200
1492
1493
    @staticmethod
1494
    def on_get(req, resp, id_):
1495
        if 'API-KEY' not in req.headers or \
1496
                not isinstance(req.headers['API-KEY'], str) or \
1497
                len(str.strip(req.headers['API-KEY'])) == 0:
1498
            access_control(req)
1499
        else:
1500
            api_key_control(req)
1501
        if not id_.isdigit() or int(id_) <= 0:
1502
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1503
                                   description='API.INVALID_METER_ID')
1504
1505
        # Redis cache key
1506
        cache_key = f'meter:command:{id_}'
1507
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
1508
1509
        # Try to get from Redis cache (only if Redis is enabled)
1510
        redis_client = None
1511
        if config.redis.get('is_enabled', False):
1512
            try:
1513
                redis_client = redis.Redis(
1514
                    host=config.redis['host'],
1515
                    port=config.redis['port'],
1516
                    password=config.redis['password'] if config.redis['password'] else None,
1517
                    db=config.redis['db'],
1518
                    decode_responses=True,
1519
                    socket_connect_timeout=2,
1520
                    socket_timeout=2
1521
                )
1522
                redis_client.ping()
1523
                cached_result = redis_client.get(cache_key)
1524
                if cached_result:
1525
                    resp.text = cached_result
1526
                    return
1527
            except Exception:
1528
                # If Redis connection fails, continue to database query
1529
                pass
1530
1531
        # Cache miss or Redis error - query database
1532
        cnx = mysql.connector.connect(**config.myems_system_db)
1533
        cursor = cnx.cursor()
1534
1535
        cursor.execute(" SELECT name "
1536
                       " FROM tbl_meters "
1537
                       " WHERE id = %s ", (id_,))
1538
        if cursor.fetchone() is None:
1539
            cursor.close()
1540
            cnx.close()
1541
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1542
                                   description='API.METER_NOT_FOUND')
1543
1544
        query = (" SELECT c.id, c.name, c.uuid "
1545
                 " FROM tbl_meters m, tbl_meters_commands mc, tbl_commands c "
1546
                 " WHERE mc.meter_id = m.id AND c.id = mc.command_id AND m.id = %s "
1547
                 " ORDER BY c.id ")
1548
        cursor.execute(query, (id_,))
1549
        rows = cursor.fetchall()
1550
1551
        result = list()
1552
        if rows is not None and len(rows) > 0:
1553
            for row in rows:
1554
                meta_result = {"id": row[0], "name": row[1], "uuid": row[2]}
1555
                result.append(meta_result)
1556
1557
        # Store result in Redis cache
1558
        result_json = json.dumps(result)
1559
        if redis_client:
1560
            try:
1561
                redis_client.setex(cache_key, cache_expire, result_json)
1562
            except Exception:
1563
                # If cache set fails, ignore and continue
1564
                pass
1565
1566
        resp.text = result_json
1567
1568
    @staticmethod
1569
    @user_logger
1570
    def on_post(req, resp, id_):
1571
        """Handles POST requests"""
1572
        admin_control(req)
1573
        try:
1574
            raw_json = req.stream.read().decode('utf-8')
1575
        except UnicodeDecodeError as ex:
1576
            print("Failed to decode request")
1577
            raise falcon.HTTPError(status=falcon.HTTP_400,
1578
                                   title='API.BAD_REQUEST',
1579
                                   description='API.INVALID_ENCODING')
1580
        except Exception as ex:
1581
            print("Unexpected error reading request stream")
1582
            raise falcon.HTTPError(status=falcon.HTTP_400,
1583
                                   title='API.BAD_REQUEST',
1584
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1585
1586
        if not id_.isdigit() or int(id_) <= 0:
1587
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1588
                                   description='API.INVALID_METER_ID')
1589
1590
        new_values = json.loads(raw_json)
1591
1592
        if 'command_id' not in new_values['data'].keys() or \
1593
                not isinstance(new_values['data']['command_id'], int) or \
1594
                new_values['data']['command_id'] <= 0:
1595
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1596
                                   description='API.INVALID_COMMAND_ID')
1597
        command_id = new_values['data']['command_id']
1598
1599
        cnx = mysql.connector.connect(**config.myems_system_db)
1600
        cursor = cnx.cursor()
1601
1602
        cursor.execute(" SELECT name "
1603
                       " from tbl_meters "
1604
                       " WHERE id = %s ", (id_,))
1605
        if cursor.fetchone() is None:
1606
            cursor.close()
1607
            cnx.close()
1608
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1609
                                   description='API.METER_NOT_FOUND')
1610
1611
        cursor.execute(" SELECT name "
1612
                       " FROM tbl_commands "
1613
                       " WHERE id = %s ", (command_id,))
1614
        if cursor.fetchone() is None:
1615
            cursor.close()
1616
            cnx.close()
1617
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1618
                                   description='API.COMMAND_NOT_FOUND')
1619
1620
        query = (" SELECT id "
1621
                 " FROM tbl_meters_commands "
1622
                 " WHERE meter_id = %s AND command_id = %s")
1623
        cursor.execute(query, (id_, command_id,))
1624
        if cursor.fetchone() is not None:
1625
            cursor.close()
1626
            cnx.close()
1627
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
1628
                                   description='API.METER_COMMAND_RELATION_EXISTS')
1629
1630
        add_row = (" INSERT INTO tbl_meters_commands (meter_id, command_id) "
1631
                   " VALUES (%s, %s) ")
1632
        cursor.execute(add_row, (id_, command_id,))
1633
        cnx.commit()
1634
        cursor.close()
1635
        cnx.close()
1636
1637
        # Clear cache after adding command to meter
1638
        clear_meter_cache(meter_id=id_)
1639
1640
        resp.status = falcon.HTTP_201
1641
        resp.location = '/meters/' + str(id_) + '/commands/' + str(command_id)
1642
1643
1644 View Code Duplication
class MeterCommandItem:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1645
    def __init__(self):
1646
        pass
1647
1648
    @staticmethod
1649
    def on_options(req, resp, id_, cid):
1650
        _ = req
1651
        _ = id_
1652
        _ = cid
1653
        resp.status = falcon.HTTP_200
1654
1655
    @staticmethod
1656
    @user_logger
1657
    def on_delete(req, resp, id_, cid):
1658
        admin_control(req)
1659
        if not id_.isdigit() or int(id_) <= 0:
1660
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1661
                                   description='API.INVALID_METER_ID')
1662
1663
        if not cid.isdigit() or int(cid) <= 0:
1664
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1665
                                   description='API.INVALID_COMMAND_ID')
1666
1667
        cnx = mysql.connector.connect(**config.myems_system_db)
1668
        cursor = cnx.cursor()
1669
1670
        cursor.execute(" SELECT name "
1671
                       " FROM tbl_meters "
1672
                       " WHERE id = %s ", (id_,))
1673
        if cursor.fetchone() is None:
1674
            cursor.close()
1675
            cnx.close()
1676
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1677
                                   description='API.METER_NOT_FOUND')
1678
1679
        cursor.execute(" SELECT name "
1680
                       " FROM tbl_commands "
1681
                       " WHERE id = %s ", (cid,))
1682
        if cursor.fetchone() is None:
1683
            cursor.close()
1684
            cnx.close()
1685
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1686
                                   description='API.COMMAND_NOT_FOUND')
1687
1688
        cursor.execute(" SELECT id "
1689
                       " FROM tbl_meters_commands "
1690
                       " WHERE meter_id = %s AND command_id = %s ", (id_, cid))
1691
        if cursor.fetchone() is None:
1692
            cursor.close()
1693
            cnx.close()
1694
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1695
                                   description='API.METER_COMMAND_RELATION_NOT_FOUND')
1696
1697
        cursor.execute(" DELETE FROM tbl_meters_commands WHERE meter_id = %s AND command_id = %s ", (id_, cid))
1698
        cnx.commit()
1699
1700
        cursor.close()
1701
        cnx.close()
1702
1703
        # Clear cache after removing command from meter
1704
        clear_meter_cache(meter_id=id_)
1705
1706
        resp.status = falcon.HTTP_204
1707
1708
1709
class MeterExport:
1710
    def __init__(self):
1711
        pass
1712
1713
    @staticmethod
1714
    def on_options(req, resp, id_):
1715
        _ = req
1716
        _ = id_
1717
        resp.status = falcon.HTTP_200
1718
1719
    @staticmethod
1720
    def on_get(req, resp, id_):
1721
        if 'API-KEY' not in req.headers or \
1722
                not isinstance(req.headers['API-KEY'], str) or \
1723
                len(str.strip(req.headers['API-KEY'])) == 0:
1724
            access_control(req)
1725
        else:
1726
            api_key_control(req)
1727
        if not id_.isdigit() or int(id_) <= 0:
1728
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1729
                                   description='API.INVALID_METER_ID')
1730
1731
        # Redis cache key
1732
        cache_key = f'meter:export:{id_}'
1733
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
1734
1735
        # Try to get from Redis cache (only if Redis is enabled)
1736
        redis_client = None
1737
        if config.redis.get('is_enabled', False):
1738
            try:
1739
                redis_client = redis.Redis(
1740
                    host=config.redis['host'],
1741
                    port=config.redis['port'],
1742
                    password=config.redis['password'] if config.redis['password'] else None,
1743
                    db=config.redis['db'],
1744
                    decode_responses=True,
1745
                    socket_connect_timeout=2,
1746
                    socket_timeout=2
1747
                )
1748
                redis_client.ping()
1749
                cached_result = redis_client.get(cache_key)
1750
                if cached_result:
1751
                    resp.text = cached_result
1752
                    return
1753
            except Exception:
1754
                # If Redis connection fails, continue to database query
1755
                pass
1756
1757
        # Cache miss or Redis error - query database
1758
        cnx = mysql.connector.connect(**config.myems_system_db)
1759
        cursor = cnx.cursor()
1760
1761
        query = (" SELECT id, name, uuid "
1762
                 " FROM tbl_energy_categories ")
1763
        cursor.execute(query)
1764
        rows_energy_categories = cursor.fetchall()
1765
1766
        energy_category_dict = dict()
1767
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
1768
            for row in rows_energy_categories:
1769
                energy_category_dict[row[0]] = {"id": row[0],
1770
                                                "name": row[1],
1771
                                                "uuid": row[2]}
1772
1773
        query = (" SELECT id, name, uuid "
1774
                 " FROM tbl_cost_centers ")
1775
        cursor.execute(query)
1776
        rows_cost_centers = cursor.fetchall()
1777
1778
        cost_center_dict = dict()
1779
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
1780
            for row in rows_cost_centers:
1781
                cost_center_dict[row[0]] = {"id": row[0],
1782
                                            "name": row[1],
1783
                                            "uuid": row[2]}
1784
1785
        query = (" SELECT id, name, uuid "
1786
                 " FROM tbl_energy_items ")
1787
        cursor.execute(query)
1788
        rows_energy_items = cursor.fetchall()
1789
1790
        energy_item_dict = dict()
1791
        if rows_energy_items is not None and len(rows_energy_items) > 0:
1792
            for row in rows_energy_items:
1793
                energy_item_dict[row[0]] = {"id": row[0],
1794
                                            "name": row[1],
1795
                                            "uuid": row[2]}
1796
1797
        query = (" SELECT id, name, uuid "
1798
                 " FROM tbl_meters ")
1799
        cursor.execute(query)
1800
        rows_master_meters = cursor.fetchall()
1801
1802
        master_meter_dict = dict()
1803
        if rows_master_meters is not None and len(rows_master_meters) > 0:
1804
            for row in rows_master_meters:
1805
                master_meter_dict[row[0]] = {"id": row[0],
1806
                                             "name": row[1],
1807
                                             "uuid": row[2]}
1808
1809
        query = (" SELECT id, name, uuid, energy_category_id, "
1810
                 "        is_counted, hourly_low_limit, hourly_high_limit, "
1811
                 "        cost_center_id, energy_item_id, master_meter_id, description "
1812
                 " FROM tbl_meters "
1813
                 " WHERE id = %s ")
1814
        cursor.execute(query, (id_,))
1815
        row = cursor.fetchone()
1816
1817
        if row is None:
1818
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1819
                                   description='API.METER_NOT_FOUND')
1820
        else:
1821
            meta_result = {"name": row[1],
1822
                           "uuid": row[2],
1823
                           "energy_category": energy_category_dict.get(row[3], None),
1824
                           "is_counted": True if row[4] else False,
1825
                           "hourly_low_limit": row[5],
1826
                           "hourly_high_limit": row[6],
1827
                           "cost_center": cost_center_dict.get(row[7], None),
1828
                           "energy_item": energy_item_dict.get(row[8], None),
1829
                           "master_meter": master_meter_dict.get(row[9], None),
1830
                           "description": row[10],
1831
                           "points": None}
1832
            query = (" SELECT p.id, p.name, "
1833
                     "        ds.id, ds.name, ds.uuid, "
1834
                     "        p.address "
1835
                     " FROM tbl_points p, tbl_meters_points mp, tbl_data_sources ds "
1836
                     " WHERE mp.meter_id = %s AND p.id = mp.point_id AND p.data_source_id = ds.id "
1837
                     " ORDER BY p.name ")
1838
            cursor.execute(query, (id_,))
1839
            rows = cursor.fetchall()
1840
1841
            result = list()
1842
            if rows is not None and len(rows) > 0:
1843
                for row in rows:
1844
                    point_result = {"id": row[0], "name": row[1]}
1845
                    result.append(point_result)
1846
                meta_result['points'] = result
1847
            cursor.close()
1848
            cnx.close()
1849
1850
        # Store result in Redis cache
1851
        result_json = json.dumps(meta_result)
1852
        if redis_client:
1853
            try:
1854
                redis_client.setex(cache_key, cache_expire, result_json)
1855
            except Exception:
1856
                # If cache set fails, ignore and continue
1857
                pass
1858
1859
        resp.text = result_json
1860
1861
1862
class MeterImport:
1863
    def __init__(self):
1864
        pass
1865
1866
    @staticmethod
1867
    def on_options(req, resp):
1868
        _ = req
1869
        resp.status = falcon.HTTP_200
1870
1871
    @staticmethod
1872
    @user_logger
1873
    def on_post(req, resp):
1874
        """Handles POST requests"""
1875
        admin_control(req)
1876
        try:
1877
            raw_json = req.stream.read().decode('utf-8')
1878
        except UnicodeDecodeError as ex:
1879
            print("Failed to decode request")
1880
            raise falcon.HTTPError(status=falcon.HTTP_400,
1881
                                   title='API.BAD_REQUEST',
1882
                                   description='API.INVALID_ENCODING')
1883
        except Exception as ex:
1884
            print("Unexpected error reading request stream")
1885
            raise falcon.HTTPError(status=falcon.HTTP_400,
1886
                                   title='API.BAD_REQUEST',
1887
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1888
1889
        new_values = json.loads(raw_json)
1890
1891
        if 'name' not in new_values.keys() or \
1892
                not isinstance(new_values['name'], str) or \
1893
                len(str.strip(new_values['name'])) == 0:
1894
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1895
                                   description='API.INVALID_METER_NAME')
1896
        name = str.strip(new_values['name'])
1897
1898
        if 'energy_category' not in new_values.keys() or \
1899
            'id' not in new_values['energy_category'].keys() or \
1900
            not isinstance(new_values['energy_category']['id'], int) or \
1901
                new_values['energy_category']['id'] <= 0:
1902
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1903
                                   description='API.INVALID_ENERGY_CATEGORY_ID')
1904
        energy_category_id = new_values['energy_category']['id']
1905
1906
        if 'is_counted' not in new_values.keys() or \
1907
                not isinstance(new_values['is_counted'], bool):
1908
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1909
                                   description='API.INVALID_IS_COUNTED_VALUE')
1910
        is_counted = new_values['is_counted']
1911
1912
        if 'hourly_low_limit' not in new_values.keys() or \
1913
                not (isinstance(new_values['hourly_low_limit'], float) or
1914
                     isinstance(new_values['hourly_low_limit'], int)):
1915
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1916
                                   description='API.INVALID_HOURLY_LOW_LIMIT_VALUE')
1917
        hourly_low_limit = new_values['hourly_low_limit']
1918
1919
        if 'hourly_high_limit' not in new_values.keys() or \
1920
                not (isinstance(new_values['hourly_high_limit'], float) or
1921
                     isinstance(new_values['hourly_high_limit'], int)):
1922
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1923
                                   description='API.INVALID_HOURLY_HIGH_LIMIT_VALUE')
1924
        hourly_high_limit = new_values['hourly_high_limit']
1925
1926
        if 'cost_center' not in new_values.keys() or \
1927
            new_values['cost_center'] is None or \
1928
            'id' not in new_values['cost_center'].keys() or \
1929
                not isinstance(new_values['cost_center']['id'], int) or \
1930
                new_values['cost_center']['id'] <= 0:
1931
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1932
                                   description='API.INVALID_COST_CENTER_ID')
1933
        cost_center_id = new_values['cost_center']['id']
1934
1935 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...
1936
            new_values['energy_item'] is not None and \
1937
                'id' in new_values['energy_item'].keys() and \
1938
                new_values['energy_item']['id'] is not None:
1939
            if not isinstance(new_values['energy_item']['id'], int) or \
1940
                    new_values['energy_item']['id'] <= 0:
1941
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1942
                                       description='API.INVALID_ENERGY_ITEM_ID')
1943
            energy_item_id = new_values['energy_item']['id']
1944
        else:
1945
            energy_item_id = None
1946
1947
        if 'master_meter' in new_values.keys() and \
1948
            new_values['master_meter'] is not None and \
1949
                'id' in new_values['master_meter'].keys():
1950
            if not isinstance(new_values['master_meter']['id'], int) or \
1951
                    new_values['master_meter']['id'] <= 0:
1952
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1953
                                       description='API.INVALID_MASTER_METER_ID')
1954
            master_meter_id = new_values['master_meter']['id']
1955
        else:
1956
            master_meter_id = None
1957
1958
        if 'description' in new_values.keys() and \
1959
                new_values['description'] is not None and \
1960
                len(str(new_values['description'])) > 0:
1961
            description = str.strip(new_values['description'])
1962
        else:
1963
            description = None
1964
1965
        cnx = mysql.connector.connect(**config.myems_system_db)
1966
        cursor = cnx.cursor()
1967
1968
        cursor.execute(" SELECT name "
1969
                       " FROM tbl_meters "
1970
                       " WHERE name = %s ", (name,))
1971
        if cursor.fetchone() is not None:
1972
            cursor.close()
1973
            cnx.close()
1974
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1975
                                   description='API.METER_NAME_IS_ALREADY_IN_USE')
1976
1977
        cursor.execute(" SELECT name "
1978
                       " FROM tbl_energy_categories "
1979
                       " WHERE id = %s ",
1980
                       (energy_category_id,))
1981
        if cursor.fetchone() is None:
1982
            cursor.close()
1983
            cnx.close()
1984
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1985
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
1986
1987
        cursor.execute(" SELECT name "
1988
                       " FROM tbl_cost_centers "
1989
                       " WHERE id = %s ",
1990
                       (new_values['cost_center']['id'],))
1991
        row = cursor.fetchone()
1992
        if row is None:
1993
            cursor.close()
1994
            cnx.close()
1995
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1996
                                   description='API.COST_CENTER_NOT_FOUND')
1997
1998
        if energy_item_id is not None:
1999
            cursor.execute(" SELECT name, energy_category_id "
2000
                           " FROM tbl_energy_items "
2001
                           " WHERE id = %s ",
2002
                           (energy_item_id,))
2003
            row = cursor.fetchone()
2004
            if row is None:
2005
                cursor.close()
2006
                cnx.close()
2007
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2008
                                       description='API.ENERGY_ITEM_NOT_FOUND')
2009
            else:
2010
                if row[1] != energy_category_id:
2011
                    cursor.close()
2012
                    cnx.close()
2013
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.BAD_REQUEST',
2014
                                           description='API.ENERGY_ITEM_DOES_NOT_BELONG_TO_ENERGY_CATEGORY')
2015
2016 View Code Duplication
        if master_meter_id is not None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2017
            cursor.execute(" SELECT name, energy_category_id "
2018
                           " FROM tbl_meters "
2019
                           " WHERE id = %s ",
2020
                           (master_meter_id,))
2021
            row = cursor.fetchone()
2022
            if row is None:
2023
                cursor.close()
2024
                cnx.close()
2025
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2026
                                       description='API.MASTER_METER_NOT_FOUND')
2027
            else:
2028
                if row[1] != energy_category_id:
2029
                    cursor.close()
2030
                    cnx.close()
2031
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.BAD_REQUEST',
2032
                                           description='API.MASTER_METER_DOES_NOT_BELONG_TO_SAME_ENERGY_CATEGORY')
2033
2034
        add_values = (" INSERT INTO tbl_meters "
2035
                      "    (name, uuid, energy_category_id, is_counted, hourly_low_limit, hourly_high_limit,"
2036
                      "     cost_center_id, energy_item_id, master_meter_id, description) "
2037
                      " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
2038
        cursor.execute(add_values, (name,
2039
                                    str(uuid.uuid4()),
2040
                                    energy_category_id,
2041
                                    is_counted,
2042
                                    hourly_low_limit,
2043
                                    hourly_high_limit,
2044
                                    cost_center_id,
2045
                                    energy_item_id,
2046
                                    master_meter_id,
2047
                                    description))
2048
        new_id = cursor.lastrowid
2049
        if 'points' in new_values.keys() and \
2050
                new_values['points'] is not None and \
2051
                len(new_values['points']) > 0:
2052
            for point in new_values['points']:
2053
                if 'id' in point and isinstance(point['id'], int):
2054
                    cursor.execute(" SELECT name, object_type "
2055
                                   " FROM tbl_points "
2056
                                   " WHERE id = %s ", (point['id'],))
2057
                    row = cursor.fetchone()
2058 View Code Duplication
                    if row is None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2059
                        cursor.close()
2060
                        cnx.close()
2061
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2062
                                               description='API.POINT_NOT_FOUND')
2063
                    elif row[1] == 'ENERGY_VALUE':
2064
                        query = (" SELECT p.id "
2065
                                 " FROM tbl_meters_points mp, tbl_points p "
2066
                                 " WHERE mp.meter_id = %s AND mp.point_id = p.id AND p.object_type = 'ENERGY_VALUE' ")
2067
                        cursor.execute(query, (new_id,))
2068
                        rows_points = cursor.fetchall()
2069
                        if rows_points is not None and len(rows_points) > 0:
2070
                            cursor.close()
2071
                            cnx.close()
2072
                            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2073
                                                   description=
2074
                                                   'API.METER_CANNOT_HAVE_MORE_THAN_ONE_ENERGY_VALUE_POINTS')
2075
2076
                    query = (" SELECT id "
2077
                             " FROM tbl_meters_points "
2078
                             " WHERE meter_id = %s AND point_id = %s")
2079
                    cursor.execute(query, (new_id, point['id'],))
2080
                    if cursor.fetchone() is not None:
2081
                        cursor.close()
2082
                        cnx.close()
2083
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2084
                                               description='API.METER_POINT_RELATION_EXISTS')
2085
2086
                    add_row = (" INSERT INTO tbl_meters_points (meter_id, point_id) "
2087
                               " VALUES (%s, %s) ")
2088
                    cursor.execute(add_row, (new_id, point['id'],))
2089
                else:
2090
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.NOT_FOUND',
2091
                                           description='API.INVALID_POINT_ID')
2092
        cnx.commit()
2093
        cursor.close()
2094
        cnx.close()
2095
2096
        # Clear cache after importing meter
2097
        clear_meter_cache()
2098
2099
        resp.status = falcon.HTTP_201
2100
        resp.location = '/meters/' + str(new_id)
2101
2102
2103
class MeterClone:
2104
    def __init__(self):
2105
        pass
2106
2107
    @staticmethod
2108
    def on_options(req, resp, id_):
2109
        _ = req
2110
        _ = id_
2111
        resp.status = falcon.HTTP_200
2112
2113
    @staticmethod
2114
    @user_logger
2115
    def on_post(req, resp, id_):
2116
        """Handles POST requests"""
2117
        admin_control(req)
2118
        if not id_.isdigit() or int(id_) <= 0:
2119
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2120
                                   description='API.INVALID_METER_ID')
2121
2122
        cnx = mysql.connector.connect(**config.myems_system_db)
2123
        cursor = cnx.cursor()
2124
2125
        query = (" SELECT id, name, uuid "
2126
                 " FROM tbl_meters ")
2127
        cursor.execute(query)
2128
        rows_master_meters = cursor.fetchall()
2129
2130
        master_meter_dict = dict()
2131
        if rows_master_meters is not None and len(rows_master_meters) > 0:
2132
            for row in rows_master_meters:
2133
                master_meter_dict[row[0]] = {"id": row[0],
2134
                                             "name": row[1],
2135
                                             "uuid": row[2]}
2136
2137
        query = (" SELECT id, name, uuid, energy_category_id, "
2138
                 "        is_counted, hourly_low_limit, hourly_high_limit, "
2139
                 "        cost_center_id, energy_item_id, master_meter_id, description "
2140
                 " FROM tbl_meters "
2141
                 " WHERE id = %s ")
2142
        cursor.execute(query, (id_,))
2143
        row = cursor.fetchone()
2144
2145
        if row is None:
2146
            cursor.close()
2147
            cnx.close()
2148
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2149
                                   description='API.METER_NOT_FOUND')
2150
        else:
2151
            meta_result = {"id": row[0],
2152
                           "name": row[1],
2153
                           "uuid": row[2],
2154
                           "energy_category_id": row[3],
2155
                           "is_counted": row[4],
2156
                           "hourly_low_limit": row[5],
2157
                           "hourly_high_limit": row[6],
2158
                           "cost_center_id": row[7],
2159
                           "energy_item_id": row[8],
2160
                           "master_meter_id": row[9],
2161
                           "description": row[10],
2162
                           "points": None}
2163
            query = (" SELECT p.id, p.name, "
2164
                     "        ds.id, ds.name, ds.uuid, "
2165
                     "        p.address "
2166
                     " FROM tbl_points p, tbl_meters_points mp, tbl_data_sources ds "
2167
                     " WHERE mp.meter_id = %s AND p.id = mp.point_id AND p.data_source_id = ds.id "
2168
                     " ORDER BY p.name ")
2169
            cursor.execute(query, (id_,))
2170
            rows = cursor.fetchall()
2171
2172
            result = list()
2173
            if rows is not None and len(rows) > 0:
2174
                for row in rows:
2175
                    point_result = {"id": row[0], "name": row[1]}
2176
                    result.append(point_result)
2177
                meta_result['points'] = result
2178
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
2179
        if config.utc_offset[0] == '-':
2180
            timezone_offset = -timezone_offset
2181
        new_name = str.strip(meta_result['name']) + \
2182
            (datetime.utcnow() + timedelta(minutes=timezone_offset)).isoformat(sep='-', timespec='seconds')
2183
2184
        add_values = (" INSERT INTO tbl_meters "
2185
                      "    (name, uuid, energy_category_id, is_counted, hourly_low_limit, hourly_high_limit,"
2186
                      "     cost_center_id, energy_item_id, master_meter_id, description) "
2187
                      " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
2188
        cursor.execute(add_values, (new_name,
2189
                                    str(uuid.uuid4()),
2190
                                    meta_result['energy_category_id'],
2191
                                    meta_result['is_counted'],
2192
                                    meta_result['hourly_low_limit'],
2193
                                    meta_result['hourly_high_limit'],
2194
                                    meta_result['cost_center_id'],
2195
                                    meta_result['energy_item_id'],
2196
                                    meta_result['master_meter_id'],
2197
                                    meta_result['description']))
2198
        new_id = cursor.lastrowid
2199
        if 'points' in meta_result.keys() and \
2200
                meta_result['points'] is not None and \
2201
                len(meta_result['points']) > 0:
2202
            for point in meta_result['points']:
2203
                if 'id' in point and isinstance(point['id'], int):
2204
                    cursor.execute(" SELECT name, object_type "
2205
                                   " FROM tbl_points "
2206
                                   " WHERE id = %s ", (point['id'],))
2207
                    row = cursor.fetchone()
2208 View Code Duplication
                    if row is None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2209
                        cursor.close()
2210
                        cnx.close()
2211
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2212
                                               description='API.POINT_NOT_FOUND')
2213
                    elif row[1] == 'ENERGY_VALUE':
2214
                        query = (" SELECT p.id "
2215
                                 " FROM tbl_meters_points mp, tbl_points p "
2216
                                 " WHERE mp.meter_id = %s AND mp.point_id = p.id AND p.object_type = 'ENERGY_VALUE' ")
2217
                        cursor.execute(query, (new_id,))
2218
                        rows_points = cursor.fetchall()
2219
                        if rows_points is not None and len(rows_points) > 0:
2220
                            cursor.close()
2221
                            cnx.close()
2222
                            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2223
                                                   description=
2224
                                                   'API.METER_CANNOT_HAVE_MORE_THAN_ONE_ENERGY_VALUE_POINTS')
2225
2226
                    query = (" SELECT id "
2227
                             " FROM tbl_meters_points "
2228
                             " WHERE meter_id = %s AND point_id = %s")
2229
                    cursor.execute(query, (new_id, point['id'],))
2230
                    if cursor.fetchone() is not None:
2231
                        cursor.close()
2232
                        cnx.close()
2233
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2234
                                               description='API.METER_POINT_RELATION_EXISTS')
2235
2236
                    add_row = (" INSERT INTO tbl_meters_points (meter_id, point_id) "
2237
                               " VALUES (%s, %s) ")
2238
                    cursor.execute(add_row, (new_id, point['id'],))
2239
        cnx.commit()
2240
        cursor.close()
2241
        cnx.close()
2242
2243
        # Clear cache after cloning meter
2244
        clear_meter_cache()
2245
2246
        resp.status = falcon.HTTP_201
2247
        resp.location = '/meters/' + str(new_id)
2248