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