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

UserCreation.__init__()   A

Complexity

Conditions 1

Size

Total Lines 19
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Many Parameters   

Many Parameters

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

There are several approaches to avoid long parameter lists:

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