Passed
Push — master ( 8f221d...363a8a )
by
unknown
10:33
created

core.menu.MenuChildrenCollection.on_options()   A

Complexity

Conditions 1

Size

Total Lines 13
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 13
rs 10
c 0
b 0
f 0
cc 1
nop 3
1
import falcon
2
import mysql.connector
3
import simplejson as json
4
from core.useractivity import user_logger, admin_control, access_control, api_key_control
5
import config
6
7
8 View Code Duplication
class MenuCollection:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
9
    """
10
    Menu Collection Resource
11
12
    This class handles menu operations for the MyEMS system.
13
    It provides functionality to retrieve all menus and their hierarchical structure
14
    used for navigation in the MyEMS web interface.
15
    """
16
17
    def __init__(self):
18
        pass
19
20
    @staticmethod
21
    def on_options(req, resp):
22
        """
23
        Handle OPTIONS request for CORS preflight
24
25
        Args:
26
            req: Falcon request object
27
            resp: Falcon response object
28
        """
29
        _ = req
30
        resp.status = falcon.HTTP_200
31
32
    @staticmethod
33
    def on_get(req, resp):
34
        """
35
        Handle GET requests to retrieve all menus
36
37
        Returns a list of all menus with their metadata including:
38
        - Menu ID and name
39
        - Route path
40
        - Parent menu ID (for hierarchical structure)
41
        - Hidden status
42
43
        Args:
44
            req: Falcon request object
45
            resp: Falcon response object
46
        """
47
        # Check authentication method (API key or session)
48
        if 'API-KEY' not in req.headers or \
49
                not isinstance(req.headers['API-KEY'], str) or \
50
                len(str.strip(req.headers['API-KEY'])) == 0:
51
            access_control(req)
52
        else:
53
            api_key_control(req)
54
55
        cnx = mysql.connector.connect(**config.myems_system_db)
56
        cursor = cnx.cursor()
57
58
        # Query to retrieve all menus ordered by ID
59
        query = (" SELECT id, name, route, parent_menu_id, is_hidden "
60
                 " FROM tbl_menus "
61
                 " ORDER BY id ")
62
        cursor.execute(query)
63
        rows_menus = cursor.fetchall()
64
65
        # Build result list
66
        result = list()
67
        if rows_menus is not None and len(rows_menus) > 0:
68
            for row in rows_menus:
69
                temp = {"id": row[0],
70
                        "name": row[1],
71
                        "route": row[2],
72
                        "parent_menu_id": row[3],
73
                        "is_hidden": bool(row[4])}
74
75
                result.append(temp)
76
77
        cursor.close()
78
        cnx.close()
79
        resp.text = json.dumps(result)
80
81
82
class MenuItem:
83
    """
84
    Menu Item Resource
85
86
    This class handles individual menu operations including:
87
    - Retrieving a specific menu by ID
88
    - Updating menu visibility status
89
    """
90
91
    def __init__(self):
92
        pass
93
94
    @staticmethod
95
    def on_options(req, resp, id_):
96
        """
97
        Handle OPTIONS request for CORS preflight
98
99
        Args:
100
            req: Falcon request object
101
            resp: Falcon response object
102
            id_: Menu ID parameter
103
        """
104
        _ = req
105
        resp.status = falcon.HTTP_200
106
        _ = id_
107
108 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
109
    def on_get(req, resp, id_):
110
        """
111
        Handle GET requests to retrieve a specific menu by ID
112
113
        Retrieves a single menu with its metadata including:
114
        - Menu ID and name
115
        - Route path
116
        - Parent menu ID
117
        - Hidden status
118
119
        Args:
120
            req: Falcon request object
121
            resp: Falcon response object
122
            id_: Menu ID to retrieve
123
        """
124
        # Check authentication method (API key or session)
125
        if 'API-KEY' not in req.headers or \
126
                not isinstance(req.headers['API-KEY'], str) or \
127
                len(str.strip(req.headers['API-KEY'])) == 0:
128
            access_control(req)
129
        else:
130
            api_key_control(req)
131
132
        if not id_.isdigit() or int(id_) <= 0:
133
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
134
                                   description='API.INVALID_MENU_ID')
135
136
        cnx = mysql.connector.connect(**config.myems_system_db)
137
        cursor = cnx.cursor()
138
139
        # Query to retrieve specific menu by ID
140
        query = (" SELECT id, name, route, parent_menu_id, is_hidden "
141
                 " FROM tbl_menus "
142
                 " WHERE id= %s ")
143
        cursor.execute(query, (id_,))
144
        rows_menu = cursor.fetchone()
145
146
        # Build result object
147
        result = None
148
        if rows_menu is not None and len(rows_menu) > 0:
149
            result = {"id": rows_menu[0],
150
                      "name": rows_menu[1],
151
                      "route": rows_menu[2],
152
                      "parent_menu_id": rows_menu[3],
153
                      "is_hidden": bool(rows_menu[4])}
154
155
        cursor.close()
156
        cnx.close()
157
        resp.text = json.dumps(result)
158
159 View Code Duplication
    @staticmethod
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
160
    @user_logger
161
    def on_put(req, resp, id_):
162
        """
163
        Handle PUT requests to update menu visibility
164
165
        Updates the hidden status of a specific menu.
166
        Requires admin privileges.
167
168
        Args:
169
            req: Falcon request object containing update data:
170
                - is_hidden: Boolean value for menu visibility
171
            resp: Falcon response object
172
            id_: Menu ID to update
173
        """
174
        admin_control(req)
175
        try:
176
            raw_json = req.stream.read().decode('utf-8')
177
        except Exception as ex:
178
            print(str(ex))
179
            raise falcon.HTTPError(status=falcon.HTTP_400,
180
                                   title='API.BAD_REQUEST',
181
                                   description='API.FAILED_TO_READ_REQUEST_STREAM')
182
183
        if not id_.isdigit() or int(id_) <= 0:
184
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
185
                                   description='API.INVALID_MENU_ID')
186
187
        new_values = json.loads(raw_json)
188
189
        # Validate hidden status
190
        if 'is_hidden' not in new_values['data'].keys() or \
191
                not isinstance(new_values['data']['is_hidden'], bool):
192
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
193
                                   description='API.INVALID_IS_HIDDEN')
194
        is_hidden = new_values['data']['is_hidden']
195
196
        cnx = mysql.connector.connect(**config.myems_system_db)
197
        cursor = cnx.cursor()
198
199
        # Update menu visibility status
200
        update_row = (" UPDATE tbl_menus "
201
                      " SET is_hidden = %s "
202
                      " WHERE id = %s ")
203
        cursor.execute(update_row, (is_hidden,
204
                                    id_))
205
        cnx.commit()
206
207
        cursor.close()
208
        cnx.close()
209
210
        resp.status = falcon.HTTP_200
211
212
213
class MenuChildrenCollection:
214
    """
215
    Menu Children Collection Resource
216
217
    This class handles retrieval of menu hierarchy including:
218
    - Current menu information
219
    - Parent menu information
220
    - Child menus
221
    """
222
223
    def __init__(self):
224
        pass
225
226
    @staticmethod
227
    def on_options(req, resp, id_):
228
        """
229
        Handle OPTIONS request for CORS preflight
230
231
        Args:
232
            req: Falcon request object
233
            resp: Falcon response object
234
            id_: Menu ID parameter
235
        """
236
        _ = req
237
        resp.status = falcon.HTTP_200
238
        _ = id_
239
240
    @staticmethod
241
    def on_get(req, resp, id_):
242
        """
243
        Handle GET requests to retrieve menu hierarchy
244
245
        Returns detailed menu information including:
246
        - Current menu details
247
        - Parent menu information
248
        - List of child menus
249
250
        Args:
251
            req: Falcon request object
252
            resp: Falcon response object
253
            id_: Menu ID to retrieve hierarchy for
254
        """
255
        # Check authentication method (API key or session)
256
        if 'API-KEY' not in req.headers or \
257
                not isinstance(req.headers['API-KEY'], str) or \
258
                len(str.strip(req.headers['API-KEY'])) == 0:
259
            access_control(req)
260
        else:
261
            api_key_control(req)
262
263
        if not id_.isdigit() or int(id_) <= 0:
264
            raise falcon.HTTPError(status=falcon.HTTP_400, title='API.BAD_REQUEST',
265
                                   description='API.INVALID_MENU_ID')
266
267
        cnx = mysql.connector.connect(**config.myems_system_db)
268
        cursor = cnx.cursor()
269
270
        # Query to retrieve current menu
271
        query = (" SELECT id, name, route, parent_menu_id, is_hidden "
272
                 " FROM tbl_menus "
273
                 " WHERE id = %s ")
274
        cursor.execute(query, (id_,))
275
        row_current_menu = cursor.fetchone()
276
        if row_current_menu is None:
277
            cursor.close()
278
            cnx.close()
279
            raise falcon.HTTPError(status=falcon.HTTP_404, title='API.NOT_FOUND',
280
                                   description='API.MENU_NOT_FOUND')
281
282
        # Query to retrieve all menus for parent lookup
283
        query = (" SELECT id, name "
284
                 " FROM tbl_menus "
285
                 " ORDER BY id ")
286
        cursor.execute(query)
287
        rows_menus = cursor.fetchall()
288
289
        # Build menu dictionary for parent lookup
290
        menu_dict = dict()
291
        if rows_menus is not None and len(rows_menus) > 0:
292
            for row in rows_menus:
293
                menu_dict[row[0]] = {"id": row[0],
294
                                     "name": row[1]}
295
296
        # Build result structure
297
        result = dict()
298
        result['current'] = dict()
299
        result['current']['id'] = row_current_menu[0]
300
        result['current']['name'] = row_current_menu[1]
301
        result['current']['parent_menu'] = menu_dict.get(row_current_menu[3], None)
302
        result['current']['is_hidden'] = bool(row_current_menu[4])
303
304
        result['children'] = list()
305
306
        # Query to retrieve child menus
307
        query = (" SELECT id, name, route, parent_menu_id, is_hidden "
308
                 " FROM tbl_menus "
309
                 " WHERE parent_menu_id = %s "
310
                 " ORDER BY id ")
311
        cursor.execute(query, (id_, ))
312
        rows_menus = cursor.fetchall()
313
314
        # Build children list
315
        if rows_menus is not None and len(rows_menus) > 0:
316
            for row in rows_menus:
317
                meta_result = {"id": row[0],
318
                               "name": row[1],
319
                               "parent_menu": menu_dict.get(row[3], None),
320
                               "is_hidden": bool(row[4])}
321
                result['children'].append(meta_result)
322
323
        cursor.close()
324
        cnx.close()
325
        resp.text = json.dumps(result)
326
327
328
class MenuWebCollection:
329
    """
330
    Menu Web Collection Resource
331
332
    This class provides menu data specifically formatted for web interface.
333
    It returns a hierarchical structure of routes organized by parent-child relationships.
334
    """
335
336
    def __init__(self):
337
        pass
338
339
    @staticmethod
340
    def on_options(req, resp):
341
        """
342
        Handle OPTIONS request for CORS preflight
343
344
        Args:
345
            req: Falcon request object
346
            resp: Falcon response object
347
        """
348
        _ = req
349
        resp.status = falcon.HTTP_200
350
351
    @staticmethod
352
    def on_get(req, resp):
353
        """
354
        Handle GET requests to retrieve web menu structure
355
356
        Returns a hierarchical menu structure formatted for web interface:
357
        - First level routes (parent menus)
358
        - Child routes organized under their parents
359
        - Only non-hidden menus are included
360
361
        Args:
362
            req: Falcon request object
363
            resp: Falcon response object
364
        """
365
        # Check authentication method (API key or session)
366
        if 'API-KEY' not in req.headers or \
367
                not isinstance(req.headers['API-KEY'], str) or \
368
                len(str.strip(req.headers['API-KEY'])) == 0:
369
            access_control(req)
370
        else:
371
            api_key_control(req)
372
373
        cnx = mysql.connector.connect(**config.myems_system_db)
374
        cursor = cnx.cursor()
375
376
        # Optimized: Single query to retrieve all non-hidden menus
377
        # This reduces database round trips from 2 to 1
378
        # MySQL compatible: parent_menu_id IS NULL comes first in ORDER BY
379
        query = (" SELECT id, route, parent_menu_id "
380
                 " FROM tbl_menus "
381
                 " WHERE is_hidden = 0 "
382
                 " ORDER BY (parent_menu_id IS NULL) DESC, id ")
383
        cursor.execute(query)
384
        rows_menus = cursor.fetchall()
385
386
        # Build first level routes dictionary and result structure in one pass
387
        first_level_routes = {}
388
        result = {}
389
390
        if rows_menus:
391
            for row in rows_menus:
392
                menu_id, route, parent_menu_id = row
393
394
                if parent_menu_id is None:
395
                    # First level menu (parent)
396
                    first_level_routes[menu_id] = {
397
                        'route': route,
398
                        'children': []
399
                    }
400
                    result[route] = []
401
                else:
402
                    # Child menu - optimized: direct dictionary lookup instead of .keys()
403
                    if parent_menu_id in first_level_routes:
404
                        first_level_routes[parent_menu_id]['children'].append(route)
405
                        # Update result directly
406
                        parent_route = first_level_routes[parent_menu_id]['route']
407
                        result[parent_route].append(route)
408
409
        cursor.close()
410
        cnx.close()
411
        resp.text = json.dumps(result)
412