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