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