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

core.tenant.clear_tenant_cache()   B

Complexity

Conditions 6

Size

Total Lines 54
Code Lines 39

Duplication

Lines 54
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 39
dl 54
loc 54
rs 8.0106
c 0
b 0
f 0
cc 6
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
import uuid
2
from datetime import datetime, timedelta, timezone
3
import falcon
4
import mysql.connector
5
import simplejson as json
6
import redis
7
from core.useractivity import user_logger, admin_control, access_control, api_key_control
8
import config
9
10
11 View Code Duplication
def clear_tenant_cache(tenant_id=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
12
    """
13
    Clear tenant-related cache after data modification
14
15
    Args:
16
        tenant_id: Tenant ID (optional, for specific tenant cache)
17
    """
18
    # Check if Redis is enabled
19
    if not config.redis.get('is_enabled', False):
20
        return
21
22
    redis_client = None
23
    try:
24
        redis_client = redis.Redis(
25
            host=config.redis['host'],
26
            port=config.redis['port'],
27
            password=config.redis['password'] if config.redis['password'] else None,
28
            db=config.redis['db'],
29
            decode_responses=True,
30
            socket_connect_timeout=2,
31
            socket_timeout=2
32
        )
33
        redis_client.ping()
34
35
        # Clear tenant list cache (all search query variations)
36
        list_cache_key_pattern = 'tenant:list:*'
37
        matching_keys = redis_client.keys(list_cache_key_pattern)
38
        if matching_keys:
39
            redis_client.delete(*matching_keys)
40
41
        # Clear specific tenant item cache if tenant_id is provided
42
        if tenant_id:
43
            item_cache_key = f'tenant:item:{tenant_id}'
44
            redis_client.delete(item_cache_key)
45
            meter_cache_key = f'tenant:meter:{tenant_id}'
46
            redis_client.delete(meter_cache_key)
47
            offlinemeter_cache_key = f'tenant:offlinemeter:{tenant_id}'
48
            redis_client.delete(offlinemeter_cache_key)
49
            point_cache_key = f'tenant:point:{tenant_id}'
50
            redis_client.delete(point_cache_key)
51
            sensor_cache_key = f'tenant:sensor:{tenant_id}'
52
            redis_client.delete(sensor_cache_key)
53
            virtualmeter_cache_key = f'tenant:virtualmeter:{tenant_id}'
54
            redis_client.delete(virtualmeter_cache_key)
55
            workingcalendar_cache_key = f'tenant:workingcalendar:{tenant_id}'
56
            redis_client.delete(workingcalendar_cache_key)
57
            command_cache_key = f'tenant:command:{tenant_id}'
58
            redis_client.delete(command_cache_key)
59
            export_cache_key = f'tenant:export:{tenant_id}'
60
            redis_client.delete(export_cache_key)
61
62
    except Exception:
63
        # If cache clear fails, ignore and continue
64
        pass
65
66
67
class TenantCollection:
68
    """
69
    Tenant Collection Resource
70
71
    This class handles CRUD operations for tenant collection.
72
    It provides endpoints for listing all tenants and creating new ones.
73
    Tenants represent organizations or entities that occupy spaces within
74
    facilities, with associated energy consumption patterns, billing,
75
    and management requirements.
76
    """
77
    def __init__(self):
78
        pass
79
80
    @staticmethod
81
    def on_options(req, resp):
82
        """
83
        Handle OPTIONS request for CORS preflight
84
85
        Args:
86
            req: Falcon request object
87
            resp: Falcon response object
88
        """
89
        _ = req
90
        resp.status = falcon.HTTP_200
91
92
    @staticmethod
93
    def on_get(req, resp):
94
        """
95
        Handle GET requests to retrieve all tenants
96
97
        Returns a list of all tenants with their complete information including:
98
        - Tenant ID, name, and UUID
99
        - Associated tenant type, contact, and cost center information
100
        - Tenant specifications and parameters
101
        - Related spaces and meter associations
102
        - Billing and contract information
103
104
        Args:
105
            req: Falcon request object
106
            resp: Falcon response object
107
        """
108
        # Check authentication method (API key or session)
109
        if 'API-KEY' not in req.headers or \
110
                not isinstance(req.headers['API-KEY'], str) or \
111
                len(str.strip(req.headers['API-KEY'])) == 0:
112
            access_control(req)
113
        else:
114
            api_key_control(req)
115
        search_query = req.get_param('q', default=None)
116
        if search_query is not None:
117
            search_query = search_query.strip()
118
        else:
119
            search_query = ''
120
121
        # Redis cache key
122
        cache_key = f'tenant:list:{search_query}'
123
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
124
125
        # Try to get from Redis cache (only if Redis is enabled)
126
        redis_client = None
127
        if config.redis.get('is_enabled', False):
128
            try:
129
                redis_client = redis.Redis(
130
                    host=config.redis['host'],
131
                    port=config.redis['port'],
132
                    password=config.redis['password'] if config.redis['password'] else None,
133
                    db=config.redis['db'],
134
                    decode_responses=True,
135
                    socket_connect_timeout=2,
136
                    socket_timeout=2
137
                )
138
                redis_client.ping()
139
                cached_result = redis_client.get(cache_key)
140
                if cached_result:
141
                    resp.text = cached_result
142
                    return
143
            except Exception:
144
                # If Redis connection fails, continue to database query
145
                pass
146
147
        # Cache miss or Redis error - query database
148
        cnx = mysql.connector.connect(**config.myems_system_db)
149
        cursor = cnx.cursor()
150
151
        query = (" SELECT id, name, uuid "
152
                 " FROM tbl_tenant_types ")
153
        cursor.execute(query)
154
        rows_tenant_types = cursor.fetchall()
155
156
        tenant_type_dict = dict()
157
        if rows_tenant_types is not None and len(rows_tenant_types) > 0:
158
            for row in rows_tenant_types:
159
                tenant_type_dict[row[0]] = {"id": row[0],
160
                                            "name": row[1],
161
                                            "uuid": row[2]}
162
163
        query = (" SELECT id, name, uuid "
164
                 " FROM tbl_contacts ")
165
        cursor.execute(query)
166
        rows_contacts = cursor.fetchall()
167
168
        contact_dict = dict()
169
        if rows_contacts is not None and len(rows_contacts) > 0:
170
            for row in rows_contacts:
171
                contact_dict[row[0]] = {"id": row[0],
172
                                        "name": row[1],
173
                                        "uuid": row[2]}
174
175
        query = (" SELECT id, name, uuid "
176
                 " FROM tbl_cost_centers ")
177
        cursor.execute(query)
178
        rows_cost_centers = cursor.fetchall()
179
180
        cost_center_dict = dict()
181
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
182
            for row in rows_cost_centers:
183
                cost_center_dict[row[0]] = {"id": row[0],
184
                                            "name": row[1],
185
                                            "uuid": row[2]}
186
187
        query = (" SELECT id, name, uuid, "
188
                 "        buildings, floors, rooms, area, tenant_type_id, "
189
                 "        is_input_counted, is_key_tenant, "
190
                 "        lease_number, lease_start_datetime_utc, lease_end_datetime_utc, is_in_lease, "
191
                 "        contact_id, cost_center_id, description "
192
                 " FROM tbl_tenants ")
193
        params = []
194
195
        if search_query:
196
            query += " WHERE name LIKE %s OR description LIKE %s OR buildings LIKE %s OR floors LIKE %s OR rooms LIKE %s OR lease_number LIKE %s "
197
            params = [f'%{search_query}%', f'%{search_query}%', f'%{search_query}%', f'%{search_query}%', f'%{search_query}%', f'%{search_query}%']
198
        query += " ORDER BY id "
199
200
        cursor.execute(query,params)
201
        rows_spaces = cursor.fetchall()
202
        cursor.close()
203
        cnx.close()
204
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
205
        if config.utc_offset[0] == '-':
206
            timezone_offset = -timezone_offset
207
208
        result = list()
209
        if rows_spaces is not None and len(rows_spaces) > 0:
210
            for row in rows_spaces:
211
212
                meta_result = {"id": row[0],
213
                               "name": row[1],
214
                               "uuid": row[2],
215
                               "buildings": row[3],
216
                               "floors": row[4],
217
                               "rooms": row[5],
218
                               "area": row[6],
219
                               "tenant_type": tenant_type_dict.get(row[7], None),
220
                               "is_input_counted": bool(row[8]),
221
                               "is_key_tenant": bool(row[9]),
222
                               "lease_number": row[10],
223
                               "lease_start_datetime":
224
                                   (row[11].replace(tzinfo=timezone.utc) +
225
                                    timedelta(minutes=timezone_offset)).isoformat()[0:19],
226
                               "lease_end_datetime": (row[12].replace(tzinfo=timezone.utc) +
227
                                                      timedelta(minutes=timezone_offset)).isoformat()[0:19],
228
                               "is_in_lease": bool(row[13]),
229
                               "contact": contact_dict.get(row[14], None),
230
                               "cost_center": cost_center_dict.get(row[15], None),
231
                               "description": row[16],
232
                               "qrcode": 'tenant:' + row[2]}
233
                result.append(meta_result)
234
235
        result_json = json.dumps(result)
236
        resp.text = result_json
237
238
        # Store result in Redis cache
239
        if redis_client:
240
            try:
241
                redis_client.setex(cache_key, cache_expire, result_json)
242
            except Exception:
243
                # If cache store fails, ignore and continue
244
                pass
245
246
    @staticmethod
247
    @user_logger
248
    def on_post(req, resp):
249
        """Handles POST requests"""
250
        admin_control(req)
251
        try:
252
            raw_json = req.stream.read().decode('utf-8')
253
        except UnicodeDecodeError as ex:
254
            print("Failed to decode request")
255
            raise falcon.HTTPError(status=falcon.HTTP_400,
256
                                   title='API.BAD_REQUEST',
257
                                   description='API.INVALID_ENCODING')
258
        except Exception as ex:
259
            print("Unexpected error reading request stream")
260
            raise falcon.HTTPError(status=falcon.HTTP_400,
261
                                   title='API.BAD_REQUEST',
262
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
263
264
        new_values = json.loads(raw_json)
265
266
        if 'name' not in new_values['data'].keys() or \
267
                not isinstance(new_values['data']['name'], str) or \
268
                len(str.strip(new_values['data']['name'])) == 0:
269
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
270
                                   description='API.INVALID_TENANT_NAME')
271
        name = str.strip(new_values['data']['name'])
272
273
        if 'buildings' not in new_values['data'].keys() or \
274
                not isinstance(new_values['data']['buildings'], str) or \
275
                len(str.strip(new_values['data']['buildings'])) == 0:
276
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
277
                                   description='API.INVALID_BUILDINGS_VALUE')
278
        buildings = str.strip(new_values['data']['buildings'])
279
280
        if 'floors' not in new_values['data'].keys() or \
281
                not isinstance(new_values['data']['floors'], str) or \
282
                len(str.strip(new_values['data']['floors'])) == 0:
283
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
284
                                   description='API.INVALID_FLOORS_VALUE')
285
        floors = str.strip(new_values['data']['floors'])
286
287
        if 'rooms' not in new_values['data'].keys() or \
288
                not isinstance(new_values['data']['rooms'], str) or \
289
                len(str.strip(new_values['data']['rooms'])) == 0:
290
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
291
                                   description='API.INVALID_ROOMS_VALUE')
292
        rooms = str.strip(new_values['data']['rooms'])
293
294
        if 'area' not in new_values['data'].keys() or \
295
                not (isinstance(new_values['data']['area'], float) or
296
                     isinstance(new_values['data']['area'], int)) or \
297
                new_values['data']['area'] <= 0.0:
298
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
299
                                   description='API.INVALID_AREA_VALUE')
300
        area = new_values['data']['area']
301
302
        if 'tenant_type_id' not in new_values['data'].keys() or \
303
                not isinstance(new_values['data']['tenant_type_id'], int) or \
304
                new_values['data']['tenant_type_id'] <= 0:
305
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
306
                                   description='API.INVALID_TENANT_TYPE_ID')
307
        tenant_type_id = new_values['data']['tenant_type_id']
308
309
        if 'is_input_counted' not in new_values['data'].keys() or \
310
                not isinstance(new_values['data']['is_input_counted'], bool):
311
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
312
                                   description='API.INVALID_IS_INPUT_COUNTED_VALUE')
313
        is_input_counted = new_values['data']['is_input_counted']
314
315
        if 'is_key_tenant' not in new_values['data'].keys() or \
316
                not isinstance(new_values['data']['is_key_tenant'], bool):
317
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
318
                                   description='API.INVALID_IS_KEY_TENANT_VALUE')
319
        is_key_tenant = new_values['data']['is_key_tenant']
320
321
        if 'lease_number' not in new_values['data'].keys() or \
322
                not isinstance(new_values['data']['lease_number'], str) or \
323
                len(str.strip(new_values['data']['lease_number'])) == 0:
324
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
325
                                   description='API.INVALID_LEASE_NUMBER_VALUE')
326
        lease_number = str.strip(new_values['data']['lease_number'])
327
328
        if 'is_in_lease' not in new_values['data'].keys() or \
329
                not isinstance(new_values['data']['is_in_lease'], bool):
330
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
331
                                   description='API.INVALID_IS_IN_LEASE_VALUE')
332
        is_in_lease = new_values['data']['is_in_lease']
333
334
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
335
        if config.utc_offset[0] == '-':
336
            timezone_offset = -timezone_offset
337
338
        # todo: validate datetime values
339
        lease_start_datetime_utc = datetime.strptime(new_values['data']['lease_start_datetime'],
340
                                                     '%Y-%m-%dT%H:%M:%S')
341
        lease_start_datetime_utc = lease_start_datetime_utc.replace(tzinfo=timezone.utc)
342
        lease_start_datetime_utc -= timedelta(minutes=timezone_offset)
343
344
        lease_end_datetime_utc = datetime.strptime(new_values['data']['lease_end_datetime'],
345
                                                   '%Y-%m-%dT%H:%M:%S')
346
        lease_end_datetime_utc = lease_end_datetime_utc.replace(tzinfo=timezone.utc)
347
        lease_end_datetime_utc -= timedelta(minutes=timezone_offset)
348
349
        if 'contact_id' not in new_values['data'].keys() or \
350
                not isinstance(new_values['data']['contact_id'], int) or \
351
                new_values['data']['contact_id'] <= 0:
352
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
353
                                   description='API.INVALID_CONTACT_ID')
354
        contact_id = new_values['data']['contact_id']
355
356
        if 'cost_center_id' not in new_values['data'].keys() or \
357
                not isinstance(new_values['data']['cost_center_id'], int) or \
358
                new_values['data']['cost_center_id'] <= 0:
359
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
360
                                   description='API.INVALID_COST_CENTER_ID')
361
        cost_center_id = new_values['data']['cost_center_id']
362
363
        if 'description' in new_values['data'].keys() and \
364
                new_values['data']['description'] is not None and \
365
                len(str(new_values['data']['description'])) > 0:
366
            description = str.strip(new_values['data']['description'])
367
        else:
368
            description = None
369
370
        cnx = mysql.connector.connect(**config.myems_system_db)
371
        cursor = cnx.cursor()
372
373
        cursor.execute(" SELECT name "
374
                       " FROM tbl_tenants "
375
                       " WHERE name = %s ", (name,))
376
        if cursor.fetchone() is not None:
377
            cursor.close()
378
            cnx.close()
379
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
380
                                   description='API.TENANT_NAME_IS_ALREADY_IN_USE')
381
382
        cursor.execute(" SELECT name "
383
                       " FROM tbl_tenant_types "
384
                       " WHERE id = %s ",
385
                       (tenant_type_id,))
386
        if cursor.fetchone() is None:
387
            cursor.close()
388
            cnx.close()
389
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
390
                                   description='API.TENANT_TYPE_NOT_FOUND')
391
392
        cursor.execute(" SELECT name "
393
                       " FROM tbl_contacts "
394
                       " WHERE id = %s ",
395
                       (new_values['data']['contact_id'],))
396
        row = cursor.fetchone()
397
        if row is None:
398
            cursor.close()
399
            cnx.close()
400
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
401
                                   description='API.CONTACT_NOT_FOUND')
402
403
        cursor.execute(" SELECT name "
404
                       " FROM tbl_cost_centers "
405
                       " WHERE id = %s ",
406
                       (new_values['data']['cost_center_id'],))
407
        row = cursor.fetchone()
408
        if row is None:
409
            cursor.close()
410
            cnx.close()
411
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
412
                                   description='API.COST_CENTER_NOT_FOUND')
413
414
        add_values = (" INSERT INTO tbl_tenants "
415
                      "    (name, uuid, buildings, floors, rooms, area, tenant_type_id, "
416
                      "     is_input_counted, is_key_tenant, "
417
                      "     lease_number, lease_start_datetime_utc, lease_end_datetime_utc, is_in_lease, "
418
                      "     contact_id, cost_center_id, description) "
419
                      " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
420
        cursor.execute(add_values, (name,
421
                                    str(uuid.uuid4()),
422
                                    buildings,
423
                                    floors,
424
                                    rooms,
425
                                    area,
426
                                    tenant_type_id,
427
                                    is_input_counted,
428
                                    is_key_tenant,
429
                                    lease_number,
430
                                    lease_start_datetime_utc,
431
                                    lease_end_datetime_utc,
432
                                    is_in_lease,
433
                                    contact_id,
434
                                    cost_center_id,
435
                                    description))
436
        new_id = cursor.lastrowid
437
        cnx.commit()
438
        cursor.close()
439
        cnx.close()
440
441
        # Clear cache after creating new tenant
442
        clear_tenant_cache()
443
444
        resp.status = falcon.HTTP_201
445
        resp.location = '/tenants/' + str(new_id)
446
447
448
class TenantItem:
449
    def __init__(self):
450
        pass
451
452
    @staticmethod
453
    def on_options(req, resp, id_):
454
        _ = req
455
        resp.status = falcon.HTTP_200
456
        _ = id_
457
458
    @staticmethod
459
    def on_get(req, resp, id_):
460
        if 'API-KEY' not in req.headers or \
461
                not isinstance(req.headers['API-KEY'], str) or \
462
                len(str.strip(req.headers['API-KEY'])) == 0:
463
            access_control(req)
464
        else:
465
            api_key_control(req)
466
        if not id_.isdigit() or int(id_) <= 0:
467
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
468
                                   description='API.INVALID_TENANT_ID')
469
470
        # Redis cache key
471
        cache_key = f'tenant:item:{id_}'
472
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
473
474
        # Try to get from Redis cache (only if Redis is enabled)
475
        redis_client = None
476
        if config.redis.get('is_enabled', False):
477
            try:
478
                redis_client = redis.Redis(
479
                    host=config.redis['host'],
480
                    port=config.redis['port'],
481
                    password=config.redis['password'] if config.redis['password'] else None,
482
                    db=config.redis['db'],
483
                    decode_responses=True,
484
                    socket_connect_timeout=2,
485
                    socket_timeout=2
486
                )
487
                redis_client.ping()
488
                cached_result = redis_client.get(cache_key)
489
                if cached_result:
490
                    resp.text = cached_result
491
                    return
492
            except Exception:
493
                # If Redis connection fails, continue to database query
494
                pass
495
496
        # Cache miss or Redis error - query database
497
        cnx = mysql.connector.connect(**config.myems_system_db)
498
        cursor = cnx.cursor()
499
500
        query = (" SELECT id, name, uuid "
501
                 " FROM tbl_tenant_types ")
502
        cursor.execute(query)
503
        rows_tenant_types = cursor.fetchall()
504
505
        tenant_type_dict = dict()
506
        if rows_tenant_types is not None and len(rows_tenant_types) > 0:
507
            for row in rows_tenant_types:
508
                tenant_type_dict[row[0]] = {"id": row[0],
509
                                            "name": row[1],
510
                                            "uuid": row[2]}
511
512
        query = (" SELECT id, name, uuid "
513
                 " FROM tbl_contacts ")
514
        cursor.execute(query)
515
        rows_contacts = cursor.fetchall()
516
517
        contact_dict = dict()
518
        if rows_contacts is not None and len(rows_contacts) > 0:
519
            for row in rows_contacts:
520
                contact_dict[row[0]] = {"id": row[0],
521
                                        "name": row[1],
522
                                        "uuid": row[2]}
523
524
        query = (" SELECT id, name, uuid "
525
                 " FROM tbl_cost_centers ")
526
        cursor.execute(query)
527
        rows_cost_centers = cursor.fetchall()
528
529
        cost_center_dict = dict()
530
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
531
            for row in rows_cost_centers:
532
                cost_center_dict[row[0]] = {"id": row[0],
533
                                            "name": row[1],
534
                                            "uuid": row[2]}
535
536
        query = (" SELECT id, name, uuid, "
537
                 "        buildings, floors, rooms, area, tenant_type_id,"
538
                 "        is_key_tenant, is_input_counted, "
539
                 "        lease_number, lease_start_datetime_utc, lease_end_datetime_utc, is_in_lease, "
540
                 "        contact_id, cost_center_id, description "
541
                 " FROM tbl_tenants "
542
                 " WHERE id = %s ")
543
        cursor.execute(query, (id_,))
544
        row = cursor.fetchone()
545
        cursor.close()
546
        cnx.close()
547
548
        if row is None:
549
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
550
                                   description='API.TENANT_NOT_FOUND')
551
        else:
552
            timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
553
            if config.utc_offset[0] == '-':
554
                timezone_offset = -timezone_offset
555
556
            meta_result = {"id": row[0],
557
                           "name": row[1],
558
                           "uuid": row[2],
559
                           "buildings": row[3],
560
                           "floors": row[4],
561
                           "rooms": row[5],
562
                           "area": row[6],
563
                           "tenant_type": tenant_type_dict.get(row[7], None),
564
                           "is_key_tenant": bool(row[8]),
565
                           "is_input_counted": bool(row[9]),
566
                           "lease_number": row[10],
567
                           "lease_start_datetime": (row[11].replace(tzinfo=timezone.utc) +
568
                                                    timedelta(minutes=timezone_offset)).isoformat()[0:19],
569
                           "lease_end_datetime": (row[12].replace(tzinfo=timezone.utc) +
570
                                                  timedelta(minutes=timezone_offset)).isoformat()[0:19],
571
                           "is_in_lease": bool(row[13]),
572
                           "contact": contact_dict.get(row[14], None),
573
                           "cost_center": cost_center_dict.get(row[15], None),
574
                           "description": row[16],
575
                           "qrcode": 'tenant:' + row[2]}
576
577
        result_json = json.dumps(meta_result)
578
        resp.text = result_json
579
580
        # Store result in Redis cache
581
        if redis_client:
582
            try:
583
                redis_client.setex(cache_key, cache_expire, result_json)
584
            except Exception:
585
                # If cache store fails, ignore and continue
586
                pass
587
588 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
589
    @user_logger
590
    def on_delete(req, resp, id_):
591
        admin_control(req)
592
        if not id_.isdigit() or int(id_) <= 0:
593
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
594
                                   description='API.INVALID_TENANT_ID')
595
596
        cnx = mysql.connector.connect(**config.myems_system_db)
597
        cursor = cnx.cursor()
598
599
        cursor.execute(" SELECT name "
600
                       " FROM tbl_tenants "
601
                       " WHERE id = %s ", (id_,))
602
        if cursor.fetchone() is None:
603
            cursor.close()
604
            cnx.close()
605
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
606
                                   description='API.TENANT_NOT_FOUND')
607
608
        # check relation with space
609
        cursor.execute(" SELECT space_id "
610
                       " FROM tbl_spaces_tenants "
611
                       " WHERE tenant_id = %s ",
612
                       (id_,))
613
        rows_spaces = cursor.fetchall()
614
        if rows_spaces is not None and len(rows_spaces) > 0:
615
            cursor.close()
616
            cnx.close()
617
            raise falcon.HTTPError(status=falcon.HTTP_400,
618
                                   title='API.BAD_REQUEST',
619
                                   description='API.THERE_IS_RELATION_WITH_SPACES')
620
621
        # delete relation with meter
622
        cursor.execute(" DELETE FROM tbl_tenants_meters WHERE tenant_id = %s ", (id_,))
623
624
        # delete relation with offline meter
625
        cursor.execute(" DELETE FROM tbl_tenants_offline_meters WHERE tenant_id = %s ", (id_,))
626
627
        # delete relation with points
628
        cursor.execute(" DELETE FROM tbl_tenants_points WHERE tenant_id = %s ", (id_,))
629
630
        # delete relation with sensor
631
        cursor.execute(" DELETE FROM tbl_tenants_sensors WHERE tenant_id = %s ", (id_,))
632
633
        # delete relation with virtual meter
634
        cursor.execute(" DELETE FROM tbl_tenants_virtual_meters WHERE tenant_id = %s ", (id_,))
635
636
        # delete relation with command
637
        cursor.execute(" DELETE FROM tbl_tenants_commands WHERE tenant_id = %s ", (id_,))
638
639
        # delete relation with working calendar
640
        cursor.execute(" DELETE FROM tbl_tenants_working_calendars WHERE tenant_id = %s ", (id_,))
641
642
        cursor.execute(" DELETE FROM tbl_tenants WHERE id = %s ", (id_,))
643
        cnx.commit()
644
645
        cursor.close()
646
        cnx.close()
647
648
        # Clear cache after deleting tenant
649
        clear_tenant_cache(tenant_id=id_)
650
651
        resp.status = falcon.HTTP_204
652
653
    @staticmethod
654
    @user_logger
655
    def on_put(req, resp, id_):
656
        """Handles PUT requests"""
657
        admin_control(req)
658
        try:
659
            raw_json = req.stream.read().decode('utf-8')
660
        except UnicodeDecodeError as ex:
661
            print("Failed to decode request")
662
            raise falcon.HTTPError(status=falcon.HTTP_400,
663
                                   title='API.BAD_REQUEST',
664
                                   description='API.INVALID_ENCODING')
665
        except Exception as ex:
666
            print("Unexpected error reading request stream")
667
            raise falcon.HTTPError(status=falcon.HTTP_400,
668
                                   title='API.BAD_REQUEST',
669
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
670
671
        if not id_.isdigit() or int(id_) <= 0:
672
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
673
                                   description='API.INVALID_TENANT_ID')
674
675
        new_values = json.loads(raw_json)
676
677
        if 'name' not in new_values['data'].keys() or \
678
                not isinstance(new_values['data']['name'], str) or \
679
                len(str.strip(new_values['data']['name'])) == 0:
680
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
681
                                   description='API.INVALID_TENANT_NAME')
682
        name = str.strip(new_values['data']['name'])
683
684
        if 'buildings' not in new_values['data'].keys() or \
685
                not isinstance(new_values['data']['buildings'], str) or \
686
                len(str.strip(new_values['data']['buildings'])) == 0:
687
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
688
                                   description='API.INVALID_BUILDINGS_VALUE')
689
        buildings = str.strip(new_values['data']['buildings'])
690
691
        if 'floors' not in new_values['data'].keys() or \
692
                not isinstance(new_values['data']['floors'], str) or \
693
                len(str.strip(new_values['data']['floors'])) == 0:
694
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
695
                                   description='API.INVALID_FLOORS_VALUE')
696
        floors = str.strip(new_values['data']['floors'])
697
698
        if 'rooms' not in new_values['data'].keys() or \
699
                not isinstance(new_values['data']['rooms'], str) or \
700
                len(str.strip(new_values['data']['rooms'])) == 0:
701
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
702
                                   description='API.INVALID_ROOMS_VALUE')
703
        rooms = str.strip(new_values['data']['rooms'])
704
705
        if 'area' not in new_values['data'].keys() or \
706
                not (isinstance(new_values['data']['area'], float) or
707
                     isinstance(new_values['data']['area'], int)) or \
708
                new_values['data']['area'] <= 0.0:
709
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
710
                                   description='API.INVALID_AREA_VALUE')
711
        area = new_values['data']['area']
712
713
        if 'tenant_type_id' not in new_values['data'].keys() or \
714
                not isinstance(new_values['data']['tenant_type_id'], int) or \
715
                new_values['data']['tenant_type_id'] <= 0:
716
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
717
                                   description='API.INVALID_TENANT_TYPE_ID')
718
        tenant_type_id = new_values['data']['tenant_type_id']
719
720
        if 'is_input_counted' not in new_values['data'].keys() or \
721
                not isinstance(new_values['data']['is_input_counted'], bool):
722
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
723
                                   description='API.INVALID_IS_INPUT_COUNTED_VALUE')
724
        is_input_counted = new_values['data']['is_input_counted']
725
726
        if 'is_key_tenant' not in new_values['data'].keys() or \
727
                not isinstance(new_values['data']['is_key_tenant'], bool):
728
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
729
                                   description='API.INVALID_IS_KEY_TENANT_VALUE')
730
        is_key_tenant = new_values['data']['is_key_tenant']
731
732
        if 'lease_number' not in new_values['data'].keys() or \
733
                not isinstance(new_values['data']['lease_number'], str) or \
734
                len(str.strip(new_values['data']['lease_number'])) == 0:
735
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
736
                                   description='API.INVALID_LEASE_NUMBER_VALUE')
737
        lease_number = str.strip(new_values['data']['lease_number'])
738
739
        if 'is_in_lease' not in new_values['data'].keys() or \
740
                not isinstance(new_values['data']['is_in_lease'], bool):
741
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
742
                                   description='API.INVALID_IS_IN_LEASE_VALUE')
743
        is_in_lease = new_values['data']['is_in_lease']
744
745
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
746
        if config.utc_offset[0] == '-':
747
            timezone_offset = -timezone_offset
748
749
        # todo: validate datetime values
750
        lease_start_datetime_utc = datetime.strptime(new_values['data']['lease_start_datetime'],
751
                                                     '%Y-%m-%dT%H:%M:%S')
752
        lease_start_datetime_utc = lease_start_datetime_utc.replace(tzinfo=timezone.utc)
753
        lease_start_datetime_utc -= timedelta(minutes=timezone_offset)
754
755
        lease_end_datetime_utc = datetime.strptime(new_values['data']['lease_end_datetime'],
756
                                                   '%Y-%m-%dT%H:%M:%S')
757
        lease_end_datetime_utc = lease_end_datetime_utc.replace(tzinfo=timezone.utc)
758
        lease_end_datetime_utc -= timedelta(minutes=timezone_offset)
759
760
        if 'contact_id' not in new_values['data'].keys() or \
761
                not isinstance(new_values['data']['contact_id'], int) or \
762
                new_values['data']['contact_id'] <= 0:
763
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
764
                                   description='API.INVALID_CONTACT_ID')
765
        contact_id = new_values['data']['contact_id']
766
767
        if 'cost_center_id' not in new_values['data'].keys() or \
768
                not isinstance(new_values['data']['cost_center_id'], int) or \
769
                new_values['data']['cost_center_id'] <= 0:
770
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
771
                                   description='API.INVALID_COST_CENTER_ID')
772
        cost_center_id = new_values['data']['cost_center_id']
773
774
        if 'description' in new_values['data'].keys() and \
775
                new_values['data']['description'] is not None and \
776
                len(str(new_values['data']['description'])) > 0:
777
            description = str.strip(new_values['data']['description'])
778
        else:
779
            description = None
780
781
        cnx = mysql.connector.connect(**config.myems_system_db)
782
        cursor = cnx.cursor()
783
784
        cursor.execute(" SELECT name "
785
                       " FROM tbl_tenants "
786
                       " WHERE id = %s ", (id_,))
787
        if cursor.fetchone() is None:
788
            cursor.close()
789
            cnx.close()
790
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
791
                                   description='API.TENANT_NOT_FOUND')
792
793
        cursor.execute(" SELECT name "
794
                       " FROM tbl_tenants "
795
                       " WHERE name = %s AND id != %s ", (name, id_))
796
        if cursor.fetchone() is not None:
797
            cursor.close()
798
            cnx.close()
799
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
800
                                   description='API.TENANT_NAME_IS_ALREADY_IN_USE')
801
802
        cursor.execute(" SELECT name "
803
                       " FROM tbl_tenant_types "
804
                       " WHERE id = %s ",
805
                       (tenant_type_id,))
806
        if cursor.fetchone() is None:
807
            cursor.close()
808
            cnx.close()
809
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
810
                                   description='API.TENANT_TYPE_NOT_FOUND')
811
812
        cursor.execute(" SELECT name "
813
                       " FROM tbl_contacts "
814
                       " WHERE id = %s ",
815
                       (new_values['data']['contact_id'],))
816
        row = cursor.fetchone()
817
        if row is None:
818
            cursor.close()
819
            cnx.close()
820
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
821
                                   description='API.CONTACT_NOT_FOUND')
822
823
        cursor.execute(" SELECT name "
824
                       " FROM tbl_cost_centers "
825
                       " WHERE id = %s ",
826
                       (new_values['data']['cost_center_id'],))
827
        row = cursor.fetchone()
828
        if row is None:
829
            cursor.close()
830
            cnx.close()
831
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
832
                                   description='API.COST_CENTER_NOT_FOUND')
833
834
        update_row = (" UPDATE tbl_tenants "
835
                      " SET name = %s, buildings = %s, floors = %s, rooms = %s, area = %s, "
836
                      "     tenant_type_id = %s, is_input_counted = %s, "
837
                      "     is_key_tenant = %s, lease_number = %s, lease_start_datetime_utc = %s, "
838
                      "     lease_end_datetime_utc = %s, is_in_lease = %s, contact_id = %s, cost_center_id = %s, "
839
                      "     description = %s "
840
                      " WHERE id = %s ")
841
        cursor.execute(update_row, (name,
842
                                    buildings,
843
                                    floors,
844
                                    rooms,
845
                                    area,
846
                                    tenant_type_id,
847
                                    is_input_counted,
848
                                    is_key_tenant,
849
                                    lease_number,
850
                                    lease_start_datetime_utc,
851
                                    lease_end_datetime_utc,
852
                                    is_in_lease,
853
                                    contact_id,
854
                                    cost_center_id,
855
                                    description,
856
                                    id_))
857
        cnx.commit()
858
859
        cursor.close()
860
        cnx.close()
861
862
        # Clear cache after updating tenant
863
        clear_tenant_cache(tenant_id=id_)
864
865
        resp.status = falcon.HTTP_200
866
867
868 View Code Duplication
class TenantMeterCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
869
    def __init__(self):
870
        pass
871
872
    @staticmethod
873
    def on_options(req, resp, id_):
874
        _ = req
875
        resp.status = falcon.HTTP_200
876
        _ = id_
877
878
    @staticmethod
879
    def on_get(req, resp, id_):
880
        if 'API-KEY' not in req.headers or \
881
                not isinstance(req.headers['API-KEY'], str) or \
882
                len(str.strip(req.headers['API-KEY'])) == 0:
883
            access_control(req)
884
        else:
885
            api_key_control(req)
886
        if not id_.isdigit() or int(id_) <= 0:
887
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
888
                                   description='API.INVALID_TENANT_ID')
889
890
        # Redis cache key
891
        cache_key = f'tenant:meter:{id_}'
892
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
893
894
        # Try to get from Redis cache (only if Redis is enabled)
895
        redis_client = None
896
        if config.redis.get('is_enabled', False):
897
            try:
898
                redis_client = redis.Redis(
899
                    host=config.redis['host'],
900
                    port=config.redis['port'],
901
                    password=config.redis['password'] if config.redis['password'] else None,
902
                    db=config.redis['db'],
903
                    decode_responses=True,
904
                    socket_connect_timeout=2,
905
                    socket_timeout=2
906
                )
907
                redis_client.ping()
908
                cached_result = redis_client.get(cache_key)
909
                if cached_result:
910
                    resp.text = cached_result
911
                    return
912
            except Exception:
913
                # If Redis connection fails, continue to database query
914
                pass
915
916
        # Cache miss or Redis error - query database
917
        cnx = mysql.connector.connect(**config.myems_system_db)
918
        cursor = cnx.cursor()
919
920
        cursor.execute(" SELECT name "
921
                       " FROM tbl_tenants "
922
                       " WHERE id = %s ", (id_,))
923
        if cursor.fetchone() is None:
924
            cursor.close()
925
            cnx.close()
926
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
927
                                   description='API.TENANT_NOT_FOUND')
928
929
        query = (" SELECT id, name, uuid "
930
                 " FROM tbl_energy_categories ")
931
        cursor.execute(query)
932
        rows_energy_categories = cursor.fetchall()
933
934
        energy_category_dict = dict()
935
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
936
            for row in rows_energy_categories:
937
                energy_category_dict[row[0]] = {"id": row[0],
938
                                                "name": row[1],
939
                                                "uuid": row[2]}
940
941
        query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id "
942
                 " FROM tbl_tenants t, tbl_tenants_meters tm, tbl_meters m "
943
                 " WHERE tm.tenant_id = t.id AND m.id = tm.meter_id AND t.id = %s "
944
                 " ORDER BY m.id ")
945
        cursor.execute(query, (id_,))
946
        rows = cursor.fetchall()
947
948
        result = list()
949
        if rows is not None and len(rows) > 0:
950
            for row in rows:
951
                meta_result = {"id": row[0],
952
                               "name": row[1],
953
                               "uuid": row[2],
954
                               "energy_category": energy_category_dict.get(row[3], None)}
955
                result.append(meta_result)
956
957
        result_json = json.dumps(result)
958
        resp.text = result_json
959
960
        # Store result in Redis cache
961
        if redis_client:
962
            try:
963
                redis_client.setex(cache_key, cache_expire, result_json)
964
            except Exception:
965
                # If cache store fails, ignore and continue
966
                pass
967
968
    @staticmethod
969
    @user_logger
970
    def on_post(req, resp, id_):
971
        """Handles POST requests"""
972
        admin_control(req)
973
        try:
974
            raw_json = req.stream.read().decode('utf-8')
975
        except UnicodeDecodeError as ex:
976
            print("Failed to decode request")
977
            raise falcon.HTTPError(status=falcon.HTTP_400,
978
                                   title='API.BAD_REQUEST',
979
                                   description='API.INVALID_ENCODING')
980
        except Exception as ex:
981
            print("Unexpected error reading request stream")
982
            raise falcon.HTTPError(status=falcon.HTTP_400,
983
                                   title='API.BAD_REQUEST',
984
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
985
986
        if not id_.isdigit() or int(id_) <= 0:
987
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
988
                                   description='API.INVALID_TENANT_ID')
989
990
        new_values = json.loads(raw_json)
991
992
        if 'meter_id' not in new_values['data'].keys() or \
993
                not isinstance(new_values['data']['meter_id'], int) or \
994
                new_values['data']['meter_id'] <= 0:
995
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
996
                                   description='API.INVALID_METER_ID')
997
        meter_id = new_values['data']['meter_id']
998
999
        cnx = mysql.connector.connect(**config.myems_system_db)
1000
        cursor = cnx.cursor()
1001
1002
        cursor.execute(" SELECT name "
1003
                       " from tbl_tenants "
1004
                       " WHERE id = %s ", (id_,))
1005
        if cursor.fetchone() is None:
1006
            cursor.close()
1007
            cnx.close()
1008
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1009
                                   description='API.TENANT_NOT_FOUND')
1010
1011
        cursor.execute(" SELECT name "
1012
                       " FROM tbl_meters "
1013
                       " WHERE id = %s ", (meter_id,))
1014
        if cursor.fetchone() is None:
1015
            cursor.close()
1016
            cnx.close()
1017
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1018
                                   description='API.METER_NOT_FOUND')
1019
1020
        query = (" SELECT id "
1021
                 " FROM tbl_tenants_meters "
1022
                 " WHERE tenant_id = %s AND meter_id = %s")
1023
        cursor.execute(query, (id_, meter_id,))
1024
        if cursor.fetchone() is not None:
1025
            cursor.close()
1026
            cnx.close()
1027
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
1028
                                   description='API.TENANT_METER_RELATION_EXISTS')
1029
1030
        add_row = (" INSERT INTO tbl_tenants_meters (tenant_id, meter_id) "
1031
                   " VALUES (%s, %s) ")
1032
        cursor.execute(add_row, (id_, meter_id,))
1033
        cnx.commit()
1034
        cursor.close()
1035
        cnx.close()
1036
1037
        # Clear cache after adding meter to tenant
1038
        clear_tenant_cache(tenant_id=id_)
1039
1040
        resp.status = falcon.HTTP_201
1041
        resp.location = '/tenants/' + str(id_) + '/meters/' + str(meter_id)
1042
1043
1044
class TenantMeterItem:
1045
    def __init__(self):
1046
        pass
1047
1048
    @staticmethod
1049
    def on_options(req, resp, id_, mid):
1050
        _ = req
1051
        resp.status = falcon.HTTP_200
1052
        _ = id_
1053
1054
    @staticmethod
1055
    @user_logger
1056
    def on_delete(req, resp, id_, mid):
1057
        admin_control(req)
1058
        if not id_.isdigit() or int(id_) <= 0:
1059
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1060
                                   description='API.INVALID_TENANT_ID')
1061
1062
        if not mid.isdigit() or int(mid) <= 0:
1063
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1064
                                   description='API.INVALID_METER_ID')
1065
1066
        cnx = mysql.connector.connect(**config.myems_system_db)
1067
        cursor = cnx.cursor()
1068
1069
        cursor.execute(" SELECT name "
1070
                       " FROM tbl_tenants "
1071
                       " WHERE id = %s ", (id_,))
1072
        if cursor.fetchone() is None:
1073
            cursor.close()
1074
            cnx.close()
1075
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1076
                                   description='API.TENANT_NOT_FOUND')
1077
1078
        cursor.execute(" SELECT name "
1079
                       " FROM tbl_meters "
1080
                       " WHERE id = %s ", (mid,))
1081
        if cursor.fetchone() is None:
1082
            cursor.close()
1083
            cnx.close()
1084
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1085
                                   description='API.METER_NOT_FOUND')
1086
1087
        cursor.execute(" SELECT id "
1088
                       " FROM tbl_tenants_meters "
1089
                       " WHERE tenant_id = %s AND meter_id = %s ", (id_, mid))
1090
        if cursor.fetchone() is None:
1091
            cursor.close()
1092
            cnx.close()
1093
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1094
                                   description='API.TENANT_METER_RELATION_NOT_FOUND')
1095
1096
        cursor.execute(" DELETE FROM tbl_tenants_meters WHERE tenant_id = %s AND meter_id = %s ", (id_, mid))
1097
        cnx.commit()
1098
1099
        cursor.close()
1100
        cnx.close()
1101
1102
        # Clear cache after removing meter from tenant
1103
        clear_tenant_cache(tenant_id=id_)
1104
1105
        resp.status = falcon.HTTP_204
1106
1107
1108 View Code Duplication
class TenantOfflineMeterCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1109
    def __init__(self):
1110
        pass
1111
1112
    @staticmethod
1113
    def on_options(req, resp, id_):
1114
        _ = req
1115
        resp.status = falcon.HTTP_200
1116
        _ = id_
1117
1118
    @staticmethod
1119
    def on_get(req, resp, id_):
1120
        if 'API-KEY' not in req.headers or \
1121
                not isinstance(req.headers['API-KEY'], str) or \
1122
                len(str.strip(req.headers['API-KEY'])) == 0:
1123
            access_control(req)
1124
        else:
1125
            api_key_control(req)
1126
        if not id_.isdigit() or int(id_) <= 0:
1127
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1128
                                   description='API.INVALID_TENANT_ID')
1129
1130
        cnx = mysql.connector.connect(**config.myems_system_db)
1131
        cursor = cnx.cursor()
1132
1133
        cursor.execute(" SELECT name "
1134
                       " FROM tbl_tenants "
1135
                       " WHERE id = %s ", (id_,))
1136
        if cursor.fetchone() is None:
1137
            cursor.close()
1138
            cnx.close()
1139
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1140
                                   description='API.TENANT_NOT_FOUND')
1141
1142
        query = (" SELECT id, name, uuid "
1143
                 " FROM tbl_energy_categories ")
1144
        cursor.execute(query)
1145
        rows_energy_categories = cursor.fetchall()
1146
1147
        energy_category_dict = dict()
1148
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
1149
            for row in rows_energy_categories:
1150
                energy_category_dict[row[0]] = {"id": row[0],
1151
                                                "name": row[1],
1152
                                                "uuid": row[2]}
1153
1154
        query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id "
1155
                 " FROM tbl_tenants s, tbl_tenants_offline_meters sm, tbl_offline_meters m "
1156
                 " WHERE sm.tenant_id = s.id AND m.id = sm.offline_meter_id AND s.id = %s "
1157
                 " ORDER BY m.id ")
1158
        cursor.execute(query, (id_,))
1159
        rows = cursor.fetchall()
1160
1161
        result = list()
1162
        if rows is not None and len(rows) > 0:
1163
            for row in rows:
1164
                meta_result = {"id": row[0],
1165
                               "name": row[1],
1166
                               "uuid": row[2],
1167
                               "energy_category": energy_category_dict.get(row[3], None)}
1168
                result.append(meta_result)
1169
1170
        resp.text = json.dumps(result)
1171
1172
    @staticmethod
1173
    @user_logger
1174
    def on_post(req, resp, id_):
1175
        """Handles POST requests"""
1176
        admin_control(req)
1177
        try:
1178
            raw_json = req.stream.read().decode('utf-8')
1179
        except UnicodeDecodeError as ex:
1180
            print("Failed to decode request")
1181
            raise falcon.HTTPError(status=falcon.HTTP_400,
1182
                                   title='API.BAD_REQUEST',
1183
                                   description='API.INVALID_ENCODING')
1184
        except Exception as ex:
1185
            print("Unexpected error reading request stream")
1186
            raise falcon.HTTPError(status=falcon.HTTP_400,
1187
                                   title='API.BAD_REQUEST',
1188
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1189
1190
        if not id_.isdigit() or int(id_) <= 0:
1191
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1192
                                   description='API.INVALID_TENANT_ID')
1193
1194
        new_values = json.loads(raw_json)
1195
1196
        if 'offline_meter_id' not in new_values['data'].keys() or \
1197
                not isinstance(new_values['data']['offline_meter_id'], int) or \
1198
                new_values['data']['offline_meter_id'] <= 0:
1199
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1200
                                   description='API.INVALID_OFFLINE_METER_ID')
1201
        offline_meter_id = new_values['data']['offline_meter_id']
1202
1203
        cnx = mysql.connector.connect(**config.myems_system_db)
1204
        cursor = cnx.cursor()
1205
1206
        cursor.execute(" SELECT name "
1207
                       " from tbl_tenants "
1208
                       " WHERE id = %s ", (id_,))
1209
        if cursor.fetchone() is None:
1210
            cursor.close()
1211
            cnx.close()
1212
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1213
                                   description='API.TENANT_NOT_FOUND')
1214
1215
        cursor.execute(" SELECT name "
1216
                       " FROM tbl_offline_meters "
1217
                       " WHERE id = %s ", (offline_meter_id,))
1218
        if cursor.fetchone() is None:
1219
            cursor.close()
1220
            cnx.close()
1221
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1222
                                   description='API.OFFLINE_METER_NOT_FOUND')
1223
1224
        query = (" SELECT id "
1225
                 " FROM tbl_tenants_offline_meters "
1226
                 " WHERE tenant_id = %s AND offline_meter_id = %s")
1227
        cursor.execute(query, (id_, offline_meter_id,))
1228
        if cursor.fetchone() is not None:
1229
            cursor.close()
1230
            cnx.close()
1231
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
1232
                                   description='API.TENANT_OFFLINE_METER_RELATION_EXISTS')
1233
1234
        add_row = (" INSERT INTO tbl_tenants_offline_meters (tenant_id, offline_meter_id) "
1235
                   " VALUES (%s, %s) ")
1236
        cursor.execute(add_row, (id_, offline_meter_id,))
1237
        cnx.commit()
1238
        cursor.close()
1239
        cnx.close()
1240
1241
        # Clear cache after adding offline meter to tenant
1242
        clear_tenant_cache(tenant_id=id_)
1243
1244
        resp.status = falcon.HTTP_201
1245
        resp.location = '/tenants/' + str(id_) + '/offlinemeters/' + str(offline_meter_id)
1246
1247
1248
class TenantOfflineMeterItem:
1249
    def __init__(self):
1250
        pass
1251
1252
    @staticmethod
1253
    def on_options(req, resp, id_, mid):
1254
        _ = req
1255
        resp.status = falcon.HTTP_200
1256
        _ = id_
1257
1258
    @staticmethod
1259
    @user_logger
1260
    def on_delete(req, resp, id_, mid):
1261
        admin_control(req)
1262
        if not id_.isdigit() or int(id_) <= 0:
1263
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1264
                                   description='API.INVALID_TENANT_ID')
1265
1266
        if not mid.isdigit() or int(mid) <= 0:
1267
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1268
                                   description='API.INVALID_OFFLINE_METER_ID')
1269
1270
        cnx = mysql.connector.connect(**config.myems_system_db)
1271
        cursor = cnx.cursor()
1272
1273
        cursor.execute(" SELECT name "
1274
                       " FROM tbl_tenants "
1275
                       " WHERE id = %s ", (id_,))
1276
        if cursor.fetchone() is None:
1277
            cursor.close()
1278
            cnx.close()
1279
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1280
                                   description='API.TENANT_NOT_FOUND')
1281
1282
        cursor.execute(" SELECT name "
1283
                       " FROM tbl_offline_meters "
1284
                       " WHERE id = %s ", (mid,))
1285
        if cursor.fetchone() is None:
1286
            cursor.close()
1287
            cnx.close()
1288
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1289
                                   description='API.OFFLINE_METER_NOT_FOUND')
1290
1291
        cursor.execute(" SELECT id "
1292
                       " FROM tbl_tenants_offline_meters "
1293
                       " WHERE tenant_id = %s AND offline_meter_id = %s ", (id_, mid))
1294
        if cursor.fetchone() is None:
1295
            cursor.close()
1296
            cnx.close()
1297
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1298
                                   description='API.TENANT_OFFLINE_METER_RELATION_NOT_FOUND')
1299
1300
        cursor.execute(" DELETE FROM tbl_tenants_offline_meters "
1301
                       " WHERE tenant_id = %s AND offline_meter_id = %s ", (id_, mid))
1302
        cnx.commit()
1303
1304
        cursor.close()
1305
        cnx.close()
1306
1307
        # Clear cache after removing offline meter from tenant
1308
        clear_tenant_cache(tenant_id=id_)
1309
1310
        resp.status = falcon.HTTP_204
1311
1312
1313 View Code Duplication
class TenantPointCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1314
    def __init__(self):
1315
        pass
1316
1317
    @staticmethod
1318
    def on_options(req, resp, id_):
1319
        _ = req
1320
        resp.status = falcon.HTTP_200
1321
        _ = id_
1322
1323
    @staticmethod
1324
    def on_get(req, resp, id_):
1325
        if 'API-KEY' not in req.headers or \
1326
                not isinstance(req.headers['API-KEY'], str) or \
1327
                len(str.strip(req.headers['API-KEY'])) == 0:
1328
            access_control(req)
1329
        else:
1330
            api_key_control(req)
1331
        if not id_.isdigit() or int(id_) <= 0:
1332
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1333
                                   description='API.INVALID_TENANT_ID')
1334
1335
        cnx = mysql.connector.connect(**config.myems_system_db)
1336
        cursor = cnx.cursor()
1337
1338
        cursor.execute(" SELECT name "
1339
                       " FROM tbl_tenants "
1340
                       " WHERE id = %s ", (id_,))
1341
        if cursor.fetchone() is None:
1342
            cursor.close()
1343
            cnx.close()
1344
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1345
                                   description='API.TENANT_NOT_FOUND')
1346
1347
        query = (" SELECT id, name, uuid "
1348
                 " FROM tbl_data_sources ")
1349
        cursor.execute(query)
1350
        rows_data_sources = cursor.fetchall()
1351
1352
        data_source_dict = dict()
1353
        if rows_data_sources is not None and len(rows_data_sources) > 0:
1354
            for row in rows_data_sources:
1355
                data_source_dict[row[0]] = {"id": row[0],
1356
                                            "name": row[1],
1357
                                            "uuid": row[2]}
1358
1359
        query = (" SELECT p.id, p.name, p.data_source_id "
1360
                 " FROM tbl_tenants t, tbl_tenants_points tp, tbl_points p "
1361
                 " WHERE tp.tenant_id = t.id AND p.id = tp.point_id AND t.id = %s "
1362
                 " ORDER BY p.id ")
1363
        cursor.execute(query, (id_,))
1364
        rows = cursor.fetchall()
1365
1366
        result = list()
1367
        if rows is not None and len(rows) > 0:
1368
            for row in rows:
1369
                meta_result = {"id": row[0],
1370
                               "name": row[1],
1371
                               "data_source": data_source_dict.get(row[2], None)}
1372
                result.append(meta_result)
1373
1374
        resp.text = json.dumps(result)
1375
1376
    @staticmethod
1377
    @user_logger
1378
    def on_post(req, resp, id_):
1379
        """Handles POST requests"""
1380
        admin_control(req)
1381
        try:
1382
            raw_json = req.stream.read().decode('utf-8')
1383
        except UnicodeDecodeError as ex:
1384
            print("Failed to decode request")
1385
            raise falcon.HTTPError(status=falcon.HTTP_400,
1386
                                   title='API.BAD_REQUEST',
1387
                                   description='API.INVALID_ENCODING')
1388
        except Exception as ex:
1389
            print("Unexpected error reading request stream")
1390
            raise falcon.HTTPError(status=falcon.HTTP_400,
1391
                                   title='API.BAD_REQUEST',
1392
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1393
1394
        if not id_.isdigit() or int(id_) <= 0:
1395
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1396
                                   description='API.INVALID_TENANT_ID')
1397
1398
        new_values = json.loads(raw_json)
1399
1400
        if 'point_id' not in new_values['data'].keys() or \
1401
                not isinstance(new_values['data']['point_id'], int) or \
1402
                new_values['data']['point_id'] <= 0:
1403
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1404
                                   description='API.INVALID_POINT_ID')
1405
        point_id = new_values['data']['point_id']
1406
1407
        cnx = mysql.connector.connect(**config.myems_system_db)
1408
        cursor = cnx.cursor()
1409
1410
        cursor.execute(" SELECT name "
1411
                       " from tbl_tenants "
1412
                       " WHERE id = %s ", (id_,))
1413
        if cursor.fetchone() is None:
1414
            cursor.close()
1415
            cnx.close()
1416
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1417
                                   description='API.TENANT_NOT_FOUND')
1418
1419
        cursor.execute(" SELECT name "
1420
                       " FROM tbl_points "
1421
                       " WHERE id = %s ", (point_id,))
1422
        if cursor.fetchone() is None:
1423
            cursor.close()
1424
            cnx.close()
1425
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1426
                                   description='API.POINT_NOT_FOUND')
1427
1428
        query = (" SELECT id "
1429
                 " FROM tbl_tenants_points "
1430
                 " WHERE tenant_id = %s AND point_id = %s")
1431
        cursor.execute(query, (id_, point_id,))
1432
        if cursor.fetchone() is not None:
1433
            cursor.close()
1434
            cnx.close()
1435
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
1436
                                   description='API.TENANT_POINT_RELATION_EXISTS')
1437
1438
        add_row = (" INSERT INTO tbl_tenants_points (tenant_id, point_id) "
1439
                   " VALUES (%s, %s) ")
1440
        cursor.execute(add_row, (id_, point_id,))
1441
        cnx.commit()
1442
        cursor.close()
1443
        cnx.close()
1444
1445
        # Clear cache after adding point to tenant
1446
        clear_tenant_cache(tenant_id=id_)
1447
1448
        resp.status = falcon.HTTP_201
1449
        resp.location = '/tenants/' + str(id_) + '/points/' + str(point_id)
1450
1451
1452
class TenantPointItem:
1453
    def __init__(self):
1454
        pass
1455
1456
    @staticmethod
1457
    def on_options(req, resp, id_, pid):
1458
        _ = req
1459
        resp.status = falcon.HTTP_200
1460
        _ = id_
1461
1462
    @staticmethod
1463
    @user_logger
1464
    def on_delete(req, resp, id_, pid):
1465
        admin_control(req)
1466
        if not id_.isdigit() or int(id_) <= 0:
1467
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1468
                                   description='API.INVALID_TENANT_ID')
1469
1470
        if not pid.isdigit() or int(pid) <= 0:
1471
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1472
                                   description='API.INVALID_POINT_ID')
1473
1474
        cnx = mysql.connector.connect(**config.myems_system_db)
1475
        cursor = cnx.cursor()
1476
1477
        cursor.execute(" SELECT name "
1478
                       " FROM tbl_tenants "
1479
                       " WHERE id = %s ", (id_,))
1480
        if cursor.fetchone() is None:
1481
            cursor.close()
1482
            cnx.close()
1483
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1484
                                   description='API.TENANT_NOT_FOUND')
1485
1486
        cursor.execute(" SELECT name "
1487
                       " FROM tbl_points "
1488
                       " WHERE id = %s ", (pid,))
1489
        if cursor.fetchone() is None:
1490
            cursor.close()
1491
            cnx.close()
1492
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1493
                                   description='API.POINT_NOT_FOUND')
1494
1495
        cursor.execute(" SELECT id "
1496
                       " FROM tbl_tenants_points "
1497
                       " WHERE tenant_id = %s AND point_id = %s ", (id_, pid))
1498
        if cursor.fetchone() is None:
1499
            cursor.close()
1500
            cnx.close()
1501
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1502
                                   description='API.TENANT_POINT_RELATION_NOT_FOUND')
1503
1504
        cursor.execute(" DELETE FROM tbl_tenants_points "
1505
                       " WHERE tenant_id = %s AND point_id = %s ", (id_, pid))
1506
        cnx.commit()
1507
1508
        cursor.close()
1509
        cnx.close()
1510
1511
        # Clear cache after removing point from tenant
1512
        clear_tenant_cache(tenant_id=id_)
1513
1514
        resp.status = falcon.HTTP_204
1515
1516
1517 View Code Duplication
class TenantSensorCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1518
    def __init__(self):
1519
        pass
1520
1521
    @staticmethod
1522
    def on_options(req, resp, id_):
1523
        _ = req
1524
        resp.status = falcon.HTTP_200
1525
        _ = id_
1526
1527
    @staticmethod
1528
    def on_get(req, resp, id_):
1529
        if 'API-KEY' not in req.headers or \
1530
                not isinstance(req.headers['API-KEY'], str) or \
1531
                len(str.strip(req.headers['API-KEY'])) == 0:
1532
            access_control(req)
1533
        else:
1534
            api_key_control(req)
1535
        if not id_.isdigit() or int(id_) <= 0:
1536
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1537
                                   description='API.INVALID_TENANT_ID')
1538
1539
        cnx = mysql.connector.connect(**config.myems_system_db)
1540
        cursor = cnx.cursor()
1541
1542
        cursor.execute(" SELECT name "
1543
                       " FROM tbl_tenants "
1544
                       " WHERE id = %s ", (id_,))
1545
        if cursor.fetchone() is None:
1546
            cursor.close()
1547
            cnx.close()
1548
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1549
                                   description='API.TENANT_NOT_FOUND')
1550
1551
        query = (" SELECT s.id, s.name, s.uuid "
1552
                 " FROM tbl_tenants t, tbl_tenants_sensors ts, tbl_sensors s "
1553
                 " WHERE ts.tenant_id = t.id AND s.id = ts.sensor_id AND t.id = %s "
1554
                 " ORDER BY s.id ")
1555
        cursor.execute(query, (id_,))
1556
        rows = cursor.fetchall()
1557
1558
        result = list()
1559
        if rows is not None and len(rows) > 0:
1560
            for row in rows:
1561
                meta_result = {"id": row[0],
1562
                               "name": row[1],
1563
                               "uuid": row[2]}
1564
                result.append(meta_result)
1565
1566
        resp.text = json.dumps(result)
1567
1568
    @staticmethod
1569
    @user_logger
1570
    def on_post(req, resp, id_):
1571
        """Handles POST requests"""
1572
        admin_control(req)
1573
        try:
1574
            raw_json = req.stream.read().decode('utf-8')
1575
        except UnicodeDecodeError as ex:
1576
            print("Failed to decode request")
1577
            raise falcon.HTTPError(status=falcon.HTTP_400,
1578
                                   title='API.BAD_REQUEST',
1579
                                   description='API.INVALID_ENCODING')
1580
        except Exception as ex:
1581
            print("Unexpected error reading request stream")
1582
            raise falcon.HTTPError(status=falcon.HTTP_400,
1583
                                   title='API.BAD_REQUEST',
1584
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1585
1586
        if not id_.isdigit() or int(id_) <= 0:
1587
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1588
                                   description='API.INVALID_TENANT_ID')
1589
1590
        new_values = json.loads(raw_json)
1591
1592
        if 'sensor_id' not in new_values['data'].keys() or \
1593
                not isinstance(new_values['data']['sensor_id'], int) or \
1594
                new_values['data']['sensor_id'] <= 0:
1595
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1596
                                   description='API.INVALID_SENSOR_ID')
1597
        sensor_id = new_values['data']['sensor_id']
1598
1599
        cnx = mysql.connector.connect(**config.myems_system_db)
1600
        cursor = cnx.cursor()
1601
1602
        cursor.execute(" SELECT name "
1603
                       " from tbl_tenants "
1604
                       " WHERE id = %s ", (id_,))
1605
        if cursor.fetchone() is None:
1606
            cursor.close()
1607
            cnx.close()
1608
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1609
                                   description='API.TENANT_NOT_FOUND')
1610
1611
        cursor.execute(" SELECT name "
1612
                       " FROM tbl_sensors "
1613
                       " WHERE id = %s ", (sensor_id,))
1614
        if cursor.fetchone() is None:
1615
            cursor.close()
1616
            cnx.close()
1617
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1618
                                   description='API.SENSOR_NOT_FOUND')
1619
1620
        query = (" SELECT id "
1621
                 " FROM tbl_tenants_sensors "
1622
                 " WHERE tenant_id = %s AND sensor_id = %s")
1623
        cursor.execute(query, (id_, sensor_id,))
1624
        if cursor.fetchone() is not None:
1625
            cursor.close()
1626
            cnx.close()
1627
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
1628
                                   description='API.TENANT_SENSOR_RELATION_EXISTS')
1629
1630
        add_row = (" INSERT INTO tbl_tenants_sensors (tenant_id, sensor_id) "
1631
                   " VALUES (%s, %s) ")
1632
        cursor.execute(add_row, (id_, sensor_id,))
1633
        cnx.commit()
1634
        cursor.close()
1635
        cnx.close()
1636
1637
        # Clear cache after adding sensor to tenant
1638
        clear_tenant_cache(tenant_id=id_)
1639
1640
        resp.status = falcon.HTTP_201
1641
        resp.location = '/tenants/' + str(id_) + '/sensors/' + str(sensor_id)
1642
1643
1644
class TenantSensorItem:
1645
    def __init__(self):
1646
        pass
1647
1648
    @staticmethod
1649
    def on_options(req, resp, id_, sid):
1650
        _ = req
1651
        resp.status = falcon.HTTP_200
1652
        _ = id_
1653
1654
    @staticmethod
1655
    @user_logger
1656
    def on_delete(req, resp, id_, sid):
1657
        admin_control(req)
1658
        if not id_.isdigit() or int(id_) <= 0:
1659
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1660
                                   description='API.INVALID_TENANT_ID')
1661
1662
        if not sid.isdigit() or int(sid) <= 0:
1663
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1664
                                   description='API.INVALID_SENSOR_ID')
1665
1666
        cnx = mysql.connector.connect(**config.myems_system_db)
1667
        cursor = cnx.cursor()
1668
1669
        cursor.execute(" SELECT name "
1670
                       " FROM tbl_tenants "
1671
                       " WHERE id = %s ", (id_,))
1672
        if cursor.fetchone() is None:
1673
            cursor.close()
1674
            cnx.close()
1675
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1676
                                   description='API.TENANT_NOT_FOUND')
1677
1678
        cursor.execute(" SELECT name "
1679
                       " FROM tbl_sensors "
1680
                       " WHERE id = %s ", (sid,))
1681
        if cursor.fetchone() is None:
1682
            cursor.close()
1683
            cnx.close()
1684
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1685
                                   description='API.SENSOR_NOT_FOUND')
1686
1687
        cursor.execute(" SELECT id "
1688
                       " FROM tbl_tenants_sensors "
1689
                       " WHERE tenant_id = %s AND sensor_id = %s ", (id_, sid))
1690
        if cursor.fetchone() is None:
1691
            cursor.close()
1692
            cnx.close()
1693
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1694
                                   description='API.TENANT_SENSOR_RELATION_NOT_FOUND')
1695
1696
        cursor.execute(" DELETE FROM tbl_tenants_sensors WHERE tenant_id = %s AND sensor_id = %s ", (id_, sid))
1697
        cnx.commit()
1698
1699
        cursor.close()
1700
        cnx.close()
1701
1702
        # Clear cache after removing sensor from tenant
1703
        clear_tenant_cache(tenant_id=id_)
1704
1705
        resp.status = falcon.HTTP_204
1706
1707
1708 View Code Duplication
class TenantVirtualMeterCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1709
    def __init__(self):
1710
        pass
1711
1712
    @staticmethod
1713
    def on_options(req, resp, id_):
1714
        _ = req
1715
        resp.status = falcon.HTTP_200
1716
        _ = id_
1717
1718
    @staticmethod
1719
    def on_get(req, resp, id_):
1720
        if 'API-KEY' not in req.headers or \
1721
                not isinstance(req.headers['API-KEY'], str) or \
1722
                len(str.strip(req.headers['API-KEY'])) == 0:
1723
            access_control(req)
1724
        else:
1725
            api_key_control(req)
1726
        if not id_.isdigit() or int(id_) <= 0:
1727
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1728
                                   description='API.INVALID_TENANT_ID')
1729
1730
        cnx = mysql.connector.connect(**config.myems_system_db)
1731
        cursor = cnx.cursor()
1732
1733
        cursor.execute(" SELECT name "
1734
                       " FROM tbl_tenants "
1735
                       " WHERE id = %s ", (id_,))
1736
        if cursor.fetchone() is None:
1737
            cursor.close()
1738
            cnx.close()
1739
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1740
                                   description='API.TENANT_NOT_FOUND')
1741
1742
        query = (" SELECT id, name, uuid "
1743
                 " FROM tbl_energy_categories ")
1744
        cursor.execute(query)
1745
        rows_energy_categories = cursor.fetchall()
1746
1747
        energy_category_dict = dict()
1748
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
1749
            for row in rows_energy_categories:
1750
                energy_category_dict[row[0]] = {"id": row[0],
1751
                                                "name": row[1],
1752
                                                "uuid": row[2]}
1753
1754
        query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id "
1755
                 " FROM tbl_tenants t, tbl_tenants_virtual_meters tm, tbl_virtual_meters m "
1756
                 " WHERE tm.tenant_id = t.id AND m.id = tm.virtual_meter_id AND t.id = %s "
1757
                 " ORDER BY m.id ")
1758
        cursor.execute(query, (id_,))
1759
        rows = cursor.fetchall()
1760
1761
        result = list()
1762
        if rows is not None and len(rows) > 0:
1763
            for row in rows:
1764
                meta_result = {"id": row[0],
1765
                               "name": row[1],
1766
                               "uuid": row[2],
1767
                               "energy_category": energy_category_dict.get(row[3], None)}
1768
                result.append(meta_result)
1769
1770
        resp.text = json.dumps(result)
1771
1772
    @staticmethod
1773
    @user_logger
1774
    def on_post(req, resp, id_):
1775
        """Handles POST requests"""
1776
        admin_control(req)
1777
        try:
1778
            raw_json = req.stream.read().decode('utf-8')
1779
        except UnicodeDecodeError as ex:
1780
            print("Failed to decode request")
1781
            raise falcon.HTTPError(status=falcon.HTTP_400,
1782
                                   title='API.BAD_REQUEST',
1783
                                   description='API.INVALID_ENCODING')
1784
        except Exception as ex:
1785
            print("Unexpected error reading request stream")
1786
            raise falcon.HTTPError(status=falcon.HTTP_400,
1787
                                   title='API.BAD_REQUEST',
1788
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1789
1790
        if not id_.isdigit() or int(id_) <= 0:
1791
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1792
                                   description='API.INVALID_TENANT_ID')
1793
1794
        new_values = json.loads(raw_json)
1795
1796
        if 'virtual_meter_id' not in new_values['data'].keys() or \
1797
                not isinstance(new_values['data']['virtual_meter_id'], int) or \
1798
                new_values['data']['virtual_meter_id'] <= 0:
1799
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1800
                                   description='API.INVALID_VIRTUAL_METER_ID')
1801
        virtual_meter_id = new_values['data']['virtual_meter_id']
1802
1803
        cnx = mysql.connector.connect(**config.myems_system_db)
1804
        cursor = cnx.cursor()
1805
1806
        cursor.execute(" SELECT name "
1807
                       " from tbl_tenants "
1808
                       " WHERE id = %s ", (id_,))
1809
        if cursor.fetchone() is None:
1810
            cursor.close()
1811
            cnx.close()
1812
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1813
                                   description='API.TENANT_NOT_FOUND')
1814
1815
        cursor.execute(" SELECT name "
1816
                       " FROM tbl_virtual_meters "
1817
                       " WHERE id = %s ", (virtual_meter_id,))
1818
        if cursor.fetchone() is None:
1819
            cursor.close()
1820
            cnx.close()
1821
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1822
                                   description='API.VIRTUAL_METER_NOT_FOUND')
1823
1824
        query = (" SELECT id "
1825
                 " FROM tbl_tenants_virtual_meters "
1826
                 " WHERE tenant_id = %s AND virtual_meter_id = %s")
1827
        cursor.execute(query, (id_, virtual_meter_id,))
1828
        if cursor.fetchone() is not None:
1829
            cursor.close()
1830
            cnx.close()
1831
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
1832
                                   description='API.TENANT_VIRTUAL_METER_RELATION_EXISTS')
1833
1834
        add_row = (" INSERT INTO tbl_tenants_virtual_meters (tenant_id, virtual_meter_id) "
1835
                   " VALUES (%s, %s) ")
1836
        cursor.execute(add_row, (id_, virtual_meter_id,))
1837
        cnx.commit()
1838
        cursor.close()
1839
        cnx.close()
1840
1841
        # Clear cache after adding virtual meter to tenant
1842
        clear_tenant_cache(tenant_id=id_)
1843
1844
        resp.status = falcon.HTTP_201
1845
        resp.location = '/tenants/' + str(id_) + '/virtualmeters/' + str(virtual_meter_id)
1846
1847
1848
class TenantVirtualMeterItem:
1849
    def __init__(self):
1850
        pass
1851
1852
    @staticmethod
1853
    def on_options(req, resp, id_, mid):
1854
        _ = req
1855
        resp.status = falcon.HTTP_200
1856
        _ = id_
1857
1858
    @staticmethod
1859
    @user_logger
1860
    def on_delete(req, resp, id_, mid):
1861
        admin_control(req)
1862
        if not id_.isdigit() or int(id_) <= 0:
1863
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1864
                                   description='API.INVALID_TENANT_ID')
1865
1866
        if not mid.isdigit() or int(mid) <= 0:
1867
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1868
                                   description='API.INVALID_VIRTUAL_METER_ID')
1869
1870
        cnx = mysql.connector.connect(**config.myems_system_db)
1871
        cursor = cnx.cursor()
1872
1873
        cursor.execute(" SELECT name "
1874
                       " FROM tbl_tenants "
1875
                       " WHERE id = %s ", (id_,))
1876
        if cursor.fetchone() is None:
1877
            cursor.close()
1878
            cnx.close()
1879
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1880
                                   description='API.TENANT_NOT_FOUND')
1881
1882
        cursor.execute(" SELECT name "
1883
                       " FROM tbl_virtual_meters "
1884
                       " WHERE id = %s ", (mid,))
1885
        if cursor.fetchone() is None:
1886
            cursor.close()
1887
            cnx.close()
1888
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1889
                                   description='API.VIRTUAL_METER_NOT_FOUND')
1890
1891
        cursor.execute(" SELECT id "
1892
                       " FROM tbl_tenants_virtual_meters "
1893
                       " WHERE tenant_id = %s AND virtual_meter_id = %s ", (id_, mid))
1894
        if cursor.fetchone() is None:
1895
            cursor.close()
1896
            cnx.close()
1897
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1898
                                   description='API.TENANT_VIRTUAL_METER_RELATION_NOT_FOUND')
1899
1900
        cursor.execute(" DELETE FROM tbl_tenants_virtual_meters "
1901
                       " WHERE tenant_id = %s AND virtual_meter_id = %s ", (id_, mid))
1902
        cnx.commit()
1903
1904
        cursor.close()
1905
        cnx.close()
1906
1907
        # Clear cache after removing virtual meter from tenant
1908
        clear_tenant_cache(tenant_id=id_)
1909
1910
        resp.status = falcon.HTTP_204
1911
1912
1913 View Code Duplication
class TenantWorkingCalendarCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1914
    def __init__(self):
1915
        pass
1916
1917
    @staticmethod
1918
    def on_options(req, resp, id_):
1919
        _ = req
1920
        resp.status = falcon.HTTP_200
1921
        _ = id_
1922
1923
    @staticmethod
1924
    def on_get(req, resp, id_):
1925
        if 'API-KEY' not in req.headers or \
1926
                not isinstance(req.headers['API-KEY'], str) or \
1927
                len(str.strip(req.headers['API-KEY'])) == 0:
1928
            access_control(req)
1929
        else:
1930
            api_key_control(req)
1931
        if not id_.isdigit() or int(id_) <= 0:
1932
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1933
                                   description='API.INVALID_TENANT_ID')
1934
1935
        cnx = mysql.connector.connect(**config.myems_system_db)
1936
        cursor = cnx.cursor()
1937
1938
        cursor.execute(" SELECT name "
1939
                       " FROM tbl_tenants "
1940
                       " WHERE id = %s ", (id_,))
1941
        if cursor.fetchone() is None:
1942
            cursor.close()
1943
            cnx.close()
1944
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
1945
                                   description='API.TENANT_NOT_FOUND')
1946
1947
        query = (" SELECT wc.id, wc.name, wc.description "
1948
                 " FROM tbl_tenants t, tbl_tenants_working_calendars twc, tbl_working_calendars wc "
1949
                 " WHERE twc.tenant_id = t.id AND wc.id = twc.working_calendar_id AND t.id = %s "
1950
                 " ORDER BY wc.id ")
1951
        cursor.execute(query, (id_,))
1952
        rows = cursor.fetchall()
1953
1954
        result = list()
1955
        if rows is not None and len(rows) > 0:
1956
            for row in rows:
1957
                meta_result = {"id": row[0],
1958
                               "name": row[1],
1959
                               "description": row[2]}
1960
                result.append(meta_result)
1961
1962
        resp.text = json.dumps(result)
1963
1964
    @staticmethod
1965
    @user_logger
1966
    def on_post(req, resp, id_):
1967
        """Handles POST requests"""
1968
        admin_control(req)
1969
        try:
1970
            raw_json = req.stream.read().decode('utf-8')
1971
        except UnicodeDecodeError as ex:
1972
            print("Failed to decode request")
1973
            raise falcon.HTTPError(status=falcon.HTTP_400,
1974
                                   title='API.BAD_REQUEST',
1975
                                   description='API.INVALID_ENCODING')
1976
        except Exception as ex:
1977
            print("Unexpected error reading request stream")
1978
            raise falcon.HTTPError(status=falcon.HTTP_400,
1979
                                   title='API.BAD_REQUEST',
1980
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
1981
1982
        if not id_.isdigit() or int(id_) <= 0:
1983
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1984
                                   description='API.INVALID_TENANT_ID')
1985
1986
        new_values = json.loads(raw_json)
1987
1988
        if 'working_calendar_id' not in new_values['data'].keys() or \
1989
                not isinstance(new_values['data']['working_calendar_id'], int) or \
1990
                new_values['data']['working_calendar_id'] <= 0:
1991
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
1992
                                   description='API.INVALID_WORKING_CALENDAR_ID')
1993
        working_calendar_id = new_values['data']['working_calendar_id']
1994
1995
        cnx = mysql.connector.connect(**config.myems_system_db)
1996
        cursor = cnx.cursor()
1997
1998
        cursor.execute(" SELECT name "
1999
                       " from tbl_tenants "
2000
                       " WHERE id = %s ", (id_,))
2001
        if cursor.fetchone() is None:
2002
            cursor.close()
2003
            cnx.close()
2004
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2005
                                   description='API.TENANT_NOT_FOUND')
2006
2007
        cursor.execute(" SELECT name "
2008
                       " FROM tbl_working_calendars "
2009
                       " WHERE id = %s ", (working_calendar_id,))
2010
        if cursor.fetchone() is None:
2011
            cursor.close()
2012
            cnx.close()
2013
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2014
                                   description='API.WORKING_CALENDAR_NOT_FOUND')
2015
2016
        query = (" SELECT id "
2017
                 " FROM tbl_tenants_working_calendars "
2018
                 " WHERE tenant_id = %s AND working_calendar_id = %s")
2019
        cursor.execute(query, (id_, working_calendar_id,))
2020
        if cursor.fetchone() is not None:
2021
            cursor.close()
2022
            cnx.close()
2023
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2024
                                   description='API.TENANT_WORKING_CALENDAR_RELATION_EXISTS')
2025
2026
        add_row = (" INSERT INTO tbl_tenants_working_calendars (tenant_id, working_calendar_id) "
2027
                   " VALUES (%s, %s) ")
2028
        cursor.execute(add_row, (id_, working_calendar_id,))
2029
        cnx.commit()
2030
        cursor.close()
2031
        cnx.close()
2032
2033
        # Clear cache after adding working calendar to tenant
2034
        clear_tenant_cache(tenant_id=id_)
2035
2036
        resp.status = falcon.HTTP_201
2037
        resp.location = '/tenants/' + str(id_) + '/workingcalendars/' + str(working_calendar_id)
2038
2039
2040
class TenantWorkingCalendarItem:
2041
    def __init__(self):
2042
        pass
2043
2044
    @staticmethod
2045
    def on_options(req, resp, id_, wcid):
2046
        _ = req
2047
        resp.status = falcon.HTTP_200
2048
        _ = id_
2049
2050
    @staticmethod
2051
    @user_logger
2052
    def on_delete(req, resp, id_, wcid):
2053
        admin_control(req)
2054
        if not id_.isdigit() or int(id_) <= 0:
2055
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2056
                                   description='API.INVALID_TENANT_ID')
2057
2058
        if not wcid.isdigit() or int(wcid) <= 0:
2059
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2060
                                   description='API.INVALID_WORKING_CALENDAR_ID')
2061
2062
        cnx = mysql.connector.connect(**config.myems_system_db)
2063
        cursor = cnx.cursor()
2064
2065
        cursor.execute(" SELECT name "
2066
                       " FROM tbl_tenants "
2067
                       " WHERE id = %s ", (id_,))
2068
        if cursor.fetchone() is None:
2069
            cursor.close()
2070
            cnx.close()
2071
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2072
                                   description='API.TENANT_NOT_FOUND')
2073
2074
        cursor.execute(" SELECT name "
2075
                       " FROM tbl_working_calendars "
2076
                       " WHERE id = %s ", (wcid,))
2077
        if cursor.fetchone() is None:
2078
            cursor.close()
2079
            cnx.close()
2080
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2081
                                   description='API.WORKING_CALENDAR_NOT_FOUND')
2082
2083
        cursor.execute(" SELECT id "
2084
                       " FROM tbl_tenants_working_calendars "
2085
                       " WHERE tenant_id = %s AND working_calendar_id = %s ", (id_, wcid))
2086
        if cursor.fetchone() is None:
2087
            cursor.close()
2088
            cnx.close()
2089
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2090
                                   description='API.TENANT_WORKING_CALENDAR_RELATION_NOT_FOUND')
2091
2092
        cursor.execute(" DELETE FROM tbl_tenants_working_calendars "
2093
                       " WHERE tenant_id = %s AND working_calendar_id = %s ", (id_, wcid))
2094
        cnx.commit()
2095
2096
        cursor.close()
2097
        cnx.close()
2098
2099
        # Clear cache after removing working calendar from tenant
2100
        clear_tenant_cache(tenant_id=id_)
2101
2102
        resp.status = falcon.HTTP_204
2103
2104
2105 View Code Duplication
class TenantCommandCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
2106
    def __init__(self):
2107
        pass
2108
2109
    @staticmethod
2110
    def on_options(req, resp, id_):
2111
        _ = req
2112
        resp.status = falcon.HTTP_200
2113
        _ = id_
2114
2115
    @staticmethod
2116
    def on_get(req, resp, id_):
2117
        if 'API-KEY' not in req.headers or \
2118
                not isinstance(req.headers['API-KEY'], str) or \
2119
                len(str.strip(req.headers['API-KEY'])) == 0:
2120
            access_control(req)
2121
        else:
2122
            api_key_control(req)
2123
        if not id_.isdigit() or int(id_) <= 0:
2124
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2125
                                   description='API.INVALID_TENANT_ID')
2126
2127
        cnx = mysql.connector.connect(**config.myems_system_db)
2128
        cursor = cnx.cursor()
2129
2130
        cursor.execute(" SELECT name "
2131
                       " FROM tbl_tenants "
2132
                       " WHERE id = %s ", (id_,))
2133
        if cursor.fetchone() is None:
2134
            cursor.close()
2135
            cnx.close()
2136
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2137
                                   description='API.TENANT_NOT_FOUND')
2138
2139
        query = (" SELECT c.id, c.name, c.uuid "
2140
                 " FROM tbl_tenants t, tbl_tenants_commands tc, tbl_commands c "
2141
                 " WHERE tc.tenant_id = t.id AND c.id = tc.command_id AND t.id = %s "
2142
                 " ORDER BY c.id ")
2143
        cursor.execute(query, (id_,))
2144
        rows = cursor.fetchall()
2145
2146
        result = list()
2147
        if rows is not None and len(rows) > 0:
2148
            for row in rows:
2149
                meta_result = {"id": row[0],
2150
                               "name": row[1],
2151
                               "uuid": row[2]}
2152
                result.append(meta_result)
2153
2154
        resp.text = json.dumps(result)
2155
2156
    @staticmethod
2157
    @user_logger
2158
    def on_post(req, resp, id_):
2159
        """Handles POST requests"""
2160
        admin_control(req)
2161
        try:
2162
            raw_json = req.stream.read().decode('utf-8')
2163
        except UnicodeDecodeError as ex:
2164
            print("Failed to decode request")
2165
            raise falcon.HTTPError(status=falcon.HTTP_400,
2166
                                   title='API.BAD_REQUEST',
2167
                                   description='API.INVALID_ENCODING')
2168
        except Exception as ex:
2169
            print("Unexpected error reading request stream")
2170
            raise falcon.HTTPError(status=falcon.HTTP_400,
2171
                                   title='API.BAD_REQUEST',
2172
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
2173
2174
        if not id_.isdigit() or int(id_) <= 0:
2175
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2176
                                   description='API.INVALID_TENANT_ID')
2177
2178
        new_values = json.loads(raw_json)
2179
2180
        if 'command_id' not in new_values['data'].keys() or \
2181
                not isinstance(new_values['data']['command_id'], int) or \
2182
                new_values['data']['command_id'] <= 0:
2183
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2184
                                   description='API.INVALID_COMMAND_ID')
2185
        command_id = new_values['data']['command_id']
2186
2187
        cnx = mysql.connector.connect(**config.myems_system_db)
2188
        cursor = cnx.cursor()
2189
2190
        cursor.execute(" SELECT name "
2191
                       " from tbl_tenants "
2192
                       " WHERE id = %s ", (id_,))
2193
        if cursor.fetchone() is None:
2194
            cursor.close()
2195
            cnx.close()
2196
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2197
                                   description='API.TENANT_NOT_FOUND')
2198
2199
        cursor.execute(" SELECT name "
2200
                       " FROM tbl_commands "
2201
                       " WHERE id = %s ", (command_id,))
2202
        if cursor.fetchone() is None:
2203
            cursor.close()
2204
            cnx.close()
2205
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2206
                                   description='API.COMMAND_NOT_FOUND')
2207
2208
        query = (" SELECT id "
2209
                 " FROM tbl_tenants_commands "
2210
                 " WHERE tenant_id = %s AND command_id = %s")
2211
        cursor.execute(query, (id_, command_id,))
2212
        if cursor.fetchone() is not None:
2213
            cursor.close()
2214
            cnx.close()
2215
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2216
                                   description='API.TENANT_COMMAND_RELATION_EXISTS')
2217
2218
        add_row = (" INSERT INTO tbl_tenants_commands (tenant_id, command_id) "
2219
                   " VALUES (%s, %s) ")
2220
        cursor.execute(add_row, (id_, command_id,))
2221
        cnx.commit()
2222
        cursor.close()
2223
        cnx.close()
2224
2225
        # Clear cache after adding command to tenant
2226
        clear_tenant_cache(tenant_id=id_)
2227
2228
        resp.status = falcon.HTTP_201
2229
        resp.location = '/tenants/' + str(id_) + '/commands/' + str(command_id)
2230
2231
2232
class TenantCommandItem:
2233
    def __init__(self):
2234
        pass
2235
2236
    @staticmethod
2237
    def on_options(req, resp, id_, cid):
2238
        _ = req
2239
        resp.status = falcon.HTTP_200
2240
        _ = id_
2241
2242
    @staticmethod
2243
    @user_logger
2244
    def on_delete(req, resp, id_, cid):
2245
        admin_control(req)
2246
        if not id_.isdigit() or int(id_) <= 0:
2247
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2248
                                   description='API.INVALID_TENANT_ID')
2249
2250
        if not cid.isdigit() or int(cid) <= 0:
2251
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2252
                                   description='API.INVALID_COMMAND_ID')
2253
2254
        cnx = mysql.connector.connect(**config.myems_system_db)
2255
        cursor = cnx.cursor()
2256
2257
        cursor.execute(" SELECT name "
2258
                       " FROM tbl_tenants "
2259
                       " WHERE id = %s ", (id_,))
2260
        if cursor.fetchone() is None:
2261
            cursor.close()
2262
            cnx.close()
2263
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2264
                                   description='API.TENANT_NOT_FOUND')
2265
2266
        cursor.execute(" SELECT name "
2267
                       " FROM tbl_commands "
2268
                       " WHERE id = %s ", (cid,))
2269
        if cursor.fetchone() is None:
2270
            cursor.close()
2271
            cnx.close()
2272
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2273
                                   description='API.COMMAND_NOT_FOUND')
2274
2275
        cursor.execute(" SELECT id "
2276
                       " FROM tbl_tenants_commands "
2277
                       " WHERE tenant_id = %s AND command_id = %s ", (id_, cid))
2278
        if cursor.fetchone() is None:
2279
            cursor.close()
2280
            cnx.close()
2281
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2282
                                   description='API.TENANT_COMMAND_RELATION_NOT_FOUND')
2283
2284
        cursor.execute(" DELETE FROM tbl_tenants_commands WHERE tenant_id = %s AND command_id = %s ", (id_, cid))
2285
        cnx.commit()
2286
2287
        cursor.close()
2288
        cnx.close()
2289
2290
        # Clear cache after removing command from tenant
2291
        clear_tenant_cache(tenant_id=id_)
2292
2293
        resp.status = falcon.HTTP_204
2294
2295
2296
class TenantExport:
2297
    def __init__(self):
2298
        pass
2299
2300
    @staticmethod
2301
    def on_options(req, resp, id_):
2302
        _ = req
2303
        resp.status = falcon.HTTP_200
2304
        _ = id_
2305
2306
    @staticmethod
2307
    def on_get(req, resp, id_):
2308
        if 'API-KEY' not in req.headers or \
2309
                not isinstance(req.headers['API-KEY'], str) or \
2310
                len(str.strip(req.headers['API-KEY'])) == 0:
2311
            access_control(req)
2312
        else:
2313
            api_key_control(req)
2314
        if not id_.isdigit() or int(id_) <= 0:
2315
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2316
                                   description='API.INVALID_TENANT_ID')
2317
2318
        # Redis cache key
2319
        cache_key = f'tenant:export:{id_}'
2320
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
2321
2322
        # Try to get from Redis cache (only if Redis is enabled)
2323
        redis_client = None
2324
        if config.redis.get('is_enabled', False):
2325
            try:
2326
                redis_client = redis.Redis(
2327
                    host=config.redis['host'],
2328
                    port=config.redis['port'],
2329
                    password=config.redis['password'] if config.redis['password'] else None,
2330
                    db=config.redis['db'],
2331
                    decode_responses=True,
2332
                    socket_connect_timeout=2,
2333
                    socket_timeout=2
2334
                )
2335
                redis_client.ping()
2336
                cached_result = redis_client.get(cache_key)
2337
                if cached_result:
2338
                    resp.text = cached_result
2339
                    return
2340
            except Exception:
2341
                # If Redis connection fails, continue to database query
2342
                pass
2343
2344
        # Cache miss or Redis error - query database
2345
        cnx = mysql.connector.connect(**config.myems_system_db)
2346
        cursor = cnx.cursor()
2347
2348
        query = (" SELECT id, name, uuid "
2349
                 " FROM tbl_tenant_types ")
2350
        cursor.execute(query)
2351
        rows_tenant_types = cursor.fetchall()
2352
2353
        tenant_type_dict = dict()
2354
        if rows_tenant_types is not None and len(rows_tenant_types) > 0:
2355
            for row in rows_tenant_types:
2356
                tenant_type_dict[row[0]] = {"id": row[0],
2357
                                            "name": row[1],
2358
                                            "uuid": row[2]}
2359
2360
        query = (" SELECT id, name, uuid "
2361
                 " FROM tbl_contacts ")
2362
        cursor.execute(query)
2363
        rows_contacts = cursor.fetchall()
2364
2365
        contact_dict = dict()
2366
        if rows_contacts is not None and len(rows_contacts) > 0:
2367
            for row in rows_contacts:
2368
                contact_dict[row[0]] = {"id": row[0],
2369
                                        "name": row[1],
2370
                                        "uuid": row[2]}
2371
2372
        query = (" SELECT id, name, uuid "
2373
                 " FROM tbl_cost_centers ")
2374
        cursor.execute(query)
2375
        rows_cost_centers = cursor.fetchall()
2376
2377
        cost_center_dict = dict()
2378
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
2379
            for row in rows_cost_centers:
2380
                cost_center_dict[row[0]] = {"id": row[0],
2381
                                            "name": row[1],
2382
                                            "uuid": row[2]}
2383
2384
        query = (" SELECT id, name, uuid, "
2385
                 "        buildings, floors, rooms, area, tenant_type_id,"
2386
                 "        is_key_tenant, is_input_counted, "
2387
                 "        lease_number, lease_start_datetime_utc, lease_end_datetime_utc, is_in_lease, "
2388
                 "        contact_id, cost_center_id, description "
2389
                 " FROM tbl_tenants "
2390
                 " WHERE id = %s ")
2391
        cursor.execute(query, (id_,))
2392
        row = cursor.fetchone()
2393
2394
        if row is None:
2395
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2396
                                   description='API.TENANT_NOT_FOUND')
2397
        else:
2398
            timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
2399
            if config.utc_offset[0] == '-':
2400
                timezone_offset = -timezone_offset
2401
2402
            meta_result = {"id": row[0],
2403
                           "name": row[1],
2404
                           "uuid": row[2],
2405
                           "buildings": row[3],
2406
                           "floors": row[4],
2407
                           "rooms": row[5],
2408
                           "area": row[6],
2409
                           "tenant_type": tenant_type_dict.get(row[7], None),
2410
                           "is_key_tenant": bool(row[8]),
2411
                           "is_input_counted": bool(row[9]),
2412
                           "lease_number": row[10],
2413
                           "lease_start_datetime": (row[11].replace(tzinfo=timezone.utc) +
2414
                                                    timedelta(minutes=timezone_offset)).isoformat()[0:19],
2415
                           "lease_end_datetime": (row[12].replace(tzinfo=timezone.utc) +
2416
                                                  timedelta(minutes=timezone_offset)).isoformat()[0:19],
2417
                           "is_in_lease": bool(row[13]),
2418
                           "contact": contact_dict.get(row[14], None),
2419
                           "cost_center": cost_center_dict.get(row[15], None),
2420
                           "description": row[16],
2421
                           "commands": None,
2422
                           "meters": None,
2423
                           "offline_meters": None,
2424
                           "virtual_meters": None,
2425
                           "points": None,
2426
                           "sensors": None,
2427
                           "working_calendars": None
2428
                           }
2429
            query = (" SELECT c.id, c.name, c.uuid "
2430
                     " FROM tbl_tenants t, tbl_tenants_commands tc, tbl_commands c "
2431
                     " WHERE tc.tenant_id = t.id AND c.id = tc.command_id AND t.id = %s "
2432
                     " ORDER BY c.id ")
2433
            cursor.execute(query, (id_,))
2434
            rows = cursor.fetchall()
2435
2436
            command_result = list()
2437
            if rows is not None and len(rows) > 0:
2438
                for row in rows:
2439
                    result = {"id": row[0], "name": row[1], "uuid": row[2]}
2440
                    command_result.append(result)
2441
                meta_result['commands'] = command_result
2442
            query = (" SELECT id, name, uuid "
2443
                     " FROM tbl_energy_categories ")
2444
            cursor.execute(query)
2445
            rows_energy_categories = cursor.fetchall()
2446
2447
            energy_category_dict = dict()
2448
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
2449
                for row in rows_energy_categories:
2450
                    energy_category_dict[row[0]] = {"id": row[0],
2451
                                                    "name": row[1],
2452
                                                    "uuid": row[2]}
2453
2454
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id "
2455
                     " FROM tbl_tenants t, tbl_tenants_meters tm, tbl_meters m "
2456
                     " WHERE tm.tenant_id = t.id AND m.id = tm.meter_id AND t.id = %s "
2457
                     " ORDER BY m.id ")
2458
            cursor.execute(query, (id_,))
2459
            rows = cursor.fetchall()
2460
2461
            meter_result = list()
2462
            if rows is not None and len(rows) > 0:
2463
                for row in rows:
2464
                    energy_category = energy_category_dict.get(row[3], None)
2465
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
2466
                              "energy_category": energy_category}
2467
                    meter_result.append(result)
2468
                meta_result['meters'] = meter_result
2469
            query = (" SELECT id, name, uuid "
2470
                     " FROM tbl_energy_categories ")
2471
            cursor.execute(query)
2472
            rows_energy_categories = cursor.fetchall()
2473
2474
            energy_category_dict = dict()
2475
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
2476
                for row in rows_energy_categories:
2477
                    energy_category_dict[row[0]] = {"id": row[0],
2478
                                                    "name": row[1],
2479
                                                    "uuid": row[2]}
2480
2481
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id "
2482
                     " FROM tbl_tenants s, tbl_tenants_offline_meters sm, tbl_offline_meters m "
2483
                     " WHERE sm.tenant_id = s.id AND m.id = sm.offline_meter_id AND s.id = %s "
2484
                     " ORDER BY m.id ")
2485
            cursor.execute(query, (id_,))
2486
            rows = cursor.fetchall()
2487
2488
            offlinemeter_result = list()
2489
            if rows is not None and len(rows) > 0:
2490
                for row in rows:
2491
                    energy_category = energy_category_dict.get(row[3], None)
2492
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
2493
                              "energy_category": energy_category}
2494
                    offlinemeter_result.append(result)
2495
                meta_result['offline_meters'] = offlinemeter_result
2496
            query = (" SELECT id, name, uuid "
2497
                     " FROM tbl_energy_categories ")
2498
            cursor.execute(query)
2499
            rows_energy_categories = cursor.fetchall()
2500
2501
            energy_category_dict = dict()
2502
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
2503
                for row in rows_energy_categories:
2504
                    energy_category_dict[row[0]] = {"id": row[0],
2505
                                                    "name": row[1],
2506
                                                    "uuid": row[2]}
2507
2508
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id "
2509
                     " FROM tbl_tenants t, tbl_tenants_virtual_meters tm, tbl_virtual_meters m "
2510
                     " WHERE tm.tenant_id = t.id AND m.id = tm.virtual_meter_id AND t.id = %s "
2511
                     " ORDER BY m.id ")
2512
            cursor.execute(query, (id_,))
2513
            rows = cursor.fetchall()
2514
2515
            virtualmeter_result = list()
2516
            if rows is not None and len(rows) > 0:
2517
                for row in rows:
2518
                    energy_category = energy_category_dict.get(row[3], None)
2519
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
2520
                              "energy_category": energy_category}
2521
                    virtualmeter_result.append(result)
2522
                meta_result['virtual_meters'] = virtualmeter_result
2523
            query = (" SELECT id, name, uuid "
2524
                     " FROM tbl_data_sources ")
2525
            cursor.execute(query)
2526
            rows_data_sources = cursor.fetchall()
2527
2528
            data_source_dict = dict()
2529
            if rows_data_sources is not None and len(rows_data_sources) > 0:
2530
                for row in rows_data_sources:
2531
                    data_source_dict[row[0]] = {"id": row[0],
2532
                                                "name": row[1],
2533
                                                "uuid": row[2]}
2534
2535
            query = (" SELECT p.id, p.name, p.data_source_id "
2536
                     " FROM tbl_tenants t, tbl_tenants_points tp, tbl_points p "
2537
                     " WHERE tp.tenant_id = t.id AND p.id = tp.point_id AND t.id = %s "
2538
                     " ORDER BY p.id ")
2539
            cursor.execute(query, (id_,))
2540
            rows = cursor.fetchall()
2541
2542
            point_result = list()
2543
            if rows is not None and len(rows) > 0:
2544
                for row in rows:
2545
                    data_source = data_source_dict.get(row[2], None)
2546
                    result = {"id": row[0], "name": row[1], "data_source": data_source}
2547
                    point_result.append(result)
2548
                meta_result['points'] = point_result
2549
            query = (" SELECT s.id, s.name, s.uuid "
2550
                     " FROM tbl_tenants t, tbl_tenants_sensors ts, tbl_sensors s "
2551
                     " WHERE ts.tenant_id = t.id AND s.id = ts.sensor_id AND t.id = %s "
2552
                     " ORDER BY s.id ")
2553
            cursor.execute(query, (id_,))
2554
            rows = cursor.fetchall()
2555
2556
            sensor_result = list()
2557
            if rows is not None and len(rows) > 0:
2558
                for row in rows:
2559
                    result = {"id": row[0], "name": row[1], "uuid": row[2]}
2560
                    sensor_result.append(result)
2561
                meta_result['sensors'] = sensor_result
2562
            query = (" SELECT wc.id, wc.name, wc.description "
2563
                     " FROM tbl_tenants t, tbl_tenants_working_calendars twc, tbl_working_calendars wc "
2564
                     " WHERE twc.tenant_id = t.id AND wc.id = twc.working_calendar_id AND t.id = %s "
2565
                     " ORDER BY wc.id ")
2566
            cursor.execute(query, (id_,))
2567
            rows = cursor.fetchall()
2568
2569
            workingcalendars_result = list()
2570
            if rows is not None and len(rows) > 0:
2571
                for row in rows:
2572
                    result = {"id": row[0], "name": row[1], "description": row[2]}
2573
                    workingcalendars_result.append(result)
2574
                meta_result['working_calendars'] = workingcalendars_result
2575
2576
        cursor.close()
2577
        cnx.close()
2578
        result_json = json.dumps(meta_result)
2579
        resp.text = result_json
2580
2581
        # Store result in Redis cache
2582
        if redis_client:
2583
            try:
2584
                redis_client.setex(cache_key, cache_expire, result_json)
2585
            except Exception:
2586
                # If cache store fails, ignore and continue
2587
                pass
2588
2589
2590
class TenantImport:
2591
    def __init__(self):
2592
        pass
2593
2594
    @staticmethod
2595
    def on_options(req, resp):
2596
        _ = req
2597
        resp.status = falcon.HTTP_200
2598
2599
    @staticmethod
2600
    @user_logger
2601
    def on_post(req, resp):
2602
        """Handles POST requests"""
2603
        admin_control(req)
2604
        try:
2605
            raw_json = req.stream.read().decode('utf-8')
2606
        except UnicodeDecodeError as ex:
2607
            print("Failed to decode request")
2608
            raise falcon.HTTPError(status=falcon.HTTP_400,
2609
                                   title='API.BAD_REQUEST',
2610
                                   description='API.INVALID_ENCODING')
2611
        except Exception as ex:
2612
            print("Unexpected error reading request stream")
2613
            raise falcon.HTTPError(status=falcon.HTTP_400,
2614
                                   title='API.BAD_REQUEST',
2615
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
2616
2617
        new_values = json.loads(raw_json)
2618
2619
        if 'name' not in new_values.keys() or \
2620
                not isinstance(new_values['name'], str) or \
2621
                len(str.strip(new_values['name'])) == 0:
2622
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2623
                                   description='API.INVALID_TENANT_NAME')
2624
        name = str.strip(new_values['name'])
2625
2626
        if 'buildings' not in new_values.keys() or \
2627
                not isinstance(new_values['buildings'], str) or \
2628
                len(str.strip(new_values['buildings'])) == 0:
2629
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2630
                                   description='API.INVALID_BUILDINGS_VALUE')
2631
        buildings = str.strip(new_values['buildings'])
2632
2633
        if 'floors' not in new_values.keys() or \
2634
                not isinstance(new_values['floors'], str) or \
2635
                len(str.strip(new_values['floors'])) == 0:
2636
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2637
                                   description='API.INVALID_FLOORS_VALUE')
2638
        floors = str.strip(new_values['floors'])
2639
2640
        if 'rooms' not in new_values.keys() or \
2641
                not isinstance(new_values['rooms'], str) or \
2642
                len(str.strip(new_values['rooms'])) == 0:
2643
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2644
                                   description='API.INVALID_ROOMS_VALUE')
2645
        rooms = str.strip(new_values['rooms'])
2646
2647
        if 'area' not in new_values.keys() or \
2648
                not (isinstance(new_values['area'], float) or
2649
                     isinstance(new_values['area'], int)) or \
2650
                new_values['area'] <= 0.0:
2651
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2652
                                   description='API.INVALID_AREA_VALUE')
2653
        area = new_values['area']
2654
2655
        if 'id' not in new_values['tenant_type'].keys() or \
2656
                not isinstance(new_values['tenant_type']['id'], int) or \
2657
                new_values['tenant_type']['id'] <= 0:
2658
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2659
                                   description='API.INVALID_TENANT_TYPE_ID')
2660
        tenant_type_id = new_values['tenant_type']['id']
2661
2662
        if 'is_input_counted' not in new_values.keys() or \
2663
                not isinstance(new_values['is_input_counted'], bool):
2664
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2665
                                   description='API.INVALID_IS_INPUT_COUNTED_VALUE')
2666
        is_input_counted = new_values['is_input_counted']
2667
2668
        if 'is_key_tenant' not in new_values.keys() or \
2669
                not isinstance(new_values['is_key_tenant'], bool):
2670
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2671
                                   description='API.INVALID_IS_KEY_TENANT_VALUE')
2672
        is_key_tenant = new_values['is_key_tenant']
2673
2674
        if 'lease_number' not in new_values.keys() or \
2675
                not isinstance(new_values['lease_number'], str) or \
2676
                len(str.strip(new_values['lease_number'])) == 0:
2677
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2678
                                   description='API.INVALID_LEASE_NUMBER_VALUE')
2679
        lease_number = str.strip(new_values['lease_number'])
2680
2681
        if 'is_in_lease' not in new_values.keys() or \
2682
                not isinstance(new_values['is_in_lease'], bool):
2683
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2684
                                   description='API.INVALID_IS_IN_LEASE_VALUE')
2685
        is_in_lease = new_values['is_in_lease']
2686
2687
        timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
2688
        if config.utc_offset[0] == '-':
2689
            timezone_offset = -timezone_offset
2690
2691
        # todo: validate datetime values
2692
        lease_start_datetime_utc = datetime.strptime(new_values['lease_start_datetime'],
2693
                                                     '%Y-%m-%dT%H:%M:%S')
2694
        lease_start_datetime_utc = lease_start_datetime_utc.replace(tzinfo=timezone.utc)
2695
        lease_start_datetime_utc -= timedelta(minutes=timezone_offset)
2696
2697
        lease_end_datetime_utc = datetime.strptime(new_values['lease_end_datetime'],
2698
                                                   '%Y-%m-%dT%H:%M:%S')
2699
        lease_end_datetime_utc = lease_end_datetime_utc.replace(tzinfo=timezone.utc)
2700
        lease_end_datetime_utc -= timedelta(minutes=timezone_offset)
2701
2702
        if 'id' not in new_values['contact'].keys() or \
2703
                not isinstance(new_values['contact']['id'], int) or \
2704
                new_values['contact']['id'] <= 0:
2705
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2706
                                   description='API.INVALID_CONTACT_ID')
2707
        contact_id = new_values['contact']['id']
2708
2709
        if 'id' not in new_values['cost_center'].keys() or \
2710
                not isinstance(new_values['cost_center']['id'], int) or \
2711
                new_values['cost_center']['id'] <= 0:
2712
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2713
                                   description='API.INVALID_COST_CENTER_ID')
2714
        cost_center_id = new_values['cost_center']['id']
2715
2716
        if 'description' in new_values.keys() and \
2717
                new_values['description'] is not None and \
2718
                len(str(new_values['description'])) > 0:
2719
            description = str.strip(new_values['description'])
2720
        else:
2721
            description = None
2722
2723
        cnx = mysql.connector.connect(**config.myems_system_db)
2724
        cursor = cnx.cursor()
2725
2726
        cursor.execute(" SELECT name "
2727
                       " FROM tbl_tenants "
2728
                       " WHERE name = %s ", (name,))
2729
        if cursor.fetchone() is not None:
2730
            cursor.close()
2731
            cnx.close()
2732
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2733
                                   description='API.TENANT_NAME_IS_ALREADY_IN_USE')
2734
2735
        cursor.execute(" SELECT name "
2736
                       " FROM tbl_tenant_types "
2737
                       " WHERE id = %s ",
2738
                       (tenant_type_id,))
2739
        if cursor.fetchone() is None:
2740
            cursor.close()
2741
            cnx.close()
2742
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2743
                                   description='API.TENANT_TYPE_NOT_FOUND')
2744
2745
        cursor.execute(" SELECT name "
2746
                       " FROM tbl_contacts "
2747
                       " WHERE id = %s ",
2748
                       (new_values['contact']['id'],))
2749
        row = cursor.fetchone()
2750
        if row is None:
2751
            cursor.close()
2752
            cnx.close()
2753
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2754
                                   description='API.CONTACT_NOT_FOUND')
2755
2756
        cursor.execute(" SELECT name "
2757
                       " FROM tbl_cost_centers "
2758
                       " WHERE id = %s ",
2759
                       (new_values['cost_center']['id'],))
2760
        row = cursor.fetchone()
2761
        if row is None:
2762
            cursor.close()
2763
            cnx.close()
2764
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2765
                                   description='API.COST_CENTER_NOT_FOUND')
2766
2767
        add_values = (" INSERT INTO tbl_tenants "
2768
                      "    (name, uuid, buildings, floors, rooms, area, tenant_type_id, "
2769
                      "     is_input_counted, is_key_tenant, "
2770
                      "     lease_number, lease_start_datetime_utc, lease_end_datetime_utc, is_in_lease, "
2771
                      "     contact_id, cost_center_id, description) "
2772
                      " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
2773
        cursor.execute(add_values, (name,
2774
                                    str(uuid.uuid4()),
2775
                                    buildings,
2776
                                    floors,
2777
                                    rooms,
2778
                                    area,
2779
                                    tenant_type_id,
2780
                                    is_input_counted,
2781
                                    is_key_tenant,
2782
                                    lease_number,
2783
                                    lease_start_datetime_utc,
2784
                                    lease_end_datetime_utc,
2785
                                    is_in_lease,
2786
                                    contact_id,
2787
                                    cost_center_id,
2788
                                    description))
2789
        new_id = cursor.lastrowid
2790
        if new_values['commands'] is not None and len(new_values['commands']) > 0:
2791
            for command in new_values['commands']:
2792
                cursor.execute(" SELECT name "
2793
                               " FROM tbl_commands "
2794
                               " WHERE id = %s ", (command['id'],))
2795
                if cursor.fetchone() is None:
2796
                    cursor.close()
2797
                    cnx.close()
2798
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2799
                                           description='API.COMMAND_NOT_FOUND')
2800
2801
                query = (" SELECT id "
2802
                         " FROM tbl_tenants_commands "
2803
                         " WHERE tenant_id = %s AND command_id = %s")
2804
                cursor.execute(query, (new_id, command['id'],))
2805
                if cursor.fetchone() is not None:
2806
                    cursor.close()
2807
                    cnx.close()
2808
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2809
                                           description='API.TENANT_COMMAND_RELATION_EXISTS')
2810
2811
                add_row = (" INSERT INTO tbl_tenants_commands (tenant_id, command_id) "
2812
                           " VALUES (%s, %s) ")
2813
                cursor.execute(add_row, (new_id, command['id'],))
2814
        if new_values['meters'] is not None and len(new_values['meters']) > 0:
2815
            for meter in new_values['meters']:
2816
                cursor.execute(" SELECT name "
2817
                               " FROM tbl_meters "
2818
                               " WHERE id = %s ", (meter['id'],))
2819
                if cursor.fetchone() is None:
2820
                    cursor.close()
2821
                    cnx.close()
2822
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2823
                                           description='API.METER_NOT_FOUND')
2824
2825
                query = (" SELECT id "
2826
                         " FROM tbl_tenants_meters "
2827
                         " WHERE tenant_id = %s AND meter_id = %s")
2828
                cursor.execute(query, (new_id, meter['id'],))
2829
                if cursor.fetchone() is not None:
2830
                    cursor.close()
2831
                    cnx.close()
2832
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2833
                                           description='API.TENANT_METER_RELATION_EXISTS')
2834
2835
                add_row = (" INSERT INTO tbl_tenants_meters (tenant_id, meter_id) "
2836
                           " VALUES (%s, %s) ")
2837
                cursor.execute(add_row, (new_id, meter['id'],))
2838
        if new_values['offline_meters'] is not None and len(new_values['offline_meters']) > 0:
2839
            for offline_meter in new_values['offline_meters']:
2840
                cursor.execute(" SELECT name "
2841
                               " FROM tbl_offline_meters "
2842
                               " WHERE id = %s ", (offline_meter['id'],))
2843
                if cursor.fetchone() is None:
2844
                    cursor.close()
2845
                    cnx.close()
2846
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2847
                                           description='API.OFFLINE_METER_NOT_FOUND')
2848
2849
                query = (" SELECT id "
2850
                         " FROM tbl_tenants_offline_meters "
2851
                         " WHERE tenant_id = %s AND offline_meter_id = %s")
2852
                cursor.execute(query, (new_id, offline_meter['id'],))
2853
                if cursor.fetchone() is not None:
2854
                    cursor.close()
2855
                    cnx.close()
2856
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2857
                                           description='API.TENANT_OFFLINE_METER_RELATION_EXISTS')
2858
2859
                add_row = (" INSERT INTO tbl_tenants_offline_meters (tenant_id, offline_meter_id) "
2860
                           " VALUES (%s, %s) ")
2861
                cursor.execute(add_row, (new_id, offline_meter['id'],))
2862
        if new_values['virtual_meters'] is not None and len(new_values['virtual_meters']) > 0:
2863
            for virtual_meter in new_values['virtual_meters']:
2864
                cursor.execute(" SELECT name "
2865
                               " FROM tbl_virtual_meters "
2866
                               " WHERE id = %s ", (virtual_meter['id'],))
2867
                if cursor.fetchone() is None:
2868
                    cursor.close()
2869
                    cnx.close()
2870
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2871
                                           description='API.VIRTUAL_METER_NOT_FOUND')
2872
2873
                query = (" SELECT id "
2874
                         " FROM tbl_tenants_virtual_meters "
2875
                         " WHERE tenant_id = %s AND virtual_meter_id = %s")
2876
                cursor.execute(query, (new_id, virtual_meter['id'],))
2877
                if cursor.fetchone() is not None:
2878
                    cursor.close()
2879
                    cnx.close()
2880
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2881
                                           description='API.TENANT_VIRTUAL_METER_RELATION_EXISTS')
2882
2883
                add_row = (" INSERT INTO tbl_tenants_virtual_meters (tenant_id, virtual_meter_id) "
2884
                           " VALUES (%s, %s) ")
2885
                cursor.execute(add_row, (new_id, virtual_meter['id'],))
2886
        if new_values['points'] is not None and len(new_values['points']) > 0:
2887
            for point in new_values['points']:
2888
                cursor.execute(" SELECT name "
2889
                               " FROM tbl_points "
2890
                               " WHERE id = %s ", (point['id'],))
2891
                if cursor.fetchone() is None:
2892
                    cursor.close()
2893
                    cnx.close()
2894
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2895
                                           description='API.POINT_NOT_FOUND')
2896
2897
                query = (" SELECT id "
2898
                         " FROM tbl_tenants_points "
2899
                         " WHERE tenant_id = %s AND point_id = %s")
2900
                cursor.execute(query, (new_id, point['id'],))
2901
                if cursor.fetchone() is not None:
2902
                    cursor.close()
2903
                    cnx.close()
2904
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2905
                                           description='API.TENANT_POINT_RELATION_EXISTS')
2906
2907
                add_row = (" INSERT INTO tbl_tenants_points (tenant_id, point_id) "
2908
                           " VALUES (%s, %s) ")
2909
                cursor.execute(add_row, (new_id, point['id'],))
2910
        if new_values['sensors'] is not None and len(new_values['sensors']) > 0:
2911
            for sensor in new_values['sensors']:
2912
                cursor.execute(" SELECT name "
2913
                               " FROM tbl_sensors "
2914
                               " WHERE id = %s ", (sensor['id'],))
2915
                if cursor.fetchone() is None:
2916
                    cursor.close()
2917
                    cnx.close()
2918
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2919
                                           description='API.SENSOR_NOT_FOUND')
2920
2921
                query = (" SELECT id "
2922
                         " FROM tbl_tenants_sensors "
2923
                         " WHERE tenant_id = %s AND sensor_id = %s")
2924
                cursor.execute(query, (new_id, sensor['id'],))
2925
                if cursor.fetchone() is not None:
2926
                    cursor.close()
2927
                    cnx.close()
2928
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2929
                                           description='API.TENANT_SENSOR_RELATION_EXISTS')
2930
2931
                add_row = (" INSERT INTO tbl_tenants_sensors (tenant_id, sensor_id) "
2932
                           " VALUES (%s, %s) ")
2933
                cursor.execute(add_row, (new_id, sensor['id'],))
2934
        if new_values['working_calendars'] is not None and len(new_values['working_calendars']) > 0:
2935
            for working_calendar in new_values['working_calendars']:
2936
                cursor.execute(" SELECT name "
2937
                               " FROM tbl_working_calendars "
2938
                               " WHERE id = %s ", (working_calendar['id'],))
2939
                if cursor.fetchone() is None:
2940
                    cursor.close()
2941
                    cnx.close()
2942
                    raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
2943
                                           description='API.WORKING_CALENDAR_NOT_FOUND')
2944
2945
                query = (" SELECT id "
2946
                         " FROM tbl_tenants_working_calendars "
2947
                         " WHERE tenant_id = %s AND working_calendar_id = %s")
2948
                cursor.execute(query, (new_id, working_calendar['id'],))
2949
                if cursor.fetchone() is not None:
2950
                    cursor.close()
2951
                    cnx.close()
2952
                    raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
2953
                                           description='API.TENANT_WORKING_CALENDAR_RELATION_EXISTS')
2954
2955
                add_row = (" INSERT INTO tbl_tenants_working_calendars (tenant_id, working_calendar_id) "
2956
                           " VALUES (%s, %s) ")
2957
                cursor.execute(add_row, (new_id, working_calendar['id'],))
2958
        cnx.commit()
2959
        cursor.close()
2960
        cnx.close()
2961
2962
        # Clear cache after importing tenant
2963
        clear_tenant_cache()
2964
2965
        resp.status = falcon.HTTP_201
2966
        resp.location = '/tenants/' + str(new_id)
2967
2968
2969
class TenantClone:
2970
    def __init__(self):
2971
        pass
2972
2973
    @staticmethod
2974
    def on_options(req, resp, id_):
2975
        _ = req
2976
        resp.status = falcon.HTTP_200
2977
        _ = id_
2978
2979
    @staticmethod
2980
    @user_logger
2981
    def on_post(req, resp, id_):
2982
        admin_control(req)
2983
        if not id_.isdigit() or int(id_) <= 0:
2984
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
2985
                                   description='API.INVALID_TENANT_ID')
2986
2987
        cnx = mysql.connector.connect(**config.myems_system_db)
2988
        cursor = cnx.cursor()
2989
2990
        query = (" SELECT id, name, uuid "
2991
                 " FROM tbl_tenant_types ")
2992
        cursor.execute(query)
2993
        rows_tenant_types = cursor.fetchall()
2994
2995
        tenant_type_dict = dict()
2996
        if rows_tenant_types is not None and len(rows_tenant_types) > 0:
2997
            for row in rows_tenant_types:
2998
                tenant_type_dict[row[0]] = {"id": row[0],
2999
                                            "name": row[1],
3000
                                            "uuid": row[2]}
3001
3002
        query = (" SELECT id, name, uuid "
3003
                 " FROM tbl_contacts ")
3004
        cursor.execute(query)
3005
        rows_contacts = cursor.fetchall()
3006
3007
        contact_dict = dict()
3008
        if rows_contacts is not None and len(rows_contacts) > 0:
3009
            for row in rows_contacts:
3010
                contact_dict[row[0]] = {"id": row[0],
3011
                                        "name": row[1],
3012
                                        "uuid": row[2]}
3013
3014
        query = (" SELECT id, name, uuid "
3015
                 " FROM tbl_cost_centers ")
3016
        cursor.execute(query)
3017
        rows_cost_centers = cursor.fetchall()
3018
3019
        cost_center_dict = dict()
3020
        if rows_cost_centers is not None and len(rows_cost_centers) > 0:
3021
            for row in rows_cost_centers:
3022
                cost_center_dict[row[0]] = {"id": row[0],
3023
                                            "name": row[1],
3024
                                            "uuid": row[2]}
3025
3026
        query = (" SELECT id, name, uuid, "
3027
                 "        buildings, floors, rooms, area, tenant_type_id,"
3028
                 "        is_key_tenant, is_input_counted, "
3029
                 "        lease_number, lease_start_datetime_utc, lease_end_datetime_utc, is_in_lease, "
3030
                 "        contact_id, cost_center_id, description "
3031
                 " FROM tbl_tenants "
3032
                 " WHERE id = %s ")
3033
        cursor.execute(query, (id_,))
3034
        row = cursor.fetchone()
3035
3036
        if row is None:
3037
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3038
                                   description='API.TENANT_NOT_FOUND')
3039
        else:
3040
            timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
3041
            if config.utc_offset[0] == '-':
3042
                timezone_offset = -timezone_offset
3043
3044
            meta_result = {"id": row[0],
3045
                           "name": row[1],
3046
                           "uuid": row[2],
3047
                           "buildings": row[3],
3048
                           "floors": row[4],
3049
                           "rooms": row[5],
3050
                           "area": row[6],
3051
                           "tenant_type": tenant_type_dict.get(row[7], None),
3052
                           "is_key_tenant": bool(row[8]),
3053
                           "is_input_counted": bool(row[9]),
3054
                           "lease_number": row[10],
3055
                           "lease_start_datetime": (row[11].replace(tzinfo=timezone.utc) +
3056
                                                    timedelta(minutes=timezone_offset)).isoformat()[0:19],
3057
                           "lease_end_datetime": (row[12].replace(tzinfo=timezone.utc) +
3058
                                                  timedelta(minutes=timezone_offset)).isoformat()[0:19],
3059
                           "is_in_lease": bool(row[13]),
3060
                           "contact": contact_dict.get(row[14], None),
3061
                           "cost_center": cost_center_dict.get(row[15], None),
3062
                           "description": row[16],
3063
                           "commands": None,
3064
                           "meters": None,
3065
                           "offline_meters": None,
3066
                           "virtual_meters": None,
3067
                           "points": None,
3068
                           "sensors": None,
3069
                           "working_calendars": None
3070
                           }
3071
            query = (" SELECT c.id, c.name, c.uuid "
3072
                     " FROM tbl_tenants t, tbl_tenants_commands tc, tbl_commands c "
3073
                     " WHERE tc.tenant_id = t.id AND c.id = tc.command_id AND t.id = %s "
3074
                     " ORDER BY c.id ")
3075
            cursor.execute(query, (id_,))
3076
            rows = cursor.fetchall()
3077
3078
            command_result = list()
3079
            if rows is not None and len(rows) > 0:
3080
                for row in rows:
3081
                    result = {"id": row[0], "name": row[1], "uuid": row[2]}
3082
                    command_result.append(result)
3083
                meta_result['commands'] = command_result
3084
            query = (" SELECT id, name, uuid "
3085
                     " FROM tbl_energy_categories ")
3086
            cursor.execute(query)
3087
            rows_energy_categories = cursor.fetchall()
3088
3089
            energy_category_dict = dict()
3090
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
3091
                for row in rows_energy_categories:
3092
                    energy_category_dict[row[0]] = {"id": row[0],
3093
                                                    "name": row[1],
3094
                                                    "uuid": row[2]}
3095
3096
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id "
3097
                     " FROM tbl_tenants t, tbl_tenants_meters tm, tbl_meters m "
3098
                     " WHERE tm.tenant_id = t.id AND m.id = tm.meter_id AND t.id = %s "
3099
                     " ORDER BY m.id ")
3100
            cursor.execute(query, (id_,))
3101
            rows = cursor.fetchall()
3102
3103
            meter_result = list()
3104
            if rows is not None and len(rows) > 0:
3105
                for row in rows:
3106
                    energy_category = energy_category_dict.get(row[3], None)
3107
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
3108
                              "energy_category": energy_category}
3109
                    meter_result.append(result)
3110
                meta_result['meters'] = meter_result
3111
            query = (" SELECT id, name, uuid "
3112
                     " FROM tbl_energy_categories ")
3113
            cursor.execute(query)
3114
            rows_energy_categories = cursor.fetchall()
3115
3116
            energy_category_dict = dict()
3117
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
3118
                for row in rows_energy_categories:
3119
                    energy_category_dict[row[0]] = {"id": row[0],
3120
                                                    "name": row[1],
3121
                                                    "uuid": row[2]}
3122
3123
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id "
3124
                     " FROM tbl_tenants s, tbl_tenants_offline_meters sm, tbl_offline_meters m "
3125
                     " WHERE sm.tenant_id = s.id AND m.id = sm.offline_meter_id AND s.id = %s "
3126
                     " ORDER BY m.id ")
3127
            cursor.execute(query, (id_,))
3128
            rows = cursor.fetchall()
3129
3130
            offlinemeter_result = list()
3131
            if rows is not None and len(rows) > 0:
3132
                for row in rows:
3133
                    energy_category = energy_category_dict.get(row[3], None)
3134
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
3135
                              "energy_category": energy_category}
3136
                    offlinemeter_result.append(result)
3137
                meta_result['offline_meters'] = offlinemeter_result
3138
            query = (" SELECT id, name, uuid "
3139
                     " FROM tbl_energy_categories ")
3140
            cursor.execute(query)
3141
            rows_energy_categories = cursor.fetchall()
3142
3143
            energy_category_dict = dict()
3144
            if rows_energy_categories is not None and len(rows_energy_categories) > 0:
3145
                for row in rows_energy_categories:
3146
                    energy_category_dict[row[0]] = {"id": row[0],
3147
                                                    "name": row[1],
3148
                                                    "uuid": row[2]}
3149
3150
            query = (" SELECT m.id, m.name, m.uuid, m.energy_category_id "
3151
                     " FROM tbl_tenants t, tbl_tenants_virtual_meters tm, tbl_virtual_meters m "
3152
                     " WHERE tm.tenant_id = t.id AND m.id = tm.virtual_meter_id AND t.id = %s "
3153
                     " ORDER BY m.id ")
3154
            cursor.execute(query, (id_,))
3155
            rows = cursor.fetchall()
3156
3157
            virtualmeter_result = list()
3158
            if rows is not None and len(rows) > 0:
3159
                for row in rows:
3160
                    energy_category = energy_category_dict.get(row[3], None)
3161
                    result = {"id": row[0], "name": row[1], "uuid": row[2],
3162
                              "energy_category": energy_category}
3163
                    virtualmeter_result.append(result)
3164
                meta_result['virtual_meters'] = virtualmeter_result
3165
            query = (" SELECT id, name, uuid "
3166
                     " FROM tbl_data_sources ")
3167
            cursor.execute(query)
3168
            rows_data_sources = cursor.fetchall()
3169
3170
            data_source_dict = dict()
3171
            if rows_data_sources is not None and len(rows_data_sources) > 0:
3172
                for row in rows_data_sources:
3173
                    data_source_dict[row[0]] = {"id": row[0],
3174
                                                "name": row[1],
3175
                                                "uuid": row[2]}
3176
3177
            query = (" SELECT p.id, p.name, p.data_source_id "
3178
                     " FROM tbl_tenants t, tbl_tenants_points tp, tbl_points p "
3179
                     " WHERE tp.tenant_id = t.id AND p.id = tp.point_id AND t.id = %s "
3180
                     " ORDER BY p.id ")
3181
            cursor.execute(query, (id_,))
3182
            rows = cursor.fetchall()
3183
3184
            point_result = list()
3185
            if rows is not None and len(rows) > 0:
3186
                for row in rows:
3187
                    data_source = data_source_dict.get(row[2], None)
3188
                    result = {"id": row[0], "name": row[1], "data_source": data_source}
3189
                    point_result.append(result)
3190
                meta_result['points'] = point_result
3191
            query = (" SELECT s.id, s.name, s.uuid "
3192
                     " FROM tbl_tenants t, tbl_tenants_sensors ts, tbl_sensors s "
3193
                     " WHERE ts.tenant_id = t.id AND s.id = ts.sensor_id AND t.id = %s "
3194
                     " ORDER BY s.id ")
3195
            cursor.execute(query, (id_,))
3196
            rows = cursor.fetchall()
3197
3198
            sensor_result = list()
3199
            if rows is not None and len(rows) > 0:
3200
                for row in rows:
3201
                    result = {"id": row[0], "name": row[1], "uuid": row[2]}
3202
                    sensor_result.append(result)
3203
                meta_result['sensors'] = sensor_result
3204
            query = (" SELECT wc.id, wc.name, wc.description "
3205
                     " FROM tbl_tenants t, tbl_tenants_working_calendars twc, tbl_working_calendars wc "
3206
                     " WHERE twc.tenant_id = t.id AND wc.id = twc.working_calendar_id AND t.id = %s "
3207
                     " ORDER BY wc.id ")
3208
            cursor.execute(query, (id_,))
3209
            rows = cursor.fetchall()
3210
3211
            workingcalendars_result = list()
3212
            if rows is not None and len(rows) > 0:
3213
                for row in rows:
3214
                    result = {"id": row[0], "name": row[1], "description": row[2]}
3215
                    workingcalendars_result.append(result)
3216
                meta_result['working_calendars'] = workingcalendars_result
3217
            timezone_offset = int(config.utc_offset[1:3]) * 60 + int(config.utc_offset[4:6])
3218
            if config.utc_offset[0] == '-':
3219
                timezone_offset = -timezone_offset
3220
            new_name = (str.strip(meta_result['name']) +
3221
                        (datetime.utcnow() + timedelta(minutes=timezone_offset)).isoformat(sep='-', timespec='seconds'))
3222
            add_values = (" INSERT INTO tbl_tenants "
3223
                          "    (name, uuid, buildings, floors, rooms, area, tenant_type_id, "
3224
                          "     is_input_counted, is_key_tenant, "
3225
                          "     lease_number, lease_start_datetime_utc, lease_end_datetime_utc, is_in_lease, "
3226
                          "     contact_id, cost_center_id, description) "
3227
                          " VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) ")
3228
            cursor.execute(add_values, (new_name,
3229
                                        str(uuid.uuid4()),
3230
                                        meta_result['buildings'],
3231
                                        meta_result['floors'],
3232
                                        meta_result['rooms'],
3233
                                        meta_result['area'],
3234
                                        meta_result['tenant_type']['id'],
3235
                                        meta_result['is_input_counted'],
3236
                                        meta_result['is_key_tenant'],
3237
                                        meta_result['lease_number'],
3238
                                        meta_result['lease_start_datetime'],
3239
                                        meta_result['lease_end_datetime'],
3240
                                        meta_result['is_in_lease'],
3241
                                        meta_result['contact']['id'],
3242
                                        meta_result['cost_center']['id'],
3243
                                        meta_result['description']))
3244
            new_id = cursor.lastrowid
3245
            if meta_result['commands'] is not None and len(meta_result['commands']) > 0:
3246
                for command in meta_result['commands']:
3247
                    cursor.execute(" SELECT name "
3248
                                   " FROM tbl_commands "
3249
                                   " WHERE id = %s ", (command['id'],))
3250
                    if cursor.fetchone() is None:
3251
                        cursor.close()
3252
                        cnx.close()
3253
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3254
                                               description='API.COMMAND_NOT_FOUND')
3255
3256
                    query = (" SELECT id "
3257
                             " FROM tbl_tenants_commands "
3258
                             " WHERE tenant_id = %s AND command_id = %s")
3259
                    cursor.execute(query, (new_id, command['id'],))
3260
                    if cursor.fetchone() is not None:
3261
                        cursor.close()
3262
                        cnx.close()
3263
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3264
                                               description='API.TENANT_COMMAND_RELATION_EXISTS')
3265
3266
                    add_row = (" INSERT INTO tbl_tenants_commands (tenant_id, command_id) "
3267
                               " VALUES (%s, %s) ")
3268
                    cursor.execute(add_row, (new_id, command['id'],))
3269
            if meta_result['meters'] is not None and len(meta_result['meters']) > 0:
3270
                for meter in meta_result['meters']:
3271
                    cursor.execute(" SELECT name "
3272
                                   " FROM tbl_meters "
3273
                                   " WHERE id = %s ", (meter['id'],))
3274
                    if cursor.fetchone() is None:
3275
                        cursor.close()
3276
                        cnx.close()
3277
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3278
                                               description='API.METER_NOT_FOUND')
3279
3280
                    query = (" SELECT id "
3281
                             " FROM tbl_tenants_meters "
3282
                             " WHERE tenant_id = %s AND meter_id = %s")
3283
                    cursor.execute(query, (new_id, meter['id'],))
3284
                    if cursor.fetchone() is not None:
3285
                        cursor.close()
3286
                        cnx.close()
3287
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3288
                                               description='API.TENANT_METER_RELATION_EXISTS')
3289
3290
                    add_row = (" INSERT INTO tbl_tenants_meters (tenant_id, meter_id) "
3291
                               " VALUES (%s, %s) ")
3292
                    cursor.execute(add_row, (new_id, meter['id'],))
3293
            if meta_result['offline_meters'] is not None and len(meta_result['offline_meters']) > 0:
3294
                for offline_meter in meta_result['offline_meters']:
3295
                    cursor.execute(" SELECT name "
3296
                                   " FROM tbl_offline_meters "
3297
                                   " WHERE id = %s ", (offline_meter['id'],))
3298
                    if cursor.fetchone() is None:
3299
                        cursor.close()
3300
                        cnx.close()
3301
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3302
                                               description='API.OFFLINE_METER_NOT_FOUND')
3303
3304
                    query = (" SELECT id "
3305
                             " FROM tbl_tenants_offline_meters "
3306
                             " WHERE tenant_id = %s AND offline_meter_id = %s")
3307
                    cursor.execute(query, (new_id, offline_meter['id'],))
3308
                    if cursor.fetchone() is not None:
3309
                        cursor.close()
3310
                        cnx.close()
3311
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3312
                                               description='API.TENANT_OFFLINE_METER_RELATION_EXISTS')
3313
3314
                    add_row = (" INSERT INTO tbl_tenants_offline_meters (tenant_id, offline_meter_id) "
3315
                               " VALUES (%s, %s) ")
3316
                    cursor.execute(add_row, (new_id, offline_meter['id'],))
3317
            if meta_result['virtual_meters'] is not None and len(meta_result['virtual_meters']) > 0:
3318
                for virtual_meter in meta_result['virtual_meters']:
3319
                    cursor.execute(" SELECT name "
3320
                                   " FROM tbl_virtual_meters "
3321
                                   " WHERE id = %s ", (virtual_meter['id'],))
3322
                    if cursor.fetchone() is None:
3323
                        cursor.close()
3324
                        cnx.close()
3325
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3326
                                               description='API.VIRTUAL_METER_NOT_FOUND')
3327
3328
                    query = (" SELECT id "
3329
                             " FROM tbl_tenants_virtual_meters "
3330
                             " WHERE tenant_id = %s AND virtual_meter_id = %s")
3331
                    cursor.execute(query, (new_id, virtual_meter['id'],))
3332
                    if cursor.fetchone() is not None:
3333
                        cursor.close()
3334
                        cnx.close()
3335
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3336
                                               description='API.TENANT_VIRTUAL_METER_RELATION_EXISTS')
3337
3338
                    add_row = (" INSERT INTO tbl_tenants_virtual_meters (tenant_id, virtual_meter_id) "
3339
                               " VALUES (%s, %s) ")
3340
                    cursor.execute(add_row, (new_id, virtual_meter['id'],))
3341
            if meta_result['points'] is not None and len(meta_result['points']) > 0:
3342
                for point in meta_result['points']:
3343
                    cursor.execute(" SELECT name "
3344
                                   " FROM tbl_points "
3345
                                   " WHERE id = %s ", (point['id'],))
3346
                    if cursor.fetchone() is None:
3347
                        cursor.close()
3348
                        cnx.close()
3349
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3350
                                               description='API.POINT_NOT_FOUND')
3351
3352
                    query = (" SELECT id "
3353
                             " FROM tbl_tenants_points "
3354
                             " WHERE tenant_id = %s AND point_id = %s")
3355
                    cursor.execute(query, (new_id, point['id'],))
3356
                    if cursor.fetchone() is not None:
3357
                        cursor.close()
3358
                        cnx.close()
3359
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3360
                                               description='API.TENANT_POINT_RELATION_EXISTS')
3361
3362
                    add_row = (" INSERT INTO tbl_tenants_points (tenant_id, point_id) "
3363
                               " VALUES (%s, %s) ")
3364
                    cursor.execute(add_row, (new_id, point['id'],))
3365
            if meta_result['sensors'] is not None and len(meta_result['sensors']) > 0:
3366
                for sensor in meta_result['sensors']:
3367
                    cursor.execute(" SELECT name "
3368
                                   " FROM tbl_sensors "
3369
                                   " WHERE id = %s ", (sensor['id'],))
3370
                    if cursor.fetchone() is None:
3371
                        cursor.close()
3372
                        cnx.close()
3373
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3374
                                               description='API.SENSOR_NOT_FOUND')
3375
3376
                    query = (" SELECT id "
3377
                             " FROM tbl_tenants_sensors "
3378
                             " WHERE tenant_id = %s AND sensor_id = %s")
3379
                    cursor.execute(query, (new_id, sensor['id'],))
3380
                    if cursor.fetchone() is not None:
3381
                        cursor.close()
3382
                        cnx.close()
3383
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3384
                                               description='API.TENANT_SENSOR_RELATION_EXISTS')
3385
3386
                    add_row = (" INSERT INTO tbl_tenants_sensors (tenant_id, sensor_id) "
3387
                               " VALUES (%s, %s) ")
3388
                    cursor.execute(add_row, (new_id, sensor['id'],))
3389
            if meta_result['working_calendars'] is not None and len(meta_result['working_calendars']) > 0:
3390
                for working_calendar in meta_result['working_calendars']:
3391
                    cursor.execute(" SELECT name "
3392
                                   " FROM tbl_working_calendars "
3393
                                   " WHERE id = %s ", (working_calendar['id'],))
3394
                    if cursor.fetchone() is None:
3395
                        cursor.close()
3396
                        cnx.close()
3397
                        raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
3398
                                               description='API.WORKING_CALENDAR_NOT_FOUND')
3399
3400
                    query = (" SELECT id "
3401
                             " FROM tbl_tenants_working_calendars "
3402
                             " WHERE tenant_id = %s AND working_calendar_id = %s")
3403
                    cursor.execute(query, (new_id, working_calendar['id'],))
3404
                    if cursor.fetchone() is not None:
3405
                        cursor.close()
3406
                        cnx.close()
3407
                        raise falcon.HTTPError(status=falcon.HTTP_400, title='API.ERROR',
3408
                                               description='API.TENANT_WORKING_CALENDAR_RELATION_EXISTS')
3409
3410
                    add_row = (" INSERT INTO tbl_tenants_working_calendars (tenant_id, working_calendar_id) "
3411
                               " VALUES (%s, %s) ")
3412
                    cursor.execute(add_row, (new_id, working_calendar['id'],))
3413
            cnx.commit()
3414
            cursor.close()
3415
            cnx.close()
3416
3417
            # Clear cache after cloning tenant
3418
            clear_tenant_cache()
3419
3420
            resp.status = falcon.HTTP_201
3421
            resp.location = '/tenants/' + str(new_id)
3422