Passed
Pull Request — develop (#36)
by inkhey
03:02
created

UserCreation.__init__()   A

Complexity

Conditions 1

Size

Total Lines 19
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 19
rs 9.6
c 0
b 0
f 0
cc 1
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
# coding=utf-8
2
import typing
3
from datetime import datetime
4
from enum import Enum
5
6
from slugify import slugify
7
from sqlalchemy.orm import Session
8
from tracim_backend.config import CFG
9
from tracim_backend.config import PreviewDim
10
from tracim_backend.extensions import app_list
11
from tracim_backend.lib.core.application import ApplicationApi
12
from tracim_backend.lib.utils.utils import get_root_frontend_url
13
from tracim_backend.lib.utils.utils import password_generator
14
from tracim_backend.lib.utils.utils import CONTENT_FRONTEND_URL_SCHEMA
15
from tracim_backend.lib.utils.utils import WORKSPACE_FRONTEND_URL_SCHEMA
16
from tracim_backend.models import User
17
from tracim_backend.models.auth import Profile
18
from tracim_backend.models.auth import Group
19
from tracim_backend.models.data import Content
20
from tracim_backend.models.data import ContentRevisionRO
21
from tracim_backend.models.data import Workspace
22
from tracim_backend.models.data import UserRoleInWorkspace
23
from tracim_backend.models.roles import WorkspaceRoles
24
from tracim_backend.app_models.workspace_menu_entries import WorkspaceMenuEntry
25
from tracim_backend.app_models.contents import CONTENT_TYPES
26
27
28
class PreviewAllowedDim(object):
29
30
    def __init__(
31
            self,
32
            restricted:bool,
33
            dimensions: typing.List[PreviewDim]
34
    ) -> None:
35
        self.restricted = restricted
36
        self.dimensions = dimensions
37
38
39
class MoveParams(object):
40
    """
41
    Json body params for move action model
42
    """
43
    def __init__(self, new_parent_id: str, new_workspace_id: str = None) -> None:  # nopep8
44
        self.new_parent_id = new_parent_id
45
        self.new_workspace_id = new_workspace_id
46
47
48
class LoginCredentials(object):
49
    """
50
    Login credentials model for login model
51
    """
52
53
    def __init__(self, email: str, password: str) -> None:
54
        self.email = email
55
        self.password = password
56
57
58
class SetEmail(object):
59
    """
60
    Just an email
61
    """
62
    def __init__(self, loggedin_user_password: str, email: str) -> None:
63
        self.loggedin_user_password = loggedin_user_password
64
        self.email = email
65
66
67
class SetPassword(object):
68
    """
69
    Just an password
70
    """
71
    def __init__(self,
72
        loggedin_user_password: str,
73
        new_password: str,
74
        new_password2: str
75
    ) -> None:
76
        self.loggedin_user_password = loggedin_user_password
77
        self.new_password = new_password
78
        self.new_password2 = new_password2
79
80
81
class UserInfos(object):
82
    """
83
    Just some user infos
84
    """
85
    def __init__(self, timezone: str, public_name: str, lang: str) -> None:
86
        self.timezone = timezone
87
        self.public_name = public_name
88
        self.lang = lang
89
90
91
class UserProfile(object):
92
    """
93
    Just some user infos
94
    """
95
    def __init__(self, profile: str) -> None:
96
        self.profile = profile
97
98
99
class UserCreation(object):
100
    """
101
    Just some user infos
102
    """
103
    def __init__(
104
            self,
105
            email: str,
106
            password: str = None,
107
            public_name: str = None,
108
            timezone: str = None,
109
            profile: str = None,
110
            lang: str = None,
111
            email_notification: bool = True,
112
    ) -> None:
113
        self.email = email
114
        # INFO - G.M - 2018-08-16 - cleartext password, default value
115
        # is auto-generated.
116
        self.password = password or password_generator()
117
        self.public_name = public_name or None
118
        self.timezone = timezone or ''
119
        self.lang = lang or None
120
        self.profile = profile or Group.TIM_USER_GROUPNAME
121
        self.email_notification = email_notification
122
123
124
class WorkspaceAndContentPath(object):
125
    """
126
    Paths params with workspace id and content_id model
127
    """
128
    def __init__(self, workspace_id: int, content_id: int) -> None:
129
        self.content_id = content_id
130
        self.workspace_id = workspace_id
131
132
133
class WorkspaceAndContentRevisionPath(object):
134
    """
135
    Paths params with workspace id and content_id model
136
    """
137
    def __init__(self, workspace_id: int, content_id: int, revision_id) -> None:
138
        self.content_id = content_id
139
        self.revision_id = revision_id
140
        self.workspace_id = workspace_id
141
142
143
class ContentPreviewSizedPath(object):
144
    """
145
    Paths params with workspace id and content_id, width, heigth
146
    """
147
    def __init__(self, workspace_id: int, content_id: int, width: int, height: int) -> None:  # nopep8
148
        self.content_id = content_id
149
        self.workspace_id = workspace_id
150
        self.width = width
151
        self.height = height
152
153
154
class RevisionPreviewSizedPath(object):
155
    """
156
    Paths params with workspace id and content_id, revision_id width, heigth
157
    """
158
    def __init__(self, workspace_id: int, content_id: int, revision_id: int, width: int, height: int) -> None:  # nopep8
159
        self.content_id = content_id
160
        self.revision_id = revision_id
161
        self.workspace_id = workspace_id
162
        self.width = width
163
        self.height = height
164
165
166
class WorkspaceAndUserPath(object):
167
    """
168
    Paths params with workspace id and user_id
169
    """
170
    def __init__(self, workspace_id: int, user_id: int):
171
        self.workspace_id = workspace_id
172
        self.user_id = user_id
173
174
175
class UserWorkspaceAndContentPath(object):
176
    """
177
    Paths params with user_id, workspace id and content_id model
178
    """
179
    def __init__(self, user_id: int, workspace_id: int, content_id: int) -> None:  # nopep8
180
        self.content_id = content_id
181
        self.workspace_id = workspace_id
182
        self.user_id = user_id
183
184
185
class CommentPath(object):
186
    """
187
    Paths params with workspace id and content_id and comment_id model
188
    """
189
    def __init__(
190
        self,
191
        workspace_id: int,
192
        content_id: int,
193
        comment_id: int
194
    ) -> None:
195
        self.content_id = content_id
196
        self.workspace_id = workspace_id
197
        self.comment_id = comment_id
198
199
200
class AutocompleteQuery(object):
201
    """
202
    Autocomplete query model
203
    """
204
    def __init__(self, acp: str):
205
        self.acp = acp
206
207
208
class PageQuery(object):
209
    """
210
    Page query model
211
    """
212
    def __init__(
213
            self,
214
            page: int = 0
215
    ):
216
        self.page = page
217
218
219
class ContentFilter(object):
220
    """
221
    Content filter model
222
    """
223
    def __init__(
224
            self,
225
            workspace_id: int = None,
226
            parent_id: int = None,
227
            show_archived: int = 0,
228
            show_deleted: int = 0,
229
            show_active: int = 1,
230
            content_type: str = None,
231
            offset: int = None,
232
            limit: int = None,
233
    ) -> None:
234
        self.parent_id = parent_id
235
        self.workspace_id = workspace_id
236
        self.show_archived = bool(show_archived)
237
        self.show_deleted = bool(show_deleted)
238
        self.show_active = bool(show_active)
239
        self.limit = limit
240
        self.offset = offset
241
        self.content_type = content_type
242
243
244
class ActiveContentFilter(object):
245
    def __init__(
246
            self,
247
            limit: int = None,
248
            before_content_id: datetime = None,
249
    ):
250
        self.limit = limit
251
        self.before_content_id = before_content_id
252
253
254
class ContentIdsQuery(object):
255
    def __init__(
256
            self,
257
            contents_ids: typing.List[int] = None,
258
    ):
259
        self.contents_ids = contents_ids
260
261
262
class RoleUpdate(object):
263
    """
264
    Update role
265
    """
266
    def __init__(
267
        self,
268
        role: str,
269
    ):
270
        self.role = role
271
272
273
class WorkspaceMemberInvitation(object):
274
    """
275
    Workspace Member Invitation
276
    """
277
    def __init__(
278
        self,
279
        user_id: int,
280
        user_email_or_public_name: str,
281
        role: str,
282
    ):
283
        self.role = role
284
        self.user_email_or_public_name = user_email_or_public_name
285
        self.user_id = user_id
286
287
288
class WorkspaceUpdate(object):
289
    """
290
    Update workspace
291
    """
292
    def __init__(
293
        self,
294
        label: str,
295
        description: str,
296
    ):
297
        self.label = label
298
        self.description = description
299
300
301
class ContentCreation(object):
302
    """
303
    Content creation model
304
    """
305
    def __init__(
306
        self,
307
        label: str,
308
        content_type: str,
309
        parent_id: typing.Optional[int] = None,
310
    ) -> None:
311
        self.label = label
312
        self.content_type = content_type
313
        self.parent_id = parent_id or None
314
315
316
class CommentCreation(object):
317
    """
318
    Comment creation model
319
    """
320
    def __init__(
321
        self,
322
        raw_content: str,
323
    ) -> None:
324
        self.raw_content = raw_content
325
326
327
class SetContentStatus(object):
328
    """
329
    Set content status
330
    """
331
    def __init__(
332
        self,
333
        status: str,
334
    ) -> None:
335
        self.status = status
336
337
338
class TextBasedContentUpdate(object):
339
    """
340
    TextBasedContent update model
341
    """
342
    def __init__(
343
        self,
344
        label: str,
345
        raw_content: str,
346
    ) -> None:
347
        self.label = label
348
        self.raw_content = raw_content
349
350
351
class FolderContentUpdate(object):
352
    """
353
    Folder Content update model
354
    """
355
    def __init__(
356
        self,
357
        label: str,
358
        raw_content: str,
359
        sub_content_types: typing.List[str],
360
    ) -> None:
361
        self.label = label
362
        self.raw_content = raw_content
363
        self.sub_content_types = sub_content_types
364
365
366
class TypeUser(Enum):
367
    """Params used to find user"""
368
    USER_ID = 'found_id'
369
    EMAIL = 'found_email'
370
    PUBLIC_NAME = 'found_public_name'
371
372
373
class UserInContext(object):
374
    """
375
    Interface to get User data and User data related to context.
376
    """
377
378
    def __init__(self, user: User, dbsession: Session, config: CFG):
379
        self.user = user
380
        self.dbsession = dbsession
381
        self.config = config
382
383
    # Default
384
385
    @property
386
    def email(self) -> str:
387
        return self.user.email
388
389
    @property
390
    def user_id(self) -> int:
391
        return self.user.user_id
392
393
    @property
394
    def public_name(self) -> str:
395
        return self.display_name
396
397
    @property
398
    def display_name(self) -> str:
399
        return self.user.display_name
400
401
    @property
402
    def created(self) -> datetime:
403
        return self.user.created
404
405
    @property
406
    def is_active(self) -> bool:
407
        return self.user.is_active
408
409
    @property
410
    def timezone(self) -> str:
411
        return self.user.timezone
412
413
    @property
414
    def lang(self) -> str:
415
        return self.user.lang
416
417
    @property
418
    def profile(self) -> Profile:
419
        return self.user.profile.name
420
421
    @property
422
    def is_deleted(self) -> bool:
423
        return self.user.is_deleted
424
425
    # Context related
426
427
    @property
428
    def calendar_url(self) -> typing.Optional[str]:
429
        # TODO - G-M - 20-04-2018 - [Calendar] Replace calendar code to get
430
        # url calendar url.
431
        #
432
        # from tracim.lib.calendar import CalendarManager
433
        # calendar_manager = CalendarManager(None)
434
        # return calendar_manager.get_workspace_calendar_url(self.workspace_id)
435
        return None
436
437
    @property
438
    def avatar_url(self) -> typing.Optional[str]:
439
        # TODO - G-M - 20-04-2018 - [Avatar] Add user avatar feature
440
        return None
441
442
443
class WorkspaceInContext(object):
444
    """
445
    Interface to get Workspace data and Workspace data related to context.
446
    """
447
448
    def __init__(self, workspace: Workspace, dbsession: Session, config: CFG):
449
        self.workspace = workspace
450
        self.dbsession = dbsession
451
        self.config = config
452
453
    @property
454
    def workspace_id(self) -> int:
455
        """
456
        numeric id of the workspace.
457
        """
458
        return self.workspace.workspace_id
459
460
    @property
461
    def id(self) -> int:
462
        """
463
        alias of workspace_id
464
        """
465
        return self.workspace_id
466
467
    @property
468
    def label(self) -> str:
469
        """
470
        get workspace label
471
        """
472
        return self.workspace.label
473
474
    @property
475
    def description(self) -> str:
476
        """
477
        get workspace description
478
        """
479
        return self.workspace.description
480
481
    @property
482
    def slug(self) -> str:
483
        """
484
        get workspace slug
485
        """
486
        return slugify(self.workspace.label)
487
488
    @property
489
    def is_deleted(self) -> bool:
490
        """
491
        Is the workspace deleted ?
492
        """
493
        return self.workspace.is_deleted
494
495
    @property
496
    def sidebar_entries(self) -> typing.List[WorkspaceMenuEntry]:
497
        """
498
        get sidebar entries, those depends on activated apps.
499
        """
500
        # TODO - G.M - 22-05-2018 - Rework on this in
501
        # order to not use hardcoded list
502
        # list should be able to change (depending on activated/disabled
503
        # apps)
504
        app_api = ApplicationApi(
505
            app_list
506
        )
507
        return app_api.get_default_workspace_menu_entry(self.workspace)
508
509
    @property
510
    def frontend_url(self):
511
        root_frontend_url = get_root_frontend_url(self.config)
512
        workspace_frontend_url = WORKSPACE_FRONTEND_URL_SCHEMA.format(
513
            workspace_id=self.workspace_id,
514
        )
515
        return root_frontend_url + workspace_frontend_url
516
517
518
class UserRoleWorkspaceInContext(object):
519
    """
520
    Interface to get UserRoleInWorkspace data and related content
521
522
    """
523
    def __init__(
524
            self,
525
            user_role: UserRoleInWorkspace,
526
            dbsession: Session,
527
            config: CFG,
528
            # Extended params
529
            newly_created: bool = None,
530
            email_sent: bool = None
531
    )-> None:
532
        self.user_role = user_role
533
        self.dbsession = dbsession
534
        self.config = config
535
        # Extended params
536
        self.newly_created = newly_created
537
        self.email_sent = email_sent
538
539
    @property
540
    def user_id(self) -> int:
541
        """
542
        User who has the role has this id
543
        :return: user id as integer
544
        """
545
        return self.user_role.user_id
546
547
    @property
548
    def workspace_id(self) -> int:
549
        """
550
        This role apply only on the workspace with this workspace_id
551
        :return: workspace id as integer
552
        """
553
        return self.user_role.workspace_id
554
555
    # TODO - G.M - 23-05-2018 - Check the API spec for this this !
556
557
    @property
558
    def role_id(self) -> int:
559
        """
560
        role as int id, each value refer to a different role.
561
        """
562
        return self.user_role.role
563
564
    @property
565
    def role(self) -> str:
566
        return self.role_slug
567
568
    @property
569
    def role_slug(self) -> str:
570
        """
571
        simple name of the role of the user.
572
        can be anything from UserRoleInWorkspace SLUG, like
573
        'not_applicable', 'reader',
574
        'contributor', 'content-manager', 'workspace-manager'
575
        :return: user workspace role as slug.
576
        """
577
        return WorkspaceRoles.get_role_from_level(self.user_role.role).slug
578
579
    @property
580
    def is_active(self) -> bool:
581
        return self.user.is_active
582
583
    @property
584
    def do_notify(self) -> bool:
585
        return self.user_role.do_notify
586
587
    @property
588
    def user(self) -> UserInContext:
589
        """
590
        User who has this role, with context data
591
        :return: UserInContext object
592
        """
593
        return UserInContext(
594
            self.user_role.user,
595
            self.dbsession,
596
            self.config
597
        )
598
599
    @property
600
    def workspace(self) -> WorkspaceInContext:
601
        """
602
        Workspace related to this role, with his context data
603
        :return: WorkspaceInContext object
604
        """
605
        return WorkspaceInContext(
606
            self.user_role.workspace,
607
            self.dbsession,
608
            self.config
609
        )
610
611
612
class ContentInContext(object):
613
    """
614
    Interface to get Content data and Content data related to context.
615
    """
616
617
    def __init__(self, content: Content, dbsession: Session, config: CFG, user: User=None):  # nopep8
618
        self.content = content
619
        self.dbsession = dbsession
620
        self.config = config
621
        self._user = user
622
623
    # Default
624
    @property
625
    def content_id(self) -> int:
626
        return self.content.content_id
627
628
    @property
629
    def parent_id(self) -> int:
630
        """
631
        Return parent_id of the content
632
        """
633
        return self.content.parent_id
634
635
    @property
636
    def workspace_id(self) -> int:
637
        return self.content.workspace_id
638
639
    @property
640
    def label(self) -> str:
641
        return self.content.label
642
643
    @property
644
    def content_type(self) -> str:
645
        content_type = CONTENT_TYPES.get_one_by_slug(self.content.type)
646
        return content_type.slug
647
648
    @property
649
    def sub_content_types(self) -> typing.List[str]:
650
        return [_type.slug for _type in self.content.get_allowed_content_types()]  # nopep8
651
652
    @property
653
    def status(self) -> str:
654
        return self.content.status
655
656
    @property
657
    def is_archived(self) -> bool:
658
        return self.content.is_archived
659
660
    @property
661
    def is_deleted(self) -> bool:
662
        return self.content.is_deleted
663
664
    @property
665
    def raw_content(self) -> str:
666
        return self.content.description
667
668
    @property
669
    def author(self) -> UserInContext:
670
        return UserInContext(
671
            dbsession=self.dbsession,
672
            config=self.config,
673
            user=self.content.first_revision.owner
674
        )
675
676
    @property
677
    def current_revision_id(self) -> int:
678
        return self.content.revision_id
679
680
    @property
681
    def created(self) -> datetime:
682
        return self.content.created
683
684
    @property
685
    def modified(self) -> datetime:
686
        return self.updated
687
688
    @property
689
    def updated(self) -> datetime:
690
        return self.content.updated
691
692
    @property
693
    def last_modifier(self) -> UserInContext:
694
        return UserInContext(
695
            dbsession=self.dbsession,
696
            config=self.config,
697
            user=self.content.last_revision.owner
698
        )
699
700
    # Context-related
701
    @property
702
    def show_in_ui(self) -> bool:
703
        # TODO - G.M - 31-05-2018 - Enable Show_in_ui params
704
        # if false, then do not show content in the treeview.
705
        # This may his maybe used for specific contents or for sub-contents.
706
        # Default is True.
707
        # In first version of the API, this field is always True
708
        return True
709
710
    @property
711
    def slug(self) -> str:
712
        return slugify(self.content.label)
713
714
    @property
715
    def read_by_user(self) -> bool:
716
        assert self._user
717
        return not self.content.has_new_information_for(self._user)
718
719
    @property
720
    def frontend_url(self) -> str:
721
        root_frontend_url = get_root_frontend_url(self.config)
722
        content_frontend_url = CONTENT_FRONTEND_URL_SCHEMA.format(
723
            workspace_id=self.workspace_id,
724
            content_type=self.content_type,
725
            content_id=self.content_id,
726
        )
727
        return root_frontend_url + content_frontend_url
728
729
    # file specific
730
    @property
731
    def page_nb(self) -> typing.Optional[int]:
732
        """
733
        :return: page_nb of content if available, None if unavailable
734
        """
735
        if self.content.depot_file:
736
            from tracim_backend.lib.core.content import ContentApi
737
            content_api = ContentApi(
738
                current_user=self._user,
739
                session=self.dbsession,
740
                config=self.config
741
            )
742
            return content_api.get_preview_page_nb(self.content.revision_id)
743
        else:
744
            return None
745
746
    @property
747
    def mimetype(self) -> str:
748
        """
749
        :return: mimetype of content if available, None if unavailable
750
        """
751
        return self.content.file_mimetype
752
753
    @property
754
    def size(self) -> typing.Optional[int]:
755
        """
756
        :return: size of content if available, None if unavailable
757
        """
758
        if self.content.depot_file:
759
            return self.content.depot_file.file.content_length
760
        else:
761
            return None
762
763
764
class RevisionInContext(object):
765
    """
766
    Interface to get Content data and Content data related to context.
767
    """
768
769
    def __init__(self, content_revision: ContentRevisionRO, dbsession: Session, config: CFG,  user: User=None) -> None:  # nopep8
770
        assert content_revision is not None
771
        self.revision = content_revision
772
        self.dbsession = dbsession
773
        self.config = config
774
        self._user = user
775
776
    # Default
777
    @property
778
    def content_id(self) -> int:
779
        return self.revision.content_id
780
781
    @property
782
    def parent_id(self) -> int:
783
        """
784
        Return parent_id of the content
785
        """
786
        return self.revision.parent_id
787
788
    @property
789
    def workspace_id(self) -> int:
790
        return self.revision.workspace_id
791
792
    @property
793
    def label(self) -> str:
794
        return self.revision.label
795
796
    @property
797
    def revision_type(self) -> str:
798
        return self.revision.revision_type
799
800
    @property
801
    def content_type(self) -> str:
802
        return CONTENT_TYPES.get_one_by_slug(self.revision.type).slug
803
804
    @property
805
    def sub_content_types(self) -> typing.List[str]:
806
        return [_type.slug for _type
807
                in self.revision.node.get_allowed_content_types()]
808
809
    @property
810
    def status(self) -> str:
811
        return self.revision.status
812
813
    @property
814
    def is_archived(self) -> bool:
815
        return self.revision.is_archived
816
817
    @property
818
    def is_deleted(self) -> bool:
819
        return self.revision.is_deleted
820
821
    @property
822
    def raw_content(self) -> str:
823
        return self.revision.description
824
825
    @property
826
    def author(self) -> UserInContext:
827
        return UserInContext(
828
            dbsession=self.dbsession,
829
            config=self.config,
830
            user=self.revision.owner
831
        )
832
833
    @property
834
    def revision_id(self) -> int:
835
        return self.revision.revision_id
836
837
    @property
838
    def created(self) -> datetime:
839
        return self.updated
840
841
    @property
842
    def modified(self) -> datetime:
843
        return self.updated
844
845
    @property
846
    def updated(self) -> datetime:
847
        return self.revision.updated
848
849
    @property
850
    def next_revision(self) -> typing.Optional[ContentRevisionRO]:
851
        """
852
        Get next revision (later revision)
853
        :return: next_revision
854
        """
855
        next_revision = None
856
        revisions = self.revision.node.revisions
857
        # INFO - G.M - 2018-06-177 - Get revisions more recent that
858
        # current one
859
        next_revisions = [
860
            revision for revision in revisions
861
            if revision.revision_id > self.revision.revision_id
862
        ]
863
        if next_revisions:
864
            # INFO - G.M - 2018-06-177 -sort revisions by date
865
            sorted_next_revisions = sorted(
866
                next_revisions,
867
                key=lambda revision: revision.updated
868
            )
869
            # INFO - G.M - 2018-06-177 - return only next revision
870
            return sorted_next_revisions[0]
871
        else:
872
            return None
873
874
    @property
875
    def comment_ids(self) -> typing.List[int]:
876
        """
877
        Get list of ids of all current revision related comments
878
        :return: list of comments ids
879
        """
880
        comments = self.revision.node.get_comments()
881
        # INFO - G.M - 2018-06-177 - Get comments more recent than revision.
882
        revision_comments = [
883
            comment for comment in comments
884
            if comment.created > self.revision.updated
885
        ]
886
        if self.next_revision:
887
            # INFO - G.M - 2018-06-177 - if there is a revision more recent
888
            # than current remove comments from theses rev (comments older
889
            # than next_revision.)
890
            revision_comments = [
891
                comment for comment in revision_comments
892
                if comment.created < self.next_revision.updated
893
            ]
894
        sorted_revision_comments = sorted(
895
            revision_comments,
896
            key=lambda revision: revision.created
897
        )
898
        comment_ids = []
899
        for comment in sorted_revision_comments:
900
            comment_ids.append(comment.content_id)
901
        return comment_ids
902
903
    # Context-related
904
    @property
905
    def show_in_ui(self) -> bool:
906
        # TODO - G.M - 31-05-2018 - Enable Show_in_ui params
907
        # if false, then do not show content in the treeview.
908
        # This may his maybe used for specific contents or for sub-contents.
909
        # Default is True.
910
        # In first version of the API, this field is always True
911
        return True
912
913
    @property
914
    def slug(self) -> str:
915
        return slugify(self.revision.label)
916
917
    # file specific
918
    @property
919
    def page_nb(self) -> typing.Optional[int]:
920
        """
921
        :return: page_nb of content if available, None if unavailable
922
        """
923
        if self.revision.depot_file:
924
            # TODO - G.M - 2018-09-05 - Fix circular import better
925
            from tracim_backend.lib.core.content import ContentApi
926
            content_api = ContentApi(
927
                current_user=self._user,
928
                session=self.dbsession,
929
                config=self.config
930
            )
931
            return content_api.get_preview_page_nb(self.revision.revision_id)
932
        else:
933
            return None
934
935
    @property
936
    def mimetype(self) -> str:
937
        """
938
        :return: mimetype of content if available, None if unavailable
939
        """
940
        return self.revision.file_mimetype
941
942
    @property
943
    def size(self) -> typing.Optional[int]:
944
        """
945
        :return: size of content if available, None if unavailable
946
        """
947
        if self.revision.depot_file:
948
            return self.revision.depot_file.file.content_length
949
        else:
950
            return None
951