Skip to content

Commit 73babe6

Browse files
committed
OP#2859 : add API calendar display
1 parent 84eb6cc commit 73babe6

6 files changed

Lines changed: 97 additions & 3 deletions

File tree

app/api/v1/user/ApiUserPreferences.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ def get(self) -> ResponseReturnValue:
7171
interface_api: InterfaceUserPreferences = g.inter
7272
return interface_api.get_user_folders()
7373

74+
@blp.arguments(sch.UserPreferencesFoldersPatch, example=sch.UserPreferencesFoldersPatch.example(), error_status_code=400)
75+
@blp.response(200)
76+
def patch(self, new_data: dict) -> ResponseReturnValue:
77+
"""
78+
Update a single folder key's value in the user's folders structure
79+
"""
80+
interface_api: InterfaceUserPreferences = g.inter
81+
return interface_api.update_folder_value(new_data["resource"], new_data["id"], new_data["value"])
82+
7483

7584
# @blp.route("/<string:pref_type>")
7685
# class ApiUserPreferencesPart(MethodView):

app/api/v1/user/schema/userPreferences.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from marshmallow import Schema, fields
1+
from marshmallow import Schema, fields, validate
22

33
from app.utils.api.ApiBaseResponse import ApiBaseResponse
44

@@ -86,4 +86,28 @@ def example(cls) -> dict:
8686
"SOGO_U_LANGUAGE": "French",
8787
}
8888
}
89+
}
90+
91+
class UserPreferencesFoldersPatch(Schema):
92+
"""
93+
Schema of the body expected for PATCH /preferences/folders
94+
95+
Updates a single folder key's boolean value within the folders column (CALENDAR or ADDRESSBOOKS)
96+
"""
97+
resource = fields.String(required=True, validate=validate.OneOf(["CALENDAR", "ADDRESSBOOKS"]))
98+
id = fields.String(required=True)
99+
value = fields.Boolean(required=True)
100+
101+
@classmethod
102+
def example(cls) -> dict:
103+
"""
104+
Example of data for the patch request
105+
106+
:return: Example data
107+
:rtype: dict
108+
"""
109+
return {
110+
"resource": "CALENDAR",
111+
"id": "3c4b0bc9-3aab-4243-abb2-75a5edc8c239",
112+
"value": True
89113
}

app/interface/user/InterfaceUserPreferences.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,26 @@ def get_user_folders(self) -> tuple[dict, int]:
5555

5656
return create_api_base_response(folders)
5757

58+
def update_folder_value(self, resource: str, folder_id: str, value: bool) -> tuple[dict, int]:
59+
"""
60+
Update a single folder key's boolean value in the user's folders structure (CALENDAR or ADDRESSBOOKS)
61+
62+
:param resource: Resource type - "CALENDAR" or "ADDRESSBOOKS"
63+
:type resource: str
64+
:param folder_id: Unique id of the folder to update
65+
:type folder_id: str
66+
:param value: New boolean value to set
67+
:type value: bool
68+
:return: Tuple containing response dict and HTTP status code
69+
:rtype: tuple[dict, int]
70+
"""
71+
try:
72+
folders = self.module_user_profile.update_folder_value(self.user.uid, resource, folder_id, value)
73+
except RequestException as ex:
74+
return create_api_base_response(None, ex.error)
75+
76+
return create_api_base_response(folders)
77+
5878
def get_partial_preferences(self, subparent:str) -> tuple[dict, int]:
5979
"""Get partial user preferences for a specific subparent
6080

app/module/user/ModuleUserProfile.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,40 @@ def remove_folder_key(self, uid: str, folder_type: str, key: str, owner_key: str
283283

284284
self._update_user_column(uid, tbl.COL_USER_FOLDERS.name, current_folders)
285285

286+
def update_folder_value(self, uid: str, resource: str, folder_id: str, value: bool) -> dict:
287+
"""
288+
Update the boolean value of a single folder key within the folders column (CALENDAR or ADDRESSBOOKS).
289+
290+
Searches every owner-type sub-section (OWNER, SUBS, EXT, ...) of the given resource for folder_id
291+
and updates the first match found; folder_id is expected to be unique across the whole structure.
292+
293+
:param uid: User unique identifier
294+
:type uid: str
295+
:param resource: Resource type - "CALENDAR" or "ADDRESSBOOKS"
296+
:type resource: str
297+
:param folder_id: Unique id of the folder to update
298+
:type folder_id: str
299+
:param value: New boolean value to set
300+
:type value: bool
301+
:return: The updated folders dictionary
302+
:rtype: dict
303+
:raises RequestException: If user profile not found or folder_id not found for this resource
304+
:raises AggravatedException: If multiple user profiles found
305+
"""
306+
logger_user_profile.debug("Updating folder value for uid: %s, resource: %s, id: %s, value: %s",
307+
uid, resource, folder_id, value)
308+
309+
current_folders = self._get_user_column(uid, tbl.COL_USER_FOLDERS.name)
310+
311+
for section in current_folders.get(resource, {}).values():
312+
if folder_id in section:
313+
section[folder_id] = value
314+
self._update_user_column(uid, tbl.COL_USER_FOLDERS.name, current_folders)
315+
return current_folders
316+
317+
logger_user_profile.error("Folder key not found for uid: %s, resource: %s, id: %s", uid, resource, folder_id)
318+
raise RequestException(err.ERROR_FOLDER_KEY_NOT_FOUND.m, err.ERROR_FOLDER_KEY_NOT_FOUND)
319+
286320
def _get_user_column(self, uid: str, field_name: str) -> Any:
287321
"""
288322
Generic method to get a specific field from user profile

app/utils/errors.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ def __init__(self, c:str, m:str, h:int = HTTPStatus.INTERNAL_SERVER_ERROR):
181181

182182
#Preferences
183183
ERROR_PREF_UNKNOWN_SUB = E("S000340", "Subparent of User Settings does not exist", HTTPStatus.BAD_REQUEST)
184+
ERROR_FOLDER_KEY_NOT_FOUND = E("S000341", "Folder Key Not Found In Folders Preferences", HTTPStatus.NOT_FOUND)
184185

185186
#Delegations
186187
ERROR_DELEGATION_NOT_FOUND = E("S000350", "Delegation Not Found", HTTPStatus.NOT_FOUND)

tests/test_calendar/test_ModuleCalendarEvent.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,19 @@ def _require_event(uid, event_key):
119119

120120
sources_mock.require_event.side_effect = _require_event
121121

122-
def _get_events(uid, start, end, search, calendar_key=None):
122+
def _get_events(uid, start, end, search, calendar_key=None, subscribed_keys=None):
123123
if calendar_key is not None:
124124
source = sources.get(calendar_key)
125125
if source is None:
126126
raise RequestException(error=err.ERROR_CALENDAR_NOT_FOUND)
127+
if subscribed_keys is not None and calendar_key not in subscribed_keys:
128+
return []
127129
return source.get_all_events(start, end, search)
128-
return [e for s in sources.values() for e in s.get_all_events(start, end, search)]
130+
return [
131+
e for key, s in sources.items()
132+
if subscribed_keys is None or key in subscribed_keys
133+
for e in s.get_all_events(start, end, search)
134+
]
129135

130136
sources_mock.get_all_events.side_effect = _get_events
131137
module._sources = sources_mock

0 commit comments

Comments
 (0)