Skip to content

Commit 5e5177e

Browse files
authored
Merge pull request #119 from pneumaticapp/backend/accounts/44398__users_crud_api
⚙️ 44398 backend [ users ] Add users manually
2 parents d25c3e4 + b832eb8 commit 5e5177e

31 files changed

Lines changed: 4644 additions & 1761 deletions

backend/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@ Open a terminal in the "backend" directory and run the following commands:
4545
1. Place the database dump file named ``dump.sql`` in the ``pneumaticworkflow/postgres/backups`` directory.
4646
2. Recreate the pneumatic database with the command: ``docker exec -it pneumatic-postgres sh -c "dropdb -U postgres_user postgres_db && createdb -U postgres_user --owner postgres_user postgres_db"``.
4747
3. Load the dump file into the database with the command: ``docker exec -it pneumatic-postgres sh -c "psql -U postgres_user -h localhost postgres_db < /backups/dump.sql"``. This process will take several minutes.
48-
4. (Optional). Connect to the database and verify the schema. Command: ``docker exec -it pneumatic-postgres sh -c "psql -U postgres_user postgres_db"``
48+
49+
#### Create a backup copy of the database
50+
Command: ``docker exec -it pneumatic-postgres sh -c "pg_dump -U postgres_user postgres_db > /backups/pneumatic-backup-$(date +%Y-%m-%d[%Hh:%Mm]%ZTZ).sql"``
51+
52+
#### Connect to the database
53+
Command: ``docker exec -it pneumatic-postgres sh -c "psql -U postgres_user postgres_db"``
4954

5055
### Windows
5156
Open PowerShell in the backend directory and run the following commands:

backend/src/accounts/enums.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,16 @@ class UserFirstDayWeek:
9292
(SATURDAY, 'Saturday'),
9393
)
9494

95+
LITERALS = Literal[
96+
SUNDAY,
97+
MONDAY,
98+
TUESDAY,
99+
WEDNESDAY,
100+
THURSDAY,
101+
FRIDAY,
102+
SATURDAY,
103+
]
104+
95105

96106
class UserDateFormat:
97107

@@ -137,6 +147,7 @@ class UserDateFormat:
137147
PY_USA_24: API_USA_24,
138148
PY_EUROPE_24: API_EUROPE_24,
139149
}
150+
LITERALS = Literal[PY_USA_12, PY_EUROPE_12, PY_USA_24, PY_EUROPE_24]
140151

141152

142153
class LeaseLevel:

backend/src/accounts/filters.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,24 @@ class Meta:
4141
)
4242

4343

44+
class PublicUsersListFilterSet(FilterSet):
45+
46+
class Meta:
47+
model = User
48+
fields = (
49+
'ordering',
50+
)
51+
52+
ordering = DefaultOrderingFilter(
53+
fields=(
54+
('first_name', 'first_name'),
55+
('last_name', 'last_name'),
56+
('status', 'status'),
57+
),
58+
default=('last_name',),
59+
)
60+
61+
4462
class UsersListFilterSet(FilterSet):
4563

4664
class Meta:

backend/src/accounts/messages.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,9 @@
9595
_('Group with the name "{name}" already exists.'),
9696
name=name,
9797
)
98+
MSG_A_0046 = _(
99+
'Permission denied. '
100+
'Only an administrator can grant administrator privileges.',
101+
)
102+
MSG_A_0047 = _('You can\'t delete yourself.')
103+
MSG_A_0048 = _('You can\'t delete the account owner.')

backend/src/accounts/serializers/user.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from src.accounts.serializers.group import (
1313
GroupNameSerializer,
1414
)
15+
from src.accounts.messages import MSG_A_0036, MSG_A_0046
1516
from src.accounts.serializers.user_invites import (
1617
UserListInviteSerializer,
1718
)
@@ -56,7 +57,6 @@ class Meta:
5657
'photo',
5758
'status',
5859
'is_admin',
59-
'date_joined',
6060
'date_joined_tsp',
6161
'is_account_owner',
6262
'is_tasks_digest_subscriber',
@@ -72,22 +72,21 @@ class Meta:
7272
'date_fdw',
7373
'invite',
7474
'groups',
75+
'password',
7576
)
7677
read_only_fields = (
7778
'id',
7879
'type',
79-
'email',
8080
'invite',
8181
'status',
82-
'is_admin',
8382
'is_account_owner',
84-
'groups',
83+
'date_joined_tsp',
8584
)
8685

8786
groups = RelatedListField(
8887
source='user_groups',
8988
child=serializers.IntegerField(),
90-
read_only=True,
89+
required=False,
9190
)
9291
date_joined_tsp = TimeStampField(source='date_joined', read_only=True)
9392
timezone = serializers.ChoiceField(
@@ -96,6 +95,7 @@ class Meta:
9695
)
9796
date_fmt = DateFormatField(required=False)
9897
invite = serializers.SerializerMethodField(allow_null=True, read_only=True)
98+
password = serializers.CharField(write_only=True, required=False)
9999

100100
def get_invite(self, instance: UserModel):
101101
if instance.status_invited and instance.invite:
@@ -109,6 +109,22 @@ def __init__(self, *args, **kwargs):
109109
else:
110110
self.fields['language'].choices = Language.EURO_CHOICES
111111

112+
def validate_is_admin(self, value):
113+
if value is True and not self.context['user'].is_admin:
114+
raise serializers.ValidationError(MSG_A_0046)
115+
return value
116+
117+
def validate(self, attrs):
118+
if 'password' in attrs:
119+
attrs['raw_password'] = attrs.pop('password')
120+
if (
121+
self.instance
122+
and self.instance.is_account_owner
123+
and self.instance != self.context['user']
124+
):
125+
raise serializers.ValidationError(MSG_A_0036)
126+
return attrs
127+
112128

113129
class UserPrivilegesSerializer(UserSerializer):
114130
class Meta(UserSerializer.Meta):

backend/src/accounts/services/exceptions.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
MSG_A_0042,
1515
MSG_A_0043,
1616
MSG_A_0044,
17+
MSG_A_0047,
18+
MSG_A_0048,
1719
)
1820
from src.generics.exceptions import BaseServiceException
1921

@@ -66,22 +68,32 @@ class ExpiredTransferTokenException(BaseServiceException):
6668
default_message = MSG_A_0009
6769

6870

69-
class UserNotFoundException(BaseServiceException):
71+
class UserNotFoundException(UserServiceException):
7072

7173
default_message = MSG_A_0010
7274

7375

74-
class UserIsPerformerException(BaseServiceException):
76+
class UserIsPerformerException(UserServiceException):
7577

7678
default_message = MSG_A_0011
7779

7880

79-
class ReassignUserDoesNotExist(BaseServiceException):
81+
class ReassignUserDoesNotExist(UserServiceException):
8082

8183
default_message = MSG_A_0012
8284

8385

84-
class InvalidOrExpiredToken(BaseServiceException):
86+
class PreventSelfDeletion(BaseServiceException):
87+
88+
default_message = MSG_A_0047
89+
90+
91+
class PreventAccountOwnerDeletion(BaseServiceException):
92+
93+
default_message = MSG_A_0048
94+
95+
96+
class InvalidOrExpiredToken(UserServiceException):
8597

8698
default_message = MSG_A_0013
8799

0 commit comments

Comments
 (0)