core.energyitem.clear_energyitem_cache()   B
last analyzed

Complexity

Conditions 6

Size

Total Lines 38
Code Lines 23

Duplication

Lines 38
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 23
dl 38
loc 38
rs 8.3946
c 0
b 0
f 0
cc 6
nop 1
1
import uuid
2
import falcon
3
import mysql.connector
4
import simplejson as json
5
import redis
6
from core.useractivity import user_logger, admin_control, access_control, api_key_control
7
import config
8
9
10 View Code Duplication
def clear_energyitem_cache(energy_item_id=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
11
    """
12
    Clear energy item-related cache after data modification
13
14
    Args:
15
        energy_item_id: Energy item ID (optional, for specific item cache)
16
    """
17
    # Check if Redis is enabled
18
    if not config.redis.get('is_enabled', False):
19
        return
20
21
    redis_client = None
22
    try:
23
        redis_client = redis.Redis(
24
            host=config.redis['host'],
25
            port=config.redis['port'],
26
            password=config.redis['password'] if config.redis['password'] else None,
27
            db=config.redis['db'],
28
            decode_responses=True,
29
            socket_connect_timeout=2,
30
            socket_timeout=2
31
        )
32
        redis_client.ping()
33
34
        # Clear energy item list cache
35
        list_cache_key_pattern = 'energyitem:list*'
36
        matching_keys = redis_client.keys(list_cache_key_pattern)
37
        if matching_keys:
38
            redis_client.delete(*matching_keys)
39
40
        # Clear specific energy item cache if energy_item_id is provided
41
        if energy_item_id:
42
            item_cache_key = f'energyitem:item:{energy_item_id}'
43
            redis_client.delete(item_cache_key)
44
45
    except Exception:
46
        # If cache clear fails, ignore and continue
47
        pass
48
49
50
class EnergyItemCollection:
51
    """
52
    Energy Item Collection Resource
53
54
    This class handles CRUD operations for energy item collection.
55
    It provides endpoints for listing all energy items and creating new items.
56
    Energy items represent specific energy types within energy categories.
57
    """
58
    def __init__(self):
59
        """Initialize EnergyItemCollection"""
60
        pass
61
62
    @staticmethod
63
    def on_options(req, resp):
64
        """Handle OPTIONS requests for CORS preflight"""
65
        _ = req
66
        resp.status = falcon.HTTP_200
67
68
    @staticmethod
69
    def on_get(req, resp):
70
        if 'API-KEY' not in req.headers or \
71
                not isinstance(req.headers['API-KEY'], str) or \
72
                len(str.strip(req.headers['API-KEY'])) == 0:
73
            access_control(req)
74
        else:
75
            api_key_control(req)
76
77
        # Redis cache key
78
        cache_key = 'energyitem:list'
79
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
80
81
        # Try to get from Redis cache (only if Redis is enabled)
82
        redis_client = None
83
        if config.redis.get('is_enabled', False):
84
            try:
85
                redis_client = redis.Redis(
86
                    host=config.redis['host'],
87
                    port=config.redis['port'],
88
                    password=config.redis['password'] if config.redis['password'] else None,
89
                    db=config.redis['db'],
90
                    decode_responses=True,
91
                    socket_connect_timeout=2,
92
                    socket_timeout=2
93
                )
94
                redis_client.ping()
95
                cached_result = redis_client.get(cache_key)
96
                if cached_result:
97
                    resp.text = cached_result
98
                    return
99
            except Exception:
100
                # If Redis connection fails, continue to database query
101
                pass
102
103
        # Cache miss or Redis error - query database
104
        cnx = mysql.connector.connect(**config.myems_system_db)
105
        cursor = cnx.cursor()
106
107
        query = (" SELECT id, name, uuid "
108
                 " FROM tbl_energy_categories ")
109
        cursor.execute(query)
110
        rows_energy_categories = cursor.fetchall()
111
112
        energy_category_dict = dict()
113
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
114
            for row in rows_energy_categories:
115
                energy_category_dict[row[0]] = {"id": row[0],
116
                                                "name": row[1],
117
                                                "uuid": row[2]}
118
119
        query = (" SELECT id, name, uuid, energy_category_id "
120
                 " FROM tbl_energy_items "
121
                 " ORDER BY id ")
122
        cursor.execute(query)
123
        rows = cursor.fetchall()
124
        cursor.close()
125
        cnx.close()
126
127
        result = list()
128
        if rows is not None and len(rows) > 0:
129
            for row in rows:
130
                meta_result = {"id": row[0],
131
                               "name": row[1],
132
                               "uuid": row[2],
133
                               "energy_category": energy_category_dict.get(row[3], None)}
134
                result.append(meta_result)
135
136
        # Store result in Redis cache
137
        result_json = json.dumps(result)
138
        if redis_client:
139
            try:
140
                redis_client.setex(cache_key, cache_expire, result_json)
141
            except Exception:
142
                # If cache set fails, ignore and continue
143
                pass
144
145
        resp.text = result_json
146
147
    @staticmethod
148
    @user_logger
149
    def on_post(req, resp):
150
        """Handles POST requests"""
151
        admin_control(req)
152
        try:
153
            raw_json = req.stream.read().decode('utf-8')
154
        except UnicodeDecodeError as ex:
155
            print("Failed to decode request")
156
            raise falcon.HTTPError(status=falcon.HTTP_400,
157
                                   title='API.BAD_REQUEST',
158
                                   description='API.INVALID_ENCODING')
159
        except Exception as ex:
160
            print("Unexpected error reading request stream")
161
            raise falcon.HTTPError(status=falcon.HTTP_400,
162
                                   title='API.BAD_REQUEST',
163
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
164
165
        new_values = json.loads(raw_json)
166
167
        if 'name' not in new_values['data'].keys() or \
168
                not isinstance(new_values['data']['name'], str) or \
169
                len(str.strip(new_values['data']['name'])) == 0:
170
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
171
                                   description='API.INVALID_ENERGY_ITEM_NAME')
172
173
        name = str.strip(new_values['data']['name'])
174
175
        if 'energy_category_id' not in new_values['data'].keys() or \
176
                new_values['data']['energy_category_id'] <= 0:
177
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
178
                                   description='API.INVALID_ENERGY_CATEGORY_ID')
179
        energy_category_id = new_values['data']['energy_category_id']
180
181
        cnx = mysql.connector.connect(**config.myems_system_db)
182
        cursor = cnx.cursor()
183
184
        cursor.execute(" SELECT name "
185
                       " FROM tbl_energy_items "
186
                       " WHERE name = %s ", (name,))
187
        if cursor.fetchone() is not None:
188
            cursor.close()
189
            cnx.close()
190
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
191
                                   description='API.ENERGY_ITEM_NAME_IS_ALREADY_IN_USE')
192
193
        cursor.execute(" SELECT name "
194
                       " FROM tbl_energy_categories "
195
                       " WHERE id = %s ",
196
                       (energy_category_id,))
197
        if cursor.fetchone() is None:
198
            cursor.close()
199
            cnx.close()
200
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
201
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
202
203
        add_value = (" INSERT INTO tbl_energy_items "
204
                     "    (name, uuid, energy_category_id) "
205
                     " VALUES (%s, %s, %s) ")
206
        cursor.execute(add_value, (name,
207
                                   str(uuid.uuid4()),
208
                                   energy_category_id))
209
        new_id = cursor.lastrowid
210
        cnx.commit()
211
        cursor.close()
212
        cnx.close()
213
214
        # Clear cache after creating new energy item
215
        clear_energyitem_cache()
216
217
        resp.status = falcon.HTTP_201
218
        resp.location = '/energyitems/' + str(new_id)
219
220
221
class EnergyItemItem:
222
    def __init__(self):
223
        pass
224
225
    @staticmethod
226
    def on_options(req, resp, id_):
227
        _ = req
228
        resp.status = falcon.HTTP_200
229
        _ = id_
230
231
    @staticmethod
232
    def on_get(req, resp, id_):
233
        if 'API-KEY' not in req.headers or \
234
                not isinstance(req.headers['API-KEY'], str) or \
235
                len(str.strip(req.headers['API-KEY'])) == 0:
236
            access_control(req)
237
        else:
238
            api_key_control(req)
239
        if not id_.isdigit() or int(id_) <= 0:
240
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
241
                                   description='API.INVALID_ENERGY_ITEM_ID')
242
243
        # Redis cache key
244
        cache_key = f'energyitem:item:{id_}'
245
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
246
247
        # Try to get from Redis cache (only if Redis is enabled)
248
        redis_client = None
249
        if config.redis.get('is_enabled', False):
250
            try:
251
                redis_client = redis.Redis(
252
                    host=config.redis['host'],
253
                    port=config.redis['port'],
254
                    password=config.redis['password'] if config.redis['password'] else None,
255
                    db=config.redis['db'],
256
                    decode_responses=True,
257
                    socket_connect_timeout=2,
258
                    socket_timeout=2
259
                )
260
                redis_client.ping()
261
                cached_result = redis_client.get(cache_key)
262
                if cached_result:
263
                    resp.text = cached_result
264
                    return
265
            except Exception:
266
                # If Redis connection fails, continue to database query
267
                pass
268
269
        # Cache miss or Redis error - query database
270
        cnx = mysql.connector.connect(**config.myems_system_db)
271
        cursor = cnx.cursor()
272
273
        query = (" SELECT id, name, uuid "
274
                 " FROM tbl_energy_categories ")
275
        cursor.execute(query)
276
        rows_energy_categories = cursor.fetchall()
277
278
        energy_category_dict = dict()
279
        if rows_energy_categories is not None and len(rows_energy_categories) > 0:
280
            for row in rows_energy_categories:
281
                energy_category_dict[row[0]] = {"id": row[0],
282
                                                "name": row[1],
283
                                                "uuid": row[2]}
284
285
        query = (" SELECT id, name, uuid, energy_category_id "
286
                 " FROM tbl_energy_items "
287
                 " WHERE id = %s ")
288
        cursor.execute(query, (id_,))
289
        row = cursor.fetchone()
290
        cursor.close()
291
        cnx.close()
292
        if row is None:
293
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
294
                                   description='API.ENERGY_ITEM_NOT_FOUND')
295
        result = {"id": row[0],
296
                  "name": row[1],
297
                  "uuid": row[2],
298
                  "energy_category": energy_category_dict.get(row[3], None)}
299
300
        # Store result in Redis cache
301
        result_json = json.dumps(result)
302
        if redis_client:
303
            try:
304
                redis_client.setex(cache_key, cache_expire, result_json)
305
            except Exception:
306
                # If cache set fails, ignore and continue
307
                pass
308
309
        resp.text = result_json
310
311
    @staticmethod
312
    @user_logger
313
    def on_delete(req, resp, id_):
314
        admin_control(req)
315
        if not id_.isdigit() or int(id_) <= 0:
316
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
317
                                   description='API.INVALID_ENERGY_ITEM_ID')
318
319
        cnx = mysql.connector.connect(**config.myems_system_db)
320
        cursor = cnx.cursor()
321
322
        cursor.execute(" SELECT name "
323
                       " FROM tbl_energy_items "
324
                       " WHERE id = %s ", (id_,))
325
        if cursor.fetchone() is None:
326
            cursor.close()
327
            cnx.close()
328
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
329
                                   description='API.ENERGY_ITEM_NOT_FOUND')
330
331
        cursor.execute(" SELECT id "
332
                       " FROM tbl_meters "
333
                       " WHERE energy_item_id = %s ", (id_,))
334
        rows_meters = cursor.fetchall()
335
        if rows_meters is not None and len(rows_meters) > 0:
336
            cursor.close()
337
            cnx.close()
338
            raise falcon.HTTPError(status=falcon.HTTP_400,
339
                                   title='API.BAD_REQUEST',
340
                                   description='API.ENERGY_ITEM_USED_IN_METER')
341
342
        cursor.execute(" SELECT id "
343
                       " FROM tbl_virtual_meters "
344
                       " WHERE energy_item_id = %s ", (id_,))
345
        rows_virtual_meters = cursor.fetchall()
346
        if rows_virtual_meters is not None and len(rows_virtual_meters) > 0:
347
            cursor.close()
348
            cnx.close()
349
            raise falcon.HTTPError(status=falcon.HTTP_400,
350
                                   title='API.BAD_REQUEST',
351
                                   description='API.ENERGY_ITEM_USED_IN_VIRTUAL_METER')
352
353
        cursor.execute(" SELECT id "
354
                       " FROM tbl_offline_meters "
355
                       " WHERE energy_item_id = %s ", (id_,))
356
        rows_offline_meters = cursor.fetchall()
357
        if rows_offline_meters is not None and len(rows_offline_meters) > 0:
358
            cursor.close()
359
            cnx.close()
360
            raise falcon.HTTPError(status=falcon.HTTP_400,
361
                                   title='API.BAD_REQUEST',
362
                                   description='API.ENERGY_ITEM_USED_IN_OFFLINE_METER')
363
364
        cursor.execute(" DELETE FROM tbl_energy_items WHERE id = %s ", (id_,))
365
        cnx.commit()
366
367
        cursor.close()
368
        cnx.close()
369
370
        # Clear cache after deleting energy item
371
        clear_energyitem_cache(energy_item_id=id_)
372
373
        resp.status = falcon.HTTP_204
374
375
    @staticmethod
376
    @user_logger
377
    def on_put(req, resp, id_):
378
        """Handles PUT requests"""
379
        admin_control(req)
380
        try:
381
            raw_json = req.stream.read().decode('utf-8')
382
        except UnicodeDecodeError as ex:
383
            print("Failed to decode request")
384
            raise falcon.HTTPError(status=falcon.HTTP_400,
385
                                   title='API.BAD_REQUEST',
386
                                   description='API.INVALID_ENCODING')
387
        except Exception as ex:
388
            print("Unexpected error reading request stream")
389
            raise falcon.HTTPError(status=falcon.HTTP_400,
390
                                   title='API.BAD_REQUEST',
391
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
392
393
        if not id_.isdigit() or int(id_) <= 0:
394
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
395
                                   description='API.INVALID_ENERGY_ITEM_ID')
396
397
        new_values = json.loads(raw_json)
398
        if 'name' not in new_values['data'].keys() or \
399
                not isinstance(new_values['data']['name'], str) or \
400
                len(str.strip(new_values['data']['name'])) == 0:
401
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
402
                                   description='API.INVALID_ENERGY_ITEM_NAME')
403
404
        name = str.strip(new_values['data']['name'])
405
406
        if 'energy_category_id' not in new_values['data'].keys() or new_values['data']['energy_category_id'] <= 0:
407
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
408
                                   description='API.INVALID_ENERGY_CATEGORY_ID')
409
        energy_category_id = new_values['data']['energy_category_id']
410
411
        cnx = mysql.connector.connect(**config.myems_system_db)
412
        cursor = cnx.cursor()
413
414
        cursor.execute(" SELECT name "
415
                       " FROM tbl_energy_items "
416
                       " WHERE id = %s ", (id_,))
417
        if cursor.fetchone() is None:
418
            cursor.close()
419
            cnx.close()
420
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
421
                                   description='API.ENERGY_ITEM_NOT_FOUND')
422
423
        cursor.execute(" SELECT name "
424
                       " FROM tbl_energy_items "
425
                       " WHERE name = %s AND id != %s ", (name, id_))
426
        if cursor.fetchone() is not None:
427
            cursor.close()
428
            cnx.close()
429
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
430
                                   description='API.ENERGY_ITEM_NAME_IS_ALREADY_IN_USE')
431
432
        cursor.execute(" SELECT name "
433
                       " FROM tbl_energy_categories "
434
                       " WHERE id = %s ",
435
                       (energy_category_id,))
436
        if cursor.fetchone() is None:
437
            cursor.close()
438
            cnx.close()
439
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
440
                                   description='API.ENERGY_CATEGORY_NOT_FOUND')
441
442
        update_row = (" UPDATE tbl_energy_items "
443
                      " SET name = %s, energy_category_id = %s "
444
                      " WHERE id = %s ")
445
        cursor.execute(update_row, (name,
446
                                    energy_category_id,
447
                                    id_,))
448
        cnx.commit()
449
        cursor.close()
450
        cnx.close()
451
452
        # Clear cache after updating energy item
453
        clear_energyitem_cache(energy_item_id=id_)
454
455
        resp.status = falcon.HTTP_200
456
457