core.datasource.DataSourceItem.on_delete()   B
last analyzed

Complexity

Conditions 5

Size

Total Lines 42
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 31
dl 0
loc 42
rs 8.6693
c 0
b 0
f 0
cc 5
nop 3
1
import uuid
2
from datetime import datetime, timezone, timedelta
3
import falcon
4
import mysql.connector
5
import simplejson as json
6
from core.useractivity import user_logger, admin_control
7
import config
8
from decimal import Decimal
9
10
11
class DataSourceCollection:
12
    """
13
    Data Source Collection Resource
14
15
    This class handles CRUD operations for data source collection.
16
    It provides endpoints for listing all data sources and creating new data sources.
17
    Data sources represent external systems that provide data to the energy management system.
18
    """
19
    def __init__(self):
20
        """Initialize DataSourceCollection"""
21
        pass
22
23
    @staticmethod
24
    def on_options(req, resp):
25
        """Handle OPTIONS requests for CORS preflight"""
26
        _ = req
27
        resp.status = falcon.HTTP_200
28
29
    @staticmethod
30
    def on_get(req, resp):
31
        admin_control(req)
32
        search_query = req.get_param('q', default=None)
33
        if search_query is not None:
34
            search_query = search_query.strip()
35
        else:
36
            search_query = ''
37
        cnx = mysql.connector.connect(**config.myems_system_db)
38
        cursor = cnx.cursor()
39
40
        query = (" SELECT id, name, uuid "
41
                 " FROM tbl_gateways ")
42
        cursor.execute(query)
43
        rows_gateways = cursor.fetchall()
44
        gateway_dict = dict()
45
        if rows_gateways is not None and len(rows_gateways) > 0:
46
            for row in rows_gateways:
47
                gateway_dict[row[0]] = {"id": row[0],
48
                                        "name": row[1],
49
                                        "uuid": row[2]}
50
51
        query = (" SELECT id, name, uuid, gateway_id, protocol, connection, last_seen_datetime_utc, description "
52
                 " FROM tbl_data_sources " )
53
        params = []
54
        if search_query:
55
            query += " WHERE name LIKE %s OR description LIKE %s "
56
            params = [f'%{search_query}%', f'%{search_query}%']
57
        query += " ORDER BY id "
58
        cursor.execute(query,params)
59
        rows = cursor.fetchall()
60
        cursor.close()
61
        cnx.close()
62
63
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
64
        if config.utc_offset[0] == '-':
65
            timezone_offset = -timezone_offset
66
67
        result = list()
68
        if rows is not None and len(rows) > 0:
69
            for row in rows:
70
                if isinstance(row[6], datetime):
71
                    last_seen_datetime_local = row[6].replace(tzinfo=timezone.utc) + timedelta(minutes=timezone_offset)
72
                    last_seen_datetime = last_seen_datetime_local.isoformat()[0:19]
73
                else:
74
                    last_seen_datetime = None
75
                meta_result = {"id": row[0],
76
                               "name": row[1],
77
                               "uuid": row[2],
78
                               "gateway": gateway_dict.get(row[3]),
79
                               "protocol": row[4],
80
                               "connection": row[5],
81
                               "last_seen_datetime": last_seen_datetime,
82
                               "description": row[7]
83
                               }
84
85
                result.append(meta_result)
86
87
        resp.text = json.dumps(result)
88
89
    @staticmethod
90
    @user_logger
91
    def on_post(req, resp):
92
        """Handles POST requests"""
93
        admin_control(req)
94
        try:
95
            raw_json = req.stream.read().decode('utf-8')
96
        except Exception as ex:
97
            print(ex)
98
            raise falcon.HTTPError(status=falcon.HTTP_400,
99
                                   title='API.BAD_REQUEST',
100
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
101
102
        new_values = json.loads(raw_json)
103
104
        if 'name' not in new_values['data'].keys() or \
105
                not isinstance(new_values['data']['name'], str) or \
106
                len(str.strip(new_values['data']['name'])) == 0:
107
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
108
                                   description='API.INVALID_DATA_SOURCE_NAME')
109
        name = str.strip(new_values['data']['name'])
110
111
        if 'gateway_id' not in new_values['data'].keys() or \
112
                not isinstance(new_values['data']['gateway_id'], int) or \
113
                new_values['data']['gateway_id'] <= 0:
114
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
115
                                   description='API.INVALID_GATEWAY_ID')
116
        gateway_id = new_values['data']['gateway_id']
117
118
        if 'protocol' not in new_values['data'].keys() or \
119
                not isinstance(new_values['data']['protocol'], str) or \
120
                len(str.strip(new_values['data']['protocol'])) == 0:
121
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
122
                                   description='API.INVALID_DATA_SOURCE_PROTOCOL')
123
        protocol = new_values['data']['protocol']
124
125
        cnx = mysql.connector.connect(**config.myems_system_db)
126
        cursor = cnx.cursor()
127
128
        query = (" SELECT id, name, code "
129
                 " FROM tbl_protocols ")
130
        cursor.execute(query)
131
        rows_protocols = cursor.fetchall()
132
133
        procotol_dict = dict()
134
        if rows_protocols is not None and len(rows_protocols) > 0:
135
            for row in rows_protocols:
136
                procotol_dict[row[2]] = {"id": row[0],
137
                                         "name": row[1],
138
                                         "code": row[2]}
139
140
        if protocol not in procotol_dict.keys():
141
            cursor.close()
142
            cnx.close()
143
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
144
                                   description='API.INVALID_DATA_SOURCE_PROTOCOL')
145
146
        if 'connection' not in new_values['data'].keys() or \
147
                not isinstance(new_values['data']['connection'], str) or \
148
                len(str.strip(new_values['data']['connection'])) == 0:
149
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
150
                                   description='API.INVALID_CONNECTION')
151
        connection = str.strip(new_values['data']['connection'])
152
153
        if 'description' in new_values['data'].keys() and \
154
                new_values['data']['description'] is not None and \
155
                len(str(new_values['data']['description'])) > 0:
156
            description = str.strip(new_values['data']['description'])
157
        else:
158
            description = None
159
160
        cursor.execute(" SELECT name "
161
                       " FROM tbl_data_sources "
162
                       " WHERE name = %s ", (name,))
163
        if cursor.fetchone() is not None:
164
            cursor.close()
165
            cnx.close()
166
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
167
                                   description='API.DATA_SOURCE_NAME_IS_ALREADY_IN_USE')
168
169
        cursor.execute(" SELECT name "
170
                       " FROM tbl_gateways "
171
                       " WHERE id = %s ", (gateway_id,))
172
        if cursor.fetchone() is None:
173
            cursor.close()
174
            cnx.close()
175
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
176
                                   description='API.INVALID_GATEWAY_ID')
177
178
        add_values = (" INSERT INTO tbl_data_sources (name, uuid, gateway_id, protocol, connection, description) "
179
                      " VALUES (%s, %s, %s, %s, %s, %s) ")
180
        cursor.execute(add_values, (name,
181
                                    str(uuid.uuid4()),
182
                                    gateway_id,
183
                                    protocol,
184
                                    connection,
185
                                    description))
186
        new_id = cursor.lastrowid
187
        cnx.commit()
188
        cursor.close()
189
        cnx.close()
190
191
        resp.status = falcon.HTTP_201
192
        resp.location = '/datasources/' + str(new_id)
193
194
195
class DataSourceItem:
196
    def __init__(self):
197
        pass
198
199
    @staticmethod
200
    def on_options(req, resp, id_):
201
        _ = req
202
        resp.status = falcon.HTTP_200
203
        _ = id_
204
205
    @staticmethod
206
    def on_get(req, resp, id_):
207
        admin_control(req)
208
        if not id_.isdigit() or int(id_) <= 0:
209
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
210
                                   description='API.INVALID_DATA_SOURCE_ID')
211
212
        cnx = mysql.connector.connect(**config.myems_system_db)
213
        cursor = cnx.cursor()
214
215
        query = (" SELECT id, name, uuid "
216
                 " FROM tbl_gateways ")
217
        cursor.execute(query)
218
        rows_gateways = cursor.fetchall()
219
        gateway_dict = dict()
220
        if rows_gateways is not None and len(rows_gateways) > 0:
221
            for row in rows_gateways:
222
                gateway_dict[row[0]] = {"id": row[0],
223
                                        "name": row[1],
224
                                        "uuid": row[2]}
225
226
        query = (" SELECT id, name, uuid, gateway_id, protocol, connection, last_seen_datetime_utc, description "
227
                 " FROM tbl_data_sources "
228
                 " WHERE id = %s ")
229
        cursor.execute(query, (id_,))
230
        row = cursor.fetchone()
231
        cursor.close()
232
        cnx.close()
233
        if row is None:
234
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
235
                                   description='API.DATA_SOURCE_NOT_FOUND')
236
237
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
238
        if config.utc_offset[0] == '-':
239
            timezone_offset = -timezone_offset
240
241
        if isinstance(row[6], datetime):
242
            last_seen_datetime_local = row[6].replace(tzinfo=timezone.utc) + \
243
                timedelta(minutes=timezone_offset)
244
            last_seen_datetime = last_seen_datetime_local.isoformat()[0:19]
245
        else:
246
            last_seen_datetime = None
247
248
        result = {"id": row[0],
249
                  "name": row[1],
250
                  "uuid": row[2],
251
                  "gateway": gateway_dict.get(row[3]),
252
                  "protocol": row[4],
253
                  "connection": row[5],
254
                  "last_seen_datetime": last_seen_datetime,
255
                  "description": row[7]
256
                  }
257
258
        resp.text = json.dumps(result)
259
260
    @staticmethod
261
    @user_logger
262
    def on_delete(req, resp, id_):
263
        admin_control(req)
264
        if not id_.isdigit() or int(id_) <= 0:
265
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
266
                                   description='API.INVALID_DATA_SOURCE_ID')
267
268
        cnx = mysql.connector.connect(**config.myems_system_db)
269
        cursor = cnx.cursor()
270
271
        cursor.execute(" SELECT name "
272
                       " FROM tbl_data_sources "
273
                       " WHERE id = %s ", (id_,))
274
        if cursor.fetchone() is None:
275
            cursor.close()
276
            cnx.close()
277
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
278
                                   description='API.DATA_SOURCE_NOT_FOUND')
279
280
        # check if this data source is being used by any meters
281
        cursor.execute(" SELECT DISTINCT(m.name) "
282
                       " FROM tbl_meters m, tbl_meters_points mp, tbl_points p, tbl_data_sources ds "
283
                       " WHERE m.id = mp.meter_id AND mp.point_id = p.id AND p.data_source_id = ds.id "
284
                       "       AND ds.id = %s "
285
                       " LIMIT 1 ",
286
                       (id_,))
287
        row_meter = cursor.fetchone()
288
        if row_meter is not None:
289
            cursor.close()
290
            cnx.close()
291
            raise falcon.HTTPError(status=falcon.HTTP_400,
292
                                   title='API.BAD_REQUEST',
293
                                   description='API.THIS_DATA_SOURCE_IS_BEING_USED_BY_A_METER')
294
        # todo : check if this data source is being used by any other objects
295
        cursor.execute(" DELETE FROM tbl_points WHERE data_source_id = %s ", (id_,))
296
        cursor.execute(" DELETE FROM tbl_data_sources WHERE id = %s ", (id_,))
297
        cnx.commit()
298
299
        cursor.close()
300
        cnx.close()
301
        resp.status = falcon.HTTP_204
302
303
    @staticmethod
304
    @user_logger
305
    def on_put(req, resp, id_):
306
        """Handles PUT requests"""
307
        admin_control(req)
308
        try:
309
            raw_json = req.stream.read().decode('utf-8')
310
        except Exception as ex:
311
            print(str(ex))
312
            raise falcon.HTTPError(status=falcon.HTTP_400,
313
                                   title='API.BAD_REQUEST',
314
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
315
316
        if not id_.isdigit() or int(id_) <= 0:
317
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
318
                                   description='API.INVALID_DATA_SOURCE_ID')
319
320
        new_values = json.loads(raw_json)
321
322
        if 'name' not in new_values['data'].keys() or \
323
                not isinstance(new_values['data']['name'], str) or \
324
                len(str.strip(new_values['data']['name'])) == 0:
325
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
326
                                   description='API.INVALID_DATA_SOURCE_NAME')
327
        name = str.strip(new_values['data']['name'])
328
329
        if 'gateway_id' not in new_values['data'].keys() or \
330
                not isinstance(new_values['data']['gateway_id'], int) or \
331
                new_values['data']['gateway_id'] <= 0:
332
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
333
                                   description='API.INVALID_GATEWAY_ID')
334
        gateway_id = new_values['data']['gateway_id']
335
336
        if 'protocol' not in new_values['data'].keys() or \
337
                not isinstance(new_values['data']['protocol'], str) or \
338
                len(str.strip(new_values['data']['protocol'])) == 0:
339
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
340
                                   description='API.INVALID_DATA_SOURCE_PROTOCOL')
341
        protocol = new_values['data']['protocol']
342
343
        cnx = mysql.connector.connect(**config.myems_system_db)
344
        cursor = cnx.cursor()
345
346
        query = (" SELECT id, name, code "
347
                 " FROM tbl_protocols ")
348
        cursor.execute(query)
349
        rows_protocols = cursor.fetchall()
350
351
        procotol_dict = dict()
352
        if rows_protocols is not None and len(rows_protocols) > 0:
353
            for row in rows_protocols:
354
                procotol_dict[row[2]] = {"id": row[0],
355
                                         "name": row[1],
356
                                         "code": row[2]}
357
358
        if protocol not in procotol_dict.keys():
359
            cursor.close()
360
            cnx.close()
361
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
362
                                   description='API.INVALID_DATA_SOURCE_PROTOCOL')
363
364
        if 'connection' not in new_values['data'].keys() or \
365
                not isinstance(new_values['data']['connection'], str) or \
366
                len(str.strip(new_values['data']['connection'])) == 0:
367
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
368
                                   description='API.INVALID_CONNECTION')
369
        connection = str.strip(new_values['data']['connection'])
370
371
        if 'description' in new_values['data'].keys() and \
372
                new_values['data']['description'] is not None and \
373
                len(str(new_values['data']['description'])) > 0:
374
            description = str.strip(new_values['data']['description'])
375
        else:
376
            description = None
377
378
        cursor.execute(" SELECT name "
379
                       " FROM tbl_data_sources "
380
                       " WHERE id = %s ", (id_,))
381
        if cursor.fetchone() is None:
382
            cursor.close()
383
            cnx.close()
384
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
385
                                   description='API.DATA_SOURCE_NOT_FOUND')
386
387
        cursor.execute(" SELECT name "
388
                       " FROM tbl_gateways "
389
                       " WHERE id = %s ", (gateway_id,))
390
        if cursor.fetchone() is None:
391
            cursor.close()
392
            cnx.close()
393
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
394
                                   description='API.INVALID_GATEWAY_ID')
395
396
        update_row = (" UPDATE tbl_data_sources "
397
                      " SET name = %s, gateway_id = %s, protocol = %s, connection = %s, description = %s "
398
                      " WHERE id = %s ")
399
        cursor.execute(update_row, (name,
400
                                    gateway_id,
401
                                    protocol,
402
                                    connection,
403
                                    description,
404
                                    id_,))
405
        cnx.commit()
406
407
        cursor.close()
408
        cnx.close()
409
410
        resp.status = falcon.HTTP_200
411
412
413
class DataSourcePointCollection:
414
    def __init__(self):
415
        pass
416
417
    @staticmethod
418
    def on_options(req, resp, id_):
419
        _ = req
420
        resp.status = falcon.HTTP_200
421
        _ = id_
422
423
    @staticmethod
424
    def on_get(req, resp, id_):
425
        admin_control(req)
426
        if not id_.isdigit() or int(id_) <= 0:
427
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
428
                                   description='API.INVALID_DATA_SOURCE_ID')
429
430
        cnx_system_db = mysql.connector.connect(**config.myems_system_db)
431
        cursor_system_db = cnx_system_db.cursor()
432
433
        cursor_system_db.execute(" SELECT name "
434
                                 " FROM tbl_data_sources "
435
                                 " WHERE id = %s ", (id_,))
436
        if cursor_system_db.fetchone() is None:
437
            cursor_system_db.close()
438
            cnx_system_db.close()
439
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
440
                                   description='API.DATA_SOURCE_NOT_FOUND')
441
442
        result = list()
443
        # Get points of the data source
444
        # NOTE: there is no uuid in tbl_points
445
        query_point = (" SELECT id, name, object_type, "
446
                       "        units, high_limit, low_limit, higher_limit, lower_limit, ratio, offset_constant, "
447
                       "        is_trend, is_virtual, address, description, faults, definitions "
448
                       " FROM tbl_points "
449
                       " WHERE data_source_id = %s "
450
                       " ORDER BY id ")
451
        cursor_system_db.execute(query_point, (id_,))
452
        rows_point = cursor_system_db.fetchall()
453
454
        cnx_historical_db = mysql.connector.connect(**config.myems_historical_db)
455
        cursor_historical_db = cnx_historical_db.cursor()
456
        cursor_historical_db.execute(" SELECT point_id, actual_value "
457
                                     " FROM tbl_analog_value_latest "
458
                                     " WHERE utc_date_time >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 MINUTE)")
459
        analog_values = cursor_historical_db.fetchall()
460
        analog_value_dict = dict()
461
        for v in analog_values:
462
            analog_value_dict[v[0]] = v[1]
463
464
        cursor_historical_db.execute(" SELECT point_id, actual_value "
465
                                     " FROM tbl_digital_value_latest "
466
                                     " WHERE utc_date_time >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 MINUTE)")
467
        digital_values = cursor_historical_db.fetchall()
468
        digital_value_dict = dict()
469
        for v in digital_values:
470
            digital_value_dict[v[0]] = v[1]
471
472
        cursor_historical_db.execute(" SELECT point_id, actual_value "
473
                                     " FROM tbl_energy_value_latest "
474
                                     " WHERE utc_date_time >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 MINUTE)")
475
        energy_values = cursor_historical_db.fetchall()
476
        energy_value_dict = dict()
477
        for v in energy_values:
478
            energy_value_dict[v[0]] = v[1]
479
480
        cursor_historical_db.execute(" SELECT point_id, actual_value "
481
                                     " FROM tbl_text_value_latest "
482
                                     " WHERE utc_date_time >= DATE_SUB(UTC_TIMESTAMP(), INTERVAL 30 MINUTE)")
483
        text_values = cursor_historical_db.fetchall()
484
        text_value_dict = dict()
485
        for v in text_values:
486
            text_value_dict[v[0]] = v[1]
487
488
        if rows_point is not None and len(rows_point) > 0:
489
            for row in rows_point:
490
                if row[2] == 'ANALOG_VALUE':
491
                    latest_value = analog_value_dict.get(row[0], None)
492
                    latest_value = Decimal(latest_value) if latest_value is not None else None
493
                elif row[2] == 'DIGITAL_VALUE':
494
                    latest_value = digital_value_dict.get(row[0], None)
495
                    latest_value = int(latest_value) if latest_value is not None else None
496
                elif row[2] == 'ENERGY_VALUE':
497
                    latest_value = energy_value_dict.get(row[0], None)
498
                    latest_value = Decimal(latest_value) if latest_value is not None else None
499
                elif row[2] == 'TEXT_VALUE':
500
                    latest_value = text_value_dict.get(row[0], None)
501
                    latest_value = str(latest_value) if latest_value is not None else None
502
503
                meta_result = {"id": row[0],
504
                               "name": row[1],
505
                               "object_type": row[2],
506
                               "units": row[3],
507
                               "high_limit": row[4],
508
                               "low_limit": row[5],
509
                               "higher_limit": row[6],
510
                               "lower_limit": row[7],
511
                               "ratio": Decimal(row[8]),
512
                               "offset_constant": Decimal(row[9]),
513
                               "is_trend": bool(row[10]),
514
                               "is_virtual": bool(row[11]),
515
                               "address": row[12],
516
                               "description": row[13],
517
                               "faults": row[14],
518
                               "definitions": row[15],
519
                               "latest_value": latest_value,
0 ignored issues
show
introduced by
The variable latest_value does not seem to be defined for all execution paths.
Loading history...
520
                               }
521
522
                result.append(meta_result)
523
524
        cursor_system_db.close()
525
        cnx_system_db.close()
526
        cursor_historical_db.close()
527
        cnx_historical_db.close()
528
        resp.text = json.dumps(result)
529
530
531
class DataSourceExport:
532
    def __init__(self):
533
        pass
534
535
    @staticmethod
536
    def on_options(req, resp, id_):
537
        _ = req
538
        resp.status = falcon.HTTP_200
539
        _ = id_
540
541
    @staticmethod
542
    def on_get(req, resp, id_):
543
        admin_control(req)
544
        if not id_.isdigit() or int(id_) <= 0:
545
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
546
                                   description='API.INVALID_DATA_SOURCE_ID')
547
548
        cnx = mysql.connector.connect(**config.myems_system_db)
549
        cursor = cnx.cursor()
550
551
        query = (" SELECT id, name, uuid "
552
                 " FROM tbl_gateways ")
553
        cursor.execute(query)
554
        rows_gateways = cursor.fetchall()
555
        gateway_dict = dict()
556
        if rows_gateways is not None and len(rows_gateways) > 0:
557
            for row in rows_gateways:
558
                gateway_dict[row[0]] = {"id": row[0],
559
                                        "name": row[1],
560
                                        "uuid": row[2]}
561
562
        query = (" SELECT id, name, uuid, gateway_id, protocol, connection, last_seen_datetime_utc, description "
563
                 " FROM tbl_data_sources "
564
                 " WHERE id = %s ")
565
        cursor.execute(query, (id_,))
566
        row = cursor.fetchone()
567
        if row is None:
568
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
569
                                   description='API.DATA_SOURCE_NOT_FOUND')
570
571
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
572
        if config.utc_offset[0] == '-':
573
            timezone_offset = -timezone_offset
574
575
        if isinstance(row[6], datetime):
576
            last_seen_datetime_local = row[6].replace(tzinfo=timezone.utc) + \
577
                                       timedelta(minutes=timezone_offset)
578
            last_seen_datetime = last_seen_datetime_local.isoformat()[0:19]
579
        else:
580
            last_seen_datetime = None
581
582
        result = {"name": row[1],
583
                  "uuid": row[2],
584
                  "gateway": gateway_dict.get(row[3]),
585
                  "protocol": row[4],
586
                  "connection": row[5],
587
                  "last_seen_datetime": last_seen_datetime,
588
                  "description": row[7],
589
                  "points": None
590
                  }
591
        point_result = list()
592
        # Get points of the data source
593
        # NOTE: there is no uuid in tbl_points
594
        query_point = (" SELECT id, name, object_type, "
595
                       "        units, high_limit, low_limit, higher_limit, lower_limit, ratio, "
596
                       "        is_trend, is_virtual, address, description, faults, definitions "
597
                       " FROM tbl_points "
598
                       " WHERE data_source_id = %s "
599
                       " ORDER BY id ")
600
        cursor.execute(query_point, (id_,))
601
        rows_point = cursor.fetchall()
602
603 View Code Duplication
        if rows_point is not None and len(rows_point) > 0:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
604
            for row in rows_point:
605
                meta_result = {"id": row[0],
606
                               "name": row[1],
607
                               "object_type": row[2],
608
                               "units": row[3],
609
                               "high_limit": row[4],
610
                               "low_limit": row[5],
611
                               "higher_limit": row[6],
612
                               "lower_limit": row[7],
613
                               "ratio": Decimal(row[8]),
614
                               "is_trend": bool(row[9]),
615
                               "is_virtual": bool(row[10]),
616
                               "address": row[11],
617
                               "description": row[12],
618
                               "faults": row[13],
619
                               "definitions": row[14]}
620
                point_result.append(meta_result)
621
            result['points'] = point_result
622
        cursor.close()
623
        cnx.close()
624
625
        resp.text = json.dumps(result)
626
627
628
class DataSourceImport:
629
    def __init__(self):
630
        pass
631
632
    @staticmethod
633
    def on_options(req, resp):
634
        _ = req
635
        resp.status = falcon.HTTP_200
636
637
    @staticmethod
638
    @user_logger
639
    def on_post(req, resp):
640
        """Handles POST requests"""
641
        admin_control(req)
642
        try:
643
            raw_json = req.stream.read().decode('utf-8')
644
        except Exception as ex:
645
            print(str(ex))
646
            raise falcon.HTTPError(status=falcon.HTTP_400,
647
                                   title='API.BAD_REQUEST',
648
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
649
650
        new_values = json.loads(raw_json)
651
652
        if 'name' not in new_values.keys() or \
653
                not isinstance(new_values['name'], str) or \
654
                len(str.strip(new_values['name'])) == 0:
655
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
656
                                   description='API.INVALID_DATA_SOURCE_NAME')
657
        name = str.strip(new_values['name'])
658
659
        if 'gateway' not in new_values.keys() or \
660
                'id' not in new_values['gateway'].keys() or \
661
                not isinstance(new_values['gateway']['id'], int) or \
662
                new_values['gateway']['id'] <= 0:
663
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
664
                                   description='API.INVALID_GATEWAY_ID')
665
        gateway_id = new_values['gateway']['id']
666
667
        cnx = mysql.connector.connect(**config.myems_system_db)
668
        cursor = cnx.cursor()
669
670
        query = (" SELECT id, name, code "
671
                 " FROM tbl_protocols ")
672
        cursor.execute(query)
673
        rows_protocols = cursor.fetchall()
674
        procotol_dict = dict()
675
        if rows_protocols is not None and len(rows_protocols) > 0:
676
            for row in rows_protocols:
677
                procotol_dict[row[2]] = {"id": row[0],
678
                                         "name": row[1],
679
                                         "code": row[2]}
680
681
        if 'protocol' not in new_values.keys() \
682
                or new_values['protocol'] not in procotol_dict.keys():
683
            cursor.close()
684
            cnx.close()
685
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
686
                                   description='API.INVALID_DATA_SOURCE_PROTOCOL')
687
        protocol = new_values['protocol']
688
689
        if 'connection' not in new_values.keys() or \
690
                not isinstance(new_values['connection'], str) or \
691
                len(str.strip(new_values['connection'])) == 0:
692
            cursor.close()
693
            cnx.close()
694
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
695
                                   description='API.INVALID_CONNECTION')
696
        connection = str.strip(new_values['connection'])
697
698
        if 'description' in new_values.keys() and \
699
                new_values['description'] is not None and \
700
                len(str(new_values['description'])) > 0:
701
            description = str.strip(new_values['description'])
702
        else:
703
            description = None
704
705
        cursor.execute(" SELECT name "
706
                       " FROM tbl_data_sources "
707
                       " WHERE name = %s ", (name,))
708
        if cursor.fetchone() is not None:
709
            cursor.close()
710
            cnx.close()
711
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
712
                                   description='API.DATA_SOURCE_NAME_IS_ALREADY_IN_USE')
713
714
        cursor.execute(" SELECT name "
715
                       " FROM tbl_gateways "
716
                       " WHERE id = %s ", (gateway_id,))
717
        if cursor.fetchone() is None:
718
            cursor.close()
719
            cnx.close()
720
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
721
                                   description='API.INVALID_GATEWAY_ID')
722
723
        add_values = (" INSERT INTO tbl_data_sources (name, uuid, gateway_id, protocol, connection, description) "
724
                      " VALUES (%s, %s, %s, %s, %s, %s) ")
725
        cursor.execute(add_values, (name,
726
                                    str(uuid.uuid4()),
727
                                    gateway_id,
728
                                    protocol,
729
                                    connection,
730
                                    description))
731
        new_id = cursor.lastrowid
732 View Code Duplication
        if new_values['points'] is not None and len(new_values['points']) > 0:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
733
            for point in new_values['points']:
734
                # todo: validate point properties
735
                add_value = (" INSERT INTO tbl_points (name, data_source_id, object_type, units, "
736
                             "                         high_limit, low_limit, higher_limit, lower_limit, ratio, "
737
                             "                         is_trend, is_virtual, address, description, faults, "
738
                             "                         definitions) "
739
                             " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
740
                cursor.execute(add_value, (point['name'],
741
                                           new_id,
742
                                           point['object_type'],
743
                                           point['units'],
744
                                           point['high_limit'],
745
                                           point['low_limit'],
746
                                           point['higher_limit'],
747
                                           point['lower_limit'],
748
                                           point['ratio'],
749
                                           point['is_trend'],
750
                                           point['is_virtual'],
751
                                           point['address'],
752
                                           point['description'],
753
                                           point['faults'],
754
                                           point['definitions']))
755
        cnx.commit()
756
        cursor.close()
757
        cnx.close()
758
759
        resp.status = falcon.HTTP_201
760
        resp.location = '/datasources/' + str(new_id)
761
762
763
class DataSourceClone:
764
    def __init__(self):
765
        pass
766
767
    @staticmethod
768
    def on_options(req, resp, id_):
769
        _ = req
770
        resp.status = falcon.HTTP_200
771
        _ = id_
772
773
    @staticmethod
774
    @user_logger
775
    def on_post(req, resp, id_):
776
        """Handles POST requests"""
777
        admin_control(req)
778
        if not id_.isdigit() or int(id_) <= 0:
779
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
780
                                   description='API.INVALID_DATA_SOURCE_ID')
781
782
        cnx = mysql.connector.connect(**config.myems_system_db)
783
        cursor = cnx.cursor()
784
785
        query = (" SELECT id, name, uuid, gateway_id, protocol, connection, description "
786
                 " FROM tbl_data_sources "
787
                 " WHERE id = %s ")
788
        cursor.execute(query, (id_,))
789
        row = cursor.fetchone()
790
        if row is None:
791
            cursor.close()
792
            cnx.close()
793
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
794
                                   description='API.DATA_SOURCE_NOT_FOUND')
795
796
        meta_result = {"id": row[0],
797
                       "name": row[1],
798
                       "uuid": row[2],
799
                       "gateway_id": row[3],
800
                       "protocol": row[4],
801
                       "connection": row[5],
802
                       "description": row[6],
803
                       "points": None
804
                       }
805
        point_result = list()
806
        # Get points of the data source
807
        # NOTE: there is no uuid in tbl_points
808
        query_point = (" SELECT id, name, object_type, "
809
                       "        units, high_limit, low_limit, higher_limit, lower_limit, ratio, "
810
                       "        is_trend, is_virtual, address, description, faults, definitions "
811
                       " FROM tbl_points "
812
                       " WHERE data_source_id = %s "
813
                       " ORDER BY id ")
814
        cursor.execute(query_point, (id_,))
815
        rows_point = cursor.fetchall()
816
817 View Code Duplication
        if rows_point is not None and len(rows_point) > 0:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
818
            for row in rows_point:
819
                result = {"id": row[0],
820
                          "name": row[1],
821
                          "object_type": row[2],
822
                          "units": row[3],
823
                          "high_limit": row[4],
824
                          "low_limit": row[5],
825
                          "higher_limit": row[6],
826
                          "lower_limit": row[7],
827
                          "ratio": Decimal(row[8]),
828
                          "is_trend": bool(row[9]),
829
                          "is_virtual": bool(row[10]),
830
                          "address": row[11],
831
                          "description": row[12],
832
                          "faults": row[13],
833
                          "definitions": row[14]}
834
                point_result.append(result)
835
            meta_result['points'] = point_result
836
837
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
838
        if config.utc_offset[0] == '-':
839
            timezone_offset = -timezone_offset
840
        new_name = str.strip(meta_result['name']) + \
841
            (datetime.utcnow() + timedelta(minutes=timezone_offset)).isoformat(sep='-', timespec='seconds')
842
843
        add_values = (" INSERT INTO tbl_data_sources (name, uuid, gateway_id, protocol, connection, description) "
844
                      " VALUES (%s, %s, %s, %s, %s, %s) ")
845
        cursor.execute(add_values, (new_name,
846
                                    str(uuid.uuid4()),
847
                                    meta_result['gateway_id'],
848
                                    meta_result['protocol'],
849
                                    meta_result['connection'],
850
                                    meta_result['description']))
851
        new_id = cursor.lastrowid
852 View Code Duplication
        if meta_result['points'] is not None:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
853
            for point in meta_result['points']:
854
                add_value = (" INSERT INTO tbl_points (name, data_source_id, object_type, units, "
855
                             "                         high_limit, low_limit, higher_limit, lower_limit, ratio, "
856
                             "                         is_trend, is_virtual, address, description, faults, "
857
                             "                         definitions) "
858
                             " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
859
                cursor.execute(add_value, (point['name'],
860
                                           new_id,
861
                                           point['object_type'],
862
                                           point['units'],
863
                                           point['high_limit'],
864
                                           point['low_limit'],
865
                                           point['higher_limit'],
866
                                           point['lower_limit'],
867
                                           point['ratio'],
868
                                           point['is_trend'],
869
                                           point['is_virtual'],
870
                                           point['address'],
871
                                           point['description'],
872
                                           point['faults'],
873
                                           point['definitions']))
874
        cnx.commit()
875
        cursor.close()
876
        cnx.close()
877
878
        resp.status = falcon.HTTP_201
879
        resp.location = '/datasources/' + str(new_id)
880