Passed
Push — master ( cb176a...4b8481 )
by
unknown
13:30
created

core.point.PointSetValue.on_put()   C

Complexity

Conditions 9

Size

Total Lines 49
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 39
dl 0
loc 49
rs 6.6106
c 0
b 0
f 0
cc 9
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 View Code Duplication
        if rows is not None and len(rows) > 0:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
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 UnicodeDecodeError as ex:
215
            print("Failed to decode request")
216
            raise falcon.HTTPError(status=falcon.HTTP_400,
217
                                   title='API.BAD_REQUEST',
218
                                   description='API.INVALID_ENCODING')
219
        except Exception as ex:
220
            print("Unexpected error reading request stream")
221
            raise falcon.HTTPError(status=falcon.HTTP_400,
222
                                   title='API.BAD_REQUEST',
223
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
224
225
        new_values = json.loads(raw_json)
226
227
        if 'name' not in new_values['data'].keys() or \
228
                not isinstance(new_values['data']['name'], str) or \
229
                len(str.strip(new_values['data']['name'])) == 0:
230
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
231
                                   description='API.INVALID_POINT_NAME')
232
        name = str.strip(new_values['data']['name'])
233
234
        if 'data_source_id' not in new_values['data'].keys() or \
235
                not isinstance(new_values['data']['data_source_id'], int) or \
236
                new_values['data']['data_source_id'] <= 0:
237
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
238
                                   description='API.INVALID_DATA_SOURCE_ID')
239
        data_source_id = new_values['data']['data_source_id']
240
241
        if 'object_type' not in new_values['data'].keys() \
242
                or str.strip(new_values['data']['object_type']) not in \
243
                ('ENERGY_VALUE', 'ANALOG_VALUE', 'DIGITAL_VALUE', 'TEXT_VALUE'):
244
            raise falcon.HTTPError(status=falcon.HTTP_400,
245
                                   title='API.BAD_REQUEST',
246
                                   description='API.INVALID_OBJECT_TYPE')
247
        object_type = str.strip(new_values['data']['object_type'])
248
249
        if 'units' not in new_values['data'].keys() or \
250
                not isinstance(new_values['data']['units'], str) or \
251
                len(str.strip(new_values['data']['units'])) == 0:
252
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
253
                                   description='API.INVALID_UNITS')
254
        units = str.strip(new_values['data']['units'])
255
256
        if 'high_limit' not in new_values['data'].keys() or \
257
                not (isinstance(new_values['data']['high_limit'], float) or
258
                     isinstance(new_values['data']['high_limit'], int)):
259
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
260
                                   description='API.INVALID_HIGH_LIMIT_VALUE')
261
        high_limit = new_values['data']['high_limit']
262
263
        if 'low_limit' not in new_values['data'].keys() or \
264
                not (isinstance(new_values['data']['low_limit'], float) or
265
                     isinstance(new_values['data']['low_limit'], int)):
266
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
267
                                   description='API.INVALID_LOW_LIMIT_VALUE')
268
        low_limit = new_values['data']['low_limit']
269
270
        # the higher_limit is optional
271
        if 'higher_limit' not in new_values['data'].keys() or \
272
                new_values['data']['higher_limit'] is None:
273
            higher_limit = None
274
        elif not (isinstance(new_values['data']['higher_limit'], float) or
275
                  isinstance(new_values['data']['higher_limit'], int)):
276
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
277
                                   description='API.INVALID_HIGHER_LIMIT_VALUE')
278
        else:
279
            higher_limit = new_values['data']['higher_limit']
280
281
        # the lower_limit is optional
282
        if 'lower_limit' not in new_values['data'].keys() or \
283
                new_values['data']['lower_limit'] is None:
284
            lower_limit = None
285
        elif not (isinstance(new_values['data']['lower_limit'], float) or
286
                  isinstance(new_values['data']['lower_limit'], int)):
287
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
288
                                   description='API.INVALID_LOWER_LIMIT_VALUE')
289
        else:
290
            lower_limit = new_values['data']['lower_limit']
291
292
        if 'ratio' not in new_values['data'].keys() or \
293
                not (isinstance(new_values['data']['ratio'], float) or
294
                     isinstance(new_values['data']['ratio'], int)):
295
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
296
                                   description='API.INVALID_RATIO_VALUE')
297
        ratio = new_values['data']['ratio']
298
299
        if 'offset_constant' not in new_values['data'].keys() or \
300
                not (isinstance(new_values['data']['offset_constant'], float) or
301
                     isinstance(new_values['data']['offset_constant'], int)):
302
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
303
                                   description='API.INVALID_OFFSET_CONSTANT_VALUE')
304
        offset_constant = new_values['data']['offset_constant']
305
306
        if 'is_trend' not in new_values['data'].keys() or \
307
                not isinstance(new_values['data']['is_trend'], bool):
308
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
309
                                   description='API.INVALID_IS_TREND_VALUE')
310
        is_trend = new_values['data']['is_trend']
311
312
        if 'is_virtual' not in new_values['data'].keys() or \
313
                not isinstance(new_values['data']['is_virtual'], bool):
314
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
315
                                   description='API.INVALID_IS_VIRTUAL_VALUE')
316
        if new_values['data']['is_virtual'] is True and object_type == 'DIGITAL_VALUE':
317
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
318
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_DIGITAL_VALUE')
319
        if new_values['data']['is_virtual'] is True and object_type == 'TEXT_VALUE':
320
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
321
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_TEXT_VALUE')
322
        is_virtual = new_values['data']['is_virtual']
323
324
        if 'address' not in new_values['data'].keys() or \
325
                not isinstance(new_values['data']['address'], str) or \
326
                len(str.strip(new_values['data']['address'])) == 0:
327
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
328
                                   description='API.INVALID_ADDRESS')
329
        address = str.strip(new_values['data']['address'])
330
331
        if 'description' in new_values['data'].keys() and \
332
                new_values['data']['description'] is not None and \
333
                len(str(new_values['data']['description'])) > 0:
334
            description = str.strip(new_values['data']['description'])
335
        else:
336
            description = None
337
338
        if 'faults' in new_values['data'].keys() and \
339
                new_values['data']['faults'] is not None and \
340
                len(str(new_values['data']['faults'])) > 0:
341
            faults = str.strip(new_values['data']['faults'])
342
        else:
343
            faults = None
344
345
        if 'definitions' in new_values['data'].keys() and \
346
                new_values['data']['definitions'] is not None and \
347
                len(str(new_values['data']['definitions'])) > 0:
348
            definitions = str.strip(new_values['data']['definitions'])
349
        else:
350
            definitions = None
351
352
        cnx = mysql.connector.connect(**config.myems_system_db)
353
        cursor = cnx.cursor()
354
355
        cursor.execute(" SELECT name "
356
                       " FROM tbl_points "
357
                       " WHERE name = %s AND data_source_id = %s ", (name, data_source_id))
358
        if cursor.fetchone() is not None:
359
            cursor.close()
360
            cnx.close()
361
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
362
                                   description='API.POINT_NAME_IS_ALREADY_IN_USE')
363
364
        cursor.execute(" SELECT name "
365
                       " FROM tbl_data_sources "
366
                       " WHERE id = %s ", (data_source_id,))
367
        if cursor.fetchone() is None:
368
            cursor.close()
369
            cnx.close()
370
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
371
                                   description='API.INVALID_DATA_SOURCE_ID')
372
373
        add_value = (" INSERT INTO tbl_points (name, data_source_id, object_type, units, "
374
                     "                         high_limit, low_limit, higher_limit, lower_limit, ratio, "
375
                     "                         offset_constant, is_trend, is_virtual, address, description, faults, "
376
                     "                         definitions) "
377
                     " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
378
        cursor.execute(add_value, (name,
379
                                   data_source_id,
380
                                   object_type,
381
                                   units,
382
                                   high_limit,
383
                                   low_limit,
384
                                   higher_limit,
385
                                   lower_limit,
386
                                   ratio,
387
                                   offset_constant,
388
                                   is_trend,
389
                                   is_virtual,
390
                                   address,
391
                                   description,
392
                                   faults,
393
                                   definitions))
394
        new_id = cursor.lastrowid
395
        cnx.commit()
396
        cursor.close()
397
        cnx.close()
398
399
        resp.status = falcon.HTTP_201
400
        resp.location = '/points/' + str(new_id)
401
402
403
class PointItem:
404
    def __init__(self):
405
        pass
406
407
    @staticmethod
408
    def on_options(req, resp, id_):
409
        _ = req
410
        resp.status = falcon.HTTP_200
411
        _ = id_
412
413 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
414
    def on_get(req, resp, id_):
415
        """Handles GET requests"""
416
        admin_control(req)
417
        if not id_.isdigit() or int(id_) <= 0:
418
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
419
                                   description='API.INVALID_POINT_ID')
420
421
        cnx = mysql.connector.connect(**config.myems_system_db)
422
        cursor = cnx.cursor()
423
424
        query = (" SELECT id, name, uuid "
425
                 " FROM tbl_data_sources ")
426
        cursor.execute(query)
427
        rows_data_sources = cursor.fetchall()
428
429
        data_source_dict = dict()
430
        if rows_data_sources is not None and len(rows_data_sources) > 0:
431
            for row in rows_data_sources:
432
                data_source_dict[row[0]] = {"id": row[0],
433
                                            "name": row[1],
434
                                            "uuid": row[2]}
435
436
        query = (" SELECT id, name, data_source_id, object_type, units, "
437
                 "        high_limit, low_limit, higher_limit, lower_limit, ratio, offset_constant, "
438
                 "        is_trend, is_virtual, address, description, faults, definitions "
439
                 " FROM tbl_points "
440
                 " WHERE id = %s ")
441
        cursor.execute(query, (id_,))
442
        row = cursor.fetchone()
443
        cursor.close()
444
        cnx.close()
445
        if row is None:
446
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
447
                                   description='API.POINT_NOT_FOUND')
448
449
        result = {"id": row[0],
450
                  "name": row[1],
451
                  "data_source": data_source_dict.get(row[2], None),
452
                  "object_type": row[3],
453
                  "units": row[4],
454
                  "high_limit": row[5],
455
                  "low_limit": row[6],
456
                  "higher_limit": row[7],
457
                  "lower_limit": row[8],
458
                  "ratio": Decimal(row[9]),
459
                  "offset_constant": Decimal(row[10]),
460
                  "is_trend": bool(row[11]),
461
                  "is_virtual": bool(row[12]),
462
                  "address": row[13],
463
                  "description": row[14],
464
                  "faults": row[15],
465
                  "definitions": row[16]}
466
        resp.text = json.dumps(result)
467
468
    @staticmethod
469
    @user_logger
470
    def on_delete(req, resp, id_):
471
        admin_control(req)
472
        if not id_.isdigit() or int(id_) <= 0:
473
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
474
                                   description='API.INVALID_POINT_ID')
475
476
        cnx = mysql.connector.connect(**config.myems_system_db)
477
        cursor = cnx.cursor()
478
479
        cursor.execute(" SELECT name "
480
                       " FROM tbl_points "
481
                       " WHERE id = %s ", (id_,))
482
        if cursor.fetchone() is None:
483
            cursor.close()
484
            cnx.close()
485
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
486
                                   description='API.POINT_NOT_FOUND')
487
488
        point_id = int(id_)
489
490
        def _raise_relation_error(description):
491
            cursor.close()
492
            cnx.close()
493
            raise falcon.HTTPError(status=falcon.HTTP_400,
494
                                   title='API.BAD_REQUEST',
495
                                   description=description)
496
497
        for table_name, columns, error_description in POINT_RELATION_CHECKS:
498
            for column in columns:
499
                try:
500
                    cursor.execute(
501
                        f" SELECT 1 FROM {table_name} WHERE {column} = %s LIMIT 1 ",
502
                        (point_id,))
503
                except mysql.connector.Error:
504
                    continue
505
                if cursor.fetchone() is not None:
506
                    _raise_relation_error(error_description)
507
508
        cursor.execute(" DELETE FROM tbl_points WHERE id = %s ", (id_,))
509
        cnx.commit()
510
511
        cursor.close()
512
        cnx.close()
513
514
        resp.status = falcon.HTTP_204
515
516
    @staticmethod
517
    @user_logger
518
    def on_put(req, resp, id_):
519
        """Handles PUT requests"""
520
        admin_control(req)
521
        try:
522
            raw_json = req.stream.read().decode('utf-8')
523
        except UnicodeDecodeError as ex:
524
            print("Failed to decode request")
525
            raise falcon.HTTPError(status=falcon.HTTP_400,
526
                                   title='API.BAD_REQUEST',
527
                                   description='API.INVALID_ENCODING')
528
        except Exception as ex:
529
            print("Unexpected error reading request stream")
530
            raise falcon.HTTPError(status=falcon.HTTP_400,
531
                                   title='API.BAD_REQUEST',
532
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
533
534
        if not id_.isdigit() or int(id_) <= 0:
535
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
536
                                   description='API.INVALID_POINT_ID')
537
538
        new_values = json.loads(raw_json)
539
540
        if 'name' not in new_values['data'].keys() or \
541
                not isinstance(new_values['data']['name'], str) or \
542
                len(str.strip(new_values['data']['name'])) == 0:
543
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
544
                                   description='API.INVALID_POINT_NAME')
545
        name = str.strip(new_values['data']['name'])
546
547
        if 'data_source_id' not in new_values['data'].keys() or \
548
                not isinstance(new_values['data']['data_source_id'], int) or \
549
                new_values['data']['data_source_id'] <= 0:
550
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
551
                                   description='API.INVALID_DATA_SOURCE_ID')
552
        data_source_id = new_values['data']['data_source_id']
553
554
        if 'object_type' not in new_values['data'].keys() \
555
                or str.strip(new_values['data']['object_type']) not in \
556
                ('ENERGY_VALUE', 'ANALOG_VALUE', 'DIGITAL_VALUE', 'TEXT_VALUE'):
557
            raise falcon.HTTPError(status=falcon.HTTP_400,
558
                                   title='API.BAD_REQUEST',
559
                                   description='API.INVALID_OBJECT_TYPE')
560
        object_type = str.strip(new_values['data']['object_type'])
561
562
        if 'units' not in new_values['data'].keys() or \
563
                not isinstance(new_values['data']['units'], str) or \
564
                len(str.strip(new_values['data']['units'])) == 0:
565
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
566
                                   description='API.INVALID_UNITS')
567
        units = str.strip(new_values['data']['units'])
568
569
        if 'high_limit' not in new_values['data'].keys() or \
570
                not (isinstance(new_values['data']['high_limit'], float) or
571
                     isinstance(new_values['data']['high_limit'], int)):
572
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
573
                                   description='API.INVALID_HIGH_LIMIT_VALUE')
574
        high_limit = new_values['data']['high_limit']
575
576
        if 'low_limit' not in new_values['data'].keys() or \
577
                not (isinstance(new_values['data']['low_limit'], float) or
578
                     isinstance(new_values['data']['low_limit'], int)):
579
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
580
                                   description='API.INVALID_LOW_LIMIT_VALUE')
581
        low_limit = new_values['data']['low_limit']
582
583
        # the higher_limit is optional
584
        if 'higher_limit' not in new_values['data'].keys() or \
585
                new_values['data']['higher_limit'] is None:
586
            higher_limit = None
587
        elif not (isinstance(new_values['data']['higher_limit'], float) or
588
                  isinstance(new_values['data']['higher_limit'], int)):
589
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
590
                                   description='API.INVALID_HIGHER_LIMIT_VALUE')
591
        else:
592
            higher_limit = new_values['data']['higher_limit']
593
594
        # the lower_limit is optional
595
        if 'lower_limit' not in new_values['data'].keys() or \
596
                new_values['data']['lower_limit'] is None:
597
            lower_limit = None
598
        elif not (isinstance(new_values['data']['lower_limit'], float) or
599
                  isinstance(new_values['data']['lower_limit'], int)):
600
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
601
                                   description='API.INVALID_LOWER_LIMIT_VALUE')
602
        else:
603
            lower_limit = new_values['data']['lower_limit']
604
605
        if 'ratio' not in new_values['data'].keys() or \
606
                not (isinstance(new_values['data']['ratio'], float) or
607
                     isinstance(new_values['data']['ratio'], int)):
608
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
609
                                   description='API.INVALID_RATIO_VALUE')
610
        ratio = new_values['data']['ratio']
611
612
        if 'offset_constant' not in new_values['data'].keys() or \
613
                not (isinstance(new_values['data']['offset_constant'], float) or
614
                     isinstance(new_values['data']['offset_constant'], int)):
615
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
616
                                   description='API.INVALID_OFFSET_CONSTANT_VALUE')
617
        offset_constant = new_values['data']['offset_constant']
618
619
        if 'is_trend' not in new_values['data'].keys() or \
620
                not isinstance(new_values['data']['is_trend'], bool):
621
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
622
                                   description='API.INVALID_IS_TREND_VALUE')
623
        is_trend = new_values['data']['is_trend']
624
625
        if 'is_virtual' not in new_values['data'].keys() or \
626
                not isinstance(new_values['data']['is_virtual'], bool):
627
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
628
                                   description='API.INVALID_IS_VIRTUAL_VALUE')
629
        if new_values['data']['is_virtual'] is True and object_type == 'DIGITAL_VALUE':
630
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
631
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_DIGITAL_VALUE')
632
        if new_values['data']['is_virtual'] is True and object_type == 'TEXT_VALUE':
633
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
634
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_TEXT_VALUE')
635
        is_virtual = new_values['data']['is_virtual']
636
637
        if 'address' not in new_values['data'].keys() or \
638
                not isinstance(new_values['data']['address'], str) or \
639
                len(str.strip(new_values['data']['address'])) == 0:
640
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
641
                                   description='API.INVALID_ADDRESS')
642
        address = str.strip(new_values['data']['address'])
643
644
        if 'description' in new_values['data'].keys() and \
645
                new_values['data']['description'] is not None and \
646
                len(str(new_values['data']['description'])) > 0:
647
            description = str.strip(new_values['data']['description'])
648
        else:
649
            description = None
650
651
        if 'faults' in new_values['data'].keys() and \
652
                new_values['data']['faults'] is not None and \
653
                len(str(new_values['data']['faults'])) > 0:
654
            faults = str.strip(new_values['data']['faults'])
655
        else:
656
            faults = None
657
658
        if 'definitions' in new_values['data'].keys() and \
659
                new_values['data']['definitions'] is not None and \
660
                len(str(new_values['data']['definitions'])) > 0:
661
            definitions = str.strip(new_values['data']['definitions'])
662
        else:
663
            definitions = None
664
        cnx = mysql.connector.connect(**config.myems_system_db)
665
        cursor = cnx.cursor()
666
667
        cursor.execute(" SELECT name "
668
                       " FROM tbl_points "
669
                       " WHERE id = %s ", (id_,))
670
        if cursor.fetchone() is None:
671
            cursor.close()
672
            cnx.close()
673
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
674
                                   description='API.POINT_NOT_FOUND')
675
676
        cursor.execute(" SELECT name "
677
                       " FROM tbl_data_sources "
678
                       " WHERE id = %s ", (data_source_id,))
679
        if cursor.fetchone() is None:
680
            cursor.close()
681
            cnx.close()
682
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
683
                                   description='API.INVALID_DATA_SOURCE_ID')
684
685
        cursor.execute(" SELECT name "
686
                       " FROM tbl_points "
687
                       " WHERE name = %s AND data_source_id = %s AND id != %s ", (name, data_source_id, id_))
688
        if cursor.fetchone() is not None:
689
            cursor.close()
690
            cnx.close()
691
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
692
                                   description='API.POINT_NAME_IS_ALREADY_IN_USE')
693
694
        update_row = (" UPDATE tbl_points "
695
                      " SET name = %s, data_source_id = %s, "
696
                      "     object_type = %s, units = %s, "
697
                      "     high_limit = %s, low_limit = %s, higher_limit = %s, lower_limit = %s, ratio = %s, "
698
                      "     offset_constant = %s, is_trend = %s, is_virtual = %s, address = %s, description = %s, "
699
                      "     faults = %s, definitions = %s "
700
                      " WHERE id = %s ")
701
        cursor.execute(update_row, (name,
702
                                    data_source_id,
703
                                    object_type,
704
                                    units,
705
                                    high_limit,
706
                                    low_limit,
707
                                    higher_limit,
708
                                    lower_limit,
709
                                    ratio,
710
                                    offset_constant,
711
                                    is_trend,
712
                                    is_virtual,
713
                                    address,
714
                                    description,
715
                                    faults,
716
                                    definitions,
717
                                    id_,))
718
        cnx.commit()
719
720
        cursor.close()
721
        cnx.close()
722
723
        resp.status = falcon.HTTP_200
724
725
726
class PointLimit:
727
    def __init__(self):
728
        pass
729
730
    @staticmethod
731
    def on_options(req, resp, id_):
732
        _ = req
733
        resp.status = falcon.HTTP_200
734
        _ = id_
735
736
    @staticmethod
737
    @user_logger
738
    def on_put(req, resp, id_):
739
        """Handles PUT requests"""
740
        admin_control(req)
741
        try:
742
            raw_json = req.stream.read().decode('utf-8')
743
        except UnicodeDecodeError as ex:
744
            print("Failed to decode request")
745
            raise falcon.HTTPError(status=falcon.HTTP_400,
746
                                   title='API.BAD_REQUEST',
747
                                   description='API.INVALID_ENCODING')
748
        except Exception as ex:
749
            print("Unexpected error reading request stream")
750
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.EXCEPTION', description=str(ex))
751
752
        if not id_.isdigit() or int(id_) <= 0:
753
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
754
                                   description='API.INVALID_POINT_ID')
755
756
        new_values = json.loads(raw_json)
757
758
        if 'high_limit' not in new_values['data'].keys() or \
759
                not (isinstance(new_values['data']['high_limit'], float) or
760
                     isinstance(new_values['data']['high_limit'], int)):
761
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
762
                                   description='API.INVALID_HIGH_LIMIT_VALUE')
763
        high_limit = new_values['data']['high_limit']
764
765
        if 'low_limit' not in new_values['data'].keys() or \
766
                not (isinstance(new_values['data']['low_limit'], float) or
767
                     isinstance(new_values['data']['low_limit'], int)):
768
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
769
                                   description='API.INVALID_LOW_LIMIT_VALUE')
770
        low_limit = new_values['data']['low_limit']
771
772
        if 'higher_limit' not in new_values['data'].keys() or \
773
                not (isinstance(new_values['data']['higher_limit'], float) or
774
                     isinstance(new_values['data']['higher_limit'], int)):
775
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
776
                                   description='API.INVALID_HIGHER_LIMIT_VALUE')
777
        higher_limit = new_values['data']['higher_limit']
778
779
        if 'lower_limit' not in new_values['data'].keys() or \
780
                not (isinstance(new_values['data']['lower_limit'], float) or
781
                     isinstance(new_values['data']['lower_limit'], int)):
782
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
783
                                   description='API.INVALID_LOWER_LIMIT_VALUE')
784
        lower_limit = new_values['data']['lower_limit']
785
786
        cnx = mysql.connector.connect(**config.myems_system_db)
787
        cursor = cnx.cursor()
788
789
        cursor.execute(" SELECT name "
790
                       " FROM tbl_points "
791
                       " WHERE id = %s ", (id_,))
792
        if cursor.fetchone() is None:
793
            cursor.close()
794
            cnx.close()
795
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
796
                                   description='API.POINT_NOT_FOUND')
797
798
        update_row = (" UPDATE tbl_points "
799
                      " SET  high_limit = %s, low_limit = %s, higher_limit = %s, lower_limit = %s "
800
                      " WHERE id = %s ")
801
        cursor.execute(update_row, (high_limit,
802
                                    low_limit,
803
                                    higher_limit,
804
                                    lower_limit,
805
                                    id_,))
806
        cnx.commit()
807
808
        cursor.close()
809
        cnx.close()
810
811
        resp.status = falcon.HTTP_200
812
813
814
class PointSetValue:
815
    def __init__(self):
816
        pass
817
818
    @staticmethod
819
    def on_options(req, resp, id_):
820
        _ = req
821
        resp.status = falcon.HTTP_200
822
        _ = id_
823
824
    @staticmethod
825
    @user_logger
826
    def on_put(req, resp, id_):
827
        """Handles PUT requests"""
828
        admin_control(req)
829
        try:
830
            raw_json = req.stream.read().decode('utf-8')
831
        except UnicodeDecodeError as ex:
832
            print("Failed to decode request")
833
            raise falcon.HTTPError(status=falcon.HTTP_400,
834
                                   title='API.BAD_REQUEST',
835
                                   description='API.INVALID_ENCODING')
836
        except Exception as ex:
837
            print("Unexpected error reading request stream")
838
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.EXCEPTION', description=str(ex))
839
840
        if not id_.isdigit() or int(id_) <= 0:
841
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
842
                                   description='API.INVALID_POINT_ID')
843
844
        new_values = json.loads(raw_json)
845
846
        if 'set_value' not in new_values['data'].keys() or \
847
                not (isinstance(new_values['data']['set_value'], float) or
848
                     isinstance(new_values['data']['set_value'], int)):
849
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
850
                                   description='API.INVALID_SET_VALUE')
851
        set_value = new_values['data']['set_value']
852
853
        cnx = mysql.connector.connect(**config.myems_system_db)
854
        cursor = cnx.cursor()
855
856
        cursor.execute(" SELECT name "
857
                       " FROM tbl_points "
858
                       " WHERE id = %s ", (id_,))
859
        if cursor.fetchone() is None:
860
            cursor.close()
861
            cnx.close()
862
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
863
                                   description='API.POINT_NOT_FOUND')
864
865
        add_value = (" INSERT INTO tbl_points_set_values (point_id, set_value, utc_date_time) "
866
                     " VALUES (%s, %s, %s) ")
867
        cursor.execute(add_value, (id_, set_value, datetime.utcnow()))
868
        cnx.commit()
869
        cursor.close()
870
        cnx.close()
871
872
        resp.status = falcon.HTTP_200
873
874
875
class PointExport:
876
    def __init__(self):
877
        pass
878
879
    @staticmethod
880
    def on_options(req, resp, id_):
881
        _ = req
882
        resp.status = falcon.HTTP_200
883
        _ = id_
884
885 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
886
    def on_get(req, resp, id_):
887
        """Handles GET requests"""
888
        admin_control(req)
889
        if not id_.isdigit() or int(id_) <= 0:
890
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
891
                                   description='API.INVALID_POINT_ID')
892
893
        cnx = mysql.connector.connect(**config.myems_system_db)
894
        cursor = cnx.cursor()
895
896
        query = (" SELECT id, name, uuid "
897
                 " FROM tbl_data_sources ")
898
        cursor.execute(query)
899
        rows_data_sources = cursor.fetchall()
900
901
        data_source_dict = dict()
902
        if rows_data_sources is not None and len(rows_data_sources) > 0:
903
            for row in rows_data_sources:
904
                data_source_dict[row[0]] = {"id": row[0],
905
                                            "name": row[1],
906
                                            "uuid": row[2]}
907
908
        query = (" SELECT id, name, data_source_id, object_type, units, "
909
                 "        high_limit, low_limit, higher_limit, lower_limit, ratio, offset_constant, "
910
                 "        is_trend, is_virtual, address, description, faults, definitions "
911
                 " FROM tbl_points "
912
                 " WHERE id = %s ")
913
        cursor.execute(query, (id_,))
914
        row = cursor.fetchone()
915
        cursor.close()
916
        cnx.close()
917
        if row is None:
918
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
919
                                   description='API.POINT_NOT_FOUND')
920
921
        result = {"id": row[0],
922
                  "name": row[1],
923
                  "data_source": data_source_dict.get(row[2], None),
924
                  "object_type": row[3],
925
                  "units": row[4],
926
                  "high_limit": row[5],
927
                  "low_limit": row[6],
928
                  "higher_limit": row[7],
929
                  "lower_limit": row[8],
930
                  "ratio": Decimal(row[9]),
931
                  "offset_constant": Decimal(row[10]),
932
                  "is_trend": bool(row[11]),
933
                  "is_virtual": bool(row[12]),
934
                  "address": row[13],
935
                  "description": row[14],
936
                  "faults": row[15],
937
                  "definitions": row[16]}
938
        resp.text = json.dumps(result)
939
940
941
class PointImport:
942
    def __init__(self):
943
        pass
944
945
    @staticmethod
946
    def on_options(req, resp):
947
        _ = req
948
        resp.status = falcon.HTTP_200
949
950
    @staticmethod
951
    @user_logger
952
    def on_post(req, resp):
953
        """Handles POST requests"""
954
        admin_control(req)
955
        try:
956
            raw_json = req.stream.read().decode('utf-8')
957
        except UnicodeDecodeError as ex:
958
            print("Failed to decode request")
959
            raise falcon.HTTPError(status=falcon.HTTP_400,
960
                                   title='API.BAD_REQUEST',
961
                                   description='API.INVALID_ENCODING')
962
        except Exception as ex:
963
            print("Unexpected error reading request stream")
964
            raise falcon.HTTPError(status=falcon.HTTP_400,
965
                                   title='API.BAD_REQUEST',
966
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
967
968
        new_values = json.loads(raw_json)
969
970
        if 'name' not in new_values.keys() or \
971
                not isinstance(new_values['name'], str) or \
972
                len(str.strip(new_values['name'])) == 0:
973
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
974
                                   description='API.INVALID_POINT_NAME')
975
        name = str.strip(new_values['name'])
976
977
        if 'id' not in new_values['data_source'].keys() or \
978
                not isinstance(new_values['data_source']['id'], int) or \
979
                new_values['data_source']['id'] <= 0:
980
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
981
                                   description='API.INVALID_DATA_SOURCE_ID')
982
        data_source_id = new_values['data_source']['id']
983
984
        if 'object_type' not in new_values.keys() \
985
                or str.strip(new_values['object_type']) not in (
986
                'ENERGY_VALUE', 'ANALOG_VALUE', 'DIGITAL_VALUE', 'TEXT_VALUE'):
987
            raise falcon.HTTPError(status=falcon.HTTP_400,
988
                                   title='API.BAD_REQUEST',
989
                                   description='API.INVALID_OBJECT_TYPE')
990
        object_type = str.strip(new_values['object_type'])
991
992
        if 'units' not in new_values.keys() or \
993
                not isinstance(new_values['units'], str) or \
994
                len(str.strip(new_values['units'])) == 0:
995
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
996
                                   description='API.INVALID_UNITS')
997
        units = str.strip(new_values['units'])
998
999
        if 'high_limit' not in new_values.keys() or \
1000
                not (isinstance(new_values['high_limit'], float) or
1001
                     isinstance(new_values['high_limit'], int)):
1002
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1003
                                   description='API.INVALID_HIGH_LIMIT_VALUE')
1004
        high_limit = new_values['high_limit']
1005
1006
        if 'low_limit' not in new_values.keys() or \
1007
                not (isinstance(new_values['low_limit'], float) or
1008
                     isinstance(new_values['low_limit'], int)):
1009
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1010
                                   description='API.INVALID_LOW_LIMIT_VALUE')
1011
        low_limit = new_values['low_limit']
1012
1013
        # the higher_limit is optional
1014
        if 'higher_limit' not in new_values.keys() or \
1015
                new_values['higher_limit'] is None:
1016
            higher_limit = None
1017
        elif not (isinstance(new_values['higher_limit'], float) or
1018
                  isinstance(new_values['higher_limit'], int)):
1019
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1020
                                   description='API.INVALID_HIGHER_LIMIT_VALUE')
1021
        else:
1022
            higher_limit = new_values['higher_limit']
1023
1024
        # the lower_limit is optional
1025
        if 'lower_limit' not in new_values.keys() or \
1026
                new_values['lower_limit'] is None:
1027
            lower_limit = None
1028
        elif not (isinstance(new_values['lower_limit'], float) or
1029
                  isinstance(new_values['lower_limit'], int)):
1030
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1031
                                   description='API.INVALID_LOWER_LIMIT_VALUE')
1032
        else:
1033
            lower_limit = new_values['lower_limit']
1034
1035
        if 'ratio' not in new_values.keys() or \
1036
                not (isinstance(new_values['ratio'], float) or
1037
                     isinstance(new_values['ratio'], int)):
1038
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1039
                                   description='API.INVALID_RATIO_VALUE')
1040
        ratio = new_values['ratio']
1041
1042
        if 'offset_constant' not in new_values.keys() or \
1043
                not (isinstance(new_values['offset_constant'], float) or
1044
                     isinstance(new_values['offset_constant'], int)):
1045
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1046
                                   description='API.INVALID_OFFSET_CONSTANT_VALUE')
1047
        offset_constant = new_values['offset_constant']
1048
1049
        if 'is_trend' not in new_values.keys() or \
1050
                not isinstance(new_values['is_trend'], bool):
1051
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1052
                                   description='API.INVALID_IS_TREND_VALUE')
1053
        is_trend = new_values['is_trend']
1054
1055
        if 'is_virtual' not in new_values.keys() or \
1056
                not isinstance(new_values['is_virtual'], bool):
1057
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1058
                                   description='API.INVALID_IS_VIRTUAL_VALUE')
1059
        if new_values['is_virtual'] is True and object_type == 'DIGITAL_VALUE':
1060
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1061
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_DIGITAL_VALUE')
1062
        if new_values['is_virtual'] is True and object_type == 'TEXT_VALUE':
1063
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1064
                                   description='API.VIRTUAL_POINT_CAN_NOT_BE_TEXT_VALUE')
1065
        is_virtual = new_values['is_virtual']
1066
1067
        if 'address' not in new_values.keys() or \
1068
                not isinstance(new_values['address'], str) or \
1069
                len(str.strip(new_values['address'])) == 0:
1070
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1071
                                   description='API.INVALID_ADDRESS')
1072
        address = str.strip(new_values['address'])
1073
1074
        if 'description' in new_values.keys() and \
1075
                new_values['description'] is not None and \
1076
                len(str(new_values['description'])) > 0:
1077
            description = str.strip(new_values['description'])
1078
        else:
1079
            description = None
1080
1081
        if 'faults' in new_values.keys() and \
1082
                new_values['faults'] is not None and \
1083
                len(str(new_values['faults'])) > 0:
1084
            faults = str.strip(new_values['faults'])
1085
        else:
1086
            faults = None
1087
1088
        if 'definitions' in new_values.keys() and \
1089
                new_values['definitions'] is not None and \
1090
                len(str(new_values['definitions'])) > 0:
1091
            definitions = str.strip(new_values['definitions'])
1092
        else:
1093
            definitions = None
1094
1095
        cnx = mysql.connector.connect(**config.myems_system_db)
1096
        cursor = cnx.cursor()
1097
1098
        cursor.execute(" SELECT name "
1099
                       " FROM tbl_points "
1100
                       " WHERE name = %s AND data_source_id = %s ", (name, data_source_id))
1101
        if cursor.fetchone() is not None:
1102
            cursor.close()
1103
            cnx.close()
1104
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1105
                                   description='API.POINT_NAME_IS_ALREADY_IN_USE')
1106
1107
        cursor.execute(" SELECT name "
1108
                       " FROM tbl_data_sources "
1109
                       " WHERE id = %s ", (data_source_id,))
1110
        if cursor.fetchone() is None:
1111
            cursor.close()
1112
            cnx.close()
1113
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1114
                                   description='API.INVALID_DATA_SOURCE_ID')
1115
1116
        add_value = (" INSERT INTO tbl_points (name, data_source_id, object_type, units, "
1117
                     "                         high_limit, low_limit, higher_limit, lower_limit, ratio, "
1118
                     "                         offset_constant, is_trend, is_virtual, address, description, faults, "
1119
                     "                         definitions) "
1120
                     " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
1121
        cursor.execute(add_value, (name,
1122
                                   data_source_id,
1123
                                   object_type,
1124
                                   units,
1125
                                   high_limit,
1126
                                   low_limit,
1127
                                   higher_limit,
1128
                                   lower_limit,
1129
                                   ratio,
1130
                                   offset_constant,
1131
                                   is_trend,
1132
                                   is_virtual,
1133
                                   address,
1134
                                   description,
1135
                                   faults,
1136
                                   definitions))
1137
        new_id = cursor.lastrowid
1138
        cnx.commit()
1139
        cursor.close()
1140
        cnx.close()
1141
1142
        resp.status = falcon.HTTP_201
1143
        resp.location = '/points/' + str(new_id)
1144
1145
1146
class PointClone:
1147
    def __init__(self):
1148
        pass
1149
1150
    @staticmethod
1151
    def on_options(req, resp, id_):
1152
        _ = req
1153
        resp.status = falcon.HTTP_200
1154
        _ = id_
1155
1156
    @staticmethod
1157
    @user_logger
1158
    def on_post(req, resp, id_):
1159
        admin_control(req)
1160
        if not id_.isdigit() or int(id_) <= 0:
1161
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1162
                                   description='API.INVALID_POINT_ID')
1163
1164
        cnx = mysql.connector.connect(**config.myems_system_db)
1165
        cursor = cnx.cursor()
1166
1167
        query = (" SELECT id, name, data_source_id, object_type, units, "
1168
                 "        high_limit, low_limit, higher_limit, lower_limit, ratio, offset_constant, "
1169
                 "        is_trend, is_virtual, address, description, faults, definitions "
1170
                 " FROM tbl_points "
1171
                 " WHERE id = %s ")
1172
        cursor.execute(query, (id_,))
1173
        row = cursor.fetchone()
1174
        if row is None:
1175
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1176
                                   description='API.POINT_NOT_FOUND')
1177
1178
        result = {"id": row[0],
1179
                  "name": row[1],
1180
                  "data_source_id": row[2],
1181
                  "object_type": row[3],
1182
                  "units": row[4],
1183
                  "high_limit": row[5],
1184
                  "low_limit": row[6],
1185
                  "higher_limit": row[7],
1186
                  "lower_limit": row[8],
1187
                  "ratio": Decimal(row[9]),
1188
                  "offset_constant": Decimal(row[10]),
1189
                  "is_trend": bool(row[11]),
1190
                  "is_virtual": bool(row[12]),
1191
                  "address": row[13],
1192
                  "description": row[14],
1193
                  "faults": row[15],
1194
                  "definitions": row[16]}
1195
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
1196
        if config.utc_offset[0] == '-':
1197
            timezone_offset = -timezone_offset
1198
        new_name = (str.strip(result['name']) +
1199
                    (datetime.utcnow() + timedelta(minutes=timezone_offset)).isoformat(sep='-', timespec='seconds'))
1200
        add_value = (" INSERT INTO tbl_points (name, data_source_id, object_type, units, "
1201
                     "                         high_limit, low_limit, higher_limit, lower_limit, ratio, "
1202
                     "                         offset_constant, is_trend, is_virtual, address, description, faults, "
1203
                     "                         definitions) "
1204
                     " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
1205
        cursor.execute(add_value, (new_name,
1206
                                   result['data_source_id'],
1207
                                   result['object_type'],
1208
                                   result['units'],
1209
                                   result['high_limit'],
1210
                                   result['low_limit'],
1211
                                   result['higher_limit'],
1212
                                   result['lower_limit'],
1213
                                   result['ratio'],
1214
                                   result['offset_constant'],
1215
                                   result['is_trend'],
1216
                                   result['is_virtual'],
1217
                                   result['address'],
1218
                                   result['description'],
1219
                                   result['faults'],
1220
                                   result['definitions']))
1221
        new_id = cursor.lastrowid
1222
        cnx.commit()
1223
        cursor.close()
1224
        cnx.close()
1225
1226
        resp.status = falcon.HTTP_201
1227
        resp.location = '/points/' + str(new_id)
1228
1229