Ver código fonte

add read/unread endpoints

Guénaël Muller 6 anos atrás
pai
commit
c3652f57c8

+ 4 - 1
tracim/lib/core/content.py Ver arquivo

1149
             .filter(ContentRevisionRO.content_id==content.content_id).all()
1149
             .filter(ContentRevisionRO.content_id==content.content_id).all()
1150
 
1150
 
1151
         for revision in revisions:
1151
         for revision in revisions:
1152
-            del revision.read_by[self._user]
1152
+            try:
1153
+                del revision.read_by[self._user]
1154
+            except KeyError:
1155
+                pass
1153
 
1156
 
1154
         for child in content.get_valid_children():
1157
         for child in content.get_valid_children():
1155
             self.mark_unread(child, do_flush=False)
1158
             self.mark_unread(child, do_flush=False)

+ 20 - 1
tracim/models/context_models.py Ver arquivo

43
         self.workspace_id = workspace_id
43
         self.workspace_id = workspace_id
44
 
44
 
45
 
45
 
46
+class UserWorkspacePath(object):
47
+    """
48
+    Paths params with user_id and workspace id model
49
+    """
50
+    def __init__(self, user_id: int, workspace_id: int) -> None:
51
+        self.workspace_id = workspace_id
52
+        self.user_id = workspace_id
53
+
54
+
55
+class UserWorkspaceAndContentPath(object):
56
+    """
57
+    Paths params with user_id, workspace id and content_id model
58
+    """
59
+    def __init__(self, user_id: int, workspace_id: int, content_id: int) -> None:  # nopep8
60
+        self.content_id = content_id
61
+        self.workspace_id = workspace_id
62
+        self.user_id = user_id
63
+
64
+
46
 class CommentPath(object):
65
 class CommentPath(object):
47
     """
66
     """
48
     Paths params with workspace id and content_id and comment_id model
67
     Paths params with workspace id and content_id and comment_id model
435
     @property
454
     @property
436
     def read_by_user(self):
455
     def read_by_user(self):
437
         assert self._user
456
         assert self._user
438
-        return self.content.has_new_information_for(self._user)
457
+        return not self.content.has_new_information_for(self._user)
439
 
458
 
440
 
459
 
441
 class RevisionInContext(object):
460
 class RevisionInContext(object):

+ 21 - 0
tracim/views/core_api/schemas.py Ver arquivo

11
 from tracim.models.contents import ContentTypeLegacy as ContentType
11
 from tracim.models.contents import ContentTypeLegacy as ContentType
12
 from tracim.models.contents import ContentStatusLegacy as ContentStatus
12
 from tracim.models.contents import ContentStatusLegacy as ContentStatus
13
 from tracim.models.context_models import ContentCreation
13
 from tracim.models.context_models import ContentCreation
14
+from tracim.models.context_models import UserWorkspacePath
15
+from tracim.models.context_models import UserWorkspaceAndContentPath
14
 from tracim.models.context_models import CommentCreation
16
 from tracim.models.context_models import CommentCreation
15
 from tracim.models.context_models import TextBasedContentUpdate
17
 from tracim.models.context_models import TextBasedContentUpdate
16
 from tracim.models.context_models import SetContentStatus
18
 from tracim.models.context_models import SetContentStatus
115
         return WorkspaceAndContentPath(**data)
117
         return WorkspaceAndContentPath(**data)
116
 
118
 
117
 
119
 
120
+class UserWorkspaceAndContentIdPathSchema(
121
+    UserIdPathSchema,
122
+    WorkspaceIdPathSchema,
123
+    ContentIdPathSchema,
124
+):
125
+    @post_load
126
+    def make_path_object(self, data):
127
+        return UserWorkspaceAndContentPath(**data)
128
+
129
+
130
+class UserWorkspaceIdPathSchema(
131
+    UserIdPathSchema,
132
+    WorkspaceIdPathSchema,
133
+):
134
+    @post_load
135
+    def make_path_object(self, data):
136
+        return UserWorkspacePath(**data)
137
+
138
+
118
 class CommentsPathSchema(WorkspaceAndContentIdPathSchema):
139
 class CommentsPathSchema(WorkspaceAndContentIdPathSchema):
119
     comment_id = marshmallow.fields.Int(
140
     comment_id = marshmallow.fields.Int(
120
         example=6,
141
         example=6,

+ 67 - 4
tracim/views/core_api/user_controller.py Ver arquivo

3
 from tracim.lib.core.content import ContentApi
3
 from tracim.lib.core.content import ContentApi
4
 from tracim.lib.utils.authorization import require_same_user_or_profile
4
 from tracim.lib.utils.authorization import require_same_user_or_profile
5
 from tracim.models import Group
5
 from tracim.models import Group
6
-from tracim.models.context_models import WorkspaceInContext
7
 
6
 
8
 try:  # Python 3.5+
7
 try:  # Python 3.5+
9
     from http import HTTPStatus
8
     from http import HTTPStatus
15
 from tracim.lib.core.workspace import WorkspaceApi
14
 from tracim.lib.core.workspace import WorkspaceApi
16
 from tracim.views.controllers import Controller
15
 from tracim.views.controllers import Controller
17
 from tracim.views.core_api.schemas import UserIdPathSchema
16
 from tracim.views.core_api.schemas import UserIdPathSchema
17
+from tracim.views.core_api.schemas import NoContentSchema
18
+from tracim.views.core_api.schemas import UserWorkspaceIdPathSchema
19
+from tracim.views.core_api.schemas import UserWorkspaceAndContentIdPathSchema
18
 from tracim.views.core_api.schemas import UserContentDigestSchema
20
 from tracim.views.core_api.schemas import UserContentDigestSchema
19
 from tracim.views.core_api.schemas import ExtendedFilterQuerySchema
21
 from tracim.views.core_api.schemas import ExtendedFilterQuerySchema
20
 from tracim.views.core_api.schemas import WorkspaceDigestSchema
22
 from tracim.views.core_api.schemas import WorkspaceDigestSchema
35
         """
37
         """
36
         app_config = request.registry.settings['CFG']
38
         app_config = request.registry.settings['CFG']
37
         wapi = WorkspaceApi(
39
         wapi = WorkspaceApi(
38
-            current_user=request.current_user,  # User
40
+            current_user=request.candidate_user,  # User
39
             session=request.dbsession,
41
             session=request.dbsession,
40
             config=app_config,
42
             config=app_config,
41
         )
43
         )
58
         app_config = request.registry.settings['CFG']
60
         app_config = request.registry.settings['CFG']
59
         content_filter = hapic_data.query
61
         content_filter = hapic_data.query
60
         api = ContentApi(
62
         api = ContentApi(
61
-            current_user=request.current_user,  # User
63
+            current_user=request.candidate_user,  # User
62
             session=request.dbsession,
64
             session=request.dbsession,
63
             config=app_config,
65
             config=app_config,
64
             show_archived=content_filter.show_archived,
66
             show_archived=content_filter.show_archived,
66
             show_active=content_filter.show_active,
68
             show_active=content_filter.show_active,
67
         )
69
         )
68
         wapi = WorkspaceApi(
70
         wapi = WorkspaceApi(
69
-            current_user=request.current_user,  # User
71
+            current_user=request.candidate_user,  # User
70
             session=request.dbsession,
72
             session=request.dbsession,
71
             config=app_config,
73
             config=app_config,
72
         )
74
         )
85
             for content in last_actives
87
             for content in last_actives
86
         ]
88
         ]
87
 
89
 
90
+    @hapic.with_api_doc(tags=[USER_ENDPOINTS_TAG])
91
+    @require_same_user_or_profile(Group.TIM_ADMIN)
92
+    @hapic.input_path(UserWorkspaceAndContentIdPathSchema())
93
+    @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT)  # nopep8
94
+    def set_content_as_read(self, context, request: TracimRequest, hapic_data=None):  # nopep8
95
+        """
96
+        set user_read status of content to read
97
+        """
98
+        app_config = request.registry.settings['CFG']
99
+        api = ContentApi(
100
+            current_user=request.candidate_user,
101
+            session=request.dbsession,
102
+            config=app_config,
103
+        )
104
+        api.mark_read(request.current_content, do_flush=True)
105
+        return
106
+
107
+    @hapic.with_api_doc(tags=[USER_ENDPOINTS_TAG])
108
+    @require_same_user_or_profile(Group.TIM_ADMIN)
109
+    @hapic.input_path(UserWorkspaceAndContentIdPathSchema())
110
+    @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT)  # nopep8
111
+    def set_content_as_unread(self, context, request: TracimRequest, hapic_data=None):  # nopep8
112
+        """
113
+        set user_read status of content to unread
114
+        """
115
+        app_config = request.registry.settings['CFG']
116
+        api = ContentApi(
117
+            current_user=request.candidate_user,
118
+            session=request.dbsession,
119
+            config=app_config,
120
+        )
121
+        api.mark_unread(request.current_content, do_flush=True)
122
+        return
123
+
124
+    @hapic.with_api_doc(tags=[USER_ENDPOINTS_TAG])
125
+    @require_same_user_or_profile(Group.TIM_ADMIN)
126
+    @hapic.input_path(UserWorkspaceIdPathSchema())
127
+    @hapic.output_body(NoContentSchema(), default_http_code=HTTPStatus.NO_CONTENT)  # nopep8
128
+    def set_workspace_as_read(self, context, request: TracimRequest, hapic_data=None):  # nopep8
129
+        """
130
+        set user_read status of all content of workspace to read
131
+        """
132
+        app_config = request.registry.settings['CFG']
133
+        api = ContentApi(
134
+            current_user=request.candidate_user,
135
+            session=request.dbsession,
136
+            config=app_config,
137
+        )
138
+        api.mark_read__workspace(request.current_workspace)
139
+        return
140
+
88
     def bind(self, configurator: Configurator) -> None:
141
     def bind(self, configurator: Configurator) -> None:
89
         """
142
         """
90
         Create all routes and views using pyramid configurator
143
         Create all routes and views using pyramid configurator
98
         # last active content for user
151
         # last active content for user
99
         configurator.add_route('last_active_content', '/users/{user_id}/contents/actives', request_method='GET')  # nopep8
152
         configurator.add_route('last_active_content', '/users/{user_id}/contents/actives', request_method='GET')  # nopep8
100
         configurator.add_view(self.last_active_content, route_name='last_active_content')  # nopep8
153
         configurator.add_view(self.last_active_content, route_name='last_active_content')  # nopep8
154
+
155
+        # set content as read/unread
156
+        configurator.add_route('read_content', '/users/{user_id}/workspaces/{workspace_id}/contents/{content_id}/read', request_method='PUT')  # nopep8
157
+        configurator.add_view(self.set_content_as_read, route_name='read_content')  # nopep8
158
+        configurator.add_route('unread_content', '/users/{user_id}/workspaces/{workspace_id}/contents/{content_id}/unread', request_method='PUT')  # nopep8
159
+        configurator.add_view(self.set_content_as_unread, route_name='unread_content')  # nopep8
160
+
161
+        # set workspace as read
162
+        configurator.add_route('read_workspace', '/users/{user_id}/workspaces/{workspace_id}/read', request_method='PUT')  # nopep8
163
+        configurator.add_view(self.set_workspace_as_read, route_name='read_workspace')  # nopep8