CombinedEquipmentMeterCollection.on_options()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 5
Code Lines 5

Duplication

Lines 5
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 5
dl 5
loc 5
rs 10
c 0
b 0
f 0
cc 1
nop 3
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_combined_equipment_cache(combined_equipment_id=None):
12
    """
13
    Clear combined equipment-related cache after data modification
14
15
    Args:
16
        combined_equipment_id: Combined equipment ID (optional, for specific equipment 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 combined equipment list cache (all search query variations)
36
        list_cache_key_pattern = 'combinedequipment: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 combined equipment item cache if combined_equipment_id is provided
42
        if combined_equipment_id:
43
            item_cache_key = f'combinedequipment:item:{combined_equipment_id}'
44
            redis_client.delete(item_cache_key)
45
46
            # Clear all related caches for this combined equipment
47
            patterns = [
48
                f'combinedequipment:equipments:{combined_equipment_id}',
49
                f'combinedequipment:parameters:{combined_equipment_id}',
50
                f'combinedequipment:parameter:*:{combined_equipment_id}',
51
                f'combinedequipment:datasources:{combined_equipment_id}',
52
                f'combinedequipment:datasourcepoints:{combined_equipment_id}',
53
                f'combinedequipment:meters:{combined_equipment_id}',
54
                f'combinedequipment:offlinemeters:{combined_equipment_id}',
55
                f'combinedequipment:virtualmeters:{combined_equipment_id}',
56
                f'combinedequipment:commands:{combined_equipment_id}',
57
                f'combinedequipment:export:{combined_equipment_id}',
58
            ]
59
            for pattern in patterns:
60
                # Use keys() to find matching keys, then delete them
61
                matching_keys = redis_client.keys(pattern)
62
                if matching_keys:
63
                    redis_client.delete(*matching_keys)
64
65
    except Exception:
66
        # If cache clear fails, ignore and continue
67
        pass
68
69
70
class CombinedEquipmentCollection:
71
    """
72
    Combined Equipment Collection Resource
73
74
    This class handles CRUD operations for combined equipment collection.
75
    It provides endpoints for listing all combined equipments and creating new ones.
76
    Combined equipments represent groups of related equipment that work together
77
    in the energy management system, such as HVAC systems, lighting groups, etc.
78
    """
79
80
    def __init__(self):
81
        pass
82
83
    @staticmethod
84
    def on_options(req, resp):
85
        """
86
        Handle OPTIONS request for CORS preflight
87
88
        Args:
89
            req: Falcon request object
90
            resp: Falcon response object
91
        """
92
        _ = req
93
        resp.status = falcon.HTTP_200
94
95 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
96
    def on_get(req, resp):
97
        """
98
        Handle GET requests to retrieve all combined equipments
99
100
        Returns a list of all combined equipments with their metadata including:
101
        - Equipment ID, name, and UUID
102
        - Input/output counting status
103
        - Cost center information
104
        - SVG diagram reference
105
        - Camera URL for monitoring
106
        - Description and QR code
107
108
        Args:
109
            req: Falcon request object
110
            resp: Falcon response object
111
        """
112
        # Check authentication method (API key or session)
113
        if 'API-KEY' not in req.headers or \
114
                not isinstance(req.headers['API-KEY'], str) or \
115
                len(str.strip(req.headers['API-KEY'])) == 0:
116
            access_control(req)
117
        else:
118
            api_key_control(req)
119
120
        search_query = req.get_param('q', default=None)
121
        if search_query is not None:
122
            search_query = search_query.strip()
123
        else:
124
            search_query = ''
125
126
        # Redis cache key
127
        cache_key = f'combinedequipment:list:{search_query}'
128
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
129
130
        # Try to get from Redis cache (only if Redis is enabled)
131
        redis_client = None
132
        if config.redis.get('is_enabled', False):
133
            try:
134
                redis_client = redis.Redis(
135
                    host=config.redis['host'],
136
                    port=config.redis['port'],
137
                    password=config.redis['password'] if config.redis['password'] else None,
138
                    db=config.redis['db'],
139
                    decode_responses=True,
140
                    socket_connect_timeout=2,
141
                    socket_timeout=2
142
                )
143
                redis_client.ping()
144
                cached_result = redis_client.get(cache_key)
145
                if cached_result:
146
                    resp.text = cached_result
147
                    return
148
            except Exception:
149
                # If Redis connection fails, continue to database query
150
                pass
151
152
        # Cache miss or Redis error - query database
153
        # Connect to database and retrieve cost centers
154
        cnx = mysql.connector.connect(**config.myems_system_db)
155
        cursor = cnx.cursor()
156
157
        # Query to retrieve all cost centers for reference
158
        query = (" SELECT id, name, uuid "
159
                 " FROM tbl_cost_centers ")
160
        cursor.execute(query)
161
        rows_cost_centers = cursor.fetchall()
162
163
        # Build cost center dictionary for quick lookup
164
        cost_center_dict = dict()
165
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
166
            for row in rows_cost_centers:
167
                cost_center_dict[row[0]] = {"id": row[0],
168
                                            "name": row[1],
169
                                            "uuid": row[2]}
170
171
        # Query to retrieve all SVG diagrams for reference
172
        query = (" SELECT id, name, uuid "
173
                 " FROM tbl_svgs ")
174
        cursor.execute(query)
175
        rows_svgs = cursor.fetchall()
176
177
        # Build SVG dictionary for quick lookup
178
        svg_dict = dict()
179
        if rows_svgs is not None and len(rows_svgs) > 0:
180
            for row in rows_svgs:
181
                svg_dict[row[0]] = {"id": row[0],
182
                                    "name": row[1],
183
                                    "uuid": row[2]}
184
185
        # Query to retrieve all combined equipments
186
        query = (" SELECT id, name, uuid, "
187
                 "        is_input_counted, is_output_counted, "
188
                 "        cost_center_id, svg_id, camera_url, description "
189
                 " FROM tbl_combined_equipments ")
190
        params = []
191
        if search_query:
192
            query += " WHERE name LIKE %s OR   description LIKE %s "
193
            params = [f'%{search_query}%', f'%{search_query}%']
194
        query += " ORDER BY id "
195
        cursor.execute(query, params)
196
        rows_combined_equipments = cursor.fetchall()
197
198
        # Build result list with all combined equipment data
199
        result = list()
200
        if rows_combined_equipments is not None and len(rows_combined_equipments) > 0:
201
            for row in rows_combined_equipments:
202
                meta_result = {"id": row[0],
203
                               "name": row[1],
204
                               "uuid": row[2],
205
                               "is_input_counted": bool(row[3]),
206
                               "is_output_counted": bool(row[4]),
207
                               "cost_center": cost_center_dict.get(row[5], None),
208
                               "svg": svg_dict.get(row[6], None),
209
                               "camera_url": row[7],
210
                               "description": row[8],
211
                               "qrcode": 'combinedequipment:' + row[2]}
212
                result.append(meta_result)
213
214
        cursor.close()
215
        cnx.close()
216
217
        # Store result in Redis cache
218
        result_json = json.dumps(result)
219
        if redis_client:
220
            try:
221
                redis_client.setex(cache_key, cache_expire, result_json)
222
            except Exception:
223
                # If cache set fails, ignore and continue
224
                pass
225
226
        resp.text = result_json
227
228 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
229
    @user_logger
230
    def on_post(req, resp):
231
        """Handles POST requests"""
232
        admin_control(req)
233
        try:
234
            raw_json = req.stream.read().decode('utf-8')
235
        except UnicodeDecodeError as ex:
236
            print("Failed to decode request")
237
            raise falcon.HTTPError(status=falcon.HTTP_400,
238
                                   title='API.BAD_REQUEST',
239
                                   description='API.INVALID_ENCODING')
240
        except Exception as ex:
241
            print("Unexpected error reading request stream")
242
            raise falcon.HTTPError(status=falcon.HTTP_400,
243
                                   title='API.BAD_REQUEST',
244
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
245
246
        new_values = json.loads(raw_json)
247
248
        if 'name' not in new_values['data'].keys() or \
249
                not isinstance(new_values['data']['name'], str) or \
250
                len(str.strip(new_values['data']['name'])) == 0:
251
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
252
                                   description='API.INVALID_COMBINED_EQUIPMENT_NAME')
253
        name = str.strip(new_values['data']['name'])
254
255
        if 'is_input_counted' not in new_values['data'].keys() or \
256
                not isinstance(new_values['data']['is_input_counted'], bool):
257
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
258
                                   description='API.INVALID_IS_INPUT_COUNTED_VALUE')
259
        is_input_counted = new_values['data']['is_input_counted']
260
261
        if 'is_output_counted' not in new_values['data'].keys() or \
262
                not isinstance(new_values['data']['is_output_counted'], bool):
263
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
264
                                   description='API.INVALID_IS_OUTPUT_COUNTED_VALUE')
265
        is_output_counted = new_values['data']['is_output_counted']
266
267
        if 'cost_center_id' not in new_values['data'].keys() or \
268
                not isinstance(new_values['data']['cost_center_id'], int) or \
269
                new_values['data']['cost_center_id'] <= 0:
270
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
271
                                   description='API.INVALID_COST_CENTER_ID')
272
        cost_center_id = new_values['data']['cost_center_id']
273
274
        if 'svg_id' in new_values['data'].keys() and \
275
                isinstance(new_values['data']['svg_id'], int) and \
276
                new_values['data']['svg_id'] > 0:
277
            svg_id = new_values['data']['svg_id']
278
        else:
279
            svg_id = None
280
281
        if 'camera_url' in new_values['data'].keys() and \
282
                new_values['data']['camera_url'] is not None and \
283
                len(str(new_values['data']['camera_url'])) > 0:
284
            camera_url = str.strip(new_values['data']['camera_url'])
285
        else:
286
            camera_url = None
287
288
        if 'description' in new_values['data'].keys() and \
289
                new_values['data']['description'] is not None and \
290
                len(str(new_values['data']['description'])) > 0:
291
            description = str.strip(new_values['data']['description'])
292
        else:
293
            description = None
294
295
        cnx = mysql.connector.connect(**config.myems_system_db)
296
        cursor = cnx.cursor()
297
298
        cursor.execute(" SELECT name "
299
                       " FROM tbl_combined_equipments "
300
                       " WHERE name = %s ", (name,))
301
        if cursor.fetchone() is not None:
302
            cursor.close()
303
            cnx.close()
304
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
305
                                   description='API.COMBINED_EQUIPMENT_NAME_IS_ALREADY_IN_USE')
306
307
        if cost_center_id is not None:
308
            cursor.execute(" SELECT name "
309
                           " FROM tbl_cost_centers "
310
                           " WHERE id = %s ",
311
                           (new_values['data']['cost_center_id'],))
312
            row = cursor.fetchone()
313
            if row is None:
314
                cursor.close()
315
                cnx.close()
316
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
317
                                       description='API.COST_CENTER_NOT_FOUND')
318
319
        if svg_id is not None:
320
            cursor.execute(" SELECT name "
321
                           " FROM tbl_svgs "
322
                           " WHERE id = %s ",
323
                           (svg_id,))
324
            row = cursor.fetchone()
325
            if row is None:
326
                cursor.close()
327
                cnx.close()
328
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
329
                                       description='API.SVG_NOT_FOUND')
330
331
        add_values = (" INSERT INTO tbl_combined_equipments "
332
                      "    (name, uuid, is_input_counted, is_output_counted, "
333
                      "     cost_center_id, svg_id, camera_url, description) "
334
                      " VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ")
335
        cursor.execute(add_values, (name,
336
                                    str(uuid.uuid4()),
337
                                    is_input_counted,
338
                                    is_output_counted,
339
                                    cost_center_id,
340
                                    svg_id,
341
                                    camera_url,
342
                                    description))
343
        new_id = cursor.lastrowid
344
        cnx.commit()
345
        cursor.close()
346
        cnx.close()
347
348
        # Clear cache after creating new combined equipment
349
        clear_combined_equipment_cache()
350
351
        resp.status = falcon.HTTP_201
352
        resp.location = '/combinedequipments/' + str(new_id)
353
354
355
class CombinedEquipmentItem:
356
    def __init__(self):
357
        pass
358
359
    @staticmethod
360
    def on_options(req, resp, id_):
361
        _ = req
362
        resp.status = falcon.HTTP_200
363
        _ = id_
364
365 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
366
    def on_get(req, resp, id_):
367
        if 'API-KEY' not in req.headers or \
368
                not isinstance(req.headers['API-KEY'], str) or \
369
                len(str.strip(req.headers['API-KEY'])) == 0:
370
            access_control(req)
371
        else:
372
            api_key_control(req)
373
        if not id_.isdigit() or int(id_) <= 0:
374
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
375
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
376
377
        # Redis cache key
378
        cache_key = f'combinedequipment:item:{id_}'
379
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
380
381
        # Try to get from Redis cache (only if Redis is enabled)
382
        redis_client = None
383
        if config.redis.get('is_enabled', False):
384
            try:
385
                redis_client = redis.Redis(
386
                    host=config.redis['host'],
387
                    port=config.redis['port'],
388
                    password=config.redis['password'] if config.redis['password'] else None,
389
                    db=config.redis['db'],
390
                    decode_responses=True,
391
                    socket_connect_timeout=2,
392
                    socket_timeout=2
393
                )
394
                redis_client.ping()
395
                cached_result = redis_client.get(cache_key)
396
                if cached_result:
397
                    resp.text = cached_result
398
                    return
399
            except Exception:
400
                # If Redis connection fails, continue to database query
401
                pass
402
403
        # Cache miss or Redis error - query database
404
        cnx = mysql.connector.connect(**config.myems_system_db)
405
        cursor = cnx.cursor()
406
407
        query = (" SELECT id, name, uuid "
408
                 " FROM tbl_cost_centers ")
409
        cursor.execute(query)
410
        rows_cost_centers = cursor.fetchall()
411
412
        cost_center_dict = dict()
413
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
414
            for row in rows_cost_centers:
415
                cost_center_dict[row[0]] = {"id": row[0],
416
                                            "name": row[1],
417
                                            "uuid": row[2]}
418
419
        svg_dict = dict()
420
        query = (" SELECT id, name, uuid "
421
                 " FROM tbl_svgs ")
422
        cursor.execute(query)
423
        rows_svgs = cursor.fetchall()
424
        if rows_svgs is not None and len(rows_svgs) > 0:
425
            for row in rows_svgs:
426
                svg_dict[row[0]] = {"id": row[0],
427
                                    "name": row[1],
428
                                    "uuid": row[2]}
429
430
        query = (" SELECT id, name, uuid, "
431
                 "        is_input_counted, is_output_counted, "
432
                 "        cost_center_id, svg_id, camera_url, description "
433
                 " FROM tbl_combined_equipments "
434
                 " WHERE id = %s ")
435
        cursor.execute(query, (id_,))
436
        row = cursor.fetchone()
437
        cursor.close()
438
        cnx.close()
439
440
        if row is None:
441
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
442
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
443
444
        meta_result = {"id": row[0],
445
                       "name": row[1],
446
                       "uuid": row[2],
447
                       "is_input_counted": bool(row[3]),
448
                       "is_output_counted": bool(row[4]),
449
                       "cost_center": cost_center_dict.get(row[5], None),
450
                       "svg": svg_dict.get(row[6], None),
451
                       "camera_url": row[7],
452
                       "description": row[8],
453
                       "qrcode": 'combinedequipment:' + row[2]}
454
455
        # Store result in Redis cache
456
        result_json = json.dumps(meta_result)
457
        if redis_client:
458
            try:
459
                redis_client.setex(cache_key, cache_expire, result_json)
460
            except Exception:
461
                # If cache set fails, ignore and continue
462
                pass
463
464
        resp.text = result_json
465
466
    @staticmethod
467
    @user_logger
468
    def on_delete(req, resp, id_):
469
        admin_control(req)
470
        if not id_.isdigit() or int(id_) <= 0:
471
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
472
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
473
474
        cnx = mysql.connector.connect(**config.myems_system_db)
475
        cursor = cnx.cursor()
476
477
        # check relation with space
478
        cursor.execute(" SELECT space_id "
479
                       " FROM tbl_spaces_combined_equipments "
480
                       " WHERE combined_equipment_id = %s ",
481
                       (id_,))
482
        rows_combined_equipments = cursor.fetchall()
483
        if rows_combined_equipments is not None and len(rows_combined_equipments) > 0:
484
            cursor.close()
485
            cnx.close()
486
            raise falcon.HTTPError(status=falcon.HTTP_400,
487
                                   title='API.BAD_REQUEST',
488
                                   description='API.THERE_IS_RELATION_WITH_SPACES')
489
490
        # delete relation with commands
491
        cursor.execute(" DELETE FROM tbl_combined_equipments_commands WHERE combined_equipment_id = %s ", (id_,))
492
493
        # delete relation with equipments
494
        cursor.execute(" DELETE FROM tbl_combined_equipments_equipments WHERE combined_equipment_id = %s ", (id_,))
495
496
        # delete relation with meters
497
        cursor.execute(" DELETE FROM tbl_combined_equipments_meters WHERE combined_equipment_id = %s ", (id_,))
498
499
        # delete relation with offline meters
500
        cursor.execute(" DELETE FROM tbl_combined_equipments_offline_meters WHERE combined_equipment_id = %s ", (id_,))
501
502
        # delete all associated parameters
503
        cursor.execute(" DELETE FROM tbl_combined_equipments_parameters WHERE combined_equipment_id = %s ", (id_,))
504
505
        # delete relation with virtual meter
506
        cursor.execute(" DELETE FROM tbl_combined_equipments_virtual_meters WHERE combined_equipment_id = %s ", (id_,))
507
        cnx.commit()
508
509
        cursor.execute(" DELETE FROM tbl_combined_equipments WHERE id = %s ", (id_,))
510
        cnx.commit()
511
512
        cursor.close()
513
        cnx.close()
514
515
        # Clear cache after deleting combined equipment
516
        clear_combined_equipment_cache(combined_equipment_id=id_)
517
518
        resp.status = falcon.HTTP_204
519
520 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
521
    @user_logger
522
    def on_put(req, resp, id_):
523
        """Handles PUT requests"""
524
        admin_control(req)
525
        if not id_.isdigit() or int(id_) <= 0:
526
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
527
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
528
        try:
529
            raw_json = req.stream.read().decode('utf-8')
530
        except UnicodeDecodeError as ex:
531
            print("Failed to decode request")
532
            raise falcon.HTTPError(status=falcon.HTTP_400,
533
                                   title='API.BAD_REQUEST',
534
                                   description='API.INVALID_ENCODING')
535
        except Exception as ex:
536
            print("Unexpected error reading request stream")
537
            raise falcon.HTTPError(status=falcon.HTTP_400,
538
                                   title='API.BAD_REQUEST',
539
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
540
541
        new_values = json.loads(raw_json)
542
543
        if 'name' not in new_values['data'].keys() or \
544
                not isinstance(new_values['data']['name'], str) or \
545
                len(str.strip(new_values['data']['name'])) == 0:
546
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
547
                                   description='API.INVALID_COMBINED_EQUIPMENT_NAME')
548
        name = str.strip(new_values['data']['name'])
549
550
        if 'is_input_counted' not in new_values['data'].keys() or \
551
                not isinstance(new_values['data']['is_input_counted'], bool):
552
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
553
                                   description='API.INVALID_IS_INPUT_COUNTED_VALUE')
554
        is_input_counted = new_values['data']['is_input_counted']
555
556
        if 'is_output_counted' not in new_values['data'].keys() or \
557
                not isinstance(new_values['data']['is_output_counted'], bool):
558
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
559
                                   description='API.INVALID_IS_OUTPUT_COUNTED_VALUE')
560
        is_output_counted = new_values['data']['is_output_counted']
561
562
        if 'cost_center_id' not in new_values['data'].keys() or \
563
                not isinstance(new_values['data']['cost_center_id'], int) or \
564
                new_values['data']['cost_center_id'] <= 0:
565
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
566
                                   description='API.INVALID_COST_CENTER_ID')
567
        cost_center_id = new_values['data']['cost_center_id']
568
569
        if 'svg_id' in new_values['data'].keys() and \
570
                isinstance(new_values['data']['svg_id'], int) and \
571
                new_values['data']['svg_id'] > 0:
572
            svg_id = new_values['data']['svg_id']
573
        else:
574
            svg_id = None
575
576
        if 'camera_url' in new_values['data'].keys() and \
577
                new_values['data']['camera_url'] is not None and \
578
                len(str(new_values['data']['camera_url'])) > 0:
579
            camera_url = str.strip(new_values['data']['camera_url'])
580
        else:
581
            camera_url = None
582
583
        if 'description' in new_values['data'].keys() and \
584
                new_values['data']['description'] is not None and \
585
                len(str(new_values['data']['description'])) > 0:
586
            description = str.strip(new_values['data']['description'])
587
        else:
588
            description = None
589
590
        cnx = mysql.connector.connect(**config.myems_system_db)
591
        cursor = cnx.cursor()
592
593
        cursor.execute(" SELECT name "
594
                       " FROM tbl_combined_equipments "
595
                       " WHERE id = %s ", (id_,))
596
        if cursor.fetchone() is None:
597
            cursor.close()
598
            cnx.close()
599
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
600
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
601
602
        cursor.execute(" SELECT name "
603
                       " FROM tbl_combined_equipments "
604
                       " WHERE name = %s AND id != %s ", (name, id_))
605
        if cursor.fetchone() is not None:
606
            cursor.close()
607
            cnx.close()
608
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
609
                                   description='API.COMBINED_EQUIPMENT_NAME_IS_ALREADY_IN_USE')
610
611
        cursor.execute(" SELECT name "
612
                       " FROM tbl_cost_centers "
613
                       " WHERE id = %s ",
614
                       (new_values['data']['cost_center_id'],))
615
        row = cursor.fetchone()
616
        if row is None:
617
            cursor.close()
618
            cnx.close()
619
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
620
                                   description='API.COST_CENTER_NOT_FOUND')
621
622
        if svg_id is not None:
623
            cursor.execute(" SELECT name "
624
                           " FROM tbl_svgs "
625
                           " WHERE id = %s ",
626
                           (new_values['data']['svg_id'],))
627
            row = cursor.fetchone()
628
            if row is None:
629
                cursor.close()
630
                cnx.close()
631
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
632
                                       description='API.SVG_NOT_FOUND')
633
634
        update_row = (" UPDATE tbl_combined_equipments "
635
                      " SET name = %s, is_input_counted = %s, is_output_counted = %s, "
636
                      "     cost_center_id = %s, svg_id = %s, camera_url = %s, description = %s "
637
                      " WHERE id = %s ")
638
        cursor.execute(update_row, (name,
639
                                    is_input_counted,
640
                                    is_output_counted,
641
                                    cost_center_id,
642
                                    svg_id,
643
                                    camera_url,
644
                                    description,
645
                                    id_))
646
        cnx.commit()
647
648
        cursor.close()
649
        cnx.close()
650
651
        # Clear cache after updating combined equipment
652
        clear_combined_equipment_cache(combined_equipment_id=id_)
653
654
        resp.status = falcon.HTTP_200
655
656
    # Clone a Combined Equipment
657 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
658
    @user_logger
659
    def on_post(req, resp, id_):
660
        """Handles POST requests"""
661
        admin_control(req)
662
        if not id_.isdigit() or int(id_) <= 0:
663
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
664
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
665
666
        cnx = mysql.connector.connect(**config.myems_system_db)
667
        cursor = cnx.cursor()
668
        cursor.execute(" SELECT name "
669
                       " FROM tbl_combined_equipments "
670
                       " WHERE id = %s ", (id_,))
671
        if cursor.fetchone() is None:
672
            cursor.close()
673
            cnx.close()
674
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
675
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
676
677
        query = (" SELECT name, is_input_counted, is_output_counted, "
678
                 "        cost_center_id, svg_id, camera_url, description "
679
                 " FROM tbl_combined_equipments "
680
                 " WHERE id = %s ")
681
        cursor.execute(query, (id_,))
682
        row = cursor.fetchone()
683
684
        if row is None:
685
            cursor.close()
686
            cnx.close()
687
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
688
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
689
        else:
690
            add_values = (" INSERT INTO tbl_combined_equipments "
691
                          "    (name, uuid, is_input_counted, is_output_counted, "
692
                          "     cost_center_id, svg_id, camera_url, description) "
693
                          " VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ")
694
            cursor.execute(add_values, (row[0] + ' Copy',
695
                                        str(uuid.uuid4()),
696
                                        row[1],
697
                                        row[2],
698
                                        row[3],
699
                                        row[4],
700
                                        row[5],
701
                                        row[6]))
702
            new_id = cursor.lastrowid
703
            cnx.commit()
704
705
        # clone relation with meter
706
        cursor.execute(" SELECT meter_id, is_output "
707
                       " FROM tbl_combined_equipments_meters "
708
                       " WHERE combined_equipment_id = %s ",
709
                       (id_,))
710
        rows_meters = cursor.fetchall()
711
        if rows_meters is not None and len(rows_meters) > 0:
712
            add_values = (" INSERT INTO tbl_combined_equipments_meters (combined_equipment_id, meter_id, is_output) "
713
                          " VALUES  ")
714
            for row in rows_meters:
715
                add_values += " (" + str(new_id) + ","
716
                add_values += str(row[0]) + ","
717
                add_values += str(bool(row[1])) + "), "
718
            # trim ", " at the end of string and then execute
719
            cursor.execute(add_values[:-2])
720
            cnx.commit()
721
722
        # clone relation with offline meter
723
        cursor.execute(" SELECT offline_meter_id, is_output "
724
                       " FROM tbl_combined_equipments_offline_meters "
725
                       " WHERE combined_equipment_id = %s ",
726
                       (id_,))
727
        rows_offline_meters = cursor.fetchall()
728
        if rows_offline_meters is not None and len(rows_offline_meters) > 0:
729
            add_values = (" INSERT INTO tbl_combined_equipments_offline_meters "
730
                          " (combined_equipment_id, offline_meter_id, is_output) "
731
                          " VALUES  ")
732
            for row in rows_offline_meters:
733
                add_values += " (" + str(new_id) + ","
734
                add_values += "'" + str(row[0]) + "',"
735
                add_values += str(bool(row[1])) + "), "
736
            # trim ", " at the end of string and then execute
737
            cursor.execute(add_values[:-2])
738
            cnx.commit()
739
740
        # clone relation with virtual meter
741
        cursor.execute(" SELECT virtual_meter_id, is_output "
742
                       " FROM tbl_combined_equipments_virtual_meters "
743
                       " WHERE combined_equipment_id = %s ",
744
                       (id_,))
745
        rows_virtual_meters = cursor.fetchall()
746
        if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
747
            add_values = (" INSERT INTO tbl_combined_equipments_virtual_meters "
748
                          " (combined_equipment_id, virtual_meter_id, is_output) "
749
                          " VALUES  ")
750
            for row in rows_virtual_meters:
751
                add_values += " (" + str(new_id) + ","
752
                add_values += str(row[0]) + ","
753
                add_values += str(bool(row[1])) + "), "
754
            # trim ", " at the end of string and then execute
755
            cursor.execute(add_values[:-2])
756
            cnx.commit()
757
758
        # clone parameters
759
        cursor.execute(" SELECT name, parameter_type, constant, point_id, numerator_meter_uuid, denominator_meter_uuid "
760
                       " FROM tbl_combined_equipments_parameters "
761
                       " WHERE combined_equipment_id = %s ",
762
                       (id_,))
763
        rows_parameters = cursor.fetchall()
764
        if rows_parameters is not None and len(rows_parameters) > 0:
765
            add_values = (" INSERT INTO tbl_combined_equipments_parameters"
766
                          "     (combined_equipment_id, name, parameter_type, constant, point_id, "
767
                          "      numerator_meter_uuid, denominator_meter_uuid) "
768
                          " VALUES  ")
769
            for row in rows_parameters:
770
                add_values += " (" + str(new_id) + ","
771
                add_values += "'" + str(row[0]) + "',"
772
                add_values += "'" + str(row[1]) + "',"
773
                if row[2] is not None:
774
                    add_values += "'" + str(row[2]) + "',"
775
                else:
776
                    add_values += "null, "
777
778
                if row[3] is not None:
779
                    add_values += str(row[2]) + ","
780
                else:
781
                    add_values += "null, "
782
783
                if row[4] is not None:
784
                    add_values += "'" + row[4] + "',"
785
                else:
786
                    add_values += "null, "
787
                if row[5] is not None:
788
                    add_values += "'" + row[5] + "'), "
789
                else:
790
                    add_values += "null), "
791
792
            # trim ", " at the end of string and then execute
793
            cursor.execute(add_values[:-2])
794
            cnx.commit()
795
796
        cursor.close()
797
        cnx.close()
798
799
        # Clear cache after cloning combined equipment
800
        clear_combined_equipment_cache()
801
802
        resp.status = falcon.HTTP_201
803
        resp.location = '/combinedequipments/' + str(new_id)
804
805
806 View Code Duplication
class CombinedEquipmentEquipmentCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
807
    def __init__(self):
808
        pass
809
810
    @staticmethod
811
    def on_options(req, resp, id_):
812
        _ = req
813
        resp.status = falcon.HTTP_200
814
        _ = id_
815
816
    @staticmethod
817
    def on_get(req, resp, id_):
818
        if 'API-KEY' not in req.headers or \
819
                not isinstance(req.headers['API-KEY'], str) or \
820
                len(str.strip(req.headers['API-KEY'])) == 0:
821
            access_control(req)
822
        else:
823
            api_key_control(req)
824
        if not id_.isdigit() or int(id_) <= 0:
825
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
826
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
827
828
        # Redis cache key
829
        cache_key = f'combinedequipment:equipments:{id_}'
830
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
831
832
        # Try to get from Redis cache (only if Redis is enabled)
833
        redis_client = None
834
        if config.redis.get('is_enabled', False):
835
            try:
836
                redis_client = redis.Redis(
837
                    host=config.redis['host'],
838
                    port=config.redis['port'],
839
                    password=config.redis['password'] if config.redis['password'] else None,
840
                    db=config.redis['db'],
841
                    decode_responses=True,
842
                    socket_connect_timeout=2,
843
                    socket_timeout=2
844
                )
845
                redis_client.ping()
846
                cached_result = redis_client.get(cache_key)
847
                if cached_result:
848
                    resp.text = cached_result
849
                    return
850
            except Exception:
851
                # If Redis connection fails, continue to database query
852
                pass
853
854
        # Cache miss or Redis error - query database
855
        cnx = mysql.connector.connect(**config.myems_system_db)
856
        cursor = cnx.cursor()
857
858
        cursor.execute(" SELECT name "
859
                       " FROM tbl_combined_equipments "
860
                       " WHERE id = %s ", (id_,))
861
        if cursor.fetchone() is None:
862
            cursor.close()
863
            cnx.close()
864
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
865
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
866
867
        query = (" SELECT e.id, e.name, e.uuid "
868
                 " FROM tbl_combined_equipments c, tbl_combined_equipments_equipments ce, tbl_equipments e "
869
                 " WHERE ce.combined_equipment_id = c.id AND e.id = ce.equipment_id AND c.id = %s "
870
                 " ORDER BY e.id ")
871
        cursor.execute(query, (id_,))
872
        rows = cursor.fetchall()
873
        cursor.close()
874
        cnx.close()
875
876
        result = list()
877
        if rows is not None and len(rows) > 0:
878
            for row in rows:
879
                meta_result = {"id": row[0], "name": row[1], "uuid": row[2]}
880
                result.append(meta_result)
881
882
        # Store result in Redis cache
883
        result_json = json.dumps(result)
884
        if redis_client:
885
            try:
886
                redis_client.setex(cache_key, cache_expire, result_json)
887
            except Exception:
888
                # If cache set fails, ignore and continue
889
                pass
890
891
        resp.text = result_json
892
893
    @staticmethod
894
    @user_logger
895
    def on_post(req, resp, id_):
896
        """Handles POST requests"""
897
        admin_control(req)
898
        try:
899
            raw_json = req.stream.read().decode('utf-8')
900
        except UnicodeDecodeError as ex:
901
            print("Failed to decode request")
902
            raise falcon.HTTPError(status=falcon.HTTP_400,
903
                                   title='API.BAD_REQUEST',
904
                                   description='API.INVALID_ENCODING')
905
        except Exception as ex:
906
            print("Unexpected error reading request stream")
907
            raise falcon.HTTPError(status=falcon.HTTP_400,
908
                                   title='API.BAD_REQUEST',
909
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
910
911
        if not id_.isdigit() or int(id_) <= 0:
912
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
913
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
914
915
        new_values = json.loads(raw_json)
916
917
        if 'equipment_id' not in new_values['data'].keys() or \
918
                not isinstance(new_values['data']['equipment_id'], int) or \
919
                new_values['data']['equipment_id'] <= 0:
920
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
921
                                   description='API.INVALID_EQUIPMENT_ID')
922
        equipment_id = new_values['data']['equipment_id']
923
924
        cnx = mysql.connector.connect(**config.myems_system_db)
925
        cursor = cnx.cursor()
926
927
        cursor.execute(" SELECT name "
928
                       " from tbl_combined_equipments "
929
                       " WHERE id = %s ", (id_,))
930
        if cursor.fetchone() is None:
931
            cursor.close()
932
            cnx.close()
933
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
934
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
935
936
        cursor.execute(" SELECT name "
937
                       " FROM tbl_equipments "
938
                       " WHERE id = %s ", (equipment_id,))
939
        if cursor.fetchone() is None:
940
            cursor.close()
941
            cnx.close()
942
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
943
                                   description='API.EQUIPMENT_NOT_FOUND')
944
945
        query = (" SELECT id "
946
                 " FROM tbl_combined_equipments_equipments "
947
                 " WHERE combined_equipment_id = %s AND equipment_id = %s")
948
        cursor.execute(query, (id_, equipment_id,))
949
        if cursor.fetchone() is not None:
950
            cursor.close()
951
            cnx.close()
952
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
953
                                   description='API.COMBINED_EQUIPMENT_EQUIPMENT_RELATION_EXISTS')
954
955
        add_row = (" INSERT INTO tbl_combined_equipments_equipments (combined_equipment_id, equipment_id) "
956
                   " VALUES (%s, %s) ")
957
        cursor.execute(add_row, (id_, equipment_id,))
958
        cnx.commit()
959
        cursor.close()
960
        cnx.close()
961
962
        # Clear cache after adding equipment
963
        clear_combined_equipment_cache(combined_equipment_id=id_)
964
965
        resp.status = falcon.HTTP_201
966
        resp.location = '/combinedequipments/' + str(id_) + '/equipments/' + str(equipment_id)
967
968
969
class CombinedEquipmentEquipmentItem:
970
    def __init__(self):
971
        pass
972
973
    @staticmethod
974
    def on_options(req, resp, id_, eid):
975
        _ = req
976
        resp.status = falcon.HTTP_200
977
        _ = id_
978
979
    @staticmethod
980
    @user_logger
981
    def on_delete(req, resp, id_, eid):
982
        admin_control(req)
983
        if not id_.isdigit() or int(id_) <= 0:
984
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
985
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
986
987
        if not eid.isdigit() or int(eid) <= 0:
988
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
989
                                   description='API.INVALID_EQUIPMENT_ID')
990
991
        cnx = mysql.connector.connect(**config.myems_system_db)
992
        cursor = cnx.cursor()
993
994
        cursor.execute(" SELECT name "
995
                       " FROM tbl_combined_equipments "
996
                       " WHERE id = %s ", (id_,))
997
        if cursor.fetchone() is None:
998
            cursor.close()
999
            cnx.close()
1000
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1001
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
1002
1003
        cursor.execute(" SELECT name "
1004
                       " FROM tbl_equipments "
1005
                       " WHERE id = %s ", (eid,))
1006
        if cursor.fetchone() is None:
1007
            cursor.close()
1008
            cnx.close()
1009
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1010
                                   description='API.EQUIPMENT_NOT_FOUND')
1011
1012
        cursor.execute(" SELECT id "
1013
                       " FROM tbl_combined_equipments_equipments "
1014
                       " WHERE combined_equipment_id = %s AND equipment_id = %s ", (id_, eid))
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.COMBINED_EQUIPMENT_EQUIPMENT_RELATION_NOT_FOUND')
1020
1021
        cursor.execute(" DELETE FROM tbl_combined_equipments_equipments "
1022
                       " WHERE combined_equipment_id = %s AND equipment_id = %s ", (id_, eid))
1023
        cnx.commit()
1024
1025
        cursor.close()
1026
        cnx.close()
1027
1028
        # Clear cache after deleting equipment
1029
        clear_combined_equipment_cache(combined_equipment_id=id_)
1030
1031
        resp.status = falcon.HTTP_204
1032
1033
1034
class CombinedEquipmentParameterCollection:
1035
    def __init__(self):
1036
        pass
1037
1038
    @staticmethod
1039
    def on_options(req, resp, id_):
1040
        _ = req
1041
        resp.status = falcon.HTTP_200
1042
        _ = id_
1043
1044
    @staticmethod
1045
    def on_get(req, resp, id_):
1046
        if 'API-KEY' not in req.headers or \
1047
                not isinstance(req.headers['API-KEY'], str) or \
1048
                len(str.strip(req.headers['API-KEY'])) == 0:
1049
            access_control(req)
1050
        else:
1051
            api_key_control(req)
1052
        if not id_.isdigit() or int(id_) <= 0:
1053
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1054
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
1055
1056
        # Redis cache key
1057
        cache_key = f'combinedequipment:parameters:{id_}'
1058
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
1059
1060
        # Try to get from Redis cache (only if Redis is enabled)
1061
        redis_client = None
1062
        if config.redis.get('is_enabled', False):
1063
            try:
1064
                redis_client = redis.Redis(
1065
                    host=config.redis['host'],
1066
                    port=config.redis['port'],
1067
                    password=config.redis['password'] if config.redis['password'] else None,
1068
                    db=config.redis['db'],
1069
                    decode_responses=True,
1070
                    socket_connect_timeout=2,
1071
                    socket_timeout=2
1072
                )
1073
                redis_client.ping()
1074
                cached_result = redis_client.get(cache_key)
1075
                if cached_result:
1076
                    resp.text = cached_result
1077
                    return
1078
            except Exception:
1079
                # If Redis connection fails, continue to database query
1080
                pass
1081
1082
        # Cache miss or Redis error - query database
1083
        cnx = mysql.connector.connect(**config.myems_system_db)
1084
        cursor = cnx.cursor()
1085
1086
        cursor.execute(" SELECT name "
1087
                       " FROM tbl_combined_equipments "
1088
                       " WHERE id = %s ", (id_,))
1089
        if cursor.fetchone() is None:
1090
            cursor.close()
1091
            cnx.close()
1092
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1093
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
1094
1095
        cursor.execute(" SELECT data_source_id "
1096
                       " FROM tbl_combined_equipments_data_sources "
1097
                       " WHERE combined_equipment_id = %s ", (id_,))
1098
        rows_data_sources = cursor.fetchall()
1099
        data_source_ids = tuple({row[0] for row in rows_data_sources if row[0] is not None})
1100
1101
        if len(data_source_ids) > 0:
1102
            format_strings = ','.join(['%s'] * len(data_source_ids))
1103
            query = (" SELECT id, name "
1104
                     " FROM tbl_points "
1105
                     " WHERE data_source_id IN (" + format_strings + ") ")
1106
            cursor.execute(query, data_source_ids)
1107
            rows_points = cursor.fetchall()
1108
        else:
1109
            rows_points = list()
1110
1111
        point_dict = dict()
1112
        if rows_points is not None and len(rows_points) > 0:
1113
            for row in rows_points:
1114
                point_dict[row[0]] = {"id": row[0],
1115
                                      "name": row[1]}
1116
1117
        query = (" SELECT id, name, uuid "
1118
                 " FROM tbl_meters ")
1119
        cursor.execute(query)
1120
        rows_meters = cursor.fetchall()
1121
1122
        meter_dict = dict()
1123
        if rows_meters is not None and len(rows_meters) > 0:
1124
            for row in rows_meters:
1125
                meter_dict[row[2]] = {"type": 'meter',
1126
                                      "id": row[0],
1127
                                      "name": row[1],
1128
                                      "uuid": row[2]}
1129
1130
        query = (" SELECT id, name, uuid "
1131
                 " FROM tbl_offline_meters ")
1132
        cursor.execute(query)
1133
        rows_offline_meters = cursor.fetchall()
1134
1135
        offline_meter_dict = dict()
1136
        if rows_offline_meters is not None and len(rows_offline_meters) > 0:
1137
            for row in rows_offline_meters:
1138
                offline_meter_dict[row[2]] = {"type": 'offline_meter',
1139
                                              "id": row[0],
1140
                                              "name": row[1],
1141
                                              "uuid": row[2]}
1142
1143
        query = (" SELECT id, name, uuid "
1144
                 " FROM tbl_virtual_meters ")
1145
        cursor.execute(query)
1146
        rows_virtual_meters = cursor.fetchall()
1147
1148
        virtual_meter_dict = dict()
1149
        if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
1150
            for row in rows_virtual_meters:
1151
                virtual_meter_dict[row[2]] = {"type": 'virtual_meter',
1152
                                              "id": row[0],
1153
                                              "name": row[1],
1154
                                              "uuid": row[2]}
1155
1156
        query = (" SELECT id, name, parameter_type, "
1157
                 "        constant, point_id, numerator_meter_uuid, denominator_meter_uuid "
1158
                 " FROM tbl_combined_equipments_parameters "
1159
                 " WHERE combined_equipment_id = %s "
1160
                 " ORDER BY id ")
1161
        cursor.execute(query, (id_,))
1162
        rows_parameters = cursor.fetchall()
1163
1164
        result = list()
1165
        if rows_parameters is not None and len(rows_parameters) > 0:
1166
            for row in rows_parameters:
1167
                constant = None
1168
                point = None
1169
                numerator_meter = None
1170
                denominator_meter = None
1171 View Code Duplication
                if row[2] == 'point':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1172
                    point = point_dict.get(row[4], None)
1173
                    if point is None and row[4] is not None:
1174
                        query = (" SELECT id, name "
1175
                                 " FROM tbl_points "
1176
                                 " WHERE id = %s ")
1177
                        cursor.execute(query, (row[4],))
1178
                        point_row = cursor.fetchone()
1179
                        if point_row is not None:
1180
                            point = {"id": point_row[0],
1181
                                     "name": point_row[1]}
1182
                    constant = None
1183
                    numerator_meter = None
1184
                    denominator_meter = None
1185
                elif row[2] == 'constant':
1186
                    constant = row[3]
1187
                    point = None
1188
                    numerator_meter = None
1189
                    denominator_meter = None
1190
                elif row[2] == 'fraction':
1191
                    constant = None
1192
                    point = None
1193
                    # find numerator meter by uuid
1194
                    numerator_meter = meter_dict.get(row[5], None)
1195
                    if numerator_meter is None:
1196
                        numerator_meter = virtual_meter_dict.get(row[5], None)
1197
                    if numerator_meter is None:
1198
                        numerator_meter = offline_meter_dict.get(row[5], None)
1199
                    # find denominator meter by uuid
1200
                    denominator_meter = meter_dict.get(row[6], None)
1201
                    if denominator_meter is None:
1202
                        denominator_meter = virtual_meter_dict.get(row[6], None)
1203
                    if denominator_meter is None:
1204
                        denominator_meter = offline_meter_dict.get(row[6], None)
1205
1206
                meta_result = {"id": row[0],
1207
                               "name": row[1],
1208
                               "parameter_type": row[2],
1209
                               "constant": constant,
1210
                               "point": point,
1211
                               "numerator_meter": numerator_meter,
1212
                               "denominator_meter": denominator_meter}
1213
                result.append(meta_result)
1214
1215
        cursor.close()
1216
        cnx.close()
1217
1218
        # Store result in Redis cache
1219
        result_json = json.dumps(result)
1220
        if redis_client:
1221
            try:
1222
                redis_client.setex(cache_key, cache_expire, result_json)
1223
            except Exception:
1224
                # If cache set fails, ignore and continue
1225
                pass
1226
1227
        resp.text = result_json
1228
1229
    @staticmethod
1230
    @user_logger
1231
    def on_post(req, resp, id_):
1232
        """Handles POST requests"""
1233
        admin_control(req)
1234
        if not id_.isdigit() or int(id_) <= 0:
1235
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1236
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
1237
        try:
1238
            raw_json = req.stream.read().decode('utf-8')
1239
        except UnicodeDecodeError as ex:
1240
            print("Failed to decode request")
1241
            raise falcon.HTTPError(status=falcon.HTTP_400,
1242
                                   title='API.BAD_REQUEST',
1243
                                   description='API.INVALID_ENCODING')
1244
        except Exception as ex:
1245
            print("Unexpected error reading request stream")
1246
            raise falcon.HTTPError(status=falcon.HTTP_400,
1247
                                   title='API.BAD_REQUEST',
1248
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1249
1250
        new_values = json.loads(raw_json)
1251
1252
        if 'name' not in new_values['data'].keys() or \
1253
                not isinstance(new_values['data']['name'], str) or \
1254
                len(str.strip(new_values['data']['name'])) == 0:
1255
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1256
                                   description='API.INVALID_COMBINED_EQUIPMENT_PARAMETER_NAME')
1257
        name = str.strip(new_values['data']['name'])
1258
1259
        if 'parameter_type' not in new_values['data'].keys() or \
1260
                not isinstance(new_values['data']['parameter_type'], str) or \
1261
                len(str.strip(new_values['data']['parameter_type'])) == 0:
1262
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1263
                                   description='API.INVALID_COMBINED_EQUIPMENT_PARAMETER_TYPE')
1264
1265
        parameter_type = str.strip(new_values['data']['parameter_type'])
1266
1267
        if parameter_type not in ('constant', 'point', 'fraction'):
1268
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1269
                                   description='API.INVALID_COMBINED_EQUIPMENT_PARAMETER_TYPE')
1270
1271
        constant = None
1272
        if 'constant' in new_values['data'].keys():
1273
            if new_values['data']['constant'] is not None and \
1274
                    isinstance(new_values['data']['constant'], str) and \
1275
                    len(str.strip(new_values['data']['constant'])) > 0:
1276
                constant = str.strip(new_values['data']['constant'])
1277
1278
        point_id = None
1279
        if 'point_id' in new_values['data'].keys():
1280
            if new_values['data']['point_id'] is not None and \
1281
                    new_values['data']['point_id'] <= 0:
1282
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1283
                                       description='API.INVALID_POINT_ID')
1284
            point_id = new_values['data']['point_id']
1285
1286
        numerator_meter_uuid = None
1287
        if 'numerator_meter_uuid' in new_values['data'].keys():
1288
            if new_values['data']['numerator_meter_uuid'] is not None and \
1289
                    isinstance(new_values['data']['numerator_meter_uuid'], str) and \
1290
                    len(str.strip(new_values['data']['numerator_meter_uuid'])) > 0:
1291
                numerator_meter_uuid = str.strip(new_values['data']['numerator_meter_uuid'])
1292
1293
        denominator_meter_uuid = None
1294
        if 'denominator_meter_uuid' in new_values['data'].keys():
1295
            if new_values['data']['denominator_meter_uuid'] is not None and \
1296
                    isinstance(new_values['data']['denominator_meter_uuid'], str) and \
1297
                    len(str.strip(new_values['data']['denominator_meter_uuid'])) > 0:
1298
                denominator_meter_uuid = str.strip(new_values['data']['denominator_meter_uuid'])
1299
1300
        cnx = mysql.connector.connect(**config.myems_system_db)
1301
        cursor = cnx.cursor()
1302
1303
        cursor.execute(" SELECT name "
1304
                       " FROM tbl_combined_equipments "
1305
                       " WHERE id = %s ", (id_,))
1306
        if cursor.fetchone() is None:
1307
            cursor.close()
1308
            cnx.close()
1309
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.NOT_FOUND',
1310
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
1311
1312
        cursor.execute(" SELECT data_source_id "
1313
                       " FROM tbl_combined_equipments_data_sources "
1314
                       " WHERE combined_equipment_id = %s ", (id_,))
1315
        rows_data_sources = cursor.fetchall()
1316
        data_source_ids = tuple({row[0] for row in rows_data_sources if row[0] is not None})
1317
1318
        cursor.execute(" SELECT data_source_id "
1319
                       " FROM tbl_combined_equipments_data_sources "
1320
                       " WHERE combined_equipment_id = %s ", (id_,))
1321
        rows_data_sources = cursor.fetchall()
1322
        data_source_ids = tuple({row[0] for row in rows_data_sources if row[0] is not None})
1323
1324
        cursor.execute(" SELECT name "
1325
                       " FROM tbl_combined_equipments_parameters "
1326
                       " WHERE name = %s AND combined_equipment_id = %s ", (name, id_))
1327
        if cursor.fetchone() is not None:
1328
            cursor.close()
1329
            cnx.close()
1330
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1331
                                   description='API.COMBINED_EQUIPMENT_PARAMETER_NAME_IS_ALREADY_IN_USE')
1332
1333
        # validate by parameter type
1334 View Code Duplication
        if parameter_type == 'point':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1335
            if point_id is None:
1336
                cursor.close()
1337
                cnx.close()
1338
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1339
                                       description='API.INVALID_POINT_ID')
1340
1341
            if len(data_source_ids) == 0:
1342
                cursor.close()
1343
                cnx.close()
1344
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.NOT_FOUND',
1345
                                       description='API.POINT_NOT_FOUND')
1346
1347
            format_strings = ','.join(['%s'] * len(data_source_ids))
1348
            query = (" SELECT id, name "
1349
                     " FROM tbl_points "
1350
                     " WHERE id = %s AND data_source_id IN (" + format_strings + ") ")
1351
            cursor.execute(query, (point_id,) + data_source_ids)
1352
            if cursor.fetchone() is None:
1353
                cursor.close()
1354
                cnx.close()
1355
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1356
                                       description='API.POINT_NOT_FOUND')
1357
1358
        elif parameter_type == 'constant':
1359
            if constant is None:
1360
                cursor.close()
1361
                cnx.close()
1362
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1363
                                       description='API.INVALID_CONSTANT_VALUE')
1364
1365
        elif parameter_type == 'fraction':
1366
1367
            query = (" SELECT id, name, uuid "
1368
                     " FROM tbl_meters ")
1369
            cursor.execute(query)
1370
            rows_meters = cursor.fetchall()
1371
1372
            meter_dict = dict()
1373
            if rows_meters is not None and len(rows_meters) > 0:
1374
                for row in rows_meters:
1375
                    meter_dict[row[2]] = {"type": 'meter',
1376
                                          "id": row[0],
1377
                                          "name": row[1],
1378
                                          "uuid": row[2]}
1379
1380
            query = (" SELECT id, name, uuid "
1381
                     " FROM tbl_offline_meters ")
1382
            cursor.execute(query)
1383
            rows_offline_meters = cursor.fetchall()
1384
1385
            offline_meter_dict = dict()
1386
            if rows_offline_meters is not None and len(rows_offline_meters) > 0:
1387
                for row in rows_offline_meters:
1388
                    offline_meter_dict[row[2]] = {"type": 'offline_meter',
1389
                                                  "id": row[0],
1390
                                                  "name": row[1],
1391
                                                  "uuid": row[2]}
1392
1393
            query = (" SELECT id, name, uuid "
1394
                     " FROM tbl_virtual_meters ")
1395
            cursor.execute(query)
1396
            rows_virtual_meters = cursor.fetchall()
1397
1398
            virtual_meter_dict = dict()
1399
            if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
1400
                for row in rows_virtual_meters:
1401
                    virtual_meter_dict[row[2]] = {"type": 'virtual_meter',
1402
                                                  "id": row[0],
1403
                                                  "name": row[1],
1404
                                                  "uuid": row[2]}
1405
1406
            # validate numerator meter uuid
1407
            if meter_dict.get(numerator_meter_uuid) is None and \
1408
                    virtual_meter_dict.get(numerator_meter_uuid) is None and \
1409
                    offline_meter_dict.get(numerator_meter_uuid) is None:
1410
                cursor.close()
1411
                cnx.close()
1412
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1413
                                       description='API.INVALID_NUMERATOR_METER_UUID')
1414
1415
            # validate denominator meter uuid
1416
            if denominator_meter_uuid == numerator_meter_uuid:
1417
                cursor.close()
1418
                cnx.close()
1419
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1420
                                       description='API.INVALID_DENOMINATOR_METER_UUID')
1421
1422
            if denominator_meter_uuid not in meter_dict and \
1423
                    denominator_meter_uuid not in virtual_meter_dict and \
1424
                    denominator_meter_uuid not in offline_meter_dict:
1425
                cursor.close()
1426
                cnx.close()
1427
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1428
                                       description='API.INVALID_DENOMINATOR_METER_UUID')
1429
1430
        add_values = (" INSERT INTO tbl_combined_equipments_parameters "
1431
                      "    (combined_equipment_id, name, parameter_type, constant, "
1432
                      "     point_id, numerator_meter_uuid, denominator_meter_uuid) "
1433
                      " VALUES (%s, %s, %s, %s, %s, %s, %s) ")
1434
        cursor.execute(add_values, (id_,
1435
                                    name,
1436
                                    parameter_type,
1437
                                    constant,
1438
                                    point_id,
1439
                                    numerator_meter_uuid,
1440
                                    denominator_meter_uuid))
1441
        new_id = cursor.lastrowid
1442
        cnx.commit()
1443
        cursor.close()
1444
        cnx.close()
1445
1446
        # Clear cache after adding parameter
1447
        clear_combined_equipment_cache(combined_equipment_id=id_)
1448
1449
        resp.status = falcon.HTTP_201
1450
        resp.location = '/combinedequipments/' + str(id_) + 'parameters/' + str(new_id)
1451
1452
1453
class CombinedEquipmentParameterItem:
1454
    def __init__(self):
1455
        pass
1456
1457
    @staticmethod
1458
    def on_options(req, resp, id_, pid):
1459
        _ = req
1460
        resp.status = falcon.HTTP_200
1461
        _ = id_
1462
1463
    @staticmethod
1464
    def on_get(req, resp, id_, pid):
1465
        if 'API-KEY' not in req.headers or \
1466
                not isinstance(req.headers['API-KEY'], str) or \
1467
                len(str.strip(req.headers['API-KEY'])) == 0:
1468
            access_control(req)
1469
        else:
1470
            api_key_control(req)
1471
        if not id_.isdigit() or int(id_) <= 0:
1472
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1473
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
1474
1475
        if not pid.isdigit() or int(pid) <= 0:
1476
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1477
                                   description='API.INVALID_COMBINED_EQUIPMENT_PARAMETER_ID')
1478
1479
        cnx = mysql.connector.connect(**config.myems_system_db)
1480
        cursor = cnx.cursor()
1481
1482
        cursor.execute(" SELECT data_source_id "
1483
                       " FROM tbl_combined_equipments_data_sources "
1484
                       " WHERE combined_equipment_id = %s ", (id_,))
1485
        rows_data_sources = cursor.fetchall()
1486
        data_source_ids = tuple({row[0] for row in rows_data_sources if row[0] is not None})
1487
1488
        if len(data_source_ids) > 0:
1489
            format_strings = ','.join(['%s'] * len(data_source_ids))
1490
            query = (" SELECT id, name "
1491
                     " FROM tbl_points "
1492
                     " WHERE data_source_id IN (" + format_strings + ") ")
1493
            cursor.execute(query, data_source_ids)
1494
            rows_points = cursor.fetchall()
1495
        else:
1496
            rows_points = list()
1497
1498
        point_dict = dict()
1499
        if rows_points is not None and len(rows_points) > 0:
1500
            for row in rows_points:
1501
                point_dict[row[0]] = {"id": row[0],
1502
                                      "name": row[1]}
1503
1504
        query = (" SELECT id, name, uuid "
1505
                 " FROM tbl_meters ")
1506
        cursor.execute(query)
1507
        rows_meters = cursor.fetchall()
1508
1509
        meter_dict = dict()
1510
        if rows_meters is not None and len(rows_meters) > 0:
1511
            for row in rows_meters:
1512
                meter_dict[row[2]] = {"type": 'meter',
1513
                                      "id": row[0],
1514
                                      "name": row[1],
1515
                                      "uuid": row[2]}
1516
1517
        query = (" SELECT id, name, uuid "
1518
                 " FROM tbl_offline_meters ")
1519
        cursor.execute(query)
1520
        rows_offline_meters = cursor.fetchall()
1521
1522
        offline_meter_dict = dict()
1523
        if rows_offline_meters is not None and len(rows_offline_meters) > 0:
1524
            for row in rows_offline_meters:
1525
                offline_meter_dict[row[2]] = {"type": 'offline_meter',
1526
                                              "id": row[0],
1527
                                              "name": row[1],
1528
                                              "uuid": row[2]}
1529
1530
        query = (" SELECT id, name, uuid "
1531
                 " FROM tbl_virtual_meters ")
1532
        cursor.execute(query)
1533
        rows_virtual_meters = cursor.fetchall()
1534
1535
        virtual_meter_dict = dict()
1536
        if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
1537
            for row in rows_virtual_meters:
1538
                virtual_meter_dict[row[2]] = {"type": 'virtual_meter',
1539
                                              "id": row[0],
1540
                                              "name": row[1],
1541
                                              "uuid": row[2]}
1542
1543
        query = (" SELECT id, name, parameter_type, "
1544
                 "        constant, point_id, numerator_meter_uuid, denominator_meter_uuid "
1545
                 " FROM tbl_combined_equipments_parameters "
1546
                 " WHERE combined_equipment_id = %s AND id = %s ")
1547
        cursor.execute(query, (id_, pid))
1548
        row = cursor.fetchone()
1549
1550
        if row is None:
1551
            cursor.close()
1552
            cnx.close()
1553
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1554
                                   description='API.COMBINED_EQUIPMENT_PARAMETER_NOT_FOUND_OR_NOT_MATCH')
1555
        else:
1556
            constant = None
1557
            point = None
1558
            numerator_meter = None
1559
            denominator_meter = None
1560 View Code Duplication
            if row[2] == 'point':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1561
                point = point_dict.get(row[4], None)
1562
                if point is None and row[4] is not None:
1563
                    query = (" SELECT id, name "
1564
                             " FROM tbl_points "
1565
                             " WHERE id = %s ")
1566
                    cursor.execute(query, (row[4],))
1567
                    point_row = cursor.fetchone()
1568
                    if point_row is not None:
1569
                        point = {"id": point_row[0],
1570
                                 "name": point_row[1]}
1571
                constant = None
1572
                numerator_meter = None
1573
                denominator_meter = None
1574
            elif row[2] == 'constant':
1575
                constant = row[3]
1576
                point = None
1577
                numerator_meter = None
1578
                denominator_meter = None
1579
            elif row[2] == 'fraction':
1580
                constant = None
1581
                point = None
1582
                # find numerator meter by uuid
1583
                numerator_meter = meter_dict.get(row[5], None)
1584
                if numerator_meter is None:
1585
                    numerator_meter = virtual_meter_dict.get(row[5], None)
1586
                if numerator_meter is None:
1587
                    numerator_meter = offline_meter_dict.get(row[5], None)
1588
                # find denominator meter by uuid
1589
                denominator_meter = meter_dict.get(row[6], None)
1590
                if denominator_meter is None:
1591
                    denominator_meter = virtual_meter_dict.get(row[6], None)
1592
                if denominator_meter is None:
1593
                    denominator_meter = offline_meter_dict.get(row[6], None)
1594
1595
            meta_result = {"id": row[0],
1596
                           "name": row[1],
1597
                           "parameter_type": row[2],
1598
                           "constant": constant,
1599
                           "point": point,
1600
                           "numerator_meter": numerator_meter,
1601
                           "denominator_meter": denominator_meter}
1602
1603
        cursor.close()
1604
        cnx.close()
1605
        resp.text = json.dumps(meta_result)
1606
1607
    @staticmethod
1608
    @user_logger
1609
    def on_delete(req, resp, id_, pid):
1610
        admin_control(req)
1611
        if not id_.isdigit() or int(id_) <= 0:
1612
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1613
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
1614
1615
        if not pid.isdigit() or int(pid) <= 0:
1616
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1617
                                   description='API.INVALID_COMBINED_EQUIPMENT_PARAMETER_ID')
1618
1619
        cnx = mysql.connector.connect(**config.myems_system_db)
1620
        cursor = cnx.cursor()
1621
1622
        cursor.execute(" SELECT name "
1623
                       " FROM tbl_combined_equipments "
1624
                       " WHERE id = %s ",
1625
                       (id_,))
1626
        row = cursor.fetchone()
1627
        if row is None:
1628
            cursor.close()
1629
            cnx.close()
1630
            raise falcon.HTTPError(status=falcon.HTTP_400,
1631
                                   title='API.NOT_FOUND',
1632
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
1633
1634
        cursor.execute(" SELECT name "
1635
                       " FROM tbl_combined_equipments_parameters "
1636
                       " WHERE combined_equipment_id = %s AND id = %s ",
1637
                       (id_, pid,))
1638
        row = cursor.fetchone()
1639
        if row is None:
1640
            cursor.close()
1641
            cnx.close()
1642
            raise falcon.HTTPError(status=falcon.HTTP_400,
1643
                                   title='API.NOT_FOUND',
1644
                                   description='API.COMBINED_EQUIPMENT_PARAMETER_NOT_FOUND_OR_NOT_MATCH')
1645
1646
        cursor.execute(" DELETE FROM tbl_combined_equipments_parameters "
1647
                       " WHERE id = %s ", (pid,))
1648
        cnx.commit()
1649
1650
        cursor.close()
1651
        cnx.close()
1652
1653
        # Clear cache after deleting parameter
1654
        clear_combined_equipment_cache(combined_equipment_id=id_)
1655
1656
        resp.status = falcon.HTTP_204
1657
1658
    @staticmethod
1659
    @user_logger
1660
    def on_put(req, resp, id_, pid):
1661
        """Handles PUT requests"""
1662
        admin_control(req)
1663
        if not id_.isdigit() or int(id_) <= 0:
1664
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1665
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
1666
1667
        if not pid.isdigit() or int(pid) <= 0:
1668
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1669
                                   description='API.INVALID_COMBINED_EQUIPMENT_PARAMETER_ID')
1670
1671
        try:
1672
            raw_json = req.stream.read().decode('utf-8')
1673
        except UnicodeDecodeError as ex:
1674
            print("Failed to decode request")
1675
            raise falcon.HTTPError(status=falcon.HTTP_400,
1676
                                   title='API.BAD_REQUEST',
1677
                                   description='API.INVALID_ENCODING')
1678
        except Exception as ex:
1679
            print("Unexpected error reading request stream")
1680
            raise falcon.HTTPError(status=falcon.HTTP_400,
1681
                                   title='API.BAD_REQUEST',
1682
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1683
1684
        new_values = json.loads(raw_json)
1685
1686
        if 'name' not in new_values['data'].keys() or \
1687
                not isinstance(new_values['data']['name'], str) or \
1688
                len(str.strip(new_values['data']['name'])) == 0:
1689
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1690
                                   description='API.INVALID_COMBINED_EQUIPMENT_PARAMETER_NAME')
1691
        name = str.strip(new_values['data']['name'])
1692
1693
        if 'parameter_type' not in new_values['data'].keys() or \
1694
                not isinstance(new_values['data']['parameter_type'], str) or \
1695
                len(str.strip(new_values['data']['parameter_type'])) == 0:
1696
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1697
                                   description='API.INVALID_COMBINED_EQUIPMENT_PARAMETER_TYPE')
1698
1699
        parameter_type = str.strip(new_values['data']['parameter_type'])
1700
1701
        if parameter_type not in ('constant', 'point', 'fraction'):
1702
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1703
                                   description='API.INVALID_COMBINED_EQUIPMENT_PARAMETER_TYPE')
1704
1705
        constant = None
1706
        if 'constant' in new_values['data'].keys():
1707
            if new_values['data']['constant'] is not None and \
1708
                    isinstance(new_values['data']['constant'], str) and \
1709
                    len(str.strip(new_values['data']['constant'])) > 0:
1710
                constant = str.strip(new_values['data']['constant'])
1711
1712
        point_id = None
1713
        if 'point_id' in new_values['data'].keys():
1714
            if new_values['data']['point_id'] is not None and \
1715
                    new_values['data']['point_id'] <= 0:
1716
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1717
                                       description='API.INVALID_POINT_ID')
1718
            point_id = new_values['data']['point_id']
1719
1720
        numerator_meter_uuid = None
1721
        if 'numerator_meter_uuid' in new_values['data'].keys():
1722
            if new_values['data']['numerator_meter_uuid'] is not None and \
1723
                    isinstance(new_values['data']['numerator_meter_uuid'], str) and \
1724
                    len(str.strip(new_values['data']['numerator_meter_uuid'])) > 0:
1725
                numerator_meter_uuid = str.strip(new_values['data']['numerator_meter_uuid'])
1726
1727
        denominator_meter_uuid = None
1728
        if 'denominator_meter_uuid' in new_values['data'].keys():
1729
            if new_values['data']['denominator_meter_uuid'] is not None and \
1730
                    isinstance(new_values['data']['denominator_meter_uuid'], str) and \
1731
                    len(str.strip(new_values['data']['denominator_meter_uuid'])) > 0:
1732
                denominator_meter_uuid = str.strip(new_values['data']['denominator_meter_uuid'])
1733
1734
        cnx = mysql.connector.connect(**config.myems_system_db)
1735
        cursor = cnx.cursor()
1736
1737
        cursor.execute(" SELECT name "
1738
                       " FROM tbl_combined_equipments "
1739
                       " WHERE id = %s ", (id_,))
1740
        if cursor.fetchone() is None:
1741
            cursor.close()
1742
            cnx.close()
1743
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.NOT_FOUND',
1744
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
1745
1746
        cursor.execute(" SELECT name "
1747
                       " FROM tbl_combined_equipments_parameters "
1748
                       " WHERE combined_equipment_id = %s AND id = %s ",
1749
                       (id_, pid,))
1750
        row = cursor.fetchone()
1751
        if row is None:
1752
            cursor.close()
1753
            cnx.close()
1754
            raise falcon.HTTPError(status=falcon.HTTP_400,
1755
                                   title='API.NOT_FOUND',
1756
                                   description='API.COMBINED_EQUIPMENT_PARAMETER_NOT_FOUND_OR_NOT_MATCH')
1757
1758
        cursor.execute(" SELECT name "
1759
                       " FROM tbl_combined_equipments_parameters "
1760
                       " WHERE name = %s AND combined_equipment_id = %s  AND id != %s ", (name, id_, pid))
1761
        row = cursor.fetchone()
1762
        if row is not None:
1763
            cursor.close()
1764
            cnx.close()
1765
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1766
                                   description='API.COMBINED_EQUIPMENT_PARAMETER_NAME_IS_ALREADY_IN_USE')
1767
1768
        cursor.execute(" SELECT data_source_id "
1769
                       " FROM tbl_combined_equipments_data_sources "
1770
                       " WHERE combined_equipment_id = %s ", (id_,))
1771
        rows_data_sources = cursor.fetchall()
1772
        data_source_ids = tuple({row[0] for row in rows_data_sources if row[0] is not None})
1773
1774
        # validate by parameter type
1775 View Code Duplication
        if parameter_type == 'point':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1776
            if point_id is None:
1777
                cursor.close()
1778
                cnx.close()
1779
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1780
                                       description='API.INVALID_POINT_ID')
1781
1782
            if len(data_source_ids) == 0:
1783
                cursor.close()
1784
                cnx.close()
1785
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.NOT_FOUND',
1786
                                       description='API.POINT_NOT_FOUND')
1787
1788
            format_strings = ','.join(['%s'] * len(data_source_ids))
1789
            query = (" SELECT id, name "
1790
                     " FROM tbl_points "
1791
                     " WHERE id = %s AND data_source_id IN (" + format_strings + ") ")
1792
            cursor.execute(query, (point_id,) + data_source_ids)
1793
            row = cursor.fetchone()
1794
            if row is None:
1795
                cursor.close()
1796
                cnx.close()
1797
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1798
                                       description='API.POINT_NOT_FOUND')
1799
1800
        elif parameter_type == 'constant':
1801
            if constant is None:
1802
                cursor.close()
1803
                cnx.close()
1804
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1805
                                       description='API.INVALID_CONSTANT_VALUE')
1806
1807
        elif parameter_type == 'fraction':
1808
1809
            query = (" SELECT id, name, uuid "
1810
                     " FROM tbl_meters ")
1811
            cursor.execute(query)
1812
            rows_meters = cursor.fetchall()
1813
1814
            meter_dict = dict()
1815
            if rows_meters is not None and len(rows_meters) > 0:
1816
                for row in rows_meters:
1817
                    meter_dict[row[2]] = {"type": 'meter',
1818
                                          "id": row[0],
1819
                                          "name": row[1],
1820
                                          "uuid": row[2]}
1821
1822
            query = (" SELECT id, name, uuid "
1823
                     " FROM tbl_offline_meters ")
1824
            cursor.execute(query)
1825
            rows_offline_meters = cursor.fetchall()
1826
1827
            offline_meter_dict = dict()
1828
            if rows_offline_meters is not None and len(rows_offline_meters) > 0:
1829
                for row in rows_offline_meters:
1830
                    offline_meter_dict[row[2]] = {"type": 'offline_meter',
1831
                                                  "id": row[0],
1832
                                                  "name": row[1],
1833
                                                  "uuid": row[2]}
1834
1835
            query = (" SELECT id, name, uuid "
1836
                     " FROM tbl_virtual_meters ")
1837
            cursor.execute(query)
1838
            rows_virtual_meters = cursor.fetchall()
1839
1840
            virtual_meter_dict = dict()
1841
            if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
1842
                for row in rows_virtual_meters:
1843
                    virtual_meter_dict[row[2]] = {"type": 'virtual_meter',
1844
                                                  "id": row[0],
1845
                                                  "name": row[1],
1846
                                                  "uuid": row[2]}
1847
1848
            # validate numerator meter uuid
1849
            if meter_dict.get(numerator_meter_uuid) is None and \
1850
                    virtual_meter_dict.get(numerator_meter_uuid) is None and \
1851
                    offline_meter_dict.get(numerator_meter_uuid) is None:
1852
                cursor.close()
1853
                cnx.close()
1854
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1855
                                       description='API.INVALID_NUMERATOR_METER_UUID')
1856
1857
            # validate denominator meter uuid
1858
            if denominator_meter_uuid == numerator_meter_uuid:
1859
                cursor.close()
1860
                cnx.close()
1861
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1862
                                       description='API.INVALID_DENOMINATOR_METER_UUID')
1863
1864
            if denominator_meter_uuid not in meter_dict and \
1865
                    denominator_meter_uuid not in virtual_meter_dict and \
1866
                    denominator_meter_uuid not in offline_meter_dict:
1867
                cursor.close()
1868
                cnx.close()
1869
                raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1870
                                       description='API.INVALID_DENOMINATOR_METER_UUID')
1871
1872
        add_values = (" UPDATE tbl_combined_equipments_parameters "
1873
                      " SET name = %s , parameter_type = %s, constant = %s, "
1874
                      "     point_id = %s, numerator_meter_uuid = %s, denominator_meter_uuid = %s "
1875
                      " WHERE id = %s ")
1876
        cursor.execute(add_values, (name,
1877
                                    parameter_type,
1878
                                    constant,
1879
                                    point_id,
1880
                                    numerator_meter_uuid,
1881
                                    denominator_meter_uuid,
1882
                                    pid))
1883
        cnx.commit()
1884
1885
        cursor.close()
1886
        cnx.close()
1887
1888
        # Clear cache after updating parameter
1889
        clear_combined_equipment_cache(combined_equipment_id=id_)
1890
1891
        resp.status = falcon.HTTP_200
1892
1893
1894 View Code Duplication
class CombinedEquipmentDataSourceCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1895
    def __init__(self):
1896
        pass
1897
1898
    @staticmethod
1899
    def on_options(req, resp, id_):
1900
        _ = req
1901
        resp.status = falcon.HTTP_200
1902
        _ = id_
1903
1904
    @staticmethod
1905
    def on_get(req, resp, id_):
1906
        if 'API-KEY' not in req.headers or \
1907
                not isinstance(req.headers['API-KEY'], str) or \
1908
                len(str.strip(req.headers['API-KEY'])) == 0:
1909
            access_control(req)
1910
        else:
1911
            api_key_control(req)
1912
1913
        if not id_.isdigit() or int(id_) <= 0:
1914
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1915
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
1916
1917
        # Redis cache key
1918
        cache_key = f'combinedequipment:datasources:{id_}'
1919
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
1920
1921
        # Try to get from Redis cache (only if Redis is enabled)
1922
        redis_client = None
1923
        if config.redis.get('is_enabled', False):
1924
            try:
1925
                redis_client = redis.Redis(
1926
                    host=config.redis['host'],
1927
                    port=config.redis['port'],
1928
                    password=config.redis['password'] if config.redis['password'] else None,
1929
                    db=config.redis['db'],
1930
                    decode_responses=True,
1931
                    socket_connect_timeout=2,
1932
                    socket_timeout=2
1933
                )
1934
                redis_client.ping()
1935
                cached_result = redis_client.get(cache_key)
1936
                if cached_result:
1937
                    resp.text = cached_result
1938
                    return
1939
            except Exception:
1940
                # If Redis connection fails, continue to database query
1941
                pass
1942
1943
        # Cache miss or Redis error - query database
1944
        cnx = mysql.connector.connect(**config.myems_system_db)
1945
        cursor = cnx.cursor()
1946
1947
        cursor.execute(" SELECT name FROM tbl_combined_equipments WHERE id = %s ", (id_,))
1948
        if cursor.fetchone() is None:
1949
            cursor.close()
1950
            cnx.close()
1951
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1952
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
1953
1954
        query = (" SELECT ds.id, ds.name, ds.uuid "
1955
                 " FROM tbl_combined_equipments ce, tbl_combined_equipments_data_sources ceds, tbl_data_sources ds "
1956
                 " WHERE ceds.combined_equipment_id = ce.id AND ds.id = ceds.data_source_id AND ce.id = %s "
1957
                 " ORDER BY ds.id ")
1958
        cursor.execute(query, (id_,))
1959
        rows = cursor.fetchall()
1960
1961
        result = list()
1962
        if rows is not None and len(rows) > 0:
1963
            for row in rows:
1964
                meta_result = {"id": row[0], "name": row[1], "uuid": row[2]}
1965
                result.append(meta_result)
1966
1967
        cursor.close()
1968
        cnx.close()
1969
1970
        # Store result in Redis cache
1971
        result_json = json.dumps(result)
1972
        if redis_client:
1973
            try:
1974
                redis_client.setex(cache_key, cache_expire, result_json)
1975
            except Exception:
1976
                # If cache set fails, ignore and continue
1977
                pass
1978
1979
        resp.text = result_json
1980
1981
    @staticmethod
1982
    @user_logger
1983
    def on_post(req, resp, id_):
1984
        admin_control(req)
1985
        try:
1986
            raw_json = req.stream.read().decode('utf-8')
1987
        except UnicodeDecodeError as ex:
1988
            print("Failed to decode request")
1989
            raise falcon.HTTPError(status=falcon.HTTP_400,
1990
                                   title='API.BAD_REQUEST',
1991
                                   description='API.INVALID_ENCODING')
1992
        except Exception as ex:
1993
            print("Unexpected error reading request stream")
1994
            raise falcon.HTTPError(status=falcon.HTTP_400,
1995
                                   title='API.BAD_REQUEST',
1996
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1997
        if not id_.isdigit() or int(id_) <= 0:
1998
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1999
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2000
2001
        new_values = json.loads(raw_json)
2002
2003
        if 'data_source_id' not in new_values['data'].keys() or \
2004
                not isinstance(new_values['data']['data_source_id'], int) or \
2005
                new_values['data']['data_source_id'] <= 0:
2006
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2007
                                   description='API.INVALID_DATA_SOURCE_ID')
2008
2009
        data_source_id = new_values['data']['data_source_id']
2010
2011
        cnx = mysql.connector.connect(**config.myems_system_db)
2012
        cursor = cnx.cursor()
2013
2014
        cursor.execute(" SELECT name FROM tbl_combined_equipments WHERE id = %s ", (id_,))
2015
        if cursor.fetchone() is None:
2016
            cursor.close()
2017
            cnx.close()
2018
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2019
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2020
2021
        cursor.execute(" SELECT name FROM tbl_data_sources WHERE id = %s ", (data_source_id,))
2022
        if cursor.fetchone() is None:
2023
            cursor.close()
2024
            cnx.close()
2025
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2026
                                   description='API.DATA_SOURCE_NOT_FOUND')
2027
2028
        cursor.execute(" SELECT id "
2029
                       " FROM tbl_combined_equipments_data_sources "
2030
                       " WHERE combined_equipment_id = %s AND data_source_id = %s", (id_, data_source_id))
2031
        if cursor.fetchone() is not None:
2032
            cursor.close()
2033
            cnx.close()
2034
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2035
                                   description='API.COMBINED_EQUIPMENT_DATA_SOURCE_RELATION_EXISTS')
2036
2037
        cursor.execute(" INSERT INTO tbl_combined_equipments_data_sources (combined_equipment_id, data_source_id) "
2038
                       " VALUES (%s, %s) ", (id_, data_source_id))
2039
        cnx.commit()
2040
        cursor.close()
2041
        cnx.close()
2042
2043
        # Clear cache after adding data source
2044
        clear_combined_equipment_cache(combined_equipment_id=id_)
2045
2046
        resp.status = falcon.HTTP_201
2047
        resp.location = '/combinedequipments/' + str(id_) + '/datasources/' + str(data_source_id)
2048
2049
2050 View Code Duplication
class CombinedEquipmentDataSourceItem:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2051
    def __init__(self):
2052
        pass
2053
2054
    @staticmethod
2055
    def on_options(req, resp, id_, dsid):
2056
        _ = req
2057
        _ = id_
2058
        _ = dsid
2059
        resp.status = falcon.HTTP_200
2060
2061
    @staticmethod
2062
    @user_logger
2063
    def on_delete(req, resp, id_, dsid):
2064
        admin_control(req)
2065
2066
        if not id_.isdigit() or int(id_) <= 0:
2067
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2068
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2069
2070
        if not dsid.isdigit() or int(dsid) <= 0:
2071
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2072
                                   description='API.INVALID_DATA_SOURCE_ID')
2073
2074
        cnx = mysql.connector.connect(**config.myems_system_db)
2075
        cursor = cnx.cursor()
2076
2077
        cursor.execute(" SELECT name FROM tbl_combined_equipments WHERE id = %s ", (id_,))
2078
        if cursor.fetchone() is None:
2079
            cursor.close()
2080
            cnx.close()
2081
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2082
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2083
2084
        cursor.execute(" SELECT name FROM tbl_data_sources WHERE id = %s ", (dsid,))
2085
        if cursor.fetchone() is None:
2086
            cursor.close()
2087
            cnx.close()
2088
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2089
                                   description='API.DATA_SOURCE_NOT_FOUND')
2090
2091
        cursor.execute(" SELECT id "
2092
                       " FROM tbl_combined_equipments_data_sources "
2093
                       " WHERE combined_equipment_id = %s AND data_source_id = %s ", (id_, dsid))
2094
        if cursor.fetchone() is None:
2095
            cursor.close()
2096
            cnx.close()
2097
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2098
                                   description='API.COMBINED_EQUIPMENT_DATA_SOURCE_RELATION_NOT_FOUND')
2099
2100
        cursor.execute(" DELETE FROM tbl_combined_equipments_data_sources "
2101
                       " WHERE combined_equipment_id = %s AND data_source_id = %s ", (id_, dsid))
2102
        cnx.commit()
2103
        cursor.close()
2104
        cnx.close()
2105
2106
        # Clear cache after deleting data source
2107
        clear_combined_equipment_cache(combined_equipment_id=id_)
2108
2109
        resp.status = falcon.HTTP_204
2110
2111
2112
class CombinedEquipmentDataSourcePointCollection:
2113
    def __init__(self):
2114
        pass
2115
2116
    @staticmethod
2117
    def on_options(req, resp, id_):
2118
        _ = req
2119
        resp.status = falcon.HTTP_200
2120
        _ = id_
2121
2122
    @staticmethod
2123
    def on_get(req, resp, id_):
2124
        if 'API-KEY' not in req.headers or \
2125
                not isinstance(req.headers['API-KEY'], str) or \
2126
                len(str.strip(req.headers['API-KEY'])) == 0:
2127
            access_control(req)
2128
        else:
2129
            api_key_control(req)
2130
        if not id_.isdigit() or int(id_) <= 0:
2131
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2132
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2133
2134
        # Redis cache key
2135
        cache_key = f'combinedequipment:datasourcepoints:{id_}'
2136
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
2137
2138
        # Try to get from Redis cache (only if Redis is enabled)
2139
        redis_client = None
2140
        if config.redis.get('is_enabled', False):
2141
            try:
2142
                redis_client = redis.Redis(
2143
                    host=config.redis['host'],
2144
                    port=config.redis['port'],
2145
                    password=config.redis['password'] if config.redis['password'] else None,
2146
                    db=config.redis['db'],
2147
                    decode_responses=True,
2148
                    socket_connect_timeout=2,
2149
                    socket_timeout=2
2150
                )
2151
                redis_client.ping()
2152
                cached_result = redis_client.get(cache_key)
2153
                if cached_result:
2154
                    resp.text = cached_result
2155
                    return
2156
            except Exception:
2157
                # If Redis connection fails, continue to database query
2158
                pass
2159
2160
        # Cache miss or Redis error - query database
2161
        cnx = mysql.connector.connect(**config.myems_system_db)
2162
        cursor = cnx.cursor()
2163
2164
        cursor.execute(" SELECT name FROM tbl_combined_equipments WHERE id = %s ", (id_,))
2165
        if cursor.fetchone() is None:
2166
            cursor.close()
2167
            cnx.close()
2168
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2169
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2170
2171
        query = (" SELECT p.id, p.name "
2172
                 " FROM tbl_points p, tbl_combined_equipments_data_sources ceds, tbl_data_sources ds "
2173
                 " WHERE ceds.combined_equipment_id = %s "
2174
                 "       AND ceds.data_source_id = ds.id "
2175
                 "       AND p.data_source_id = ds.id "
2176
                 " ORDER BY p.id ")
2177
        cursor.execute(query, (id_,))
2178
        rows = cursor.fetchall()
2179
2180
        result = list()
2181
        if rows is not None and len(rows) > 0:
2182
            for row in rows:
2183
                meta_result = {"id": row[0], "name": row[1]}
2184
                result.append(meta_result)
2185
2186
        cursor.close()
2187
        cnx.close()
2188
2189
        # Store result in Redis cache
2190
        result_json = json.dumps(result)
2191
        if redis_client:
2192
            try:
2193
                redis_client.setex(cache_key, cache_expire, result_json)
2194
            except Exception:
2195
                # If cache set fails, ignore and continue
2196
                pass
2197
2198
        resp.text = result_json
2199
2200
2201 View Code Duplication
class CombinedEquipmentMeterCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2202
    def __init__(self):
2203
        pass
2204
2205
    @staticmethod
2206
    def on_options(req, resp, id_):
2207
        _ = req
2208
        resp.status = falcon.HTTP_200
2209
        _ = id_
2210
2211
    @staticmethod
2212
    def on_get(req, resp, id_):
2213
        if 'API-KEY' not in req.headers or \
2214
                not isinstance(req.headers['API-KEY'], str) or \
2215
                len(str.strip(req.headers['API-KEY'])) == 0:
2216
            access_control(req)
2217
        else:
2218
            api_key_control(req)
2219
        if not id_.isdigit() or int(id_) <= 0:
2220
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2221
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2222
2223
        # Redis cache key
2224
        cache_key = f'combinedequipment:meters:{id_}'
2225
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
2226
2227
        # Try to get from Redis cache (only if Redis is enabled)
2228
        redis_client = None
2229
        if config.redis.get('is_enabled', False):
2230
            try:
2231
                redis_client = redis.Redis(
2232
                    host=config.redis['host'],
2233
                    port=config.redis['port'],
2234
                    password=config.redis['password'] if config.redis['password'] else None,
2235
                    db=config.redis['db'],
2236
                    decode_responses=True,
2237
                    socket_connect_timeout=2,
2238
                    socket_timeout=2
2239
                )
2240
                redis_client.ping()
2241
                cached_result = redis_client.get(cache_key)
2242
                if cached_result:
2243
                    resp.text = cached_result
2244
                    return
2245
            except Exception:
2246
                # If Redis connection fails, continue to database query
2247
                pass
2248
2249
        # Cache miss or Redis error - query database
2250
        cnx = mysql.connector.connect(**config.myems_system_db)
2251
        cursor = cnx.cursor()
2252
2253
        cursor.execute(" SELECT name "
2254
                       " FROM tbl_combined_equipments "
2255
                       " WHERE id = %s ", (id_,))
2256
        if cursor.fetchone() is None:
2257
            cursor.close()
2258
            cnx.close()
2259
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2260
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2261
2262
        query = (" SELECT id, name, uuid "
2263
                 " FROM tbl_energy_categories ")
2264
        cursor.execute(query)
2265
        rows_energy_categories = cursor.fetchall()
2266
2267
        energy_category_dict = dict()
2268
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
2269
            for row in rows_energy_categories:
2270
                energy_category_dict[row[0]] = {"id": row[0],
2271
                                                "name": row[1],
2272
                                                "uuid": row[2]}
2273
2274
        query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id, em.is_output "
2275
                 " FROM tbl_combined_equipments e, tbl_combined_equipments_meters em, tbl_meters m "
2276
                 " WHERE em.combined_equipment_id = e.id AND m.id = em.meter_id AND e.id = %s "
2277
                 " ORDER BY m.id ")
2278
        cursor.execute(query, (id_,))
2279
        rows = cursor.fetchall()
2280
2281
        result = list()
2282
        if rows is not None and len(rows) > 0:
2283
            for row in rows:
2284
                meta_result = {"id": row[0], "name": row[1], "uuid": row[2],
2285
                               "energy_category": energy_category_dict.get(row[3], None),
2286
                               "is_output": bool(row[4])}
2287
                result.append(meta_result)
2288
2289
        cursor.close()
2290
        cnx.close()
2291
2292
        # Store result in Redis cache
2293
        result_json = json.dumps(result)
2294
        if redis_client:
2295
            try:
2296
                redis_client.setex(cache_key, cache_expire, result_json)
2297
            except Exception:
2298
                # If cache set fails, ignore and continue
2299
                pass
2300
2301
        resp.text = result_json
2302
2303
    @staticmethod
2304
    @user_logger
2305
    def on_post(req, resp, id_):
2306
        """Handles POST requests"""
2307
        admin_control(req)
2308
        try:
2309
            raw_json = req.stream.read().decode('utf-8')
2310
        except UnicodeDecodeError as ex:
2311
            print("Failed to decode request")
2312
            raise falcon.HTTPError(status=falcon.HTTP_400,
2313
                                   title='API.BAD_REQUEST',
2314
                                   description='API.INVALID_ENCODING')
2315
        except Exception as ex:
2316
            print("Unexpected error reading request stream")
2317
            raise falcon.HTTPError(status=falcon.HTTP_400,
2318
                                   title='API.BAD_REQUEST',
2319
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
2320
2321
        if not id_.isdigit() or int(id_) <= 0:
2322
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2323
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2324
2325
        new_values = json.loads(raw_json)
2326
2327
        if 'meter_id' not in new_values['data'].keys() or \
2328
                not isinstance(new_values['data']['meter_id'], int) or \
2329
                new_values['data']['meter_id'] <= 0:
2330
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2331
                                   description='API.INVALID_METER_ID')
2332
        meter_id = new_values['data']['meter_id']
2333
2334
        if 'is_output' not in new_values['data'].keys() or \
2335
                not isinstance(new_values['data']['is_output'], bool):
2336
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2337
                                   description='API.INVALID_IS_OUTPUT_VALUE')
2338
        is_output = new_values['data']['is_output']
2339
2340
        cnx = mysql.connector.connect(**config.myems_system_db)
2341
        cursor = cnx.cursor()
2342
2343
        cursor.execute(" SELECT name "
2344
                       " from tbl_combined_equipments "
2345
                       " WHERE id = %s ", (id_,))
2346
        if cursor.fetchone() is None:
2347
            cursor.close()
2348
            cnx.close()
2349
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2350
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2351
2352
        cursor.execute(" SELECT name "
2353
                       " FROM tbl_meters "
2354
                       " WHERE id = %s ", (meter_id,))
2355
        if cursor.fetchone() is None:
2356
            cursor.close()
2357
            cnx.close()
2358
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2359
                                   description='API.METER_NOT_FOUND')
2360
2361
        query = (" SELECT id "
2362
                 " FROM tbl_combined_equipments_meters "
2363
                 " WHERE combined_equipment_id = %s AND meter_id = %s")
2364
        cursor.execute(query, (id_, meter_id,))
2365
        if cursor.fetchone() is not None:
2366
            cursor.close()
2367
            cnx.close()
2368
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2369
                                   description='API.COMBINED_EQUIPMENT_METER_RELATION_EXISTS')
2370
2371
        add_row = (" INSERT INTO tbl_combined_equipments_meters (combined_equipment_id, meter_id, is_output ) "
2372
                   " VALUES (%s, %s, %s) ")
2373
        cursor.execute(add_row, (id_, meter_id, is_output))
2374
        cnx.commit()
2375
        cursor.close()
2376
        cnx.close()
2377
2378
        # Clear cache after adding meter
2379
        clear_combined_equipment_cache(combined_equipment_id=id_)
2380
2381
        resp.status = falcon.HTTP_201
2382
        resp.location = '/combinedequipments/' + str(id_) + '/meters/' + str(meter_id)
2383
2384
2385
class CombinedEquipmentMeterItem:
2386
    def __init__(self):
2387
        pass
2388
2389
    @staticmethod
2390
    def on_options(req, resp, id_, mid):
2391
        _ = req
2392
        resp.status = falcon.HTTP_200
2393
        _ = id_
2394
2395
    @staticmethod
2396
    @user_logger
2397
    def on_delete(req, resp, id_, mid):
2398
        admin_control(req)
2399
        if not id_.isdigit() or int(id_) <= 0:
2400
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2401
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2402
2403
        if not mid.isdigit() or int(mid) <= 0:
2404
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2405
                                   description='API.INVALID_METER_ID')
2406
2407
        cnx = mysql.connector.connect(**config.myems_system_db)
2408
        cursor = cnx.cursor()
2409
2410
        cursor.execute(" SELECT name "
2411
                       " FROM tbl_combined_equipments "
2412
                       " WHERE id = %s ", (id_,))
2413
        if cursor.fetchone() is None:
2414
            cursor.close()
2415
            cnx.close()
2416
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2417
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2418
2419
        cursor.execute(" SELECT name "
2420
                       " FROM tbl_meters "
2421
                       " WHERE id = %s ", (mid,))
2422
        if cursor.fetchone() is None:
2423
            cursor.close()
2424
            cnx.close()
2425
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2426
                                   description='API.METER_NOT_FOUND')
2427
2428
        cursor.execute(" SELECT id "
2429
                       " FROM tbl_combined_equipments_meters "
2430
                       " WHERE combined_equipment_id = %s AND meter_id = %s ", (id_, mid))
2431
        if cursor.fetchone() is None:
2432
            cursor.close()
2433
            cnx.close()
2434
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2435
                                   description='API.COMBINED_EQUIPMENT_METER_RELATION_NOT_FOUND')
2436
2437
        cursor.execute(" DELETE FROM tbl_combined_equipments_meters "
2438
                       " WHERE combined_equipment_id = %s AND meter_id = %s ", (id_, mid))
2439
        cnx.commit()
2440
2441
        cursor.close()
2442
        cnx.close()
2443
2444
        # Clear cache after deleting meter
2445
        clear_combined_equipment_cache(combined_equipment_id=id_)
2446
2447
        resp.status = falcon.HTTP_204
2448
2449
2450 View Code Duplication
class CombinedEquipmentOfflineMeterCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2451
    def __init__(self):
2452
        pass
2453
2454
    @staticmethod
2455
    def on_options(req, resp, id_):
2456
        _ = req
2457
        resp.status = falcon.HTTP_200
2458
        _ = id_
2459
2460
    @staticmethod
2461
    def on_get(req, resp, id_):
2462
        if 'API-KEY' not in req.headers or \
2463
                not isinstance(req.headers['API-KEY'], str) or \
2464
                len(str.strip(req.headers['API-KEY'])) == 0:
2465
            access_control(req)
2466
        else:
2467
            api_key_control(req)
2468
        if not id_.isdigit() or int(id_) <= 0:
2469
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2470
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2471
2472
        # Redis cache key
2473
        cache_key = f'combinedequipment:offlinemeters:{id_}'
2474
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
2475
2476
        # Try to get from Redis cache (only if Redis is enabled)
2477
        redis_client = None
2478
        if config.redis.get('is_enabled', False):
2479
            try:
2480
                redis_client = redis.Redis(
2481
                    host=config.redis['host'],
2482
                    port=config.redis['port'],
2483
                    password=config.redis['password'] if config.redis['password'] else None,
2484
                    db=config.redis['db'],
2485
                    decode_responses=True,
2486
                    socket_connect_timeout=2,
2487
                    socket_timeout=2
2488
                )
2489
                redis_client.ping()
2490
                cached_result = redis_client.get(cache_key)
2491
                if cached_result:
2492
                    resp.text = cached_result
2493
                    return
2494
            except Exception:
2495
                # If Redis connection fails, continue to database query
2496
                pass
2497
2498
        # Cache miss or Redis error - query database
2499
        cnx = mysql.connector.connect(**config.myems_system_db)
2500
        cursor = cnx.cursor()
2501
2502
        cursor.execute(" SELECT name "
2503
                       " FROM tbl_combined_equipments "
2504
                       " WHERE id = %s ", (id_,))
2505
        if cursor.fetchone() is None:
2506
            cursor.close()
2507
            cnx.close()
2508
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2509
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2510
2511
        query = (" SELECT id, name, uuid "
2512
                 " FROM tbl_energy_categories ")
2513
        cursor.execute(query)
2514
        rows_energy_categories = cursor.fetchall()
2515
2516
        energy_category_dict = dict()
2517
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
2518
            for row in rows_energy_categories:
2519
                energy_category_dict[row[0]] = {"id": row[0],
2520
                                                "name": row[1],
2521
                                                "uuid": row[2]}
2522
2523
        query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id, em.is_output "
2524
                 " FROM tbl_combined_equipments e, tbl_combined_equipments_offline_meters em, tbl_offline_meters m "
2525
                 " WHERE em.combined_equipment_id = e.id AND m.id = em.offline_meter_id AND e.id = %s "
2526
                 " ORDER BY m.id ")
2527
        cursor.execute(query, (id_,))
2528
        rows = cursor.fetchall()
2529
2530
        result = list()
2531
        if rows is not None and len(rows) > 0:
2532
            for row in rows:
2533
                meta_result = {"id": row[0], "name": row[1], "uuid": row[2],
2534
                               "energy_category": energy_category_dict.get(row[3], None),
2535
                               "is_output": bool(row[4])}
2536
                result.append(meta_result)
2537
2538
        cursor.close()
2539
        cnx.close()
2540
2541
        # Store result in Redis cache
2542
        result_json = json.dumps(result)
2543
        if redis_client:
2544
            try:
2545
                redis_client.setex(cache_key, cache_expire, result_json)
2546
            except Exception:
2547
                # If cache set fails, ignore and continue
2548
                pass
2549
2550
        resp.text = result_json
2551
2552
    @staticmethod
2553
    @user_logger
2554
    def on_post(req, resp, id_):
2555
        """Handles POST requests"""
2556
        admin_control(req)
2557
        try:
2558
            raw_json = req.stream.read().decode('utf-8')
2559
        except UnicodeDecodeError as ex:
2560
            print("Failed to decode request")
2561
            raise falcon.HTTPError(status=falcon.HTTP_400,
2562
                                   title='API.BAD_REQUEST',
2563
                                   description='API.INVALID_ENCODING')
2564
        except Exception as ex:
2565
            print("Unexpected error reading request stream")
2566
            raise falcon.HTTPError(status=falcon.HTTP_400,
2567
                                   title='API.BAD_REQUEST',
2568
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
2569
2570
        if not id_.isdigit() or int(id_) <= 0:
2571
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2572
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2573
2574
        new_values = json.loads(raw_json)
2575
2576
        if 'offline_meter_id' not in new_values['data'].keys() or \
2577
                not isinstance(new_values['data']['offline_meter_id'], int) or \
2578
                new_values['data']['offline_meter_id'] <= 0:
2579
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2580
                                   description='API.INVALID_OFFLINE_METER_ID')
2581
        offline_meter_id = new_values['data']['offline_meter_id']
2582
2583
        if 'is_output' not in new_values['data'].keys() or \
2584
                not isinstance(new_values['data']['is_output'], bool):
2585
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2586
                                   description='API.INVALID_IS_OUTPUT_VALUE')
2587
        is_output = new_values['data']['is_output']
2588
2589
        cnx = mysql.connector.connect(**config.myems_system_db)
2590
        cursor = cnx.cursor()
2591
2592
        cursor.execute(" SELECT name "
2593
                       " from tbl_combined_equipments "
2594
                       " WHERE id = %s ", (id_,))
2595
        if cursor.fetchone() is None:
2596
            cursor.close()
2597
            cnx.close()
2598
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2599
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2600
2601
        cursor.execute(" SELECT name "
2602
                       " FROM tbl_offline_meters "
2603
                       " WHERE id = %s ", (offline_meter_id,))
2604
        if cursor.fetchone() is None:
2605
            cursor.close()
2606
            cnx.close()
2607
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2608
                                   description='API.OFFLINE_METER_NOT_FOUND')
2609
2610
        query = (" SELECT id "
2611
                 " FROM tbl_combined_equipments_offline_meters "
2612
                 " WHERE combined_equipment_id = %s AND offline_meter_id = %s")
2613
        cursor.execute(query, (id_, offline_meter_id,))
2614
        if cursor.fetchone() is not None:
2615
            cursor.close()
2616
            cnx.close()
2617
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2618
                                   description='API.COMBINED_EQUIPMENT_OFFLINE_METER_RELATION_EXISTS')
2619
2620
        add_row = (" INSERT INTO tbl_combined_equipments_offline_meters "
2621
                   " (combined_equipment_id, offline_meter_id, is_output ) "
2622
                   " VALUES (%s, %s, %s) ")
2623
        cursor.execute(add_row, (id_, offline_meter_id, is_output))
2624
        cnx.commit()
2625
        cursor.close()
2626
        cnx.close()
2627
2628
        # Clear cache after adding offline meter
2629
        clear_combined_equipment_cache(combined_equipment_id=id_)
2630
2631
        resp.status = falcon.HTTP_201
2632
        resp.location = '/combinedequipments/' + str(id_) + '/offlinemeters/' + str(offline_meter_id)
2633
2634
2635
class CombinedEquipmentOfflineMeterItem:
2636
    def __init__(self):
2637
        pass
2638
2639
    @staticmethod
2640
    def on_options(req, resp, id_, mid):
2641
        _ = req
2642
        resp.status = falcon.HTTP_200
2643
        _ = id_
2644
2645
    @staticmethod
2646
    @user_logger
2647
    def on_delete(req, resp, id_, mid):
2648
        admin_control(req)
2649
        if not id_.isdigit() or int(id_) <= 0:
2650
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2651
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2652
2653
        if not mid.isdigit() or int(mid) <= 0:
2654
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2655
                                   description='API.INVALID_OFFLINE_METER_ID')
2656
2657
        cnx = mysql.connector.connect(**config.myems_system_db)
2658
        cursor = cnx.cursor()
2659
2660
        cursor.execute(" SELECT name "
2661
                       " FROM tbl_combined_equipments "
2662
                       " WHERE id = %s ", (id_,))
2663
        if cursor.fetchone() is None:
2664
            cursor.close()
2665
            cnx.close()
2666
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2667
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2668
2669
        cursor.execute(" SELECT name "
2670
                       " FROM tbl_offline_meters "
2671
                       " WHERE id = %s ", (mid,))
2672
        if cursor.fetchone() is None:
2673
            cursor.close()
2674
            cnx.close()
2675
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2676
                                   description='API.OFFLINE_METER_NOT_FOUND')
2677
2678
        cursor.execute(" SELECT id "
2679
                       " FROM tbl_combined_equipments_offline_meters "
2680
                       " WHERE combined_equipment_id = %s AND offline_meter_id = %s ", (id_, mid))
2681
        if cursor.fetchone() is None:
2682
            cursor.close()
2683
            cnx.close()
2684
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2685
                                   description='API.COMBINED_EQUIPMENT_OFFLINE_METER_RELATION_NOT_FOUND')
2686
2687
        cursor.execute(" DELETE FROM tbl_combined_equipments_offline_meters "
2688
                       " WHERE combined_equipment_id = %s AND offline_meter_id = %s ", (id_, mid))
2689
        cnx.commit()
2690
2691
        cursor.close()
2692
        cnx.close()
2693
2694
        # Clear cache after deleting offline meter
2695
        clear_combined_equipment_cache(combined_equipment_id=id_)
2696
2697
        resp.status = falcon.HTTP_204
2698
2699
2700 View Code Duplication
class CombinedEquipmentVirtualMeterCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2701
    def __init__(self):
2702
        pass
2703
2704
    @staticmethod
2705
    def on_options(req, resp, id_):
2706
        _ = req
2707
        resp.status = falcon.HTTP_200
2708
        _ = id_
2709
2710
    @staticmethod
2711
    def on_get(req, resp, id_):
2712
        if 'API-KEY' not in req.headers or \
2713
                not isinstance(req.headers['API-KEY'], str) or \
2714
                len(str.strip(req.headers['API-KEY'])) == 0:
2715
            access_control(req)
2716
        else:
2717
            api_key_control(req)
2718
        if not id_.isdigit() or int(id_) <= 0:
2719
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2720
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2721
2722
        # Redis cache key
2723
        cache_key = f'combinedequipment:virtualmeters:{id_}'
2724
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
2725
2726
        # Try to get from Redis cache (only if Redis is enabled)
2727
        redis_client = None
2728
        if config.redis.get('is_enabled', False):
2729
            try:
2730
                redis_client = redis.Redis(
2731
                    host=config.redis['host'],
2732
                    port=config.redis['port'],
2733
                    password=config.redis['password'] if config.redis['password'] else None,
2734
                    db=config.redis['db'],
2735
                    decode_responses=True,
2736
                    socket_connect_timeout=2,
2737
                    socket_timeout=2
2738
                )
2739
                redis_client.ping()
2740
                cached_result = redis_client.get(cache_key)
2741
                if cached_result:
2742
                    resp.text = cached_result
2743
                    return
2744
            except Exception:
2745
                # If Redis connection fails, continue to database query
2746
                pass
2747
2748
        # Cache miss or Redis error - query database
2749
        cnx = mysql.connector.connect(**config.myems_system_db)
2750
        cursor = cnx.cursor()
2751
2752
        cursor.execute(" SELECT name "
2753
                       " FROM tbl_combined_equipments "
2754
                       " WHERE id = %s ", (id_,))
2755
        if cursor.fetchone() is None:
2756
            cursor.close()
2757
            cnx.close()
2758
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2759
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2760
2761
        query = (" SELECT id, name, uuid "
2762
                 " FROM tbl_energy_categories ")
2763
        cursor.execute(query)
2764
        rows_energy_categories = cursor.fetchall()
2765
2766
        energy_category_dict = dict()
2767
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
2768
            for row in rows_energy_categories:
2769
                energy_category_dict[row[0]] = {"id": row[0],
2770
                                                "name": row[1],
2771
                                                "uuid": row[2]}
2772
2773
        query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id, em.is_output "
2774
                 " FROM tbl_combined_equipments e, tbl_combined_equipments_virtual_meters em, tbl_virtual_meters m "
2775
                 " WHERE em.combined_equipment_id = e.id AND m.id = em.virtual_meter_id AND e.id = %s "
2776
                 " ORDER BY m.id ")
2777
        cursor.execute(query, (id_,))
2778
        rows = cursor.fetchall()
2779
2780
        result = list()
2781
        if rows is not None and len(rows) > 0:
2782
            for row in rows:
2783
                meta_result = {"id": row[0], "name": row[1], "uuid": row[2],
2784
                               "energy_category": energy_category_dict.get(row[3], None),
2785
                               "is_output": bool(row[4])}
2786
                result.append(meta_result)
2787
2788
        cursor.close()
2789
        cnx.close()
2790
2791
        # Store result in Redis cache
2792
        result_json = json.dumps(result)
2793
        if redis_client:
2794
            try:
2795
                redis_client.setex(cache_key, cache_expire, result_json)
2796
            except Exception:
2797
                # If cache set fails, ignore and continue
2798
                pass
2799
2800
        resp.text = result_json
2801
2802
    @staticmethod
2803
    @user_logger
2804
    def on_post(req, resp, id_):
2805
        """Handles POST requests"""
2806
        admin_control(req)
2807
        try:
2808
            raw_json = req.stream.read().decode('utf-8')
2809
        except UnicodeDecodeError as ex:
2810
            print("Failed to decode request")
2811
            raise falcon.HTTPError(status=falcon.HTTP_400,
2812
                                   title='API.BAD_REQUEST',
2813
                                   description='API.INVALID_ENCODING')
2814
        except Exception as ex:
2815
            print("Unexpected error reading request stream")
2816
            raise falcon.HTTPError(status=falcon.HTTP_400,
2817
                                   title='API.BAD_REQUEST',
2818
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
2819
2820
        if not id_.isdigit() or int(id_) <= 0:
2821
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2822
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2823
2824
        new_values = json.loads(raw_json)
2825
2826
        if 'virtual_meter_id' not in new_values['data'].keys() or \
2827
                not isinstance(new_values['data']['virtual_meter_id'], int) or \
2828
                new_values['data']['virtual_meter_id'] <= 0:
2829
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2830
                                   description='API.INVALID_VIRTUAL_METER_ID')
2831
        virtual_meter_id = new_values['data']['virtual_meter_id']
2832
2833
        if 'is_output' not in new_values['data'].keys() or \
2834
                not isinstance(new_values['data']['is_output'], bool):
2835
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2836
                                   description='API.INVALID_IS_OUTPUT_VALUE')
2837
        is_output = new_values['data']['is_output']
2838
2839
        cnx = mysql.connector.connect(**config.myems_system_db)
2840
        cursor = cnx.cursor()
2841
2842
        cursor.execute(" SELECT name "
2843
                       " from tbl_combined_equipments "
2844
                       " WHERE id = %s ", (id_,))
2845
        if cursor.fetchone() is None:
2846
            cursor.close()
2847
            cnx.close()
2848
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2849
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2850
2851
        cursor.execute(" SELECT name "
2852
                       " FROM tbl_virtual_meters "
2853
                       " WHERE id = %s ", (virtual_meter_id,))
2854
        if cursor.fetchone() is None:
2855
            cursor.close()
2856
            cnx.close()
2857
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2858
                                   description='API.VIRTUAL_METER_NOT_FOUND')
2859
2860
        query = (" SELECT id "
2861
                 " FROM tbl_combined_equipments_virtual_meters "
2862
                 " WHERE combined_equipment_id = %s AND virtual_meter_id = %s")
2863
        cursor.execute(query, (id_, virtual_meter_id,))
2864
        if cursor.fetchone() is not None:
2865
            cursor.close()
2866
            cnx.close()
2867
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2868
                                   description='API.COMBINED_EQUIPMENT_VIRTUAL_METER_RELATION_EXISTS')
2869
2870
        add_row = (" INSERT INTO tbl_combined_equipments_virtual_meters "
2871
                   " (combined_equipment_id, virtual_meter_id, is_output ) "
2872
                   " VALUES (%s, %s, %s) ")
2873
        cursor.execute(add_row, (id_, virtual_meter_id, is_output))
2874
        cnx.commit()
2875
        cursor.close()
2876
        cnx.close()
2877
2878
        # Clear cache after adding virtual meter
2879
        clear_combined_equipment_cache(combined_equipment_id=id_)
2880
2881
        resp.status = falcon.HTTP_201
2882
        resp.location = '/combinedequipments/' + str(id_) + '/virtualmeters/' + str(virtual_meter_id)
2883
2884
2885
class CombinedEquipmentVirtualMeterItem:
2886
    def __init__(self):
2887
        pass
2888
2889
    @staticmethod
2890
    def on_options(req, resp, id_, mid):
2891
        _ = req
2892
        resp.status = falcon.HTTP_200
2893
        _ = id_
2894
2895
    @staticmethod
2896
    @user_logger
2897
    def on_delete(req, resp, id_, mid):
2898
        admin_control(req)
2899
        if not id_.isdigit() or int(id_) <= 0:
2900
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2901
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2902
2903
        if not mid.isdigit() or int(mid) <= 0:
2904
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2905
                                   description='API.INVALID_VIRTUAL_METER_ID')
2906
2907
        cnx = mysql.connector.connect(**config.myems_system_db)
2908
        cursor = cnx.cursor()
2909
2910
        cursor.execute(" SELECT name "
2911
                       " FROM tbl_combined_equipments "
2912
                       " WHERE id = %s ", (id_,))
2913
        if cursor.fetchone() is None:
2914
            cursor.close()
2915
            cnx.close()
2916
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2917
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
2918
2919
        cursor.execute(" SELECT name "
2920
                       " FROM tbl_virtual_meters "
2921
                       " WHERE id = %s ", (mid,))
2922
        if cursor.fetchone() is None:
2923
            cursor.close()
2924
            cnx.close()
2925
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2926
                                   description='API.VIRTUAL_METER_NOT_FOUND')
2927
2928
        cursor.execute(" SELECT id "
2929
                       " FROM tbl_combined_equipments_virtual_meters "
2930
                       " WHERE combined_equipment_id = %s AND virtual_meter_id = %s ", (id_, mid))
2931
        if cursor.fetchone() is None:
2932
            cursor.close()
2933
            cnx.close()
2934
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2935
                                   description='API.COMBINED_EQUIPMENT_VIRTUAL_METER_RELATION_NOT_FOUND')
2936
2937
        cursor.execute(" DELETE FROM tbl_combined_equipments_virtual_meters "
2938
                       " WHERE combined_equipment_id = %s AND virtual_meter_id = %s ", (id_, mid))
2939
        cnx.commit()
2940
2941
        cursor.close()
2942
        cnx.close()
2943
2944
        # Clear cache after deleting virtual meter
2945
        clear_combined_equipment_cache(combined_equipment_id=id_)
2946
2947
        resp.status = falcon.HTTP_204
2948
2949
2950 View Code Duplication
class CombinedEquipmentCommandCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2951
    def __init__(self):
2952
        pass
2953
2954
    @staticmethod
2955
    def on_options(req, resp, id_):
2956
        _ = req
2957
        resp.status = falcon.HTTP_200
2958
        _ = id_
2959
2960
    @staticmethod
2961
    def on_get(req, resp, id_):
2962
        if 'API-KEY' not in req.headers or \
2963
                not isinstance(req.headers['API-KEY'], str) or \
2964
                len(str.strip(req.headers['API-KEY'])) == 0:
2965
            access_control(req)
2966
        else:
2967
            api_key_control(req)
2968
        if not id_.isdigit() or int(id_) <= 0:
2969
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2970
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
2971
2972
        # Redis cache key
2973
        cache_key = f'combinedequipment:commands:{id_}'
2974
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
2975
2976
        # Try to get from Redis cache (only if Redis is enabled)
2977
        redis_client = None
2978
        if config.redis.get('is_enabled', False):
2979
            try:
2980
                redis_client = redis.Redis(
2981
                    host=config.redis['host'],
2982
                    port=config.redis['port'],
2983
                    password=config.redis['password'] if config.redis['password'] else None,
2984
                    db=config.redis['db'],
2985
                    decode_responses=True,
2986
                    socket_connect_timeout=2,
2987
                    socket_timeout=2
2988
                )
2989
                redis_client.ping()
2990
                cached_result = redis_client.get(cache_key)
2991
                if cached_result:
2992
                    resp.text = cached_result
2993
                    return
2994
            except Exception:
2995
                # If Redis connection fails, continue to database query
2996
                pass
2997
2998
        # Cache miss or Redis error - query database
2999
        cnx = mysql.connector.connect(**config.myems_system_db)
3000
        cursor = cnx.cursor()
3001
3002
        cursor.execute(" SELECT name "
3003
                       " FROM tbl_combined_equipments "
3004
                       " WHERE id = %s ", (id_,))
3005
        if cursor.fetchone() is None:
3006
            cursor.close()
3007
            cnx.close()
3008
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3009
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
3010
3011
        query = (" SELECT c.id, c.name, c.uuid "
3012
                 " FROM tbl_combined_equipments ce, tbl_combined_equipments_commands cec, tbl_commands c "
3013
                 " WHERE cec.combined_equipment_id = ce.id AND c.id = cec.command_id AND ce.id = %s "
3014
                 " ORDER BY c.id ")
3015
        cursor.execute(query, (id_,))
3016
        rows = cursor.fetchall()
3017
3018
        result = list()
3019
        if rows is not None and len(rows) > 0:
3020
            for row in rows:
3021
                meta_result = {"id": row[0], "name": row[1], "uuid": row[2]}
3022
                result.append(meta_result)
3023
3024
        cursor.close()
3025
        cnx.close()
3026
3027
        # Store result in Redis cache
3028
        result_json = json.dumps(result)
3029
        if redis_client:
3030
            try:
3031
                redis_client.setex(cache_key, cache_expire, result_json)
3032
            except Exception:
3033
                # If cache set fails, ignore and continue
3034
                pass
3035
3036
        resp.text = result_json
3037
3038
    @staticmethod
3039
    @user_logger
3040
    def on_post(req, resp, id_):
3041
        """Handles POST requests"""
3042
        admin_control(req)
3043
        try:
3044
            raw_json = req.stream.read().decode('utf-8')
3045
        except UnicodeDecodeError as ex:
3046
            print("Failed to decode request")
3047
            raise falcon.HTTPError(status=falcon.HTTP_400,
3048
                                   title='API.BAD_REQUEST',
3049
                                   description='API.INVALID_ENCODING')
3050
        except Exception as ex:
3051
            print("Unexpected error reading request stream")
3052
            raise falcon.HTTPError(status=falcon.HTTP_400,
3053
                                   title='API.BAD_REQUEST',
3054
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
3055
3056
        if not id_.isdigit() or int(id_) <= 0:
3057
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3058
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
3059
3060
        new_values = json.loads(raw_json)
3061
3062
        if 'command_id' not in new_values['data'].keys() or \
3063
                not isinstance(new_values['data']['command_id'], int) or \
3064
                new_values['data']['command_id'] <= 0:
3065
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3066
                                   description='API.INVALID_COMMAND_ID')
3067
        command_id = new_values['data']['command_id']
3068
3069
        cnx = mysql.connector.connect(**config.myems_system_db)
3070
        cursor = cnx.cursor()
3071
3072
        cursor.execute(" SELECT name "
3073
                       " from tbl_combined_equipments "
3074
                       " WHERE id = %s ", (id_,))
3075
        if cursor.fetchone() is None:
3076
            cursor.close()
3077
            cnx.close()
3078
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3079
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
3080
3081
        cursor.execute(" SELECT name "
3082
                       " FROM tbl_commands "
3083
                       " WHERE id = %s ", (command_id,))
3084
        if cursor.fetchone() is None:
3085
            cursor.close()
3086
            cnx.close()
3087
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3088
                                   description='API.COMMAND_NOT_FOUND')
3089
3090
        query = (" SELECT id "
3091
                 " FROM tbl_combined_equipments_commands "
3092
                 " WHERE combined_equipment_id = %s AND command_id = %s")
3093
        cursor.execute(query, (id_, command_id,))
3094
        if cursor.fetchone() is not None:
3095
            cursor.close()
3096
            cnx.close()
3097
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3098
                                   description='API.COMBINED_EQUIPMENT_COMMAND_RELATION_EXISTS')
3099
3100
        add_row = (" INSERT INTO tbl_combined_equipments_commands (combined_equipment_id, command_id) "
3101
                   " VALUES (%s, %s) ")
3102
        cursor.execute(add_row, (id_, command_id,))
3103
        cnx.commit()
3104
        cursor.close()
3105
        cnx.close()
3106
3107
        # Clear cache after adding command
3108
        clear_combined_equipment_cache(combined_equipment_id=id_)
3109
3110
        resp.status = falcon.HTTP_201
3111
        resp.location = '/combinedequipments/' + str(id_) + '/commands/' + str(command_id)
3112
3113
3114
class CombinedEquipmentCommandItem:
3115
    def __init__(self):
3116
        pass
3117
3118
    @staticmethod
3119
    def on_options(req, resp, id_, cid):
3120
        _ = req
3121
        resp.status = falcon.HTTP_200
3122
        _ = id_
3123
3124
    @staticmethod
3125
    @user_logger
3126
    def on_delete(req, resp, id_, cid):
3127
        admin_control(req)
3128
        if not id_.isdigit() or int(id_) <= 0:
3129
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3130
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
3131
3132
        if not cid.isdigit() or int(cid) <= 0:
3133
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3134
                                   description='API.INVALID_COMMAND_ID')
3135
3136
        cnx = mysql.connector.connect(**config.myems_system_db)
3137
        cursor = cnx.cursor()
3138
3139
        cursor.execute(" SELECT name "
3140
                       " FROM tbl_combined_equipments "
3141
                       " WHERE id = %s ", (id_,))
3142
        if cursor.fetchone() is None:
3143
            cursor.close()
3144
            cnx.close()
3145
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3146
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
3147
3148
        cursor.execute(" SELECT name "
3149
                       " FROM tbl_commands "
3150
                       " WHERE id = %s ", (cid,))
3151
        if cursor.fetchone() is None:
3152
            cursor.close()
3153
            cnx.close()
3154
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3155
                                   description='API.COMMAND_NOT_FOUND')
3156
3157
        cursor.execute(" SELECT id "
3158
                       " FROM tbl_combined_equipments_commands "
3159
                       " WHERE combined_equipment_id = %s AND command_id = %s ", (id_, cid))
3160
        if cursor.fetchone() is None:
3161
            cursor.close()
3162
            cnx.close()
3163
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3164
                                   description='API.COMBINED_EQUIPMENT_COMMAND_RELATION_NOT_FOUND')
3165
3166
        cursor.execute(" DELETE FROM tbl_combined_equipments_commands "
3167
                       " WHERE combined_equipment_id = %s AND command_id = %s ", (id_, cid))
3168
        cnx.commit()
3169
3170
        cursor.close()
3171
        cnx.close()
3172
3173
        # Clear cache after deleting command
3174
        clear_combined_equipment_cache(combined_equipment_id=id_)
3175
3176
        resp.status = falcon.HTTP_204
3177
3178
3179
class CombinedEquipmentExport:
3180
    def __init__(self):
3181
        pass
3182
3183
    @staticmethod
3184
    def on_options(req, resp, id_):
3185
        _ = req
3186
        resp.status = falcon.HTTP_200
3187
        _ = id_
3188
3189
    @staticmethod
3190
    def on_get(req, resp, id_):
3191
        if 'API-KEY' not in req.headers or \
3192
                not isinstance(req.headers['API-KEY'], str) or \
3193
                len(str.strip(req.headers['API-KEY'])) == 0:
3194
            access_control(req)
3195
        else:
3196
            api_key_control(req)
3197
        if not id_.isdigit() or int(id_) <= 0:
3198
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3199
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
3200
3201
        # Redis cache key
3202
        cache_key = f'combinedequipment:export:{id_}'
3203
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
3204
3205
        # Try to get from Redis cache (only if Redis is enabled)
3206
        redis_client = None
3207
        if config.redis.get('is_enabled', False):
3208
            try:
3209
                redis_client = redis.Redis(
3210
                    host=config.redis['host'],
3211
                    port=config.redis['port'],
3212
                    password=config.redis['password'] if config.redis['password'] else None,
3213
                    db=config.redis['db'],
3214
                    decode_responses=True,
3215
                    socket_connect_timeout=2,
3216
                    socket_timeout=2
3217
                )
3218
                redis_client.ping()
3219
                cached_result = redis_client.get(cache_key)
3220
                if cached_result:
3221
                    resp.text = cached_result
3222
                    return
3223
            except Exception:
3224
                # If Redis connection fails, continue to database query
3225
                pass
3226
3227
        # Cache miss or Redis error - query database
3228
        cnx = mysql.connector.connect(**config.myems_system_db)
3229
        cursor = cnx.cursor()
3230
3231
        query = (" SELECT id, name, uuid "
3232
                 " FROM tbl_cost_centers ")
3233
        cursor.execute(query)
3234
        rows_cost_centers = cursor.fetchall()
3235
3236
        cost_center_dict = dict()
3237
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
3238
            for row in rows_cost_centers:
3239
                cost_center_dict[row[0]] = {"id": row[0],
3240
                                            "name": row[1],
3241
                                            "uuid": row[2]}
3242
3243
        query = (" SELECT id, name, uuid "
3244
                 " FROM tbl_svgs ")
3245
        cursor.execute(query)
3246
        rows_svgs = cursor.fetchall()
3247
3248
        svg_dict = dict()
3249
        if rows_svgs is not None and len(rows_svgs) > 0:
3250
            for row in rows_svgs:
3251
                svg_dict[row[0]] = {"id": row[0],
3252
                                    "name": row[1],
3253
                                    "uuid": row[2]}
3254
3255
        query = (" SELECT id, name, uuid, "
3256
                 "        is_input_counted, is_output_counted, "
3257
                 "        cost_center_id, svg_id, camera_url, description "
3258
                 " FROM tbl_combined_equipments "
3259
                 " WHERE id = %s ")
3260
        cursor.execute(query, (id_,))
3261
        row = cursor.fetchone()
3262
3263
        if row is None:
3264
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3265
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
3266
        else:
3267
            meta_result = {"id": row[0],
3268
                           "name": row[1],
3269
                           "uuid": row[2],
3270
                           "is_input_counted": bool(row[3]),
3271
                           "is_output_counted": bool(row[4]),
3272
                           "cost_center": cost_center_dict.get(row[5], None),
3273
                           "svg": svg_dict.get(row[6], None),
3274
                           "camera_url": row[7],
3275
                           "description": row[8],
3276
                           "equipments": None,
3277
                           "commands": None,
3278
                           "meters": None,
3279
                           "offline_meters": None,
3280
                           "virtual_meters": None,
3281
                           "parameters": None}
3282
            query = (" SELECT e.id, e.name, e.uuid "
3283
                     " FROM tbl_combined_equipments c, tbl_combined_equipments_equipments ce, tbl_equipments e "
3284
                     " WHERE ce.combined_equipment_id = c.id AND e.id = ce.equipment_id AND c.id = %s "
3285
                     " ORDER BY e.id ")
3286
            cursor.execute(query, (id_,))
3287
            rows = cursor.fetchall()
3288
3289
            equipment_result = list()
3290
            if rows is not None and len(rows) > 0:
3291
                for row in rows:
3292
                    result = {"id": row[0], "name": row[1], "uuid": row[2]}
3293
                    equipment_result.append(result)
3294
                meta_result['equipments'] = equipment_result
3295
3296
            query = (" SELECT c.id, c.name, c.uuid "
3297
                     " FROM tbl_combined_equipments ce, tbl_combined_equipments_commands cec, tbl_commands c "
3298
                     " WHERE cec.combined_equipment_id = ce.id AND c.id = cec.command_id AND ce.id = %s "
3299
                     " ORDER BY c.id ")
3300
            cursor.execute(query, (id_,))
3301
            rows = cursor.fetchall()
3302
3303
            command_result = list()
3304
            if rows is not None and len(rows) > 0:
3305
                for row in rows:
3306
                    result = {"id": row[0], "name": row[1], "uuid": row[2]}
3307
                    command_result.append(result)
3308
                meta_result['commands'] = command_result
3309
3310
            query = (" SELECT id, name, uuid "
3311
                     " FROM tbl_energy_categories ")
3312
            cursor.execute(query)
3313
            rows_energy_categories = cursor.fetchall()
3314
3315
            energy_category_dict = dict()
3316
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
3317
                for row in rows_energy_categories:
3318
                    energy_category_dict[row[0]] = {"id": row[0],
3319
                                                    "name": row[1],
3320
                                                    "uuid": row[2]}
3321
3322
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id, em.is_output "
3323
                     " FROM tbl_combined_equipments e, tbl_combined_equipments_meters em, tbl_meters m "
3324
                     " WHERE em.combined_equipment_id = e.id AND m.id = em.meter_id AND e.id = %s "
3325
                     " ORDER BY m.id ")
3326
            cursor.execute(query, (id_,))
3327
            rows = cursor.fetchall()
3328
3329
            meter_result = list()
3330
            if rows is not None and len(rows) > 0:
3331
                for row in rows:
3332
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
3333
                              "energy_category": energy_category_dict.get(row[3], None),
3334
                              "is_output": bool(row[4])}
3335
                    meter_result.append(result)
3336
                meta_result['meters'] = meter_result
3337
3338
            query = (" SELECT id, name, uuid "
3339
                     " FROM tbl_energy_categories ")
3340
            cursor.execute(query)
3341
            rows_energy_categories = cursor.fetchall()
3342
3343
            energy_category_dict = dict()
3344
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
3345
                for row in rows_energy_categories:
3346
                    energy_category_dict[row[0]] = {"id": row[0],
3347
                                                    "name": row[1],
3348
                                                    "uuid": row[2]}
3349
3350
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id, em.is_output "
3351
                     " FROM tbl_combined_equipments e, tbl_combined_equipments_offline_meters em, tbl_offline_meters m "
3352
                     " WHERE em.combined_equipment_id = e.id AND m.id = em.offline_meter_id AND e.id = %s "
3353
                     " ORDER BY m.id ")
3354
            cursor.execute(query, (id_,))
3355
            rows = cursor.fetchall()
3356
3357
            offlinemeter_result = list()
3358
            if rows is not None and len(rows) > 0:
3359
                for row in rows:
3360
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
3361
                              "energy_category": energy_category_dict.get(row[3], None),
3362
                              "is_output": bool(row[4])}
3363
                    offlinemeter_result.append(result)
3364
                meta_result['offline_meters'] = offlinemeter_result
3365
3366
            query = (" SELECT id, name, uuid "
3367
                     " FROM tbl_energy_categories ")
3368
            cursor.execute(query)
3369
            rows_energy_categories = cursor.fetchall()
3370
3371
            energy_category_dict = dict()
3372
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
3373
                for row in rows_energy_categories:
3374
                    energy_category_dict[row[0]] = {"id": row[0],
3375
                                                    "name": row[1],
3376
                                                    "uuid": row[2]}
3377
3378
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id, em.is_output "
3379
                     " FROM tbl_combined_equipments e, tbl_combined_equipments_virtual_meters em, tbl_virtual_meters m "
3380
                     " WHERE em.combined_equipment_id = e.id AND m.id = em.virtual_meter_id AND e.id = %s "
3381
                     " ORDER BY m.id ")
3382
            cursor.execute(query, (id_,))
3383
            rows = cursor.fetchall()
3384
3385
            virtualmeter_result = list()
3386
            if rows is not None and len(rows) > 0:
3387
                for row in rows:
3388
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
3389
                              "energy_category": energy_category_dict.get(row[3], None),
3390
                              "is_output": bool(row[4])}
3391
                    virtualmeter_result.append(result)
3392
                meta_result['virtual_meters'] = virtualmeter_result
3393
3394
            query = (" SELECT id, name "
3395
                     " FROM tbl_points ")
3396
            cursor.execute(query)
3397
            rows_points = cursor.fetchall()
3398
3399
            point_dict = dict()
3400
            if rows_points is not None and len(rows_points) > 0:
3401
                for row in rows_points:
3402
                    point_dict[row[0]] = {"id": row[0],
3403
                                          "name": row[1]}
3404
3405
            query = (" SELECT id, name, uuid "
3406
                     " FROM tbl_meters ")
3407
            cursor.execute(query)
3408
            rows_meters = cursor.fetchall()
3409
3410
            meter_dict = dict()
3411
            if rows_meters is not None and len(rows_meters) > 0:
3412
                for row in rows_meters:
3413
                    meter_dict[row[2]] = {"type": 'meter',
3414
                                          "id": row[0],
3415
                                          "name": row[1],
3416
                                          "uuid": row[2]}
3417
3418
            query = (" SELECT id, name, uuid "
3419
                     " FROM tbl_offline_meters ")
3420
            cursor.execute(query)
3421
            rows_offline_meters = cursor.fetchall()
3422
3423
            offline_meter_dict = dict()
3424
            if rows_offline_meters is not None and len(rows_offline_meters) > 0:
3425
                for row in rows_offline_meters:
3426
                    offline_meter_dict[row[2]] = {"type": 'offline_meter',
3427
                                                  "id": row[0],
3428
                                                  "name": row[1],
3429
                                                  "uuid": row[2]}
3430
3431
            query = (" SELECT id, name, uuid "
3432
                     " FROM tbl_virtual_meters ")
3433
            cursor.execute(query)
3434
            rows_virtual_meters = cursor.fetchall()
3435
3436
            virtual_meter_dict = dict()
3437
            if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
3438
                for row in rows_virtual_meters:
3439
                    virtual_meter_dict[row[2]] = {"type": 'virtual_meter',
3440
                                                  "id": row[0],
3441
                                                  "name": row[1],
3442
                                                  "uuid": row[2]}
3443
3444
            query = (" SELECT id, name, parameter_type, "
3445
                     "        constant, point_id, numerator_meter_uuid, denominator_meter_uuid "
3446
                     " FROM tbl_combined_equipments_parameters "
3447
                     " WHERE combined_equipment_id = %s "
3448
                     " ORDER BY id ")
3449
            cursor.execute(query, (id_,))
3450
            rows_parameters = cursor.fetchall()
3451
3452
            parameter_result = list()
3453 View Code Duplication
            if rows_parameters is not None and len(rows_parameters) > 0:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
3454
                for row in rows_parameters:
3455
                    constant = None
3456
                    point = None
3457
                    numerator_meter = None
3458
                    denominator_meter = None
3459
                    if row[2] == 'point':
3460
                        point = point_dict.get(row[4], None)
3461
                        constant = None
3462
                        numerator_meter = None
3463
                        denominator_meter = None
3464
                    elif row[2] == 'constant':
3465
                        constant = row[3]
3466
                        point = None
3467
                        numerator_meter = None
3468
                        denominator_meter = None
3469
                    elif row[2] == 'fraction':
3470
                        constant = None
3471
                        point = None
3472
                        # find numerator meter by uuid
3473
                        numerator_meter = meter_dict.get(row[5], None)
3474
                        if numerator_meter is None:
3475
                            numerator_meter = virtual_meter_dict.get(row[5], None)
3476
                        if numerator_meter is None:
3477
                            numerator_meter = offline_meter_dict.get(row[5], None)
3478
                        # find denominator meter by uuid
3479
                        denominator_meter = meter_dict.get(row[6], None)
3480
                        if denominator_meter is None:
3481
                            denominator_meter = virtual_meter_dict.get(row[6], None)
3482
                        if denominator_meter is None:
3483
                            denominator_meter = offline_meter_dict.get(row[6], None)
3484
3485
                    result = {"id": row[0],
3486
                              "name": row[1],
3487
                              "parameter_type": row[2],
3488
                              "constant": constant,
3489
                              "point": point,
3490
                              "numerator_meter": numerator_meter,
3491
                              "denominator_meter": denominator_meter}
3492
                    parameter_result.append(result)
3493
                meta_result['parameters'] = parameter_result
3494
3495
        cursor.close()
3496
        cnx.close()
3497
3498
        # Store result in Redis cache
3499
        result_json = json.dumps(meta_result)
3500
        if redis_client:
3501
            try:
3502
                redis_client.setex(cache_key, cache_expire, result_json)
3503
            except Exception:
3504
                # If cache set fails, ignore and continue
3505
                pass
3506
3507
        resp.text = result_json
3508
3509
3510
class CombinedEquipmentImport:
3511
    def __init__(self):
3512
        """ Initializes CombinedEquipmentImport"""
3513
        pass
3514
3515
    @staticmethod
3516
    def on_options(req, resp):
3517
        _ = req
3518
        resp.status = falcon.HTTP_200
3519
3520
    @staticmethod
3521
    @user_logger
3522
    def on_post(req, resp):
3523
        """Handles POST requests"""
3524
        admin_control(req)
3525
        try:
3526
            raw_json = req.stream.read().decode('utf-8')
3527
        except UnicodeDecodeError as ex:
3528
            print("Failed to decode request")
3529
            raise falcon.HTTPError(status=falcon.HTTP_400,
3530
                                   title='API.BAD_REQUEST',
3531
                                   description='API.INVALID_ENCODING')
3532
        except Exception as ex:
3533
            print("Unexpected error reading request stream")
3534
            raise falcon.HTTPError(status=falcon.HTTP_400,
3535
                                   title='API.BAD_REQUEST',
3536
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
3537
3538
        new_values = json.loads(raw_json)
3539
3540
        if 'name' not in new_values.keys() or \
3541
                not isinstance(new_values['name'], str) or \
3542
                len(str.strip(new_values['name'])) == 0:
3543
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3544
                                   description='API.INVALID_COMBINED_EQUIPMENT_NAME')
3545
        name = str.strip(new_values['name'])
3546
3547
        if 'is_input_counted' not in new_values.keys() or \
3548
                not isinstance(new_values['is_input_counted'], bool):
3549
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3550
                                   description='API.INVALID_IS_INPUT_COUNTED_VALUE')
3551
        is_input_counted = new_values['is_input_counted']
3552
3553
        if 'is_output_counted' not in new_values.keys() or \
3554
                not isinstance(new_values['is_output_counted'], bool):
3555
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3556
                                   description='API.INVALID_IS_OUTPUT_COUNTED_VALUE')
3557
        is_output_counted = new_values['is_output_counted']
3558
3559
        if 'id' not in new_values['cost_center'].keys() or \
3560
                not isinstance(new_values['cost_center']['id'], int) or \
3561
                new_values['cost_center']['id'] <= 0:
3562
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3563
                                   description='API.INVALID_COST_CENTER_ID')
3564
        cost_center_id = new_values['cost_center']['id']
3565
3566
        if 'svg' in new_values.keys() and \
3567
                'id' in new_values['svg'].keys() and \
3568
                isinstance(new_values['svg']['id'], int) and \
3569
                new_values['svg']['id'] > 0:
3570
            svg_id = new_values['svg']['id']
3571
        else:
3572
            svg_id = None
3573
3574
        if 'camera_url' in new_values.keys() and \
3575
                new_values['camera_url'] is not None and \
3576
                len(str(new_values['camera_url'])) > 0:
3577
            camera_url = str.strip(new_values['camera_url'])
3578
        else:
3579
            camera_url = None
3580
3581
        if 'description' in new_values.keys() and \
3582
                new_values['description'] is not None and \
3583
                len(str(new_values['description'])) > 0:
3584
            description = str.strip(new_values['description'])
3585
        else:
3586
            description = None
3587
3588
        cnx = mysql.connector.connect(**config.myems_system_db)
3589
        cursor = cnx.cursor()
3590
3591
        cursor.execute(" SELECT name "
3592
                       " FROM tbl_combined_equipments "
3593
                       " WHERE name = %s ", (name,))
3594
        if cursor.fetchone() is not None:
3595
            cursor.close()
3596
            cnx.close()
3597
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3598
                                   description='API.COMBINED_EQUIPMENT_NAME_IS_ALREADY_IN_USE')
3599
3600
        if cost_center_id is not None:
3601
            cursor.execute(" SELECT name "
3602
                           " FROM tbl_cost_centers "
3603
                           " WHERE id = %s ",
3604
                           (new_values['cost_center']['id'],))
3605
            row = cursor.fetchone()
3606
            if row is None:
3607
                cursor.close()
3608
                cnx.close()
3609
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3610
                                       description='API.COST_CENTER_NOT_FOUND')
3611
        if svg_id is not None:
3612
            cursor.execute(" SELECT name "
3613
                           " FROM tbl_svgs "
3614
                           " WHERE id = %s ",
3615
                           (svg_id,))
3616
            row = cursor.fetchone()
3617
            if row is None:
3618
                cursor.close()
3619
                cnx.close()
3620
                raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3621
                                       description='API.SVG_NOT_FOUND')
3622
3623
        add_values = (" INSERT INTO tbl_combined_equipments "
3624
                      "    (name, uuid, is_input_counted, is_output_counted, "
3625
                      "     cost_center_id, svg_id, camera_url, description) "
3626
                      " VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ")
3627
        cursor.execute(add_values, (name,
3628
                                    str(uuid.uuid4()),
3629
                                    is_input_counted,
3630
                                    is_output_counted,
3631
                                    cost_center_id,
3632
                                    svg_id,
3633
                                    camera_url,
3634
                                    description))
3635
        new_id = cursor.lastrowid
3636
        if new_values['equipments'] is not None and len(new_values['equipments']) > 0:
3637
            for equipment in new_values['equipments']:
3638
                cursor.execute(" SELECT name "
3639
                               " FROM tbl_equipments "
3640
                               " WHERE id = %s ", (equipment['id'],))
3641
                if cursor.fetchone() is None:
3642
                    cursor.close()
3643
                    cnx.close()
3644
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3645
                                           description='API.EQUIPMENT_NOT_FOUND')
3646
3647
                query = (" SELECT id "
3648
                         " FROM tbl_combined_equipments_equipments "
3649
                         " WHERE combined_equipment_id = %s AND equipment_id = %s")
3650
                cursor.execute(query, (new_id, equipment['id'],))
3651
                if cursor.fetchone() is not None:
3652
                    cursor.close()
3653
                    cnx.close()
3654
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3655
                                           description='API.COMBINED_EQUIPMENT_EQUIPMENT_RELATION_EXISTS')
3656
3657
                add_row = (" INSERT INTO tbl_combined_equipments_equipments (combined_equipment_id, equipment_id) "
3658
                           " VALUES (%s, %s) ")
3659
                cursor.execute(add_row, (new_id, equipment['id'],))
3660
        if new_values['commands'] is not None and len(new_values['commands']) > 0:
3661
            for command in new_values['commands']:
3662
                cursor.execute(" SELECT name "
3663
                               " FROM tbl_commands "
3664
                               " WHERE id = %s ", (command['id'],))
3665
                if cursor.fetchone() is None:
3666
                    cursor.close()
3667
                    cnx.close()
3668
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3669
                                           description='API.COMMAND_NOT_FOUND')
3670
3671
                query = (" SELECT id "
3672
                         " FROM tbl_combined_equipments_commands "
3673
                         " WHERE combined_equipment_id = %s AND command_id = %s")
3674
                cursor.execute(query, (new_id, command['id'],))
3675
                if cursor.fetchone() is not None:
3676
                    cursor.close()
3677
                    cnx.close()
3678
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3679
                                           description='API.COMBINED_EQUIPMENT_COMMAND_RELATION_EXISTS')
3680
3681
                add_row = (" INSERT INTO tbl_combined_equipments_commands (combined_equipment_id, command_id) "
3682
                           " VALUES (%s, %s) ")
3683
                cursor.execute(add_row, (new_id, command['id'],))
3684
        if new_values['meters'] is not None and len(new_values['meters']) > 0:
3685
            for meter in new_values['meters']:
3686
                cursor.execute(" SELECT name "
3687
                               " FROM tbl_meters "
3688
                               " WHERE id = %s ", (meter['id'],))
3689
                if cursor.fetchone() is None:
3690
                    cursor.close()
3691
                    cnx.close()
3692
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3693
                                           description='API.METER_NOT_FOUND')
3694
3695
                query = (" SELECT id "
3696
                         " FROM tbl_combined_equipments_meters "
3697
                         " WHERE combined_equipment_id = %s AND meter_id = %s")
3698
                cursor.execute(query, (new_id, meter['id'],))
3699
                if cursor.fetchone() is not None:
3700
                    cursor.close()
3701
                    cnx.close()
3702
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3703
                                           description='API.COMBINED_EQUIPMENT_METER_RELATION_EXISTS')
3704
3705
                add_row = (" INSERT INTO tbl_combined_equipments_meters (combined_equipment_id, meter_id, is_output ) "
3706
                           " VALUES (%s, %s, %s) ")
3707
                cursor.execute(add_row, (new_id, meter['id'], meter['is_output']))
3708
        if new_values['offline_meters'] is not None and len(new_values['offline_meters']) > 0:
3709
            for offline_meter in new_values['offline_meters']:
3710
                cursor.execute(" SELECT name "
3711
                               " FROM tbl_offline_meters "
3712
                               " WHERE id = %s ", (offline_meter['id'],))
3713
                if cursor.fetchone() is None:
3714
                    cursor.close()
3715
                    cnx.close()
3716
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3717
                                           description='API.OFFLINE_METER_NOT_FOUND')
3718
3719
                query = (" SELECT id "
3720
                         " FROM tbl_combined_equipments_offline_meters "
3721
                         " WHERE combined_equipment_id = %s AND offline_meter_id = %s")
3722
                cursor.execute(query, (new_id, offline_meter['id'],))
3723
                if cursor.fetchone() is not None:
3724
                    cursor.close()
3725
                    cnx.close()
3726
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3727
                                           description='API.COMBINED_EQUIPMENT_OFFLINE_METER_RELATION_EXISTS')
3728
3729
                add_row = (" INSERT INTO tbl_combined_equipments_offline_meters "
3730
                           " (combined_equipment_id, offline_meter_id, is_output ) "
3731
                           " VALUES (%s, %s, %s) ")
3732
                cursor.execute(add_row, (new_id, offline_meter['id'], offline_meter['is_output']))
3733
        if new_values['virtual_meters'] is not None and len(new_values['virtual_meters']) > 0:
3734
            for virtual_meter in new_values['virtual_meters']:
3735
                cursor.execute(" SELECT name "
3736
                               " FROM tbl_virtual_meters "
3737
                               " WHERE id = %s ", (virtual_meter['id'],))
3738
                if cursor.fetchone() is None:
3739
                    cursor.close()
3740
                    cnx.close()
3741
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3742
                                           description='API.VIRTUAL_METER_NOT_FOUND')
3743
3744
                query = (" SELECT id "
3745
                         " FROM tbl_combined_equipments_virtual_meters "
3746
                         " WHERE combined_equipment_id = %s AND virtual_meter_id = %s")
3747
                cursor.execute(query, (new_id, virtual_meter['id'],))
3748
                if cursor.fetchone() is not None:
3749
                    cursor.close()
3750
                    cnx.close()
3751
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3752
                                           description='API.COMBINED_EQUIPMENT_VIRTUAL_METER_RELATION_EXISTS')
3753
3754
                add_row = (" INSERT INTO tbl_combined_equipments_virtual_meters "
3755
                           " (combined_equipment_id, virtual_meter_id, is_output ) "
3756
                           " VALUES (%s, %s, %s) ")
3757
                cursor.execute(add_row, (new_id, virtual_meter['id'], virtual_meter['is_output']))
3758 View Code Duplication
        if new_values['parameters'] is not None and len(new_values['parameters']) > 0:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
3759
            for parameters in new_values['parameters']:
3760
                cursor.execute(" SELECT name "
3761
                               " FROM tbl_combined_equipments_parameters "
3762
                               " WHERE name = %s AND combined_equipment_id = %s ", (parameters['name'], new_id))
3763
                if cursor.fetchone() is not None:
3764
                    cursor.close()
3765
                    cnx.close()
3766
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3767
                                           description='API.COMBINED_EQUIPMENT_PARAMETER_NAME_IS_ALREADY_IN_USE')
3768
                if 'point' in parameters:
3769
                    if parameters['point'] is None:
3770
                        point_id = None
3771
                    elif parameters['point']['id'] is not None and \
3772
                            parameters['point']['id'] <= 0:
3773
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3774
                                               description='API.INVALID_POINT_ID')
3775
                    else:
3776
                        point_id = parameters['point']['id']
3777
                else:
3778
                    point_id = None
3779
                numerator_meter_uuid = None
3780
                if 'numerator_meter' in parameters:
3781
                    if parameters['numerator_meter'] is not None and \
3782
                            isinstance(parameters['numerator_meter']['uuid'], str) and \
3783
                            len(str.strip(parameters['numerator_meter']['uuid'])) > 0:
3784
                        numerator_meter_uuid = str.strip(parameters['numerator_meter']['uuid'])
3785
3786
                denominator_meter_uuid = None
3787
                if 'denominator_meter' in parameters:
3788
                    if parameters['denominator_meter'] is not None and \
3789
                            isinstance(parameters['denominator_meter']['uuid'], str) and \
3790
                            len(str.strip(parameters['denominator_meter']['uuid'])) > 0:
3791
                        denominator_meter_uuid = str.strip(parameters['denominator_meter']['uuid'])
3792
3793
                # validate by parameter type
3794
                if parameters['parameter_type'] == 'point':
3795
                    if point_id is None:
3796
                        cursor.close()
3797
                        cnx.close()
3798
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3799
                                               description='API.INVALID_POINT_ID')
3800
3801
                    query = (" SELECT id, name "
3802
                             " FROM tbl_points "
3803
                             " WHERE id = %s ")
3804
                    cursor.execute(query, (point_id,))
3805
                    if cursor.fetchone() is None:
3806
                        cursor.close()
3807
                        cnx.close()
3808
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3809
                                               description='API.POINT_NOT_FOUND')
3810
3811
                elif parameters['parameter_type'] == 'constant':
3812
                    if parameters['constant'] is None:
3813
                        cursor.close()
3814
                        cnx.close()
3815
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3816
                                               description='API.INVALID_CONSTANT_VALUE')
3817
3818
                elif parameters['parameter_type'] == 'fraction':
3819
3820
                    query = (" SELECT id, name, uuid "
3821
                             " FROM tbl_meters ")
3822
                    cursor.execute(query)
3823
                    rows_meters = cursor.fetchall()
3824
3825
                    meter_dict = dict()
3826
                    if rows_meters is not None and len(rows_meters) > 0:
3827
                        for row in rows_meters:
3828
                            meter_dict[row[2]] = {"type": 'meter',
3829
                                                  "id": row[0],
3830
                                                  "name": row[1],
3831
                                                  "uuid": row[2]}
3832
3833
                    query = (" SELECT id, name, uuid "
3834
                             " FROM tbl_offline_meters ")
3835
                    cursor.execute(query)
3836
                    rows_offline_meters = cursor.fetchall()
3837
3838
                    offline_meter_dict = dict()
3839
                    if rows_offline_meters is not None and len(rows_offline_meters) > 0:
3840
                        for row in rows_offline_meters:
3841
                            offline_meter_dict[row[2]] = {"type": 'offline_meter',
3842
                                                          "id": row[0],
3843
                                                          "name": row[1],
3844
                                                          "uuid": row[2]}
3845
3846
                    query = (" SELECT id, name, uuid "
3847
                             " FROM tbl_virtual_meters ")
3848
                    cursor.execute(query)
3849
                    rows_virtual_meters = cursor.fetchall()
3850
3851
                    virtual_meter_dict = dict()
3852
                    if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
3853
                        for row in rows_virtual_meters:
3854
                            virtual_meter_dict[row[2]] = {"type": 'virtual_meter',
3855
                                                          "id": row[0],
3856
                                                          "name": row[1],
3857
                                                          "uuid": row[2]}
3858
3859
                    # validate numerator meter uuid
3860
                    if meter_dict.get(numerator_meter_uuid) is None and \
3861
                            virtual_meter_dict.get(numerator_meter_uuid) is None and \
3862
                            offline_meter_dict.get(numerator_meter_uuid) is None:
3863
                        cursor.close()
3864
                        cnx.close()
3865
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3866
                                               description='API.INVALID_NUMERATOR_METER_UUID')
3867
3868
                    # validate denominator meter uuid
3869
                    if denominator_meter_uuid is None:
3870
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3871
                                               description='API.INVALID_DENOMINATOR_METER_UUID')
3872
3873
                    if denominator_meter_uuid == numerator_meter_uuid:
3874
                        cursor.close()
3875
                        cnx.close()
3876
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3877
                                               description='API.INVALID_DENOMINATOR_METER_UUID')
3878
3879
                    if denominator_meter_uuid not in meter_dict and \
3880
                            denominator_meter_uuid not in virtual_meter_dict and \
3881
                            denominator_meter_uuid not in offline_meter_dict:
3882
                        cursor.close()
3883
                        cnx.close()
3884
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3885
                                               description='API.INVALID_DENOMINATOR_METER_UUID')
3886
3887
                add_values = (" INSERT INTO tbl_combined_equipments_parameters "
3888
                              "    (combined_equipment_id, name, parameter_type, constant, "
3889
                              "     point_id, numerator_meter_uuid, denominator_meter_uuid) "
3890
                              " VALUES (%s, %s, %s, %s, %s, %s, %s) ")
3891
                cursor.execute(add_values, (new_id,
3892
                                            parameters['name'],
3893
                                            parameters['parameter_type'],
3894
                                            parameters['constant'],
3895
                                            point_id,
3896
                                            numerator_meter_uuid,
3897
                                            denominator_meter_uuid))
3898
        cnx.commit()
3899
        cursor.close()
3900
        cnx.close()
3901
3902
        # Clear cache after importing combined equipment
3903
        clear_combined_equipment_cache()
3904
3905
        resp.status = falcon.HTTP_201
3906
        resp.location = '/combinedequipments/' + str(new_id)
3907
3908
3909
class CombinedEquipmentClone:
3910
    def __init__(self):
3911
        pass
3912
3913
    @staticmethod
3914
    def on_options(req, resp, id_):
3915
        _ = req
3916
        resp.status = falcon.HTTP_200
3917
        _ = id_
3918
3919
    @staticmethod
3920
    @user_logger
3921
    def on_post(req, resp, id_):
3922
        """Handles POST requests"""
3923
        admin_control(req)
3924
        if not id_.isdigit() or int(id_) <= 0:
3925
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
3926
                                   description='API.INVALID_COMBINED_EQUIPMENT_ID')
3927
3928
        cnx = mysql.connector.connect(**config.myems_system_db)
3929
        cursor = cnx.cursor()
3930
3931
        query = (" SELECT id, name, uuid "
3932
                 " FROM tbl_cost_centers ")
3933
        cursor.execute(query)
3934
        rows_cost_centers = cursor.fetchall()
3935
3936
        cost_center_dict = dict()
3937
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
3938
            for row in rows_cost_centers:
3939
                cost_center_dict[row[0]] = {"id": row[0],
3940
                                            "name": row[1],
3941
                                            "uuid": row[2]}
3942
3943
        query = (" SELECT id, name, uuid, "
3944
                 "        is_input_counted, is_output_counted, "
3945
                 "        cost_center_id, svg_id, camera_url, description "
3946
                 " FROM tbl_combined_equipments "
3947
                 " WHERE id = %s ")
3948
        cursor.execute(query, (id_,))
3949
        row = cursor.fetchone()
3950
3951
        if row is None:
3952
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3953
                                   description='API.COMBINED_EQUIPMENT_NOT_FOUND')
3954
        else:
3955
            meta_result = {"id": row[0],
3956
                           "name": row[1],
3957
                           "uuid": row[2],
3958
                           "is_input_counted": bool(row[3]),
3959
                           "is_output_counted": bool(row[4]),
3960
                           "cost_center": cost_center_dict.get(row[5], None),
3961
                           "svg_id": row[6],
3962
                           "camera_url": row[7],
3963
                           "description": row[8],
3964
                           "equipments": None,
3965
                           "commands": None,
3966
                           "meters": None,
3967
                           "offline_meters": None,
3968
                           "virtual_meters": None,
3969
                           "parameters": None
3970
                           }
3971
            query = (" SELECT e.id, e.name, e.uuid "
3972
                     " FROM tbl_combined_equipments c, tbl_combined_equipments_equipments ce, tbl_equipments e "
3973
                     " WHERE ce.combined_equipment_id = c.id AND e.id = ce.equipment_id AND c.id = %s "
3974
                     " ORDER BY e.id ")
3975
            cursor.execute(query, (id_,))
3976
            rows = cursor.fetchall()
3977
3978
            equipment_result = list()
3979
            if rows is not None and len(rows) > 0:
3980
                for row in rows:
3981
                    result = {"id": row[0], "name": row[1], "uuid": row[2]}
3982
                    equipment_result.append(result)
3983
                meta_result['equipments'] = equipment_result
3984
3985
            query = (" SELECT c.id, c.name, c.uuid "
3986
                     " FROM tbl_combined_equipments ce, tbl_combined_equipments_commands cec, tbl_commands c "
3987
                     " WHERE cec.combined_equipment_id = ce.id AND c.id = cec.command_id AND ce.id = %s "
3988
                     " ORDER BY c.id ")
3989
            cursor.execute(query, (id_,))
3990
            rows = cursor.fetchall()
3991
3992
            command_result = list()
3993
            if rows is not None and len(rows) > 0:
3994
                for row in rows:
3995
                    result = {"id": row[0], "name": row[1], "uuid": row[2]}
3996
                    command_result.append(result)
3997
                meta_result['commands'] = command_result
3998
3999
            query = (" SELECT id, name, uuid "
4000
                     " FROM tbl_energy_categories ")
4001
            cursor.execute(query)
4002
            rows_energy_categories = cursor.fetchall()
4003
4004
            energy_category_dict = dict()
4005
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
4006
                for row in rows_energy_categories:
4007
                    energy_category_dict[row[0]] = {"id": row[0],
4008
                                                    "name": row[1],
4009
                                                    "uuid": row[2]}
4010
4011
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id, em.is_output "
4012
                     " FROM tbl_combined_equipments e, tbl_combined_equipments_meters em, tbl_meters m "
4013
                     " WHERE em.combined_equipment_id = e.id AND m.id = em.meter_id AND e.id = %s "
4014
                     " ORDER BY m.id ")
4015
            cursor.execute(query, (id_,))
4016
            rows = cursor.fetchall()
4017
4018
            meter_result = list()
4019
            if rows is not None and len(rows) > 0:
4020
                for row in rows:
4021
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
4022
                              "energy_category": energy_category_dict.get(row[3], None),
4023
                              "is_output": bool(row[4])}
4024
                    meter_result.append(result)
4025
                meta_result['meters'] = meter_result
4026
4027
            query = (" SELECT id, name, uuid "
4028
                     " FROM tbl_energy_categories ")
4029
            cursor.execute(query)
4030
            rows_energy_categories = cursor.fetchall()
4031
4032
            energy_category_dict = dict()
4033
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
4034
                for row in rows_energy_categories:
4035
                    energy_category_dict[row[0]] = {"id": row[0],
4036
                                                    "name": row[1],
4037
                                                    "uuid": row[2]}
4038
4039
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id, em.is_output "
4040
                     " FROM tbl_combined_equipments e, tbl_combined_equipments_offline_meters em, tbl_offline_meters m "
4041
                     " WHERE em.combined_equipment_id = e.id AND m.id = em.offline_meter_id AND e.id = %s "
4042
                     " ORDER BY m.id ")
4043
            cursor.execute(query, (id_,))
4044
            rows = cursor.fetchall()
4045
4046
            offlinemeter_result = list()
4047
            if rows is not None and len(rows) > 0:
4048
                for row in rows:
4049
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
4050
                              "energy_category": energy_category_dict.get(row[3], None),
4051
                              "is_output": bool(row[4])}
4052
                    offlinemeter_result.append(result)
4053
                meta_result['offline_meters'] = offlinemeter_result
4054
4055
            query = (" SELECT id, name, uuid "
4056
                     " FROM tbl_energy_categories ")
4057
            cursor.execute(query)
4058
            rows_energy_categories = cursor.fetchall()
4059
4060
            energy_category_dict = dict()
4061
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
4062
                for row in rows_energy_categories:
4063
                    energy_category_dict[row[0]] = {"id": row[0],
4064
                                                    "name": row[1],
4065
                                                    "uuid": row[2]}
4066
4067
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id, em.is_output "
4068
                     " FROM tbl_combined_equipments e, tbl_combined_equipments_virtual_meters em, tbl_virtual_meters m "
4069
                     " WHERE em.combined_equipment_id = e.id AND m.id = em.virtual_meter_id AND e.id = %s "
4070
                     " ORDER BY m.id ")
4071
            cursor.execute(query, (id_,))
4072
            rows = cursor.fetchall()
4073
4074
            virtualmeter_result = list()
4075
            if rows is not None and len(rows) > 0:
4076
                for row in rows:
4077
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
4078
                              "energy_category": energy_category_dict.get(row[3], None),
4079
                              "is_output": bool(row[4])}
4080
                    virtualmeter_result.append(result)
4081
                meta_result['virtual_meters'] = virtualmeter_result
4082
4083
            query = (" SELECT id, name "
4084
                     " FROM tbl_points ")
4085
            cursor.execute(query)
4086
            rows_points = cursor.fetchall()
4087
4088
            point_dict = dict()
4089
            if rows_points is not None and len(rows_points) > 0:
4090
                for row in rows_points:
4091
                    point_dict[row[0]] = {"id": row[0],
4092
                                          "name": row[1]}
4093
4094
            query = (" SELECT id, name, uuid "
4095
                     " FROM tbl_meters ")
4096
            cursor.execute(query)
4097
            rows_meters = cursor.fetchall()
4098
4099
            meter_dict = dict()
4100
            if rows_meters is not None and len(rows_meters) > 0:
4101
                for row in rows_meters:
4102
                    meter_dict[row[2]] = {"type": 'meter',
4103
                                          "id": row[0],
4104
                                          "name": row[1],
4105
                                          "uuid": row[2]}
4106
4107
            query = (" SELECT id, name, uuid "
4108
                     " FROM tbl_offline_meters ")
4109
            cursor.execute(query)
4110
            rows_offline_meters = cursor.fetchall()
4111
4112
            offline_meter_dict = dict()
4113
            if rows_offline_meters is not None and len(rows_offline_meters) > 0:
4114
                for row in rows_offline_meters:
4115
                    offline_meter_dict[row[2]] = {"type": 'offline_meter',
4116
                                                  "id": row[0],
4117
                                                  "name": row[1],
4118
                                                  "uuid": row[2]}
4119
4120
            query = (" SELECT id, name, uuid "
4121
                     " FROM tbl_virtual_meters ")
4122
            cursor.execute(query)
4123
            rows_virtual_meters = cursor.fetchall()
4124
4125
            virtual_meter_dict = dict()
4126
            if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
4127
                for row in rows_virtual_meters:
4128
                    virtual_meter_dict[row[2]] = {"type": 'virtual_meter',
4129
                                                  "id": row[0],
4130
                                                  "name": row[1],
4131
                                                  "uuid": row[2]}
4132
4133
            query = (" SELECT id, name, parameter_type, "
4134
                     "        constant, point_id, numerator_meter_uuid, denominator_meter_uuid "
4135
                     " FROM tbl_combined_equipments_parameters "
4136
                     " WHERE combined_equipment_id = %s "
4137
                     " ORDER BY id ")
4138
            cursor.execute(query, (id_,))
4139
            rows_parameters = cursor.fetchall()
4140
4141
            parameter_result = list()
4142 View Code Duplication
            if rows_parameters is not None and len(rows_parameters) > 0:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
4143
                for row in rows_parameters:
4144
                    constant = None
4145
                    point = None
4146
                    numerator_meter = None
4147
                    denominator_meter = None
4148
                    if row[2] == 'point':
4149
                        point = point_dict.get(row[4], None)
4150
                        constant = None
4151
                        numerator_meter = None
4152
                        denominator_meter = None
4153
                    elif row[2] == 'constant':
4154
                        constant = row[3]
4155
                        point = None
4156
                        numerator_meter = None
4157
                        denominator_meter = None
4158
                    elif row[2] == 'fraction':
4159
                        constant = None
4160
                        point = None
4161
                        # find numerator meter by uuid
4162
                        numerator_meter = meter_dict.get(row[5], None)
4163
                        if numerator_meter is None:
4164
                            numerator_meter = virtual_meter_dict.get(row[5], None)
4165
                        if numerator_meter is None:
4166
                            numerator_meter = offline_meter_dict.get(row[5], None)
4167
                        # find denominator meter by uuid
4168
                        denominator_meter = meter_dict.get(row[6], None)
4169
                        if denominator_meter is None:
4170
                            denominator_meter = virtual_meter_dict.get(row[6], None)
4171
                        if denominator_meter is None:
4172
                            denominator_meter = offline_meter_dict.get(row[6], None)
4173
4174
                    result = {"id": row[0],
4175
                              "name": row[1],
4176
                              "parameter_type": row[2],
4177
                              "constant": constant,
4178
                              "point": point,
4179
                              "numerator_meter": numerator_meter,
4180
                              "denominator_meter": denominator_meter}
4181
                    parameter_result.append(result)
4182
                meta_result['parameters'] = parameter_result
4183
            timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
4184
            if config.utc_offset[0] == '-':
4185
                timezone_offset = -timezone_offset
4186
            new_name = (str.strip(meta_result['name']) +
4187
                        (datetime.utcnow() + timedelta(minutes=timezone_offset)).isoformat(sep='-', timespec='seconds'))
4188
            add_values = (" INSERT INTO tbl_combined_equipments "
4189
                          "    (name, uuid, is_input_counted, is_output_counted, "
4190
                          "     cost_center_id, svg_id, camera_url, description) "
4191
                          " VALUES (%s, %s, %s, %s, %s, %s, %s, %s) ")
4192
            cursor.execute(add_values, (new_name,
4193
                                        str(uuid.uuid4()),
4194
                                        meta_result['is_input_counted'],
4195
                                        meta_result['is_output_counted'],
4196
                                        meta_result['cost_center']['id'],
4197
                                        meta_result['svg_id'],
4198
                                        meta_result['camera_url'],
4199
                                        meta_result['description']))
4200
            new_id = cursor.lastrowid
4201
            if meta_result['equipments'] is not None and len(meta_result['equipments']) > 0:
4202
                for equipment in meta_result['equipments']:
4203
                    cursor.execute(" SELECT name "
4204
                                   " FROM tbl_equipments "
4205
                                   " WHERE id = %s ", (equipment['id'],))
4206
                    if cursor.fetchone() is None:
4207
                        cursor.close()
4208
                        cnx.close()
4209
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
4210
                                               description='API.EQUIPMENT_NOT_FOUND')
4211
4212
                    query = (" SELECT id "
4213
                             " FROM tbl_combined_equipments_equipments "
4214
                             " WHERE combined_equipment_id = %s AND equipment_id = %s")
4215
                    cursor.execute(query, (new_id, equipment['id'],))
4216
                    if cursor.fetchone() is not None:
4217
                        cursor.close()
4218
                        cnx.close()
4219
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
4220
                                               description='API.COMBINED_EQUIPMENT_EQUIPMENT_RELATION_EXISTS')
4221
4222
                    add_row = (" INSERT INTO tbl_combined_equipments_equipments (combined_equipment_id, equipment_id) "
4223
                               " VALUES (%s, %s) ")
4224
                    cursor.execute(add_row, (new_id, equipment['id'],))
4225
            if meta_result['commands'] is not None and len(meta_result['commands']) > 0:
4226
                for command in meta_result['commands']:
4227
                    cursor.execute(" SELECT name "
4228
                                   " FROM tbl_commands "
4229
                                   " WHERE id = %s ", (command['id'],))
4230
                    if cursor.fetchone() is None:
4231
                        cursor.close()
4232
                        cnx.close()
4233
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
4234
                                               description='API.COMMAND_NOT_FOUND')
4235
4236
                    query = (" SELECT id "
4237
                             " FROM tbl_combined_equipments_commands "
4238
                             " WHERE combined_equipment_id = %s AND command_id = %s")
4239
                    cursor.execute(query, (new_id, command['id'],))
4240
                    if cursor.fetchone() is not None:
4241
                        cursor.close()
4242
                        cnx.close()
4243
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
4244
                                               description='API.COMBINED_EQUIPMENT_COMMAND_RELATION_EXISTS')
4245
4246
                    add_row = (" INSERT INTO tbl_combined_equipments_commands (combined_equipment_id, command_id) "
4247
                               " VALUES (%s, %s) ")
4248
                    cursor.execute(add_row, (new_id, command['id'],))
4249
            if meta_result['meters'] is not None and len(meta_result['meters']) > 0:
4250
                for meter in meta_result['meters']:
4251
                    cursor.execute(" SELECT name "
4252
                                   " FROM tbl_meters "
4253
                                   " WHERE id = %s ", (meter['id'],))
4254
                    if cursor.fetchone() is None:
4255
                        cursor.close()
4256
                        cnx.close()
4257
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
4258
                                               description='API.METER_NOT_FOUND')
4259
4260
                    query = (" SELECT id "
4261
                             " FROM tbl_combined_equipments_meters "
4262
                             " WHERE combined_equipment_id = %s AND meter_id = %s")
4263
                    cursor.execute(query, (new_id, meter['id'],))
4264
                    if cursor.fetchone() is not None:
4265
                        cursor.close()
4266
                        cnx.close()
4267
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
4268
                                               description='API.COMBINED_EQUIPMENT_METER_RELATION_EXISTS')
4269
4270
                    add_row = (
4271
                        " INSERT INTO tbl_combined_equipments_meters (combined_equipment_id, meter_id, is_output ) "
4272
                        " VALUES (%s, %s, %s) ")
4273
                    cursor.execute(add_row, (new_id, meter['id'], meter['is_output']))
4274
            if meta_result['offline_meters'] is not None and len(meta_result['offline_meters']) > 0:
4275
                for offline_meter in meta_result['offline_meters']:
4276
                    cursor.execute(" SELECT name "
4277
                                   " FROM tbl_offline_meters "
4278
                                   " WHERE id = %s ", (offline_meter['id'],))
4279
                    if cursor.fetchone() is None:
4280
                        cursor.close()
4281
                        cnx.close()
4282
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
4283
                                               description='API.OFFLINE_METER_NOT_FOUND')
4284
4285
                    query = (" SELECT id "
4286
                             " FROM tbl_combined_equipments_offline_meters "
4287
                             " WHERE combined_equipment_id = %s AND offline_meter_id = %s")
4288
                    cursor.execute(query, (new_id, offline_meter['id'],))
4289
                    if cursor.fetchone() is not None:
4290
                        cursor.close()
4291
                        cnx.close()
4292
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
4293
                                               description='API.COMBINED_EQUIPMENT_OFFLINE_METER_RELATION_EXISTS')
4294
4295
                    add_row = (" INSERT INTO tbl_combined_equipments_offline_meters "
4296
                               " (combined_equipment_id, offline_meter_id, is_output ) "
4297
                               " VALUES (%s, %s, %s) ")
4298
                    cursor.execute(add_row, (new_id, offline_meter['id'], offline_meter['is_output']))
4299
            if meta_result['virtual_meters'] is not None and len(meta_result['virtual_meters']) > 0:
4300
                for virtual_meter in meta_result['virtual_meters']:
4301
                    cursor.execute(" SELECT name "
4302
                                   " FROM tbl_virtual_meters "
4303
                                   " WHERE id = %s ", (virtual_meter['id'],))
4304
                    if cursor.fetchone() is None:
4305
                        cursor.close()
4306
                        cnx.close()
4307
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
4308
                                               description='API.VIRTUAL_METER_NOT_FOUND')
4309
4310
                    query = (" SELECT id "
4311
                             " FROM tbl_combined_equipments_virtual_meters "
4312
                             " WHERE combined_equipment_id = %s AND virtual_meter_id = %s")
4313
                    cursor.execute(query, (new_id, virtual_meter['id'],))
4314
                    if cursor.fetchone() is not None:
4315
                        cursor.close()
4316
                        cnx.close()
4317
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
4318
                                               description='API.COMBINED_EQUIPMENT_VIRTUAL_METER_RELATION_EXISTS')
4319
4320
                    add_row = (" INSERT INTO tbl_combined_equipments_virtual_meters "
4321
                               " (combined_equipment_id, virtual_meter_id, is_output ) "
4322
                               " VALUES (%s, %s, %s) ")
4323
                    cursor.execute(add_row, (new_id, virtual_meter['id'], virtual_meter['is_output']))
4324 View Code Duplication
            if meta_result['parameters'] is not None and len(meta_result['parameters']) > 0:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
4325
                for parameters in meta_result['parameters']:
4326
                    cursor.execute(" SELECT name "
4327
                                   " FROM tbl_combined_equipments_parameters "
4328
                                   " WHERE name = %s AND combined_equipment_id = %s ", (parameters['name'], new_id))
4329
                    if cursor.fetchone() is not None:
4330
                        cursor.close()
4331
                        cnx.close()
4332
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
4333
                                               description='API.COMBINED_EQUIPMENT_PARAMETER_NAME_IS_ALREADY_IN_USE')
4334
                    if 'point' in parameters:
4335
                        if parameters['point'] is None:
4336
                            point_id = None
4337
                        elif parameters['point']['id'] is not None and \
4338
                                parameters['point']['id'] <= 0:
4339
                            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
4340
                                                   description='API.INVALID_POINT_ID')
4341
                        else:
4342
                            point_id = parameters['point']['id']
4343
                    else:
4344
                        point_id = None
4345
                    numerator_meter_uuid = None
4346
                    if 'numerator_meter' in parameters:
4347
                        if parameters['numerator_meter'] is not None and \
4348
                                isinstance(parameters['numerator_meter']['uuid'], str) and \
4349
                                len(str.strip(parameters['numerator_meter']['uuid'])) > 0:
4350
                            numerator_meter_uuid = str.strip(parameters['numerator_meter']['uuid'])
4351
4352
                    denominator_meter_uuid = None
4353
                    if 'denominator_meter' in parameters:
4354
                        if parameters['denominator_meter'] is not None and \
4355
                                isinstance(parameters['denominator_meter']['uuid'], str) and \
4356
                                len(str.strip(parameters['denominator_meter']['uuid'])) > 0:
4357
                            denominator_meter_uuid = str.strip(parameters['denominator_meter']['uuid'])
4358
4359
                    # validate by parameter type
4360
                    if parameters['parameter_type'] == 'point':
4361
                        if point_id is None:
4362
                            cursor.close()
4363
                            cnx.close()
4364
                            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
4365
                                                   description='API.INVALID_POINT_ID')
4366
4367
                        query = (" SELECT id, name "
4368
                                 " FROM tbl_points "
4369
                                 " WHERE id = %s ")
4370
                        cursor.execute(query, (point_id,))
4371
                        if cursor.fetchone() is None:
4372
                            cursor.close()
4373
                            cnx.close()
4374
                            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
4375
                                                   description='API.POINT_NOT_FOUND')
4376
4377
                    elif parameters['parameter_type'] == 'constant':
4378
                        if parameters['constant'] is None:
4379
                            cursor.close()
4380
                            cnx.close()
4381
                            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
4382
                                                   description='API.INVALID_CONSTANT_VALUE')
4383
4384
                    elif parameters['parameter_type'] == 'fraction':
4385
4386
                        query = (" SELECT id, name, uuid "
4387
                                 " FROM tbl_meters ")
4388
                        cursor.execute(query)
4389
                        rows_meters = cursor.fetchall()
4390
4391
                        meter_dict = dict()
4392
                        if rows_meters is not None and len(rows_meters) > 0:
4393
                            for row in rows_meters:
4394
                                meter_dict[row[2]] = {"type": 'meter',
4395
                                                      "id": row[0],
4396
                                                      "name": row[1],
4397
                                                      "uuid": row[2]}
4398
4399
                        query = (" SELECT id, name, uuid "
4400
                                 " FROM tbl_offline_meters ")
4401
                        cursor.execute(query)
4402
                        rows_offline_meters = cursor.fetchall()
4403
4404
                        offline_meter_dict = dict()
4405
                        if rows_offline_meters is not None and len(rows_offline_meters) > 0:
4406
                            for row in rows_offline_meters:
4407
                                offline_meter_dict[row[2]] = {"type": 'offline_meter',
4408
                                                              "id": row[0],
4409
                                                              "name": row[1],
4410
                                                              "uuid": row[2]}
4411
4412
                        query = (" SELECT id, name, uuid "
4413
                                 " FROM tbl_virtual_meters ")
4414
                        cursor.execute(query)
4415
                        rows_virtual_meters = cursor.fetchall()
4416
4417
                        virtual_meter_dict = dict()
4418
                        if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
4419
                            for row in rows_virtual_meters:
4420
                                virtual_meter_dict[row[2]] = {"type": 'virtual_meter',
4421
                                                              "id": row[0],
4422
                                                              "name": row[1],
4423
                                                              "uuid": row[2]}
4424
4425
                        # validate numerator meter uuid
4426
                        if meter_dict.get(numerator_meter_uuid) is None and \
4427
                                virtual_meter_dict.get(numerator_meter_uuid) is None and \
4428
                                offline_meter_dict.get(numerator_meter_uuid) is None:
4429
                            cursor.close()
4430
                            cnx.close()
4431
                            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
4432
                                                   description='API.INVALID_NUMERATOR_METER_UUID')
4433
4434
                        # validate denominator meter uuid
4435
                        if denominator_meter_uuid is None:
4436
                            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
4437
                                                   description='API.INVALID_DENOMINATOR_METER_UUID')
4438
4439
                        if denominator_meter_uuid == numerator_meter_uuid:
4440
                            cursor.close()
4441
                            cnx.close()
4442
                            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
4443
                                                   description='API.INVALID_DENOMINATOR_METER_UUID')
4444
4445
                        if denominator_meter_uuid not in meter_dict and \
4446
                                denominator_meter_uuid not in virtual_meter_dict and \
4447
                                denominator_meter_uuid not in offline_meter_dict:
4448
                            cursor.close()
4449
                            cnx.close()
4450
                            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
4451
                                                   description='API.INVALID_DENOMINATOR_METER_UUID')
4452
4453
                    add_values = (" INSERT INTO tbl_combined_equipments_parameters "
4454
                                  "    (combined_equipment_id, name, parameter_type, constant, "
4455
                                  "     point_id, numerator_meter_uuid, denominator_meter_uuid) "
4456
                                  " VALUES (%s, %s, %s, %s, %s, %s, %s) ")
4457
                    cursor.execute(add_values, (new_id,
4458
                                                parameters['name'],
4459
                                                parameters['parameter_type'],
4460
                                                parameters['constant'],
4461
                                                point_id,
4462
                                                numerator_meter_uuid,
4463
                                                denominator_meter_uuid))
4464
            cnx.commit()
4465
            cursor.close()
4466
            cnx.close()
4467
4468
            # Clear cache after cloning combined equipment
4469
            clear_combined_equipment_cache()
4470
4471
            resp.status = falcon.HTTP_201
4472
            resp.location = '/combinedequipments/' + str(new_id)
4473