Skip to content

Commit ca5ab94

Browse files
committed
add type for users ( guest and user)
1 parent acc7b60 commit ca5ab94

File tree

6 files changed

+176
-18
lines changed

6 files changed

+176
-18
lines changed

QA/py/pg_files/upgrade_prod.sql

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2921,6 +2921,46 @@ ALTER TABLE projects ADD COLUMN formulae VARCHAR;
29212921
UPDATE alembic_version SET version_num='78b24e7ba52b' WHERE alembic_version.version_num = '032dfb7159d5';
29222922

29232923
COMMIT;
2924+
BEGIN;
2925+
-- Running upgrade 78b24e7ba52b -> e6dda08ff48b
2926+
2927+
CREATE TABLE organizations (
2928+
id SERIAL NOT NULL,
2929+
name VARCHAR(255) NOT NULL,
2930+
directories VARCHAR(100)[],
2931+
PRIMARY KEY (id)
2932+
);
2933+
2934+
ALTER TABLE users ADD COLUMN type SMALLINT;
2935+
2936+
ALTER TABLE users ADD CONSTRAINT checktype CHECK (type= 1 OR (type=0 AND status=0));
2937+
2938+
ALTER TABLE users ADD CONSTRAINT checkstatus CHECK (status ANY(array[-1, 0, 1, 2]::int[]));
2939+
2940+
UPDATE users SET type=1;
2941+
2942+
UPDATE alembic_version SET version_num='e6dda08ff48b' WHERE alembic_version.version_num = '78b24e7ba52b';
2943+
2944+
COMMIT;
2945+
BEGIN;
2946+
-- Running upgrade e6dda08ff48b -> 5b706c64bae0
2947+
2948+
ALTER TABLE organizations ADD CONSTRAINT orgname UNIQUE (name);
2949+
2950+
ALTER TABLE organizations DROP COLUMN id;
2951+
2952+
INSERT INTO organizations (name) SELECT DISTINCT organisation FROM users;
2953+
2954+
ALTER TABLE users ADD CONSTRAINT org FOREIGN KEY(organisation) REFERENCES organizations (name);
2955+
2956+
ALTER TABLE users ADD CONSTRAINT orgname FOREIGN KEY(organisation) REFERENCES organizations (name);
2957+
2958+
UPDATE alembic_version SET version_num='5b706c64bae0' WHERE alembic_version.version_num = 'e6dda08ff48b';
2959+
2960+
COMMIT;
2961+
2962+
2963+
29242964

29252965
------- Leave on tail
29262966

py/API_models/constants.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from BO.DataLicense import DataLicense, AccessLevelEnum
1010
from DB.Collection import PeopleOrganizationDirectory
11-
from DB.User import UserStatus
11+
from DB.User import UserStatus, UserType
1212
from BO.User import (
1313
USER_PWD_REGEXP,
1414
USER_PWD_REGEXP_DESCRIPTION,
@@ -66,6 +66,12 @@ class Constants(BaseModel):
6666
default={st.name: st.value for st in UserStatus},
6767
example={"blocked": -1, "inactive": 0, "active": 1, "pending": 2},
6868
)
69+
user_type: Dict[str, int] = Field(
70+
title="User status",
71+
description="Application User type values",
72+
default={st.name: st.value for st in UserType},
73+
example={"guest": 0},
74+
)
6975
password_regexp: str = Field(
7076
title="Password regexp",
7177
description=USER_PWD_REGEXP_DESCRIPTION,

py/DB/User.py

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,19 @@
77
from enum import Enum
88
from typing import List, Iterable, TYPE_CHECKING
99

10-
from sqlalchemy import event, SmallInteger
11-
10+
from sqlalchemy import event, SmallInteger, CheckConstraint
11+
from sqlalchemy.dialects.postgresql import ARRAY
1212
from BO.helpers.TSVHelpers import none_to_empty
1313
from data.Countries import countries_by_name
1414
from .helpers import Session, Result
15-
from .helpers.DDL import Column, ForeignKey, Sequence, Integer, String, Boolean
15+
from .helpers.DDL import (
16+
Column,
17+
ForeignKey,
18+
Sequence,
19+
Integer,
20+
String,
21+
Boolean,
22+
)
1623
from .helpers.Direct import text, func
1724
from .helpers.ORM import Model, relationship, Insert
1825
from .helpers.Postgres import TIMESTAMP
@@ -28,12 +35,15 @@ class UserStatus(int, Enum):
2835
pending: Final = 2
2936

3037

38+
class UserType(int, Enum):
39+
guest: Final = 0
40+
user: Final = 1
41+
42+
3143
class Organization(Model):
3244
__tablename__ = "organizations"
33-
id: int = Column(Integer, primary_key=True)
34-
name: str = Column(String(255), nullable=False)
35-
directory: str = Column(String(20), nullable=True)
36-
reference: str = Column(String(40), nullable=True)
45+
name: str = Column(String(255), unique=True, primary_key=True)
46+
directories: list = Column(ARRAY(String), nullable=True)
3747

3848
@staticmethod
3949
def find_organizations(
@@ -46,21 +56,24 @@ def find_organizations(
4656
:param found_organizations: A dict in
4757
"""
4858
sql = text(
49-
"SELECT id, name, directory , reference"
59+
"SELECT name, directories"
5060
" FROM organizations "
5161
" WHERE LOWER(name) = ANY(:nms) "
5262
)
5363
res: Result = session.execute(sql, {"nms": names})
5464
for rec in res:
5565
for u in found_organzations:
56-
if (
57-
u == rec[1]
58-
or none_to_empty(found_users[u].get("email")).lower() == rec[2]
59-
):
60-
found_users[u]["id"] = rec[0]
66+
if u == rec[0]:
67+
found_organizations[u]["name"] = rec[0]
6168

6269
def __str__(self):
63-
return "{0} ({1} {2})".format(self.name, self.directory, self.reference)
70+
return "{0} ({1})".format(self.name, self.directories)
71+
72+
73+
CHK_STATUS = "status = ANY(ARRAY{0}::int[])".format([st.value for st in UserStatus])
74+
CHK_TYPE = "type= {0} OR (type={1} AND status={2})".format(
75+
UserType.user.value, UserType.guest.value, UserStatus.inactive.value
76+
)
6477

6578

6679
class User(Model):
@@ -69,16 +82,22 @@ class User(Model):
6982
email: str = Column(String(255), unique=True, nullable=False)
7083
password: str = Column(String(255))
7184
name: str = Column(String(255), nullable=False)
72-
organisation: str = Column(String(255))
85+
organisation: str = Column(
86+
String(255), ForeignKey("organizations.name"), nullable=True
87+
)
7388
status: int = Column(SmallInteger(), default=1)
7489
status_date = Column(TIMESTAMP)
7590
status_admin_comment: str = Column(String(255))
7691
preferences: str = Column(String(40000))
7792
country: str = Column(String(50))
7893
orcid: str = Column(String(20), nullable=True)
94+
type: int = Column(SmallInteger(), default=1)
7995
usercreationdate = Column(TIMESTAMP, default=func.now())
8096
usercreationreason = Column(String(1000))
8197

98+
checktype = CheckConstraint(CHK_TYPE)
99+
# table level CHECK constraint. type guest must have inactive status .
100+
checkstatus = CheckConstraint(CHK_STATUS)
82101
# Mail status: True for verified, default NULL
83102
mail_status: bool = Column(Boolean(), nullable=True)
84103
# Date the mail status was set
@@ -125,6 +144,16 @@ def __str__(self):
125144
return "{0} ({1})".format(self.name, self.email)
126145

127146

147+
def insert_new_organization(sess, **kwargs):
148+
"""
149+
Add new organization if missing on user inser or update
150+
"""
151+
print(" oninsert ", **kwargs)
152+
# if organisation :
153+
# ins = Insert(Organization.name).values((organisation))
154+
# sess.execute(ins)
155+
156+
128157
class Role(Model):
129158
"""
130159
The roles granted to users.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""organizations users relation
2+
3+
Revision ID: 5b706c64bae0
4+
Revises: e6dda08ff48b
5+
Create Date: 2025-01-17 14:48:54.137602
6+
7+
"""
8+
9+
# revision identifiers, used by Alembic.
10+
revision = "5b706c64bae0"
11+
down_revision = "e6dda08ff48b"
12+
13+
from alembic import op
14+
import sqlalchemy as sa
15+
from sqlalchemy.dialects import postgresql
16+
17+
18+
def upgrade():
19+
# ### commands auto generated by Alembic - please adjust! ###
20+
op.create_unique_constraint("orgname", "organizations", ["name"])
21+
op.drop_column("organizations", "id")
22+
op.execute(
23+
"INSERT INTO organizations (name) SELECT DISTINCT organisation FROM users WHERE organisation IS NOT NULL"
24+
)
25+
op.create_foreign_key(None, "users", "organizations", ["organisation"], ["name"])
26+
27+
# ### end Alembic commands ###
28+
29+
30+
def downgrade():
31+
# ### commands auto generated by Alembic - please adjust! ###
32+
op.drop_constraint(None, "users", type_="foreignkey")
33+
op.drop_constraint("orgname", "organizations", type_="unique")
34+
op.add_column(
35+
"organizations",
36+
sa.Column("id", sa.INTEGER(), autoincrement=True, nullable=False),
37+
)
38+
39+
# ### end Alembic commands ###
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""organizations table and users type
2+
3+
Revision ID: e6dda08ff48b
4+
Revises: 78b24e7ba52b
5+
Create Date: 2025-01-17 11:45:22.873082
6+
7+
"""
8+
9+
# revision identifiers, used by Alembic.
10+
revision = "e6dda08ff48b"
11+
down_revision = "78b24e7ba52b"
12+
13+
from alembic import op
14+
import sqlalchemy as sa
15+
from sqlalchemy.dialects import postgresql
16+
from DB.User import UserType, UserStatus, CHK_STATUS, CHK_TYPE
17+
18+
19+
def upgrade():
20+
# ### commands auto generated by Alembic - please adjust! ###
21+
op.create_table(
22+
"organizations",
23+
sa.Column("id", sa.Integer(), nullable=False),
24+
sa.Column("name", sa.String(length=255), nullable=False),
25+
sa.Column(
26+
"directories", postgresql.ARRAY(sa.String(length=100)), nullable=True
27+
),
28+
sa.PrimaryKeyConstraint("id"),
29+
)
30+
op.add_column("users", sa.Column("type", sa.SmallInteger(), nullable=True))
31+
op.create_check_constraint("checktype", "users", CHK_TYPE)
32+
op.create_check_constraint("checkstatus", "users", CHK_STATUS)
33+
op.execute("UPDATE users SET type={0}".format(UserType.user.value))
34+
# ### end Alembic commands ###
35+
36+
37+
def downgrade():
38+
# ### commands auto generated by Alembic - please adjust! ###
39+
op.drop_constraint("checktype", "users", type_="check")
40+
op.drop_constraint("checkstatus", "users", type_="check")
41+
op.drop_column("users", "type")
42+
op.drop_table("organizations")
43+
44+
# ### end Alembic commands ###

py/helpers/login.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
from API_operations.helpers.Service import Service
1717
from BO.Rights import NOT_AUTHORIZED
18-
from DB.User import User, UserStatus
18+
from DB.User import User, UserStatus, UserType
1919
from helpers.fastApiUtils import build_serializer
2020

2121
NOT_AUTHORIZED_MAIL = "__id____" + NOT_AUTHORIZED
@@ -58,7 +58,7 @@ def validate_login(self, username: str, password: str) -> Union[str, bytes]:
5858
func.lower(User.email) == func.lower(username),
5959
User.status == UserStatus.active.value,
6060
)
61-
db_users = user_qry.all()
61+
db_users = user_qry.filter(User.type == UserType.user.value).all()
6262
assert len(db_users) == 1, NOT_AUTHORIZED
6363
the_user: User = db_users[0]
6464
# verif even user is not active , in order to let modify email only if mail_status is False

0 commit comments

Comments
 (0)