Passed
Push — master ( e9b166...efda51 )
by
unknown
09:12
created

core.point.PointItem.on_delete()   B

Complexity

Conditions 8

Size

Total Lines 47
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 38
dl 0
loc 47
rs 7.1013
c 0
b 0
f 0
cc 8
nop 3
1
import falcon
2
from datetime import datetime, timedelta
3
import mysql.connector
4
import simplejson as json
5
from core.useractivity import user_logger, admin_control
6
import config
7
from decimal import Decimal
8
9
10
PV_GRID_POINT_COLUMNS = [
11
    "power_point_id",
12
    "total_active_power_point_id",
13
    "active_power_a_point_id",
14
    "active_power_b_point_id",
15
    "active_power_c_point_id",
16
    "total_reactive_power_point_id",
17
    "reactive_power_a_point_id",
18
    "reactive_power_b_point_id",
19
    "reactive_power_c_point_id",
20
    "total_apparent_power_point_id",
21
    "apparent_power_a_point_id",
22
    "apparent_power_b_point_id",
23
    "apparent_power_c_point_id",
24
    "total_power_factor_point_id",
25
    "active_energy_import_point_id",
26
    "active_energy_export_point_id",
27
    "active_energy_net_point_id",
28
]
29
30
PV_LOAD_POINT_COLUMNS = list(PV_GRID_POINT_COLUMNS)
31
32
_PV_INVERTOR_BASE_COLUMNS = [
33
    "invertor_state_point_id",
34
    "communication_state_point_id",
35
    "total_energy_point_id",
36
    "today_energy_point_id",
37
    "efficiency_point_id",
38
    "temperature_point_id",
39
    "power_factor_point_id",
40
    "active_power_point_id",
41
    "reactive_power_point_id",
42
    "frequency_point_id",
43
    "uab_point_id",
44
    "ubc_point_id",
45
    "uca_point_id",
46
    "ua_point_id",
47
    "ub_point_id",
48
    "uc_point_id",
49
    "ia_point_id",
50
    "ib_point_id",
51
    "ic_point_id",
52
]
53
_PV_INVERTOR_PV_COLUMNS = [f"pv{i}_{suffix}_point_id" for i in range(1, 29) for suffix in ("u", "i")]
54
_PV_INVERTOR_MPPT_COLUMNS = ["mppt_total_energy_point_id", "mppt_power_point_id"] + [f"mppt_{i}_energy_point_id" for i in range(1, 11)]
55
PV_INVERTOR_POINT_COLUMNS = (
56
    _PV_INVERTOR_BASE_COLUMNS
57
    + _PV_INVERTOR_PV_COLUMNS
58
    + _PV_INVERTOR_MPPT_COLUMNS
59
    + ["startup_time_point_id", "shutdown_time_point_id"]
60
)
61
62
POINT_RELATION_CHECKS = [
63
    ("tbl_microgrids_grids_points", ("point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_GRIDS_POINTS"),
64
    ("tbl_microgrids_heatpumps_points", ("point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_HEATPUMPS_POINTS"),
65
    ("tbl_microgrids_loads_points", ("point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_LOADS_POINTS"),
66
    ("tbl_microgrids_pvs_points", ("point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_PVS_POINTS"),
67
    ("tbl_photovoltaic_power_stations", ("latitude_point_id", "longitude_point_id"), "API.THERE_IS_RELATION_WITH_PHOTOVOLTAIC_POWER_STATIONS"),
68
    ("tbl_photovoltaic_power_stations_grids", tuple(PV_GRID_POINT_COLUMNS), "API.THERE_IS_RELATION_WITH_PHOTOVOLTAIC_POWER_STATIONS_GRIDS"),
69
    ("tbl_photovoltaic_power_stations_grids_points", ("point_id",), "API.THERE_IS_RELATION_WITH_PHOTOVOLTAIC_POWER_STATIONS_GRIDS"),
70
    ("tbl_photovoltaic_power_stations_invertors", tuple(PV_INVERTOR_POINT_COLUMNS), "API.THERE_IS_RELATION_WITH_PHOTOVOLTAIC_POWER_STATIONS_INVERTORS"),
71
    ("tbl_photovoltaic_power_stations_invertors_points", ("point_id",), "API.THERE_IS_RELATION_WITH_PHOTOVOLTAIC_POWER_STATIONS_INVERTORS"),
72
    ("tbl_photovoltaic_power_stations_loads", tuple(PV_LOAD_POINT_COLUMNS), "API.THERE_IS_RELATION_WITH_PHOTOVOLTAIC_POWER_STATIONS_LOADS"),
73
    ("tbl_photovoltaic_power_stations_loads_points", ("point_id",), "API.THERE_IS_RELATION_WITH_PHOTOVOLTAIC_POWER_STATIONS_LOADS"),
74
    ("tbl_points_set_values", ("point_id",), "API.THERE_IS_RELATION_WITH_POINTS_SET_VALUES"),
75
    ("tbl_power_integrators", ("power_point_id", "result_point_id"), "API.THERE_IS_RELATION_WITH_POWER_INTEGRATORS"),
76
    ("tbl_virtual_power_plants_microgrids", ("point_id", "virtual_power_plant_id", "microgrid_id"), "API.THERE_IS_RELATION_WITH_VIRTUAL_POWER_PLANTS_MICROGRIDS"),
77
    ("tbl_wind_farms", ("latitude_point_id", "longitude_point_id"), "API.THERE_IS_RELATION_WITH_WIND_FARMS"),
78
    ("tbl_meters_points", ("point_id",), "API.THERE_IS_RELATION_WITH_METERS"),
79
    ("tbl_sensors_points", ("point_id",), "API.THERE_IS_RELATION_WITH_SENSORS"),
80
    ("tbl_shopfloors_points", ("point_id",), "API.THERE_IS_RELATION_WITH_SHOPFLOORS"),
81
    ("tbl_stores_points", ("point_id",), "API.THERE_IS_RELATION_WITH_STORES"),
82
    ("tbl_spaces_points", ("point_id",), "API.THERE_IS_RELATION_WITH_SPACES"),
83
    ("tbl_tenants_points", ("point_id",), "API.THERE_IS_RELATION_WITH_TENANTS"),
84
    ("tbl_equipments_parameters", ("point_id",), "API.THERE_IS_RELATION_WITH_EQUIPMENT_PARAMETERS"),
85
    ("tbl_combined_equipments_parameters", ("point_id",), "API.THERE_IS_RELATION_WITH_COMBINED_EQUIPMENT_PARAMETERS"),
86
    ("tbl_distribution_circuits_points", ("point_id",), "API.THERE_IS_RELATION_WITH_DISTRIBUTION_CIRCUITS_POINTS"),
87
    ("tbl_heat_integrators", ("high_temperature_point_id", "low_temperature_point_id", "flow_point_id", "result_point_id"), "API.THERE_IS_RELATION_WITH_HEAT_INTEGRATORS"),
88
    ("tbl_microgrids_batteries", ("battery_state_point_id", "soc_point_id", "power_point_id"), "API.THERE_IS_RELATION_WITH_MICROGRIDS_BATTERIES"),
89
    ("tbl_microgrids_power_conversion_systems", ("run_state_point_id", "today_charge_energy_point_id", "today_discharge_energy_point_id", "total_charge_energy_point_id", "total_discharge_energy_point_id"), "API.THERE_IS_RELATION_WITH_MICROGRIDS_POWER_CONVERSION_SYSTEMS"),
90
    ("tbl_microgrids_evchargers", ("power_point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_EVCHARGERS"),
91
    ("tbl_microgrids_evchargers_points", ("point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_EVCHARGERS_POINTS"),
92
    ("tbl_microgrids_generators", ("power_point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_GENERATORS"),
93
    ("tbl_microgrids_generators_points", ("point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_GENERATORS_POINTS"),
94
    ("tbl_microgrids_grids", ("power_point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_GRIDS"),
95
    ("tbl_microgrids_heatpumps", ("power_point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_HEATPUMPS"),
96
    ("tbl_microgrids_loads", ("power_point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_LOADS"),
97
    ("tbl_microgrids_photovoltaics", ("power_point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_PHOTOVOLTAICS"),
98
    ("tbl_microgrids", ("latitude_point_id", "longitude_point_id"), "API.THERE_IS_RELATION_WITH_MICROGRIDS"),
99
    ("tbl_microgrids_bmses_points", ("point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_BMSES_POINTS"),
100
    ("tbl_microgrids_pcses_points", ("point_id",), "API.THERE_IS_RELATION_WITH_MICROGRIDS_PCSES_POINTS"),
101
    ("tbl_virtual_power_plants", ("balancing_price_point_id",), "API.THERE_IS_RELATION_WITH_VIRTUAL_POWER_PLANTS"),
102
    ("tbl_energy_storage_containers_batteries", ("battery_state_point_id", "soc_point_id", "power_point_id"), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_BATTERIES"),
103
    ("tbl_energy_storage_containers_grids", ("power_point_id",), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_GRIDS"),
104
    ("tbl_energy_storage_containers_grids_points", ("point_id",), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_GRIDS_POINTS"),
105
    ("tbl_energy_storage_containers_bmses_points", ("point_id",), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_BMSES_POINTS"),
106
    ("tbl_energy_storage_containers_dcdcs_points", ("point_id",), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_DCDCS"),
107
    ("tbl_energy_storage_containers_firecontrols_points", ("point_id",), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_FIRECONTROLS"),
108
    ("tbl_energy_storage_containers_hvacs_points", ("point_id",), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_HVACS"),
109
    ("tbl_energy_storage_containers_loads", ("power_point_id",), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_LOADS"),
110
    ("tbl_energy_storage_containers_loads_points", ("point_id",), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_LOADS"),
111
    ("tbl_energy_storage_containers_power_conversion_systems", ("run_state_point_id",), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_POWER_CONVERSION_SYSTEMS"),
112
    ("tbl_energy_storage_containers_pcses_points", ("point_id",), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_POWER_CONVERSION_SYSTEMS"),
113
    ("tbl_energy_storage_containers_stses_points", ("point_id",), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_CONTAINERS_STSES"),
114
    ("tbl_energy_storage_power_stations", ("latitude_point_id", "longitude_point_id"), "API.THERE_IS_RELATION_WITH_ENERGY_STORAGE_POWER_STATIONS"),
115
    ("tbl_fuel_integrators", ("power_point_id", "result_point_id"), "API.THERE_IS_RELATION_WITH_FUEL_INTEGRATORS"),
116
    ("tbl_meters_points", ("point_id",), "API.THERE_IS_RELATION_WITH_METERS_POINTS"),
117
    ("tbl_sensors_points", ("point_id",), "API.THERE_IS_RELATION_WITH_SENSORS_POINTS"),
118
    ("tbl_shopfloors_points", ("point_id",), "API.THERE_IS_RELATION_WITH_SHOPFLOORS_POINTS"),
119
    ("tbl_spaces_points", ("point_id",), "API.THERE_IS_RELATION_WITH_SPACES_POINTS"),
120
    ("tbl_stores_points", ("point_id",), "API.THERE_IS_RELATION_WITH_STORES_POINTS"),
121
    ("tbl_tenants_points", ("point_id",), "API.THERE_IS_RELATION_WITH_TENANTS_POINTS"),
122
]
123
124
125
class PointCollection:
126
    """
127
    Point Collection Resource
128
129
    This class handles CRUD operations for point collection.
130
    It provides endpoints for listing all points and creating new points.
131
    Points represent data collection points in the energy management system.
132
    """
133
    def __init__(self):
134
        """Initialize PointCollection"""
135
        pass
136
137
    @staticmethod
138
    def on_options(req, resp):
139
        """Handle OPTIONS requests for CORS preflight"""
140
        _ = req
141
        resp.status = falcon.HTTP_200
142
143
    @staticmethod
144
    def on_get(req, resp):
145
        """
146
        Handle GET requests to retrieve all points
147
148
        Returns a list of all points with their metadata including:
149
        - Point ID, name, and UUID
150
        - Data source information
151
        - Object type and object ID
152
        - Description and other attributes
153
154
        Args:
155
            req: Falcon request object
156
            resp: Falcon response object
157
        """
158
        admin_control(req)
159
        cnx = mysql.connector.connect(**config.myems_system_db)
160
        cursor = cnx.cursor()
161
162
        query = (" SELECT id, name, uuid "
163
                 " FROM tbl_data_sources ")
164
        cursor.execute(query)
165
        rows_data_sources = cursor.fetchall()
166
167
        data_source_dict = dict()
168
        if rows_data_sources is not None and len(rows_data_sources) > 0:
169
            for row in rows_data_sources:
170
                data_source_dict[row[0]] = {"id": row[0],
171
                                            "name": row[1],
172
                                            "uuid": row[2]}
173
174
        query = (" SELECT id, name, data_source_id, object_type, units, "
175
                 "        high_limit, low_limit, higher_limit, lower_limit, ratio, offset_constant, "
176
                 "        is_trend, is_virtual, address, description, faults, definitions "
177
                 " FROM tbl_points ")
178
        cursor.execute(query)
179
        rows = cursor.fetchall()
180
        cursor.close()
181
        cnx.close()
182
183
        result = list()
184
        if rows is not None and len(rows) > 0:
185
            for row in rows:
186
                meta_result = {"id": row[0],
187
                               "name": row[1],
188
                               "data_source": data_source_dict.get(row[2], None),
189
                               "object_type": row[3],
190
                               "units": row[4],
191
                               "high_limit": row[5],
192
                               "low_limit": row[6],
193
                               "higher_limit": row[7],
194
                               "lower_limit": row[8],
195
                               "ratio": Decimal(row[9]),
196
                               "offset_constant": Decimal(row[10]),
197
                               "is_trend": bool(row[11]),
198
                               "is_virtual": bool(row[12]),
199
                               "address": row[13],
200
                               "description": row[14],
201
                               "faults": row[15],
202
                               "definitions": row[16]}
203
                result.append(meta_result)
204
205
        resp.text = json.dumps(result)
206
207
    @staticmethod
208
    @user_logger
209
    def on_post(req, resp):
210
        """Handles POST requests"""
211
        admin_control(req)
212
        try:
213
            raw_json = req.stream.read().decode('utf-8')
214
        except Exception as ex:
215
            print(str(ex))
216
            raise falcon.HTTPError(status=falcon.HTTP_400,
217
                                   title='API.BAD_REQUEST',
218
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
219
220
        new_values = json.loads(raw_json)
221
222
        if 'name' not in new_values['data'].keys() or \
223
                not isinstance(new_values['data']['name'], str) or \
224
                len(str.strip(new_values['data']['name'])) == 0:
225
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
226
                                   description='API.INVALID_POINT_NAME')
227
        name = str.strip(new_values['data']['name'])
228
229
        if 'data_source_id' not in new_values['data'].keys() or \
230
                not isinstance(new_values['data']['data_source_id'], int) or \
231
                new_values['data']['data_source_id'] <= 0:
232
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
233
                                   description='API.INVALID_DATA_SOURCE_ID')
234
        data_source_id = new_values['data']['data_source_id']
235
236
        if 'object_type' not in new_values['data'].keys() \
237
                or str.strip(new_values['data']['object_type']) not in \
238
                ('ENERGY_VALUE', 'ANALOG_VALUE', 'DIGITAL_VALUE', 'TEXT_VALUE'):
239
            raise falcon.HTTPError(status=falcon.HTTP_400,
240
                                   title='API.BAD_REQUEST',
241
                                   description='API.INVALID_OBJECT_TYPE')
242
        object_type = str.strip(new_values['data']['object_type'])
243
244
        if 'units' not in new_values['data'].keys() or \
245
                not isinstance(new_values['data']['units'], str) or \
246
                len(str.strip(new_values['data']['units'])) == 0:
247
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
248
                                   description='API.INVALID_UNITS')
249
        units = str.strip(new_values['data']['units'])
250
251
        if 'high_limit' not in new_values['data'].keys() or \
252
                not (isinstance(new_values['data']['high_limit'], float) or
253
                     isinstance(new_values['data']['high_limit'], int)):
254
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
255
                                   description='API.INVALID_HIGH_LIMIT_VALUE')
256
        high_limit = new_values['data']['high_limit']
257
258
        if 'low_limit' not in new_values['data'].keys() or \
259
                not (isinstance(new_values['data']['low_limit'], float) or
260
                     isinstance(new_values['data']['low_limit'], int)):
261
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
262
                                   description='API.INVALID_LOW_LIMIT_VALUE')
263
        low_limit = new_values['data']['low_limit']
264
265
        # the higher_limit is optional
266
        if 'higher_limit' not in new_values['data'].keys() or \
267
                new_values['data']['higher_limit'] is None:
268
            higher_limit = None
269
        elif not (isinstance(new_values['data']['higher_limit'], float) or
270
                  isinstance(new_values['data']['higher_limit'], int)):
271
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
272
                                   description='API.INVALID_HIGHER_LIMIT_VALUE')
273
        else:
274
            higher_limit = new_values['data']['higher_limit']
275
276
        # the lower_limit is optional
277
        if 'lower_limit' not in new_values['data'].keys() or \
278
                new_values['data']['lower_limit'] is None:
279
            lower_limit = None
280
        elif not (isinstance(new_values['data']['lower_limit'], float) or
281
                  isinstance(new_values['data']['lower_limit'], int)):
282
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
283
                                   description='API.INVALID_LOWER_LIMIT_VALUE')
284
        else:
285
            lower_limit = new_values['data']['lower_limit']
286
287
        if 'ratio' not in new_values['data'].keys() or \
288
                not (isinstance(new_values['data']['ratio'], float) or
289
                     isinstance(new_values['data']['ratio'], int)):
290
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
291
                                   description='API.INVALID_RATIO_VALUE')
292
        ratio = new_values['data']['ratio']
293
294
        if 'offset_constant' not in new_values['data'].keys() or \
295
                not (isinstance(new_values['data']['offset_constant'], float) or
296
                     isinstance(new_values['data']['offset_constant'], int)):
297
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
298
                                   description='API.INVALID_OFFSET_CONSTANT_VALUE')
299
        offset_constant = new_values['data']['offset_constant']
300
301
        if 'is_trend' not in new_values['data'].keys() or \
302
                not isinstance(new_values['data']['is_trend'], bool):
303
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
304
                                   description='API.INVALID_IS_TREND_VALUE')
305
        is_trend = new_values['data']['is_trend']
306
307
        if 'is_virtual' not in new_values['data'].keys() or \
308
                not isinstance(new_values['data']['is_virtual'], bool):
309
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
310
                                   description='API.INVALID_IS_VIRTUAL_VALUE')
311
        if new_values['data']['is_virtual'] is True and object_type == 'DIGITAL_VALUE':
312
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
313
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_DIGITAL_VALUE')
314
        if new_values['data']['is_virtual'] is True and object_type == 'TEXT_VALUE':
315
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
316
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_TEXT_VALUE')
317
        is_virtual = new_values['data']['is_virtual']
318
319
        if 'address' not in new_values['data'].keys() or \
320
                not isinstance(new_values['data']['address'], str) or \
321
                len(str.strip(new_values['data']['address'])) == 0:
322
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
323
                                   description='API.INVALID_ADDRESS')
324
        address = str.strip(new_values['data']['address'])
325
326
        if 'description' in new_values['data'].keys() and \
327
                new_values['data']['description'] is not None and \
328
                len(str(new_values['data']['description'])) > 0:
329
            description = str.strip(new_values['data']['description'])
330
        else:
331
            description = None
332
333
        if 'faults' in new_values['data'].keys() and \
334
                new_values['data']['faults'] is not None and \
335
                len(str(new_values['data']['faults'])) > 0:
336
            faults = str.strip(new_values['data']['faults'])
337
        else:
338
            faults = None
339
340
        if 'definitions' in new_values['data'].keys() and \
341
                new_values['data']['definitions'] is not None and \
342
                len(str(new_values['data']['definitions'])) > 0:
343
            definitions = str.strip(new_values['data']['definitions'])
344
        else:
345
            definitions = None
346
347
        cnx = mysql.connector.connect(**config.myems_system_db)
348
        cursor = cnx.cursor()
349
350
        cursor.execute(" SELECT name "
351
                       " FROM tbl_points "
352
                       " WHERE name = %s AND data_source_id = %s ", (name, data_source_id))
353
        if cursor.fetchone() is not None:
354
            cursor.close()
355
            cnx.close()
356
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
357
                                   description='API.POINT_NAME_IS_ALREADY_IN_USE')
358
359
        cursor.execute(" SELECT name "
360
                       " FROM tbl_data_sources "
361
                       " WHERE id = %s ", (data_source_id,))
362
        if cursor.fetchone() is None:
363
            cursor.close()
364
            cnx.close()
365
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
366
                                   description='API.INVALID_DATA_SOURCE_ID')
367
368
        add_value = (" INSERT INTO tbl_points (name, data_source_id, object_type, units, "
369
                     "                         high_limit, low_limit, higher_limit, lower_limit, ratio, "
370
                     "                         offset_constant, is_trend, is_virtual, address, description, faults, "
371
                     "                         definitions) "
372
                     " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
373
        cursor.execute(add_value, (name,
374
                                   data_source_id,
375
                                   object_type,
376
                                   units,
377
                                   high_limit,
378
                                   low_limit,
379
                                   higher_limit,
380
                                   lower_limit,
381
                                   ratio,
382
                                   offset_constant,
383
                                   is_trend,
384
                                   is_virtual,
385
                                   address,
386
                                   description,
387
                                   faults,
388
                                   definitions))
389
        new_id = cursor.lastrowid
390
        cnx.commit()
391
        cursor.close()
392
        cnx.close()
393
394
        resp.status = falcon.HTTP_201
395
        resp.location = '/points/' + str(new_id)
396
397
398
class PointItem:
399
    def __init__(self):
400
        pass
401
402
    @staticmethod
403
    def on_options(req, resp, id_):
404
        _ = req
405
        resp.status = falcon.HTTP_200
406
        _ = id_
407
408 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
409
    def on_get(req, resp, id_):
410
        """Handles GET requests"""
411
        admin_control(req)
412
        if not id_.isdigit() or int(id_) <= 0:
413
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
414
                                   description='API.INVALID_POINT_ID')
415
416
        cnx = mysql.connector.connect(**config.myems_system_db)
417
        cursor = cnx.cursor()
418
419
        query = (" SELECT id, name, uuid "
420
                 " FROM tbl_data_sources ")
421
        cursor.execute(query)
422
        rows_data_sources = cursor.fetchall()
423
424
        data_source_dict = dict()
425
        if rows_data_sources is not None and len(rows_data_sources) > 0:
426
            for row in rows_data_sources:
427
                data_source_dict[row[0]] = {"id": row[0],
428
                                            "name": row[1],
429
                                            "uuid": row[2]}
430
431
        query = (" SELECT id, name, data_source_id, object_type, units, "
432
                 "        high_limit, low_limit, higher_limit, lower_limit, ratio, offset_constant, "
433
                 "        is_trend, is_virtual, address, description, faults, definitions "
434
                 " FROM tbl_points "
435
                 " WHERE id = %s ")
436
        cursor.execute(query, (id_,))
437
        row = cursor.fetchone()
438
        cursor.close()
439
        cnx.close()
440
        if row is None:
441
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
442
                                   description='API.POINT_NOT_FOUND')
443
444
        result = {"id": row[0],
445
                  "name": row[1],
446
                  "data_source": data_source_dict.get(row[2], None),
447
                  "object_type": row[3],
448
                  "units": row[4],
449
                  "high_limit": row[5],
450
                  "low_limit": row[6],
451
                  "higher_limit": row[7],
452
                  "lower_limit": row[8],
453
                  "ratio": Decimal(row[9]),
454
                  "offset_constant": Decimal(row[10]),
455
                  "is_trend": bool(row[11]),
456
                  "is_virtual": bool(row[12]),
457
                  "address": row[13],
458
                  "description": row[14],
459
                  "faults": row[15],
460
                  "definitions": row[16]}
461
        resp.text = json.dumps(result)
462
463
    @staticmethod
464
    @user_logger
465
    def on_delete(req, resp, id_):
466
        admin_control(req)
467
        if not id_.isdigit() or int(id_) <= 0:
468
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
469
                                   description='API.INVALID_POINT_ID')
470
471
        cnx = mysql.connector.connect(**config.myems_system_db)
472
        cursor = cnx.cursor()
473
474
        cursor.execute(" SELECT name "
475
                       " FROM tbl_points "
476
                       " WHERE id = %s ", (id_,))
477
        if cursor.fetchone() is None:
478
            cursor.close()
479
            cnx.close()
480
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
481
                                   description='API.POINT_NOT_FOUND')
482
483
        point_id = int(id_)
484
485
        def _raise_relation_error(description):
486
            cursor.close()
487
            cnx.close()
488
            raise falcon.HTTPError(status=falcon.HTTP_400,
489
                                   title='API.BAD_REQUEST',
490
                                   description=description)
491
492
        for table_name, columns, error_description in POINT_RELATION_CHECKS:
493
            for column in columns:
494
                try:
495
                    cursor.execute(
496
                        f" SELECT 1 FROM {table_name} WHERE {column} = %s LIMIT 1 ",
497
                        (point_id,))
498
                except mysql.connector.Error:
499
                    continue
500
                if cursor.fetchone() is not None:
501
                    _raise_relation_error(error_description)
502
503
        cursor.execute(" DELETE FROM tbl_points WHERE id = %s ", (id_,))
504
        cnx.commit()
505
506
        cursor.close()
507
        cnx.close()
508
509
        resp.status = falcon.HTTP_204
510
511
    @staticmethod
512
    @user_logger
513
    def on_put(req, resp, id_):
514
        """Handles PUT requests"""
515
        admin_control(req)
516
        try:
517
            raw_json = req.stream.read().decode('utf-8')
518
        except Exception as ex:
519
            print(str(ex))
520
            raise falcon.HTTPError(status=falcon.HTTP_400,
521
                                   title='API.BAD_REQUEST',
522
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
523
524
        if not id_.isdigit() or int(id_) <= 0:
525
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
526
                                   description='API.INVALID_POINT_ID')
527
528
        new_values = json.loads(raw_json)
529
530
        if 'name' not in new_values['data'].keys() or \
531
                not isinstance(new_values['data']['name'], str) or \
532
                len(str.strip(new_values['data']['name'])) == 0:
533
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
534
                                   description='API.INVALID_POINT_NAME')
535
        name = str.strip(new_values['data']['name'])
536
537
        if 'data_source_id' not in new_values['data'].keys() or \
538
                not isinstance(new_values['data']['data_source_id'], int) or \
539
                new_values['data']['data_source_id'] <= 0:
540
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
541
                                   description='API.INVALID_DATA_SOURCE_ID')
542
        data_source_id = new_values['data']['data_source_id']
543
544
        if 'object_type' not in new_values['data'].keys() \
545
                or str.strip(new_values['data']['object_type']) not in \
546
                ('ENERGY_VALUE', 'ANALOG_VALUE', 'DIGITAL_VALUE', 'TEXT_VALUE'):
547
            raise falcon.HTTPError(status=falcon.HTTP_400,
548
                                   title='API.BAD_REQUEST',
549
                                   description='API.INVALID_OBJECT_TYPE')
550
        object_type = str.strip(new_values['data']['object_type'])
551
552
        if 'units' not in new_values['data'].keys() or \
553
                not isinstance(new_values['data']['units'], str) or \
554
                len(str.strip(new_values['data']['units'])) == 0:
555
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
556
                                   description='API.INVALID_UNITS')
557
        units = str.strip(new_values['data']['units'])
558
559
        if 'high_limit' not in new_values['data'].keys() or \
560
                not (isinstance(new_values['data']['high_limit'], float) or
561
                     isinstance(new_values['data']['high_limit'], int)):
562
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
563
                                   description='API.INVALID_HIGH_LIMIT_VALUE')
564
        high_limit = new_values['data']['high_limit']
565
566
        if 'low_limit' not in new_values['data'].keys() or \
567
                not (isinstance(new_values['data']['low_limit'], float) or
568
                     isinstance(new_values['data']['low_limit'], int)):
569
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
570
                                   description='API.INVALID_LOW_LIMIT_VALUE')
571
        low_limit = new_values['data']['low_limit']
572
573
        # the higher_limit is optional
574
        if 'higher_limit' not in new_values['data'].keys() or \
575
                new_values['data']['higher_limit'] is None:
576
            higher_limit = None
577
        elif not (isinstance(new_values['data']['higher_limit'], float) or
578
                  isinstance(new_values['data']['higher_limit'], int)):
579
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
580
                                   description='API.INVALID_HIGHER_LIMIT_VALUE')
581
        else:
582
            higher_limit = new_values['data']['higher_limit']
583
584
        # the lower_limit is optional
585
        if 'lower_limit' not in new_values['data'].keys() or \
586
                new_values['data']['lower_limit'] is None:
587
            lower_limit = None
588
        elif not (isinstance(new_values['data']['lower_limit'], float) or
589
                  isinstance(new_values['data']['lower_limit'], int)):
590
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
591
                                   description='API.INVALID_LOWER_LIMIT_VALUE')
592
        else:
593
            lower_limit = new_values['data']['lower_limit']
594
595
        if 'ratio' not in new_values['data'].keys() or \
596
                not (isinstance(new_values['data']['ratio'], float) or
597
                     isinstance(new_values['data']['ratio'], int)):
598
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
599
                                   description='API.INVALID_RATIO_VALUE')
600
        ratio = new_values['data']['ratio']
601
602
        if 'offset_constant' not in new_values['data'].keys() or \
603
                not (isinstance(new_values['data']['offset_constant'], float) or
604
                     isinstance(new_values['data']['offset_constant'], int)):
605
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
606
                                   description='API.INVALID_OFFSET_CONSTANT_VALUE')
607
        offset_constant = new_values['data']['offset_constant']
608
609
        if 'is_trend' not in new_values['data'].keys() or \
610
                not isinstance(new_values['data']['is_trend'], bool):
611
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
612
                                   description='API.INVALID_IS_TREND_VALUE')
613
        is_trend = new_values['data']['is_trend']
614
615
        if 'is_virtual' not in new_values['data'].keys() or \
616
                not isinstance(new_values['data']['is_virtual'], bool):
617
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
618
                                   description='API.INVALID_IS_VIRTUAL_VALUE')
619
        if new_values['data']['is_virtual'] is True and object_type == 'DIGITAL_VALUE':
620
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
621
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_DIGITAL_VALUE')
622
        if new_values['data']['is_virtual'] is True and object_type == 'TEXT_VALUE':
623
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
624
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_TEXT_VALUE')
625
        is_virtual = new_values['data']['is_virtual']
626
627
        if 'address' not in new_values['data'].keys() or \
628
                not isinstance(new_values['data']['address'], str) or \
629
                len(str.strip(new_values['data']['address'])) == 0:
630
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
631
                                   description='API.INVALID_ADDRESS')
632
        address = str.strip(new_values['data']['address'])
633
634
        if 'description' in new_values['data'].keys() and \
635
                new_values['data']['description'] is not None and \
636
                len(str(new_values['data']['description'])) > 0:
637
            description = str.strip(new_values['data']['description'])
638
        else:
639
            description = None
640
641
        if 'faults' in new_values['data'].keys() and \
642
                new_values['data']['faults'] is not None and \
643
                len(str(new_values['data']['faults'])) > 0:
644
            faults = str.strip(new_values['data']['faults'])
645
        else:
646
            faults = None
647
648
        if 'definitions' in new_values['data'].keys() and \
649
                new_values['data']['definitions'] is not None and \
650
                len(str(new_values['data']['definitions'])) > 0:
651
            definitions = str.strip(new_values['data']['definitions'])
652
        else:
653
            definitions = None
654
        cnx = mysql.connector.connect(**config.myems_system_db)
655
        cursor = cnx.cursor()
656
657
        cursor.execute(" SELECT name "
658
                       " FROM tbl_points "
659
                       " WHERE id = %s ", (id_,))
660
        if cursor.fetchone() is None:
661
            cursor.close()
662
            cnx.close()
663
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
664
                                   description='API.POINT_NOT_FOUND')
665
666
        cursor.execute(" SELECT name "
667
                       " FROM tbl_data_sources "
668
                       " WHERE id = %s ", (data_source_id,))
669
        if cursor.fetchone() is None:
670
            cursor.close()
671
            cnx.close()
672
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
673
                                   description='API.INVALID_DATA_SOURCE_ID')
674
675
        cursor.execute(" SELECT name "
676
                       " FROM tbl_points "
677
                       " WHERE name = %s AND data_source_id = %s AND id != %s ", (name, data_source_id, id_))
678
        if cursor.fetchone() is not None:
679
            cursor.close()
680
            cnx.close()
681
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
682
                                   description='API.POINT_NAME_IS_ALREADY_IN_USE')
683
684
        update_row = (" UPDATE tbl_points "
685
                      " SET name = %s, data_source_id = %s, "
686
                      "     object_type = %s, units = %s, "
687
                      "     high_limit = %s, low_limit = %s, higher_limit = %s, lower_limit = %s, ratio = %s, "
688
                      "     offset_constant = %s, is_trend = %s, is_virtual = %s, address = %s, description = %s, "
689
                      "     faults = %s, definitions = %s "
690
                      " WHERE id = %s ")
691
        cursor.execute(update_row, (name,
692
                                    data_source_id,
693
                                    object_type,
694
                                    units,
695
                                    high_limit,
696
                                    low_limit,
697
                                    higher_limit,
698
                                    lower_limit,
699
                                    ratio,
700
                                    offset_constant,
701
                                    is_trend,
702
                                    is_virtual,
703
                                    address,
704
                                    description,
705
                                    faults,
706
                                    definitions,
707
                                    id_,))
708
        cnx.commit()
709
710
        cursor.close()
711
        cnx.close()
712
713
        resp.status = falcon.HTTP_200
714
715
716
class PointLimit:
717
    def __init__(self):
718
        pass
719
720
    @staticmethod
721
    def on_options(req, resp, id_):
722
        _ = req
723
        resp.status = falcon.HTTP_200
724
        _ = id_
725
726
    @staticmethod
727
    @user_logger
728
    def on_put(req, resp, id_):
729
        """Handles PUT requests"""
730
        admin_control(req)
731
        try:
732
            raw_json = req.stream.read().decode('utf-8')
733
        except Exception as ex:
734
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.EXCEPTION', description=str(ex))
735
736
        if not id_.isdigit() or int(id_) <= 0:
737
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
738
                                   description='API.INVALID_POINT_ID')
739
740
        new_values = json.loads(raw_json)
741
742
        if 'high_limit' not in new_values['data'].keys() or \
743
                not (isinstance(new_values['data']['high_limit'], float) or
744
                     isinstance(new_values['data']['high_limit'], int)):
745
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
746
                                   description='API.INVALID_HIGH_LIMIT_VALUE')
747
        high_limit = new_values['data']['high_limit']
748
749
        if 'low_limit' not in new_values['data'].keys() or \
750
                not (isinstance(new_values['data']['low_limit'], float) or
751
                     isinstance(new_values['data']['low_limit'], int)):
752
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
753
                                   description='API.INVALID_LOW_LIMIT_VALUE')
754
        low_limit = new_values['data']['low_limit']
755
756
        if 'higher_limit' not in new_values['data'].keys() or \
757
                not (isinstance(new_values['data']['higher_limit'], float) or
758
                     isinstance(new_values['data']['higher_limit'], int)):
759
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
760
                                   description='API.INVALID_HIGHER_LIMIT_VALUE')
761
        higher_limit = new_values['data']['higher_limit']
762
763
        if 'lower_limit' not in new_values['data'].keys() or \
764
                not (isinstance(new_values['data']['lower_limit'], float) or
765
                     isinstance(new_values['data']['lower_limit'], int)):
766
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
767
                                   description='API.INVALID_LOWER_LIMIT_VALUE')
768
        lower_limit = new_values['data']['lower_limit']
769
770
        cnx = mysql.connector.connect(**config.myems_system_db)
771
        cursor = cnx.cursor()
772
773
        cursor.execute(" SELECT name "
774
                       " FROM tbl_points "
775
                       " WHERE id = %s ", (id_,))
776
        if cursor.fetchone() is None:
777
            cursor.close()
778
            cnx.close()
779
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
780
                                   description='API.POINT_NOT_FOUND')
781
782
        update_row = (" UPDATE tbl_points "
783
                      " SET  high_limit = %s, low_limit = %s, higher_limit = %s, lower_limit = %s "
784
                      " WHERE id = %s ")
785
        cursor.execute(update_row, (high_limit,
786
                                    low_limit,
787
                                    higher_limit,
788
                                    lower_limit,
789
                                    id_,))
790
        cnx.commit()
791
792
        cursor.close()
793
        cnx.close()
794
795
        resp.status = falcon.HTTP_200
796
797
798
class PointSetValue:
799
    def __init__(self):
800
        pass
801
802
    @staticmethod
803
    def on_options(req, resp, id_):
804
        _ = req
805
        resp.status = falcon.HTTP_200
806
        _ = id_
807
808
    @staticmethod
809
    @user_logger
810
    def on_put(req, resp, id_):
811
        """Handles PUT requests"""
812
        admin_control(req)
813
        try:
814
            raw_json = req.stream.read().decode('utf-8')
815
        except Exception as ex:
816
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.EXCEPTION', description=str(ex))
817
818
        if not id_.isdigit() or int(id_) <= 0:
819
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
820
                                   description='API.INVALID_POINT_ID')
821
822
        new_values = json.loads(raw_json)
823
824
        if 'set_value' not in new_values['data'].keys() or \
825
                not (isinstance(new_values['data']['set_value'], float) or
826
                     isinstance(new_values['data']['set_value'], int)):
827
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
828
                                   description='API.INVALID_SET_VALUE')
829
        set_value = new_values['data']['set_value']
830
831
        cnx = mysql.connector.connect(**config.myems_system_db)
832
        cursor = cnx.cursor()
833
834
        cursor.execute(" SELECT name "
835
                       " FROM tbl_points "
836
                       " WHERE id = %s ", (id_,))
837
        if cursor.fetchone() is None:
838
            cursor.close()
839
            cnx.close()
840
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
841
                                   description='API.POINT_NOT_FOUND')
842
843
        add_value = (" INSERT INTO tbl_points_set_values (point_id, set_value, utc_date_time) "
844
                     " VALUES (%s, %s, %s) ")
845
        cursor.execute(add_value, (id_, set_value, datetime.utcnow()))
846
        cnx.commit()
847
        cursor.close()
848
        cnx.close()
849
850
        resp.status = falcon.HTTP_200
851
852
853
class PointExport:
854
    def __init__(self):
855
        pass
856
857
    @staticmethod
858
    def on_options(req, resp, id_):
859
        _ = req
860
        resp.status = falcon.HTTP_200
861
        _ = id_
862
863 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
864
    def on_get(req, resp, id_):
865
        """Handles GET requests"""
866
        admin_control(req)
867
        if not id_.isdigit() or int(id_) <= 0:
868
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
869
                                   description='API.INVALID_POINT_ID')
870
871
        cnx = mysql.connector.connect(**config.myems_system_db)
872
        cursor = cnx.cursor()
873
874
        query = (" SELECT id, name, uuid "
875
                 " FROM tbl_data_sources ")
876
        cursor.execute(query)
877
        rows_data_sources = cursor.fetchall()
878
879
        data_source_dict = dict()
880
        if rows_data_sources is not None and len(rows_data_sources) > 0:
881
            for row in rows_data_sources:
882
                data_source_dict[row[0]] = {"id": row[0],
883
                                            "name": row[1],
884
                                            "uuid": row[2]}
885
886
        query = (" SELECT id, name, data_source_id, object_type, units, "
887
                 "        high_limit, low_limit, higher_limit, lower_limit, ratio, offset_constant, "
888
                 "        is_trend, is_virtual, address, description, faults, definitions "
889
                 " FROM tbl_points "
890
                 " WHERE id = %s ")
891
        cursor.execute(query, (id_,))
892
        row = cursor.fetchone()
893
        cursor.close()
894
        cnx.close()
895
        if row is None:
896
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
897
                                   description='API.POINT_NOT_FOUND')
898
899
        result = {"id": row[0],
900
                  "name": row[1],
901
                  "data_source": data_source_dict.get(row[2], None),
902
                  "object_type": row[3],
903
                  "units": row[4],
904
                  "high_limit": row[5],
905
                  "low_limit": row[6],
906
                  "higher_limit": row[7],
907
                  "lower_limit": row[8],
908
                  "ratio": Decimal(row[9]),
909
                  "offset_constant": Decimal(row[10]),
910
                  "is_trend": bool(row[11]),
911
                  "is_virtual": bool(row[12]),
912
                  "address": row[13],
913
                  "description": row[14],
914
                  "faults": row[15],
915
                  "definitions": row[16]}
916
        resp.text = json.dumps(result)
917
918
919
class PointImport:
920
    def __init__(self):
921
        pass
922
923
    @staticmethod
924
    def on_options(req, resp):
925
        _ = req
926
        resp.status = falcon.HTTP_200
927
928
    @staticmethod
929
    @user_logger
930
    def on_post(req, resp):
931
        """Handles POST requests"""
932
        admin_control(req)
933
        try:
934
            raw_json = req.stream.read().decode('utf-8')
935
        except Exception as ex:
936
            print(str(ex))
937
            raise falcon.HTTPError(status=falcon.HTTP_400,
938
                                   title='API.BAD_REQUEST',
939
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
940
941
        new_values = json.loads(raw_json)
942
943
        if 'name' not in new_values.keys() or \
944
                not isinstance(new_values['name'], str) or \
945
                len(str.strip(new_values['name'])) == 0:
946
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
947
                                   description='API.INVALID_POINT_NAME')
948
        name = str.strip(new_values['name'])
949
950
        if 'id' not in new_values['data_source'].keys() or \
951
                not isinstance(new_values['data_source']['id'], int) or \
952
                new_values['data_source']['id'] <= 0:
953
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
954
                                   description='API.INVALID_DATA_SOURCE_ID')
955
        data_source_id = new_values['data_source']['id']
956
957
        if 'object_type' not in new_values.keys() \
958
                or str.strip(new_values['object_type']) not in (
959
                'ENERGY_VALUE', 'ANALOG_VALUE', 'DIGITAL_VALUE', 'TEXT_VALUE'):
960
            raise falcon.HTTPError(status=falcon.HTTP_400,
961
                                   title='API.BAD_REQUEST',
962
                                   description='API.INVALID_OBJECT_TYPE')
963
        object_type = str.strip(new_values['object_type'])
964
965
        if 'units' not in new_values.keys() or \
966
                not isinstance(new_values['units'], str) or \
967
                len(str.strip(new_values['units'])) == 0:
968
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
969
                                   description='API.INVALID_UNITS')
970
        units = str.strip(new_values['units'])
971
972
        if 'high_limit' not in new_values.keys() or \
973
                not (isinstance(new_values['high_limit'], float) or
974
                     isinstance(new_values['high_limit'], int)):
975
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
976
                                   description='API.INVALID_HIGH_LIMIT_VALUE')
977
        high_limit = new_values['high_limit']
978
979
        if 'low_limit' not in new_values.keys() or \
980
                not (isinstance(new_values['low_limit'], float) or
981
                     isinstance(new_values['low_limit'], int)):
982
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
983
                                   description='API.INVALID_LOW_LIMIT_VALUE')
984
        low_limit = new_values['low_limit']
985
986
        # the higher_limit is optional
987
        if 'higher_limit' not in new_values.keys() or \
988
                new_values['higher_limit'] is None:
989
            higher_limit = None
990
        elif not (isinstance(new_values['higher_limit'], float) or
991
                  isinstance(new_values['higher_limit'], int)):
992
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
993
                                   description='API.INVALID_HIGHER_LIMIT_VALUE')
994
        else:
995
            higher_limit = new_values['higher_limit']
996
997
        # the lower_limit is optional
998
        if 'lower_limit' not in new_values.keys() or \
999
                new_values['lower_limit'] is None:
1000
            lower_limit = None
1001
        elif not (isinstance(new_values['lower_limit'], float) or
1002
                  isinstance(new_values['lower_limit'], int)):
1003
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1004
                                   description='API.INVALID_LOWER_LIMIT_VALUE')
1005
        else:
1006
            lower_limit = new_values['lower_limit']
1007
1008
        if 'ratio' not in new_values.keys() or \
1009
                not (isinstance(new_values['ratio'], float) or
1010
                     isinstance(new_values['ratio'], int)):
1011
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1012
                                   description='API.INVALID_RATIO_VALUE')
1013
        ratio = new_values['ratio']
1014
1015
        if 'offset_constant' not in new_values.keys() or \
1016
                not (isinstance(new_values['offset_constant'], float) or
1017
                     isinstance(new_values['offset_constant'], int)):
1018
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1019
                                   description='API.INVALID_OFFSET_CONSTANT_VALUE')
1020
        offset_constant = new_values['offset_constant']
1021
1022
        if 'is_trend' not in new_values.keys() or \
1023
                not isinstance(new_values['is_trend'], bool):
1024
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1025
                                   description='API.INVALID_IS_TREND_VALUE')
1026
        is_trend = new_values['is_trend']
1027
1028
        if 'is_virtual' not in new_values.keys() or \
1029
                not isinstance(new_values['is_virtual'], bool):
1030
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1031
                                   description='API.INVALID_IS_VIRTUAL_VALUE')
1032
        if new_values['is_virtual'] is True and object_type == 'DIGITAL_VALUE':
1033
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1034
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_DIGITAL_VALUE')
1035
        if new_values['is_virtual'] is True and object_type == 'TEXT_VALUE':
1036
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1037
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_TEXT_VALUE')
1038
        is_virtual = new_values['is_virtual']
1039
1040
        if 'address' not in new_values.keys() or \
1041
                not isinstance(new_values['address'], str) or \
1042
                len(str.strip(new_values['address'])) == 0:
1043
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1044
                                   description='API.INVALID_ADDRESS')
1045
        address = str.strip(new_values['address'])
1046
1047
        if 'description' in new_values.keys() and \
1048
                new_values['description'] is not None and \
1049
                len(str(new_values['description'])) > 0:
1050
            description = str.strip(new_values['description'])
1051
        else:
1052
            description = None
1053
1054
        if 'faults' in new_values.keys() and \
1055
                new_values['faults'] is not None and \
1056
                len(str(new_values['faults'])) > 0:
1057
            faults = str.strip(new_values['faults'])
1058
        else:
1059
            faults = None
1060
1061
        if 'definitions' in new_values.keys() and \
1062
                new_values['definitions'] is not None and \
1063
                len(str(new_values['definitions'])) > 0:
1064
            definitions = str.strip(new_values['definitions'])
1065
        else:
1066
            definitions = None
1067
1068
        cnx = mysql.connector.connect(**config.myems_system_db)
1069
        cursor = cnx.cursor()
1070
1071
        cursor.execute(" SELECT name "
1072
                       " FROM tbl_points "
1073
                       " WHERE name = %s AND data_source_id = %s ", (name, data_source_id))
1074
        if cursor.fetchone() is not None:
1075
            cursor.close()
1076
            cnx.close()
1077
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1078
                                   description='API.POINT_NAME_IS_ALREADY_IN_USE')
1079
1080
        cursor.execute(" SELECT name "
1081
                       " FROM tbl_data_sources "
1082
                       " WHERE id = %s ", (data_source_id,))
1083
        if cursor.fetchone() is None:
1084
            cursor.close()
1085
            cnx.close()
1086
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1087
                                   description='API.INVALID_DATA_SOURCE_ID')
1088
1089
        add_value = (" INSERT INTO tbl_points (name, data_source_id, object_type, units, "
1090
                     "                         high_limit, low_limit, higher_limit, lower_limit, ratio, "
1091
                     "                         offset_constant, is_trend, is_virtual, address, description, faults, "
1092
                     "                         definitions) "
1093
                     " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
1094
        cursor.execute(add_value, (name,
1095
                                   data_source_id,
1096
                                   object_type,
1097
                                   units,
1098
                                   high_limit,
1099
                                   low_limit,
1100
                                   higher_limit,
1101
                                   lower_limit,
1102
                                   ratio,
1103
                                   offset_constant,
1104
                                   is_trend,
1105
                                   is_virtual,
1106
                                   address,
1107
                                   description,
1108
                                   faults,
1109
                                   definitions))
1110
        new_id = cursor.lastrowid
1111
        cnx.commit()
1112
        cursor.close()
1113
        cnx.close()
1114
1115
        resp.status = falcon.HTTP_201
1116
        resp.location = '/points/' + str(new_id)
1117
1118
1119
class PointClone:
1120
    def __init__(self):
1121
        pass
1122
1123
    @staticmethod
1124
    def on_options(req, resp, id_):
1125
        _ = req
1126
        resp.status = falcon.HTTP_200
1127
        _ = id_
1128
1129
    @staticmethod
1130
    @user_logger
1131
    def on_post(req, resp, id_):
1132
        admin_control(req)
1133
        if not id_.isdigit() or int(id_) <= 0:
1134
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1135
                                   description='API.INVALID_POINT_ID')
1136
1137
        cnx = mysql.connector.connect(**config.myems_system_db)
1138
        cursor = cnx.cursor()
1139
1140
        query = (" SELECT id, name, data_source_id, object_type, units, "
1141
                 "        high_limit, low_limit, higher_limit, lower_limit, ratio, offset_constant, "
1142
                 "        is_trend, is_virtual, address, description, faults, definitions "
1143
                 " FROM tbl_points "
1144
                 " WHERE id = %s ")
1145
        cursor.execute(query, (id_,))
1146
        row = cursor.fetchone()
1147
        if row is None:
1148
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1149
                                   description='API.POINT_NOT_FOUND')
1150
1151
        result = {"id": row[0],
1152
                  "name": row[1],
1153
                  "data_source_id": row[2],
1154
                  "object_type": row[3],
1155
                  "units": row[4],
1156
                  "high_limit": row[5],
1157
                  "low_limit": row[6],
1158
                  "higher_limit": row[7],
1159
                  "lower_limit": row[8],
1160
                  "ratio": Decimal(row[9]),
1161
                  "offset_constant": Decimal(row[10]),
1162
                  "is_trend": bool(row[11]),
1163
                  "is_virtual": bool(row[12]),
1164
                  "address": row[13],
1165
                  "description": row[14],
1166
                  "faults": row[15],
1167
                  "definitions": row[16]}
1168
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
1169
        if config.utc_offset[0] == '-':
1170
            timezone_offset = -timezone_offset
1171
        new_name = (str.strip(result['name']) +
1172
                    (datetime.utcnow() + timedelta(minutes=timezone_offset)).isoformat(sep='-', timespec='seconds'))
1173
        add_value = (" INSERT INTO tbl_points (name, data_source_id, object_type, units, "
1174
                     "                         high_limit, low_limit, higher_limit, lower_limit, ratio, "
1175
                     "                         offset_constant, is_trend, is_virtual, address, description, faults, "
1176
                     "                         definitions) "
1177
                     " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
1178
        cursor.execute(add_value, (new_name,
1179
                                   result['data_source_id'],
1180
                                   result['object_type'],
1181
                                   result['units'],
1182
                                   result['high_limit'],
1183
                                   result['low_limit'],
1184
                                   result['higher_limit'],
1185
                                   result['lower_limit'],
1186
                                   result['ratio'],
1187
                                   result['offset_constant'],
1188
                                   result['is_trend'],
1189
                                   result['is_virtual'],
1190
                                   result['address'],
1191
                                   result['description'],
1192
                                   result['faults'],
1193
                                   result['definitions']))
1194
        new_id = cursor.lastrowid
1195
        cnx.commit()
1196
        cursor.close()
1197
        cnx.close()
1198
1199
        resp.status = falcon.HTTP_201
1200
        resp.location = '/points/' + str(new_id)
1201
1202