Browse Source

merge with master

Guénaël Muller 7 years ago
parent
commit
acd77f215e
5 changed files with 102 additions and 46 deletions
  1. 1 1
      .gitignore
  2. 7 0
      README.txt
  3. 2 0
      setup.py
  4. 81 42
      tracim/lib/core/content.py
  5. 11 3
      tracim/models/data.py

+ 1 - 1
.gitignore View File

63
 
63
 
64
 # binary translation files
64
 # binary translation files
65
 *.mo
65
 *.mo
66
-
66
+.mypy_cache/

+ 7 - 0
README.txt View File

36
 
36
 
37
     env/bin/pserve development.ini
37
     env/bin/pserve development.ini
38
 
38
 
39
+- Run mypy checks
40
+
41
+    mypy --ignore-missing-imports --disallow-untyped-defs tracim
42
+
43
+- Run pep8 checks
44
+
45
+    pep8 tracim
39
 
46
 
40
 CI
47
 CI
41
 ---
48
 ---

+ 2 - 0
setup.py View File

30
     'pytest',
30
     'pytest',
31
     'pytest-cov',
31
     'pytest-cov',
32
     'nose',
32
     'nose',
33
+    'pep8',
34
+    'mypy',
33
 ]
35
 ]
34
 
36
 
35
 # Python version adaptations
37
 # Python version adaptations

+ 81 - 42
tracim/lib/core/content.py View File

49
 def compare_content_for_sorting_by_type_and_name(
49
 def compare_content_for_sorting_by_type_and_name(
50
         content1: Content,
50
         content1: Content,
51
         content2: Content
51
         content2: Content
52
-):
52
+) -> int:
53
     """
53
     """
54
     :param content1:
54
     :param content1:
55
     :param content2:
55
     :param content2:
58
                 0 if content1 = content2
58
                 0 if content1 = content2
59
     """
59
     """
60
 
60
 
61
-    if content1.type==content2.type:
61
+    if content1.type == content2.type:
62
         if content1.get_label().lower()>content2.get_label().lower():
62
         if content1.get_label().lower()>content2.get_label().lower():
63
             return 1
63
             return 1
64
         elif content1.get_label().lower()<content2.get_label().lower():
64
         elif content1.get_label().lower()<content2.get_label().lower():
66
         return 0
66
         return 0
67
     else:
67
     else:
68
         # TODO - D.A. - 2014-12-02 - Manage Content Types Dynamically
68
         # TODO - D.A. - 2014-12-02 - Manage Content Types Dynamically
69
-        content_type_order = [ContentType.Folder, ContentType.Page, ContentType.Thread, ContentType.File]
70
-        result = content_type_order.index(content1.type)-content_type_order.index(content2.type)
71
-        if result<0:
69
+        content_type_order = [
70
+            ContentType.Folder,
71
+            ContentType.Page,
72
+            ContentType.Thread,
73
+            ContentType.File,
74
+        ]
75
+
76
+        content_1_type_index = content_type_order.index(content1.type)
77
+        content_2_type_index = content_type_order.index(content2.type)
78
+        result = content_1_type_index - content_2_type_index
79
+
80
+        if result < 0:
72
             return -1
81
             return -1
73
-        elif result>0:
82
+        elif result > 0:
74
             return 1
83
             return 1
75
         else:
84
         else:
76
             return 0
85
             return 0
79
 def compare_tree_items_for_sorting_by_type_and_name(
88
 def compare_tree_items_for_sorting_by_type_and_name(
80
         item1: NodeTreeItem,
89
         item1: NodeTreeItem,
81
         item2: NodeTreeItem
90
         item2: NodeTreeItem
82
-):
91
+) -> int:
83
     return compare_content_for_sorting_by_type_and_name(item1.node, item2.node)
92
     return compare_content_for_sorting_by_type_and_name(item1.node, item2.node)
84
 
93
 
85
 
94
 
101
             session: Session,
110
             session: Session,
102
             current_user: typing.Optional[User],
111
             current_user: typing.Optional[User],
103
             config,
112
             config,
104
-            show_archived=False,
105
-            show_deleted=False,
106
-            show_temporary=False,
107
-            all_content_in_treeview=True,
108
-            force_show_all_types=False,
109
-            disable_user_workspaces_filter=False,
110
-    ):
113
+            show_archived: bool = False,
114
+            show_deleted: bool = False,
115
+            show_temporary: bool = False,
116
+            all_content_in_treeview: bool = True,
117
+            force_show_all_types: bool = False,
118
+            disable_user_workspaces_filter: bool = False,
119
+    ) -> None:
111
         self._session = session
120
         self._session = session
112
         self._user = current_user
121
         self._user = current_user
113
         self._config = config
122
         self._config = config
125
             show_archived: bool=False,
134
             show_archived: bool=False,
126
             show_deleted: bool=False,
135
             show_deleted: bool=False,
127
             show_temporary: bool=False,
136
             show_temporary: bool=False,
128
-    ):
137
+    ) -> typing.Generator['ContentApi', None, None]:
129
         """
138
         """
130
         Use this method as context manager to update show_archived,
139
         Use this method as context manager to update show_archived,
131
         show_deleted and show_temporary properties during context.
140
         show_deleted and show_temporary properties during context.
147
             self._show_deleted = previous_show_deleted
156
             self._show_deleted = previous_show_deleted
148
             self._show_temporary = previous_show_temporary
157
             self._show_temporary = previous_show_temporary
149
 
158
 
150
-    def get_revision_join(self):
159
+    def get_revision_join(self) -> sqlalchemy.sql.elements.BooleanClauseList:
151
         """
160
         """
152
         Return the Content/ContentRevision query join condition
161
         Return the Content/ContentRevision query join condition
153
         :return: Content/ContentRevision query join condition
162
         :return: Content/ContentRevision query join condition
154
-        :rtype sqlalchemy.sql.elements.BooleanClauseList
155
         """
163
         """
156
         return and_(Content.id == ContentRevisionRO.content_id,
164
         return and_(Content.id == ContentRevisionRO.content_id,
157
                     ContentRevisionRO.revision_id == self._session.query(
165
                     ContentRevisionRO.revision_id == self._session.query(
161
                     .limit(1)
169
                     .limit(1)
162
                     .correlate(Content))
170
                     .correlate(Content))
163
 
171
 
164
-
165
-    def get_canonical_query(self):
172
+    def get_canonical_query(self) -> Query:
166
         """
173
         """
167
         Return the Content/ContentRevision base query who join these table on the last revision.
174
         Return the Content/ContentRevision base query who join these table on the last revision.
168
         :return: Content/ContentRevision Query
175
         :return: Content/ContentRevision Query
169
-        :rtype sqlalchemy.orm.query.Query
170
         """
176
         """
171
-        return self._session.query(Content).join(ContentRevisionRO, self.get_revision_join())
177
+        return self._session.query(Content)\
178
+            .join(ContentRevisionRO, self.get_revision_join())
172
 
179
 
173
     @classmethod
180
     @classmethod
174
-    def sort_tree_items(cls, content_list: [NodeTreeItem])-> [Content]:
181
+    def sort_tree_items(
182
+        cls,
183
+        content_list: typing.List[NodeTreeItem],
184
+    )-> typing.List[NodeTreeItem]:
175
         news = []
185
         news = []
176
         for item in content_list:
186
         for item in content_list:
177
             news.append(item)
187
             news.append(item)
178
 
188
 
179
-        content_list.sort(key=cmp_to_key(compare_tree_items_for_sorting_by_type_and_name))
189
+        content_list.sort(key=cmp_to_key(
190
+            compare_tree_items_for_sorting_by_type_and_name,
191
+        ))
180
 
192
 
181
         return content_list
193
         return content_list
182
 
194
 
183
-
184
     @classmethod
195
     @classmethod
185
-    def sort_content(cls, content_list: [Content])-> [Content]:
196
+    def sort_content(
197
+        cls,
198
+        content_list: typing.List[Content],
199
+    ) -> typing.List[Content]:
186
         content_list.sort(key=cmp_to_key(compare_content_for_sorting_by_type_and_name))
200
         content_list.sort(key=cmp_to_key(compare_content_for_sorting_by_type_and_name))
187
         return content_list
201
         return content_list
188
 
202
 
189
-    def __real_base_query(self, workspace: Workspace=None):
203
+    def __real_base_query(
204
+        self,
205
+        workspace: Workspace = None,
206
+    ) -> Query:
190
         result = self.get_canonical_query()
207
         result = self.get_canonical_query()
191
 
208
 
192
         # Exclude non displayable types
209
         # Exclude non displayable types
214
 
231
 
215
         return result
232
         return result
216
 
233
 
217
-    def _base_query(self, workspace: Workspace=None):
234
+    def _base_query(self, workspace: Workspace=None) -> Query:
218
         result = self.__real_base_query(workspace)
235
         result = self.__real_base_query(workspace)
219
 
236
 
220
         if not self._show_deleted:
237
         if not self._show_deleted:
228
 
245
 
229
         return result
246
         return result
230
 
247
 
231
-    def __revisions_real_base_query(self, workspace: Workspace=None):
248
+    def __revisions_real_base_query(
249
+        self,
250
+        workspace: Workspace=None,
251
+    ) -> Query:
232
         result = self._session.query(ContentRevisionRO)
252
         result = self._session.query(ContentRevisionRO)
233
 
253
 
234
         # Exclude non displayable types
254
         # Exclude non displayable types
247
 
267
 
248
         return result
268
         return result
249
 
269
 
250
-    def _revisions_base_query(self, workspace: Workspace=None):
270
+    def _revisions_base_query(
271
+        self,
272
+        workspace: Workspace=None,
273
+    ) -> Query:
251
         result = self.__revisions_real_base_query(workspace)
274
         result = self.__revisions_real_base_query(workspace)
252
 
275
 
253
         if not self._show_deleted:
276
         if not self._show_deleted:
261
 
284
 
262
         return result
285
         return result
263
 
286
 
264
-    def _hard_filtered_base_query(self, workspace: Workspace=None):
287
+    def _hard_filtered_base_query(
288
+        self,
289
+        workspace: Workspace=None,
290
+    ) -> Query:
265
         """
291
         """
266
         If set to True, then filterign on is_deleted and is_archived will also
292
         If set to True, then filterign on is_deleted and is_archived will also
267
         filter parent properties. This is required for search() function which
293
         filter parent properties. This is required for search() function which
293
 
319
 
294
         return result
320
         return result
295
 
321
 
296
-    def get_base_query(self, workspace: Workspace) -> Query:
322
+    def get_base_query(
323
+        self,
324
+        workspace: Workspace,
325
+    ) -> Query:
297
         return self._base_query(workspace)
326
         return self._base_query(workspace)
298
 
327
 
299
-    def get_child_folders(self, parent: Content=None, workspace: Workspace=None, filter_by_allowed_content_types: list=[], removed_item_ids: list=[], allowed_node_types=None) -> [Content]:
328
+    def get_child_folders(self, parent: Content=None, workspace: Workspace=None, filter_by_allowed_content_types: list=[], removed_item_ids: list=[], allowed_node_types=None) -> typing.List[Content]:
300
         """
329
         """
301
         This method returns child items (folders or items) for left bar treeview.
330
         This method returns child items (folders or items) for left bar treeview.
302
 
331
 
308
                For example, if you want to move a Page from a folder to another, you should show only folders that accept pages
337
                For example, if you want to move a Page from a folder to another, you should show only folders that accept pages
309
         :return:
338
         :return:
310
         """
339
         """
340
+        filter_by_allowed_content_types = filter_by_allowed_content_types or []  # FDV
341
+        removed_item_ids = removed_item_ids or []  # FDV
342
+
311
         if not allowed_node_types:
343
         if not allowed_node_types:
312
             allowed_node_types = [ContentType.Folder]
344
             allowed_node_types = [ContentType.Folder]
313
         elif allowed_node_types==ContentType.Any:
345
         elif allowed_node_types==ContentType.Any:
334
         result = []
366
         result = []
335
         for folder in folders:
367
         for folder in folders:
336
             for allowed_content_type in filter_by_allowed_content_types:
368
             for allowed_content_type in filter_by_allowed_content_types:
337
-                if folder.type==ContentType.Folder and folder.properties['allowed_content'][allowed_content_type]==True:
369
+
370
+                is_folder = folder.type == ContentType.Folder
371
+                content_type__allowed = folder.properties['allowed_content'][allowed_content_type] == True
372
+
373
+                if is_folder and content_type__allowed:
338
                     result.append(folder)
374
                     result.append(folder)
339
                     break
375
                     break
340
 
376
 
638
             ),
674
             ),
639
         ))
675
         ))
640
 
676
 
641
-    def get_all(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> [Content]:
677
+    def get_all(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> typing.List[Content]:
642
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
678
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
643
         assert content_type is not None# DYN_REMOVE
679
         assert content_type is not None# DYN_REMOVE
644
         assert isinstance(content_type, str) # DYN_REMOVE
680
         assert isinstance(content_type, str) # DYN_REMOVE
655
 
691
 
656
         return resultset.all()
692
         return resultset.all()
657
 
693
 
658
-    def get_children(self, parent_id: int, content_types: list, workspace: Workspace=None) -> [Content]:
694
+    def get_children(self, parent_id: int, content_types: list, workspace: Workspace=None) -> typing.List[Content]:
659
         """
695
         """
660
         Return parent_id childs of given content_types
696
         Return parent_id childs of given content_types
661
         :param parent_id: parent id
697
         :param parent_id: parent id
674
         return resultset.all()
710
         return resultset.all()
675
 
711
 
676
     # TODO find an other name to filter on is_deleted / is_archived
712
     # TODO find an other name to filter on is_deleted / is_archived
677
-    def get_all_with_filter(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> [Content]:
713
+    def get_all_with_filter(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> typing.List[Content]:
678
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
714
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
679
         assert content_type is not None# DYN_REMOVE
715
         assert content_type is not None# DYN_REMOVE
680
         assert isinstance(content_type, str) # DYN_REMOVE
716
         assert isinstance(content_type, str) # DYN_REMOVE
692
 
728
 
693
         return resultset.all()
729
         return resultset.all()
694
 
730
 
695
-    def get_all_without_exception(self, content_type: str, workspace: Workspace=None) -> [Content]:
731
+    def get_all_without_exception(self, content_type: str, workspace: Workspace=None) -> typing.List[Content]:
696
         assert content_type is not None# DYN_REMOVE
732
         assert content_type is not None# DYN_REMOVE
697
 
733
 
698
         resultset = self._base_query(workspace)
734
         resultset = self._base_query(workspace)
702
 
738
 
703
         return resultset.all()
739
         return resultset.all()
704
 
740
 
705
-    def get_last_active(self, parent_id: int, content_type: str, workspace: Workspace=None, limit=10) -> [Content]:
741
+    def get_last_active(self, parent_id: int, content_type: str, workspace: Workspace=None, limit=10) -> typing.List[Content]:
706
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
742
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
707
         assert content_type is not None# DYN_REMOVE
743
         assert content_type is not None# DYN_REMOVE
708
         assert isinstance(content_type, str) # DYN_REMOVE
744
         assert isinstance(content_type, str) # DYN_REMOVE
738
         return result
774
         return result
739
 
775
 
740
     def get_last_unread(self, parent_id: int, content_type: str,
776
     def get_last_unread(self, parent_id: int, content_type: str,
741
-                        workspace: Workspace=None, limit=10) -> [Content]:
777
+                        workspace: Workspace=None, limit=10) -> typing.List[Content]:
742
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
778
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
743
         assert content_type is not None# DYN_REMOVE
779
         assert content_type is not None# DYN_REMOVE
744
         assert isinstance(content_type, str) # DYN_REMOVE
780
         assert isinstance(content_type, str) # DYN_REMOVE
1109
 
1145
 
1110
         return keywords
1146
         return keywords
1111
 
1147
 
1112
-    def search(self, keywords: [str]) -> sqlalchemy.orm.query.Query:
1148
+    def search(self, keywords: [str]) -> Query:
1113
         """
1149
         """
1114
         :return: a sorted list of Content items
1150
         :return: a sorted list of Content items
1115
         """
1151
         """
1126
 
1162
 
1127
         return title_keyworded_items
1163
         return title_keyworded_items
1128
 
1164
 
1129
-    def get_all_types(self) -> [ContentType]:
1165
+    def get_all_types(self) -> typing.List[ContentType]:
1130
         labels = ContentType.all()
1166
         labels = ContentType.all()
1131
         content_types = []
1167
         content_types = []
1132
         for label in labels:
1168
         for label in labels:
1134
 
1170
 
1135
         return ContentType.sorted(content_types)
1171
         return ContentType.sorted(content_types)
1136
 
1172
 
1137
-    def exclude_unavailable(self, contents: [Content]) -> [Content]:
1173
+    def exclude_unavailable(
1174
+        self,
1175
+        contents: typing.List[Content],
1176
+    ) -> typing.List[Content]:
1138
         """
1177
         """
1139
         Update and return list with content under archived/deleted removed.
1178
         Update and return list with content under archived/deleted removed.
1140
         :param contents: List of contents to parse
1179
         :param contents: List of contents to parse

+ 11 - 3
tracim/models/data.py View File

1
 # -*- coding: utf-8 -*-
1
 # -*- coding: utf-8 -*-
2
+import typing
2
 import datetime as datetime_root
3
 import datetime as datetime_root
3
 import json
4
 import json
4
 import os
5
 import os
1327
 
1328
 
1328
 class NodeTreeItem(object):
1329
 class NodeTreeItem(object):
1329
     """
1330
     """
1330
-        This class implements a model that allow to simply represents the left-panel menu items
1331
-         This model is used by dbapi but is not directly related to sqlalchemy and database
1331
+        This class implements a model that allow to simply represents
1332
+        the left-panel menu items. This model is used by dbapi but
1333
+        is not directly related to sqlalchemy and database
1332
     """
1334
     """
1333
-    def __init__(self, node: Content, children: list('NodeTreeItem'), is_selected = False):
1335
+    def __init__(
1336
+        self,
1337
+        node: Content,
1338
+        children: typing.List['NodeTreeItem'],
1339
+        is_selected: bool = False,
1340
+    ):
1334
         self.node = node
1341
         self.node = node
1335
         self.children = children
1342
         self.children = children
1336
         self.is_selected = is_selected
1343
         self.is_selected = is_selected
1337
 
1344
 
1345
+
1338
 class VirtualEvent(object):
1346
 class VirtualEvent(object):
1339
     @classmethod
1347
     @classmethod
1340
     def create_from(cls, object):
1348
     def create_from(cls, object):