Browse Source

Refactor preview: move from endpoint to contentapi

Guénaël Muller 6 years ago
parent
commit
1e07daa6b6

+ 26 - 0
tracim/config.py View File

411
         #     self.RADICALE_CLIENT_BASE_URL_HOST,
411
         #     self.RADICALE_CLIENT_BASE_URL_HOST,
412
         #     self.RADICALE_CLIENT_BASE_URL_PREFIX,
412
         #     self.RADICALE_CLIENT_BASE_URL_PREFIX,
413
         # )
413
         # )
414
+        preview_jpg_allowed_sizes = settings.get('preview.jpg.allowed_sizes', '')  # nopep8
415
+        self.PREVIEW_JPG_ALLOWED_SIZES = []
416
+        if preview_jpg_allowed_sizes:
417
+            for size in preview_jpg_allowed_sizes.split(','):
418
+                parts = preview_jpg_allowed_sizes.split('x')
419
+                assert len(parts) == 2
420
+                width, height = parts
421
+                assert width.is_decimal()
422
+                assert height.is_decimal()
423
+                size = PreviewSize(int(width), int(height))
424
+                self.PREVIEW_JPG_ALLOWED_SIZES.append(size)
425
+
426
+            self.PREVIEW_JPG_RESTRICTED_SIZES = asbool(settings.get(
427
+                'preview.jpg.restricted_sizes', False
428
+            ))
429
+        if not self.PREVIEW_JPG_ALLOWED_SIZES:
430
+            size = PreviewSize(256, 256)
431
+            self.PREVIEW_JPG_ALLOWED_SIZES.append(size)
432
+
414
 
433
 
415
     def configure_filedepot(self):
434
     def configure_filedepot(self):
416
         depot_storage_name = self.DEPOT_STORAGE_NAME
435
         depot_storage_name = self.DEPOT_STORAGE_NAME
427
 
446
 
428
         TREEVIEW_FOLDERS = 'folders'
447
         TREEVIEW_FOLDERS = 'folders'
429
         TREEVIEW_ALL = 'all'
448
         TREEVIEW_ALL = 'all'
449
+
450
+
451
+class PreviewSize(object):
452
+
453
+    def __init__(self, width: int, height: int) -> None:
454
+        self.width = width
455
+        self.height = height

+ 7 - 0
tracim/exceptions.py View File

147
 
147
 
148
 class RevisionDoesNotMatchThisContent(TracimException):
148
 class RevisionDoesNotMatchThisContent(TracimException):
149
     pass
149
     pass
150
+
151
+
152
+class PageOfPreviewNotFound(NotFound):
153
+    pass
154
+
155
+class PreviewSizeNotAllowed(TracimException):
156
+    pass

+ 92 - 0
tracim/lib/core/content.py View File

7
 from operator import itemgetter
7
 from operator import itemgetter
8
 
8
 
9
 import transaction
9
 import transaction
10
+from preview_generator.manager import PreviewManager
10
 from sqlalchemy import func
11
 from sqlalchemy import func
11
 from sqlalchemy.orm import Query
12
 from sqlalchemy.orm import Query
12
 from depot.manager import DepotManager
13
 from depot.manager import DepotManager
25
 from tracim.lib.utils.utils import cmp_to_key
26
 from tracim.lib.utils.utils import cmp_to_key
26
 from tracim.lib.core.notifications import NotifierFactory
27
 from tracim.lib.core.notifications import NotifierFactory
27
 from tracim.exceptions import SameValueError
28
 from tracim.exceptions import SameValueError
29
+from tracim.exceptions import PageOfPreviewNotFound
30
+from tracim.exceptions import PreviewSizeNotAllowed
28
 from tracim.exceptions import EmptyRawContentNotAllowed
31
 from tracim.exceptions import EmptyRawContentNotAllowed
29
 from tracim.exceptions import RevisionDoesNotMatchThisContent
32
 from tracim.exceptions import RevisionDoesNotMatchThisContent
30
 from tracim.exceptions import EmptyLabelNotAllowed
33
 from tracim.exceptions import EmptyLabelNotAllowed
134
         self._show_all_type_of_contents_in_treeview = all_content_in_treeview
137
         self._show_all_type_of_contents_in_treeview = all_content_in_treeview
135
         self._force_show_all_types = force_show_all_types
138
         self._force_show_all_types = force_show_all_types
136
         self._disable_user_workspaces_filter = disable_user_workspaces_filter
139
         self._disable_user_workspaces_filter = disable_user_workspaces_filter
140
+        self.preview_manager = PreviewManager(self._config.PREVIEW_CACHE_DIR, create_folder=True)  # nopep8
137
 
141
 
138
     @contextmanager
142
     @contextmanager
139
     def show(
143
     def show(
721
             ),
725
             ),
722
         ))
726
         ))
723
 
727
 
728
+    def get_pdf_preview_path(
729
+            self,
730
+            content_id: int,
731
+            revision_id: int,
732
+            page: int
733
+    ) -> str:
734
+        """
735
+        Get pdf preview of revision of content
736
+        :param content_id: id of content
737
+        :param revision_id: id of content revision
738
+        :param page: page number of the preview, useful for multipage content
739
+        :return: preview_path as string
740
+        """
741
+        file_path = self.get_one_revision_filepath(revision_id)
742
+        if page >= self.preview_manager.get_page_nb(file_path):
743
+            raise PageOfPreviewNotFound(
744
+                'page {page} of content {content_id} does not exist'.format(
745
+                    page=page,
746
+                    content_id=content_id
747
+                ),
748
+            )
749
+        jpg_preview_path = self.preview_manager.get_jpeg_preview(
750
+            file_path,
751
+            page=page
752
+        )
753
+        return jpg_preview_path
754
+
755
+    def get_full_pdf_preview_path(self, revision_id: int) -> str:
756
+        """
757
+        Get full(multiple page) pdf preview of revision of content
758
+        :param revision_id: id of revision
759
+        :return: path of the full pdf preview of this revision
760
+        """
761
+        file_path = self.get_one_revision_filepath(revision_id)
762
+        pdf_preview_path = self.preview_manager.get_pdf_preview(file_path)
763
+        return pdf_preview_path
764
+
765
+    def get_jpg_preview_path(
766
+        self,
767
+        content_id: int,
768
+        revision_id: int,
769
+        page: int,
770
+        width: int = None,
771
+        height: int = None,
772
+    ) -> str:
773
+        """
774
+        Get jpg preview of revision of content
775
+        :param content_id: id of content
776
+        :param revision_id: id of content revision
777
+        :param page: page number of the preview, useful for multipage content
778
+        :param width: width in pixel
779
+        :param height: height in pixel
780
+        :return: preview_path as string
781
+        """
782
+        file_path = self.get_one_revision_filepath(revision_id)
783
+        if page >= self.preview_manager.get_page_nb(file_path):
784
+            raise Exception(
785
+                'page {page} of revision {revision_id} of content {content_id} does not exist'.format(  # nopep8
786
+                    page=page,
787
+                    revision_id=revision_id,
788
+                    content_id=content_id,
789
+                ),
790
+            )
791
+        if not width and not height:
792
+            width = self._config.PREVIEW_JPG_ALLOWED_SIZES[0].width
793
+            height = self._config.PREVIEW_JPG_ALLOWED_SIZES[0].height
794
+
795
+        allowed_size = False
796
+        for preview_size in self._config.PREVIEW_JPG_ALLOWED_SIZES:
797
+            if width == preview_size.width and height == preview_size.height:
798
+                allowed_size = True
799
+                break
800
+
801
+        if not allowed_size and self._config.PREVIEW_JPG_RESTRICTED_SIZES:
802
+            raise PreviewSizeNotAllowed(
803
+                'Size {width}x{height} is not allowed for jpeg preview'.format(
804
+                    width=width,
805
+                    height=height,
806
+                )
807
+            )
808
+        jpg_preview_path = self.preview_manager.get_jpeg_preview(
809
+            file_path,
810
+            page=page,
811
+            width=width,
812
+            height=height,
813
+        )
814
+        return jpg_preview_path
815
+
724
     def get_all(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> typing.List[Content]:
816
     def get_all(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> typing.List[Content]:
725
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
817
         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
726
         assert content_type is not None# DYN_REMOVE
818
         assert content_type is not None# DYN_REMOVE

+ 1 - 0
tracim/tests/__init__.py View File

78
             'depot_storage_dir': '/tmp/test/depot',
78
             'depot_storage_dir': '/tmp/test/depot',
79
             'depot_storage_name': 'test',
79
             'depot_storage_name': 'test',
80
             'preview_cache_dir': '/tmp/test/preview_cache',
80
             'preview_cache_dir': '/tmp/test/preview_cache',
81
+            'preview.jpg.restricted_sizes': True,
81
 
82
 
82
         }
83
         }
83
         hapic.reset_context()
84
         hapic.reset_context()

+ 3 - 3
tracim/tests/functional/test_contents.py View File

1110
 
1110
 
1111
     def test_api__get_sized_jpeg_preview__ok__200__nominal_case(self) -> None:
1111
     def test_api__get_sized_jpeg_preview__ok__200__nominal_case(self) -> None:
1112
         """
1112
         """
1113
-        Set one file of a content
1113
+        get 256x256 preview of a txt file
1114
         """
1114
         """
1115
         dbsession = get_tm_session(self.session_factory, transaction.manager)
1115
         dbsession = get_tm_session(self.session_factory, transaction.manager)
1116
         admin = dbsession.query(models.User) \
1116
         admin = dbsession.query(models.User) \
1165
 
1165
 
1166
     def test_api__get_full_pdf_preview__ok__200__nominal_case(self) -> None:
1166
     def test_api__get_full_pdf_preview__ok__200__nominal_case(self) -> None:
1167
         """
1167
         """
1168
-        Set one file of a content
1168
+        get full pdf preview of a txt file
1169
         """
1169
         """
1170
         dbsession = get_tm_session(self.session_factory, transaction.manager)
1170
         dbsession = get_tm_session(self.session_factory, transaction.manager)
1171
         admin = dbsession.query(models.User) \
1171
         admin = dbsession.query(models.User) \
1228
 
1228
 
1229
     def test_api__get_full_pdf_preview__err__400__png_UnavailablePreviewType(self) -> None:
1229
     def test_api__get_full_pdf_preview__err__400__png_UnavailablePreviewType(self) -> None:
1230
         """
1230
         """
1231
-        Set one file of a content
1231
+       get full pdf preview of a png image -> error UnavailablePreviewType
1232
         """
1232
         """
1233
         dbsession = get_tm_session(self.session_factory, transaction.manager)
1233
         dbsession = get_tm_session(self.session_factory, transaction.manager)
1234
         admin = dbsession.query(models.User) \
1234
         admin = dbsession.query(models.User) \

+ 24 - 49
tracim/views/contents_api/file_controller.py View File

2
 import typing
2
 import typing
3
 
3
 
4
 import transaction
4
 import transaction
5
-from depot.io.local import LocalStoredFile
6
 from depot.manager import DepotManager
5
 from depot.manager import DepotManager
7
 from preview_generator.exception import UnavailablePreviewType
6
 from preview_generator.exception import UnavailablePreviewType
8
 from pyramid.config import Configurator
7
 from pyramid.config import Configurator
37
 from tracim.models.contents import ContentTypeLegacy as ContentType
36
 from tracim.models.contents import ContentTypeLegacy as ContentType
38
 from tracim.models.contents import file_type
37
 from tracim.models.contents import file_type
39
 from tracim.models.revision_protection import new_revision
38
 from tracim.models.revision_protection import new_revision
40
-from preview_generator.manager import PreviewManager
41
 
39
 
42
 FILE_ENDPOINTS_TAG = 'Files'
40
 FILE_ENDPOINTS_TAG = 'Files'
43
 
41
 
136
     @hapic.output_file([])
134
     @hapic.output_file([])
137
     def preview_pdf(self, context, request: TracimRequest, hapic_data=None):
135
     def preview_pdf(self, context, request: TracimRequest, hapic_data=None):
138
         app_config = request.registry.settings['CFG']
136
         app_config = request.registry.settings['CFG']
139
-        preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True)  # nopep8
140
         api = ContentApi(
137
         api = ContentApi(
141
             current_user=request.current_user,
138
             current_user=request.current_user,
142
             session=request.dbsession,
139
             session=request.dbsession,
146
             hapic_data.path.content_id,
143
             hapic_data.path.content_id,
147
             content_type=ContentType.Any
144
             content_type=ContentType.Any
148
         )
145
         )
149
-        file_path = api.get_one_revision_filepath(content.revision_id)
150
-        if hapic_data.query.page >= preview_manager.get_page_nb(file_path):
151
-            raise Exception('page {page} of content {content_id} does not exist'.format(
152
-                page=hapic_data.query.page,
153
-                content_id=content.content_id),
154
-            )
155
-        pdf_preview_path = preview_manager.get_pdf_preview(file_path, page=hapic_data.query.page)  # nopep8
146
+        pdf_preview_path = api.get_pdf_preview_path(
147
+            content.content_id,
148
+            content.revision_id,
149
+            page=hapic_data.query.page
150
+        )
156
         return FileResponse(pdf_preview_path)
151
         return FileResponse(pdf_preview_path)
157
 
152
 
158
     @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
153
     @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
163
     @hapic.output_file([])
158
     @hapic.output_file([])
164
     def preview_pdf_full(self, context, request: TracimRequest, hapic_data=None):
159
     def preview_pdf_full(self, context, request: TracimRequest, hapic_data=None):
165
         app_config = request.registry.settings['CFG']
160
         app_config = request.registry.settings['CFG']
166
-        preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True)  # nopep8
167
         api = ContentApi(
161
         api = ContentApi(
168
             current_user=request.current_user,
162
             current_user=request.current_user,
169
             session=request.dbsession,
163
             session=request.dbsession,
173
             hapic_data.path.content_id,
167
             hapic_data.path.content_id,
174
             content_type=ContentType.Any
168
             content_type=ContentType.Any
175
         )
169
         )
176
-        file_path = api.get_one_revision_filepath(content.revision_id)
177
-        pdf_preview_path = preview_manager.get_pdf_preview(file_path)
170
+        pdf_preview_path = api.get_full_pdf_preview_path(content.revision_id)
178
         return FileResponse(pdf_preview_path)
171
         return FileResponse(pdf_preview_path)
179
 
172
 
180
     @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
173
     @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
186
     @hapic.output_file([])
179
     @hapic.output_file([])
187
     def preview_pdf_revision(self, context, request: TracimRequest, hapic_data=None):
180
     def preview_pdf_revision(self, context, request: TracimRequest, hapic_data=None):
188
         app_config = request.registry.settings['CFG']
181
         app_config = request.registry.settings['CFG']
189
-        preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True)  # nopep8
190
         api = ContentApi(
182
         api = ContentApi(
191
             current_user=request.current_user,
183
             current_user=request.current_user,
192
             session=request.dbsession,
184
             session=request.dbsession,
200
             revision_id=hapic_data.path.revision_id,
192
             revision_id=hapic_data.path.revision_id,
201
             content=content
193
             content=content
202
         )
194
         )
203
-        file_path = api.get_one_revision_filepath(revision.revision_id)
204
-        if hapic_data.query.page >= preview_manager.get_page_nb(file_path):
205
-            raise Exception('page {page} of content {content_id} does not exist'.format(
206
-                page=hapic_data.query.page,
207
-                content_id=content.content_id),
208
-            )
209
-        pdf_preview_path = preview_manager.get_pdf_preview(file_path, page=hapic_data.query.page)  # nopep8
195
+        pdf_preview_path = api.get_pdf_preview_path(
196
+            revision.content_id,
197
+            revision.revision_id,
198
+            page=hapic_data.query.page
199
+        )
210
         return FileResponse(pdf_preview_path)
200
         return FileResponse(pdf_preview_path)
211
 
201
 
212
     # jpg
202
     # jpg
218
     @hapic.output_file([])
208
     @hapic.output_file([])
219
     def preview_jpg(self, context, request: TracimRequest, hapic_data=None):
209
     def preview_jpg(self, context, request: TracimRequest, hapic_data=None):
220
         app_config = request.registry.settings['CFG']
210
         app_config = request.registry.settings['CFG']
221
-        preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True)  # nopep8
222
         api = ContentApi(
211
         api = ContentApi(
223
             current_user=request.current_user,
212
             current_user=request.current_user,
224
             session=request.dbsession,
213
             session=request.dbsession,
228
             hapic_data.path.content_id,
217
             hapic_data.path.content_id,
229
             content_type=ContentType.Any
218
             content_type=ContentType.Any
230
         )
219
         )
231
-        file_path = api.get_one_revision_filepath(content.revision_id)
232
-        if hapic_data.query.page >= preview_manager.get_page_nb(file_path):
233
-            raise Exception('page {page} of content {content_id} does not exist'.format(
234
-                page=hapic_data.query.page,
235
-                content_id=content.content_id),
236
-            )
237
-        jpg_preview_path = preview_manager.get_jpeg_preview(file_path, page=hapic_data.query.page)  # nopep8
220
+        jpg_preview_path = api.get_jpg_preview_path(
221
+            content_id=content.content_id,
222
+            revision_id=content.revision_id,
223
+            page=hapic_data.query.page
224
+        )
238
         return FileResponse(jpg_preview_path)
225
         return FileResponse(jpg_preview_path)
239
 
226
 
240
     @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
227
     @hapic.with_api_doc(tags=[FILE_ENDPOINTS_TAG])
245
     @hapic.output_file([])
232
     @hapic.output_file([])
246
     def sized_preview_jpg(self, context, request: TracimRequest, hapic_data=None):
233
     def sized_preview_jpg(self, context, request: TracimRequest, hapic_data=None):
247
         app_config = request.registry.settings['CFG']
234
         app_config = request.registry.settings['CFG']
248
-        preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True)  # nopep8
249
         api = ContentApi(
235
         api = ContentApi(
250
             current_user=request.current_user,
236
             current_user=request.current_user,
251
             session=request.dbsession,
237
             session=request.dbsession,
255
             hapic_data.path.content_id,
241
             hapic_data.path.content_id,
256
             content_type=ContentType.Any
242
             content_type=ContentType.Any
257
         )
243
         )
258
-        file_path = api.get_one_revision_filepath(content.revision_id)
259
-        if hapic_data.query.page >= preview_manager.get_page_nb(file_path):
260
-            raise Exception('page {page} of content {content_id} does not exist'.format(
261
-                page=hapic_data.query.page,
262
-                content_id=content.content_id),
263
-            )
264
-        jpg_preview_path = preview_manager.get_jpeg_preview(
265
-            file_path,
244
+        jpg_preview_path = api.get_jpg_preview_path(
245
+            content_id=content.content_id,
246
+            revision_id=content.revision_id,
266
             page=hapic_data.query.page,
247
             page=hapic_data.query.page,
267
-            width=hapic_data.path.width,
268
             height=hapic_data.path.height,
248
             height=hapic_data.path.height,
249
+            width=hapic_data.path.width,
269
         )
250
         )
270
         return FileResponse(jpg_preview_path)
251
         return FileResponse(jpg_preview_path)
271
 
252
 
277
     @hapic.output_file([])
258
     @hapic.output_file([])
278
     def sized_preview_jpg_revision(self, context, request: TracimRequest, hapic_data=None):
259
     def sized_preview_jpg_revision(self, context, request: TracimRequest, hapic_data=None):
279
         app_config = request.registry.settings['CFG']
260
         app_config = request.registry.settings['CFG']
280
-        preview_manager = PreviewManager(app_config.PREVIEW_CACHE_DIR, create_folder=True)  # nopep8
281
         api = ContentApi(
261
         api = ContentApi(
282
             current_user=request.current_user,
262
             current_user=request.current_user,
283
             session=request.dbsession,
263
             session=request.dbsession,
291
             revision_id=hapic_data.path.revision_id,
271
             revision_id=hapic_data.path.revision_id,
292
             content=content
272
             content=content
293
         )
273
         )
294
-        file_path = api.get_one_revision_filepath(revision.revision_id)
295
-        if hapic_data.query.page >= preview_manager.get_page_nb(file_path):
296
-            raise Exception('page {page} of content {content_id} does not exist'.format(
297
-                page=hapic_data.query.page,
298
-                content_id=content.content_id),
299
-            )
300
-        jpg_preview_path = preview_manager.get_jpeg_preview(
301
-            file_path,
274
+        jpg_preview_path = api.get_jpg_preview_path(
275
+            content_id=content.content_id,
276
+            revision_id=revision.revision_id,
302
             page=hapic_data.query.page,
277
             page=hapic_data.query.page,
303
-            width=hapic_data.path.width,
304
             height=hapic_data.path.height,
278
             height=hapic_data.path.height,
279
+            width=hapic_data.path.width,
305
         )
280
         )
306
         return FileResponse(jpg_preview_path)
281
         return FileResponse(jpg_preview_path)
307
 
282