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