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

core.timezone.TimezoneCollection.on_get()   D

Complexity

Conditions 13

Size

Total Lines 81
Code Lines 49

Duplication

Lines 81
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 49
dl 81
loc 81
rs 4.2
c 0
b 0
f 0
cc 13
nop 2

How to fix   Long Method    Complexity   

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:

Complexity

Complex classes like core.timezone.TimezoneCollection.on_get() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import falcon
2
import mysql.connector
3
import simplejson as json
4
import redis
5
from core.useractivity import user_logger, admin_control, access_control, api_key_control
6
import config
7
8
9 View Code Duplication
def clear_timezone_cache(timezone_id=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
10
    """
11
    Clear timezone-related cache after data modification
12
13
    Args:
14
        timezone_id: Timezone ID (optional, for specific timezone cache)
15
    """
16
    # Check if Redis is enabled
17
    if not config.redis.get('is_enabled', False):
18
        return
19
20
    redis_client = None
21
    try:
22
        redis_client = redis.Redis(
23
            host=config.redis['host'],
24
            port=config.redis['port'],
25
            password=config.redis['password'] if config.redis['password'] else None,
26
            db=config.redis['db'],
27
            decode_responses=True,
28
            socket_connect_timeout=2,
29
            socket_timeout=2
30
        )
31
        redis_client.ping()
32
33
        # Clear timezone list cache
34
        list_cache_key = 'timezone:list'
35
        redis_client.delete(list_cache_key)
36
37
        # Clear specific timezone item cache if timezone_id is provided
38
        if timezone_id:
39
            item_cache_key = f'timezone:item:{timezone_id}'
40
            redis_client.delete(item_cache_key)
41
42
    except Exception:
43
        # If cache clear fails, ignore and continue
44
        pass
45
46
47 View Code Duplication
class TimezoneCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
48
    """
49
    Timezone Collection Resource
50
51
    This class handles timezone operations for the MyEMS system.
52
    It provides functionality to retrieve all available timezones
53
    with their names, descriptions, and UTC offsets.
54
    """
55
56
    def __init__(self):
57
        pass
58
59
    @staticmethod
60
    def on_options(req, resp):
61
        """
62
        Handle OPTIONS request for CORS preflight
63
64
        Args:
65
            req: Falcon request object
66
            resp: Falcon response object
67
        """
68
        _ = req
69
        resp.status = falcon.HTTP_200
70
71
    @staticmethod
72
    def on_get(req, resp):
73
        """
74
        Handle GET requests to retrieve all timezones
75
76
        Returns a list of all available timezones with their metadata including:
77
        - Timezone ID
78
        - Timezone name
79
        - Description
80
        - UTC offset
81
82
        Args:
83
            req: Falcon request object
84
            resp: Falcon response object
85
        """
86
        # Check authentication method (API key or session)
87
        if 'API-KEY' not in req.headers or \
88
                not isinstance(req.headers['API-KEY'], str) or \
89
                len(str.strip(req.headers['API-KEY'])) == 0:
90
            access_control(req)
91
        else:
92
            api_key_control(req)
93
94
        # Redis cache key
95
        cache_key = 'timezone:list'
96
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
97
98
        # Try to get from Redis cache (only if Redis is enabled)
99
        redis_client = None
100
        if config.redis.get('is_enabled', False):
101
            try:
102
                redis_client = redis.Redis(
103
                    host=config.redis['host'],
104
                    port=config.redis['port'],
105
                    password=config.redis['password'] if config.redis['password'] else None,
106
                    db=config.redis['db'],
107
                    decode_responses=True,
108
                    socket_connect_timeout=2,
109
                    socket_timeout=2
110
                )
111
                redis_client.ping()
112
                cached_result = redis_client.get(cache_key)
113
                if cached_result:
114
                    resp.text = cached_result
115
                    return
116
            except Exception:
117
                # If Redis connection fails, continue to database query
118
                pass
119
120
        # Cache miss or Redis error - query database
121
        cnx = mysql.connector.connect(**config.myems_system_db)
122
        cursor = cnx.cursor()
123
124
        # Query to retrieve all timezones
125
        query = (" SELECT id, name, description, utc_offset "
126
                 " FROM tbl_timezones ")
127
        cursor.execute(query)
128
        rows = cursor.fetchall()
129
        cursor.close()
130
        cnx.close()
131
132
        # Build result list
133
        result = list()
134
        if rows is not None and len(rows) > 0:
135
            for row in rows:
136
                meta_result = {"id": row[0],
137
                               "name": row[1],
138
                               "description": row[2],
139
                               "utc_offset": row[3]}
140
                result.append(meta_result)
141
142
        # Store result in Redis cache
143
        result_json = json.dumps(result)
144
        if redis_client:
145
            try:
146
                redis_client.setex(cache_key, cache_expire, result_json)
147
            except Exception:
148
                # If cache set fails, ignore and continue
149
                pass
150
151
        resp.text = result_json
152
153
154
class TimezoneItem:
155
    """
156
    Timezone Item Resource
157
158
    This class handles individual timezone operations including:
159
    - Retrieving a specific timezone by ID
160
    - Updating timezone information
161
    """
162
163
    def __init__(self):
164
        pass
165
166
    @staticmethod
167
    def on_options(req, resp, id_):
168
        """
169
        Handle OPTIONS request for CORS preflight
170
171
        Args:
172
            req: Falcon request object
173
            resp: Falcon response object
174
            id_: Timezone ID parameter
175
        """
176
        _ = req
177
        resp.status = falcon.HTTP_200
178
        _ = id_
179
180
    @staticmethod
181
    def on_get(req, resp, id_):
182
        """
183
        Handle GET requests to retrieve a specific timezone by ID
184
185
        Retrieves a single timezone with its metadata including:
186
        - Timezone ID
187
        - Timezone name
188
        - Description
189
        - UTC offset
190
191
        Args:
192
            req: Falcon request object
193
            resp: Falcon response object
194
            id_: Timezone ID to retrieve
195
        """
196
        # Check authentication method (API key or session)
197
        if 'API-KEY' not in req.headers or \
198
                not isinstance(req.headers['API-KEY'], str) or \
199
                len(str.strip(req.headers['API-KEY'])) == 0:
200
            access_control(req)
201
        else:
202
            api_key_control(req)
203
204
        if not id_.isdigit() or int(id_) <= 0:
205
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
206
                                   description='API.INVALID_TIMEZONE_ID')
207
208
        # Redis cache key
209
        cache_key = f'timezone:item:{id_}'
210
        cache_expire = 28800  # 8 hours in seconds (long-term cache)
211
212
        # Try to get from Redis cache (only if Redis is enabled)
213
        redis_client = None
214
        if config.redis.get('is_enabled', False):
215
            try:
216
                redis_client = redis.Redis(
217
                    host=config.redis['host'],
218
                    port=config.redis['port'],
219
                    password=config.redis['password'] if config.redis['password'] else None,
220
                    db=config.redis['db'],
221
                    decode_responses=True,
222
                    socket_connect_timeout=2,
223
                    socket_timeout=2
224
                )
225
                redis_client.ping()
226
                cached_result = redis_client.get(cache_key)
227
                if cached_result:
228
                    resp.text = cached_result
229
                    return
230
            except Exception:
231
                # If Redis connection fails, continue to database query
232
                pass
233
234
        # Cache miss or Redis error - query database
235
        cnx = mysql.connector.connect(**config.myems_system_db)
236
        cursor = cnx.cursor()
237
238
        # Query to retrieve specific timezone by ID
239
        query = (" SELECT id, name, description, utc_offset "
240
                 " FROM tbl_timezones "
241
                 " WHERE id = %s ")
242
        cursor.execute(query, (id_,))
243
        row = cursor.fetchone()
244
        if row is None:
245
            cursor.close()
246
            cnx.close()
247
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
248
                                   description='API.TIMEZONE_NOT_FOUND')
249
250
        # Build result object
251
        result = {"id": row[0],
252
                  "name": row[1],
253
                  "description": row[2],
254
                  "utc_offset": row[3]}
255
256
        cursor.close()
257
        cnx.close()
258
259
        # Store result in Redis cache
260
        result_json = json.dumps(result)
261
        if redis_client:
262
            try:
263
                redis_client.setex(cache_key, cache_expire, result_json)
264
            except Exception:
265
                # If cache set fails, ignore and continue
266
                pass
267
268
        resp.text = result_json
269
270 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
271
    @user_logger
272
    def on_put(req, resp, id_):
273
        """
274
        Handle PUT requests to update timezone information
275
276
        Updates a specific timezone with new name, description, and UTC offset.
277
        Requires admin privileges.
278
279
        Args:
280
            req: Falcon request object containing update data
281
            resp: Falcon response object
282
            id_: Timezone ID to update
283
        """
284
        admin_control(req)
285
        try:
286
            raw_json = req.stream.read().decode('utf-8')
287
        except UnicodeDecodeError as ex:
288
            print("Failed to decode request")
289
            raise falcon.HTTPError(status=falcon.HTTP_400,
290
                                   title='API.BAD_REQUEST',
291
                                   description='API.INVALID_ENCODING')
292
        except Exception as ex:
293
            print("Unexpected error reading request stream")
294
            raise falcon.HTTPError(status=falcon.HTTP_400,
295
                                   title='API.BAD_REQUEST',
296
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
297
298
        if not id_.isdigit() or int(id_) <= 0:
299
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
300
                                   description='API.INVALID_TIMEZONE_ID')
301
302
        new_values = json.loads(raw_json)
303
304
        cnx = mysql.connector.connect(**config.myems_system_db)
305
        cursor = cnx.cursor()
306
307
        # Check if timezone exists
308
        cursor.execute(" SELECT name "
309
                       " FROM tbl_timezones "
310
                       " WHERE id = %s ", (id_,))
311
        if cursor.fetchone() is None:
312
            cursor.close()
313
            cnx.close()
314
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
315
                                   description='API.TIMEZONE_NOT_FOUND')
316
317
        # Update timezone information
318
        update_row = (" UPDATE tbl_timezones "
319
                      " SET name = %s, description = %s, utc_offset = %s "
320
                      " WHERE id = %s ")
321
        cursor.execute(update_row, (new_values['data']['name'],
322
                                    new_values['data']['description'],
323
                                    new_values['data']['utc_offset'],
324
                                    id_,))
325
        cnx.commit()
326
327
        cursor.close()
328
        cnx.close()
329
330
        # Clear cache after updating timezone
331
        clear_timezone_cache(timezone_id=id_)
332
333
        resp.status = falcon.HTTP_200
334