Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Kontr 2.0
Portal API Backend
Commits
1f81087f
Verified
Commit
1f81087f
authored
Apr 14, 2019
by
Peter Stanko
Browse files
CRLF -> LF; BASE_PARAMS and LIST PARAMS
parent
8af23ea7
Changes
38
Hide whitespace changes
Inline
Side-by-side
.gitignore
View file @
1f81087f
.idea/
*.pyc
__pycache__/
.coverage
.pytest_cache/
portal.local.cfg
devel.db
log/*.*
/.env
.idea/
*.pyc
__pycache__/
.coverage
.pytest_cache/
portal.local.cfg
devel.db
log/*.*
/.env
*.tmp
management/data/data_test.py
View file @
1f81087f
from
datetime
import
timedelta
from
flask
import
Flask
from
flask_sqlalchemy
import
SQLAlchemy
from
management.data.shared
import
DataFactory
from
portal.database.models
import
Review
,
Secret
,
SubmissionState
from
portal.tools
import
time
def
init_test_data
(
app
:
Flask
,
db
:
SQLAlchemy
):
with
app
.
app_context
():
factory
=
DataFactory
(
db
)
# users
student1
=
factory
.
create_user
(
username
=
'student1'
,
uco
=
111
)
student2
=
factory
.
create_user
(
username
=
'student2'
,
uco
=
222
)
teacher1
=
factory
.
create_user
(
username
=
'teacher1'
,
password
=
'123123'
,
name
=
'Teacher And Student'
,
uco
=
1001
)
teacher2
=
factory
.
create_user
(
username
=
'teacher2'
,
password
=
'123123'
,
name
=
'Teacher Only'
,
uco
=
1002
)
teacher1
.
secrets
.
append
(
Secret
(
'teacher1_secret'
,
'ultimate_teacher_secret'
))
lecturer1
=
factory
.
create_user
(
username
=
'lecturer1'
,
name
=
'Courses Owner'
,
uco
=
1010
)
# courses
test_course1
=
factory
.
create_course
(
codename
=
'testcourse1'
,
name
=
'Test Course One'
,
faculty_id
=
100
)
test_course2
=
factory
.
create_course
(
codename
=
'testcourse2'
,
name
=
'test Course Two'
,
faculty_id
=
100
)
# groups
tc1_students
=
factory
.
create_group
(
course
=
test_course1
,
name
=
"seminar01"
)
tc1_teachers
=
factory
.
create_group
(
course
=
test_course1
,
name
=
"teachers"
)
tc2_students
=
factory
.
create_group
(
course
=
test_course2
,
name
=
"seminar01"
)
tc2_teachers
=
factory
.
create_group
(
course
=
test_course2
,
name
=
"teachers"
)
tc1_hw01
=
factory
.
scaffold_project
(
test_course1
,
'hw01'
)
tc1_hw02
=
factory
.
scaffold_project
(
test_course1
,
'hw02'
)
tc2_hw01
=
factory
.
scaffold_project
(
test_course2
,
'hw01'
)
tc2_hw01
=
factory
.
scaffold_project
(
test_course2
,
'hw02'
)
# roles
tc1_student
=
factory
.
scaffold_role
(
role_type
=
'student'
,
course
=
test_course1
)
tc1_teacher
=
factory
.
scaffold_role
(
role_type
=
'teacher'
,
course
=
test_course1
)
tc1_owner
=
factory
.
scaffold_role
(
role_type
=
'owner'
,
course
=
test_course1
)
tc1_workers
=
factory
.
scaffold_role
(
role_type
=
'worker'
,
course
=
test_course1
)
tc2_student
=
factory
.
scaffold_role
(
role_type
=
'student'
,
course
=
test_course2
)
tc2_teacher
=
factory
.
scaffold_role
(
role_type
=
'teacher'
,
course
=
test_course2
)
tc2_owner
=
factory
.
scaffold_role
(
role_type
=
'owner'
,
course
=
test_course2
)
tc2_workers
=
factory
.
scaffold_role
(
role_type
=
'worker'
,
course
=
test_course2
)
# relationships
teacher_student
=
teacher1
teacher_both
=
teacher2
student_tc1
=
student1
student_both
=
student2
# roles
lecturer1
.
roles
.
append
(
tc1_owner
)
lecturer1
.
roles
.
append
(
tc2_owner
)
teacher_student
.
roles
.
append
(
tc1_teacher
)
teacher_student
.
roles
.
append
(
tc2_student
)
teacher_both
.
roles
.
append
(
tc1_teacher
)
teacher_both
.
roles
.
append
(
tc2_teacher
)
student_tc1
.
roles
.
append
(
tc1_student
)
student_both
.
roles
.
append
(
tc1_student
)
student_both
.
roles
.
append
(
tc2_student
)
# groups
teacher_student
.
groups
.
append
(
tc1_teachers
)
teacher_student
.
groups
.
append
(
tc2_students
)
teacher_both
.
groups
.
append
(
tc1_teachers
)
teacher_both
.
groups
.
append
(
tc2_teachers
)
student_tc1
.
groups
.
append
(
tc1_students
)
student_both
.
groups
.
append
(
tc1_students
)
student_both
.
groups
.
append
(
tc2_students
)
# submissions
tc2_sub1
=
factory
.
create_submission
(
user
=
teacher_student
,
project
=
tc2_hw01
,
parameters
=
{})
tc2_sub1
.
state
=
SubmissionState
.
FINISHED
tc2_sub2
=
factory
.
create_submission
(
user
=
student_both
,
project
=
tc2_hw01
,
parameters
=
{})
tc2_sub2
.
state
=
SubmissionState
.
ABORTED
tc1_sub_p1_cancel
=
factory
.
create_submission
(
user
=
student_both
,
project
=
tc1_hw01
,
parameters
=
{})
tc1_sub_p1_cancel
.
state
=
SubmissionState
.
CANCELLED
tc1_sub_p1_abort
=
factory
.
create_submission
(
user
=
student_tc1
,
project
=
tc1_hw01
,
parameters
=
{})
tc1_sub_p1_abort
.
state
=
SubmissionState
.
ABORTED
tc1_sub_p1_finished
=
factory
.
create_submission
(
user
=
student_both
,
project
=
tc1_hw01
,
parameters
=
{})
tc1_sub_p1_finished
.
state
=
SubmissionState
.
FINISHED
tc1_sub_p1_in_progress
=
factory
.
create_submission
(
user
=
student_tc1
,
project
=
tc1_hw01
,
parameters
=
{})
tc1_sub_p1_in_progress
.
state
=
SubmissionState
.
IN_PROGRESS
# Projects in groups
tc1_students
.
projects
.
append
(
tc1_hw01
)
tc2_students
.
projects
.
append
(
tc2_hw01
)
db
.
session
.
add_all
(
[
tc2_sub1
,
tc2_sub2
,
tc1_sub_p1_cancel
,
tc1_sub_p1_abort
,
tc1_sub_p1_finished
,
tc1_sub_p1_in_progress
])
# reviews
review
=
Review
(
tc2_sub1
)
factory
.
create_review_item
(
author
=
teacher_both
,
review
=
review
,
line
=
1
,
content
=
"problem here"
)
factory
.
create_review_item
(
author
=
teacher_both
,
review
=
review
,
line
=
5
,
content
=
"good stuff"
)
factory
.
create_review_item
(
author
=
teacher_student
,
review
=
review
,
line
=
1
,
content
=
"oops"
)
db
.
session
.
add_all
([
review
])
# workers
executor
=
factory
.
create_worker
(
name
=
'executor'
,
url
=
"foo/url"
)
executor
.
secrets
.
append
(
Secret
(
'executor_secret'
,
"executor_secret"
,
time
.
current_time
()
+
timedelta
(
hours
=
1
)))
processing
=
factory
.
create_worker
(
name
=
'processing'
,
url
=
"bar/url"
)
processing
.
secrets
.
append
(
Secret
(
'processing_secret'
,
"processing_secret"
,
time
.
current_time
()
+
timedelta
(
hours
=
1
)))
executor
.
roles
.
append
(
tc1_workers
)
processing
.
roles
.
append
(
tc2_workers
)
db
.
session
.
add_all
([
executor
,
processing
])
# Commit to the DB
db
.
session
.
commit
()
from
datetime
import
timedelta
from
flask
import
Flask
from
flask_sqlalchemy
import
SQLAlchemy
from
management.data.shared
import
DataFactory
from
portal.database.models
import
Review
,
Secret
,
SubmissionState
from
portal.tools
import
time
def
init_test_data
(
app
:
Flask
,
db
:
SQLAlchemy
):
with
app
.
app_context
():
factory
=
DataFactory
(
db
)
# users
student1
=
factory
.
create_user
(
username
=
'student1'
,
uco
=
111
)
student2
=
factory
.
create_user
(
username
=
'student2'
,
uco
=
222
)
teacher1
=
factory
.
create_user
(
username
=
'teacher1'
,
password
=
'123123'
,
name
=
'Teacher And Student'
,
uco
=
1001
)
teacher2
=
factory
.
create_user
(
username
=
'teacher2'
,
password
=
'123123'
,
name
=
'Teacher Only'
,
uco
=
1002
)
teacher1
.
secrets
.
append
(
Secret
(
'teacher1_secret'
,
'ultimate_teacher_secret'
))
lecturer1
=
factory
.
create_user
(
username
=
'lecturer1'
,
name
=
'Courses Owner'
,
uco
=
1010
)
# courses
test_course1
=
factory
.
create_course
(
codename
=
'testcourse1'
,
name
=
'Test Course One'
,
faculty_id
=
100
)
test_course2
=
factory
.
create_course
(
codename
=
'testcourse2'
,
name
=
'test Course Two'
,
faculty_id
=
100
)
# groups
tc1_students
=
factory
.
create_group
(
course
=
test_course1
,
name
=
"seminar01"
)
tc1_teachers
=
factory
.
create_group
(
course
=
test_course1
,
name
=
"teachers"
)
tc2_students
=
factory
.
create_group
(
course
=
test_course2
,
name
=
"seminar01"
)
tc2_teachers
=
factory
.
create_group
(
course
=
test_course2
,
name
=
"teachers"
)
tc1_hw01
=
factory
.
scaffold_project
(
test_course1
,
'hw01'
)
tc1_hw02
=
factory
.
scaffold_project
(
test_course1
,
'hw02'
)
tc2_hw01
=
factory
.
scaffold_project
(
test_course2
,
'hw01'
)
tc2_hw01
=
factory
.
scaffold_project
(
test_course2
,
'hw02'
)
# roles
tc1_student
=
factory
.
scaffold_role
(
role_type
=
'student'
,
course
=
test_course1
)
tc1_teacher
=
factory
.
scaffold_role
(
role_type
=
'teacher'
,
course
=
test_course1
)
tc1_owner
=
factory
.
scaffold_role
(
role_type
=
'owner'
,
course
=
test_course1
)
tc1_workers
=
factory
.
scaffold_role
(
role_type
=
'worker'
,
course
=
test_course1
)
tc2_student
=
factory
.
scaffold_role
(
role_type
=
'student'
,
course
=
test_course2
)
tc2_teacher
=
factory
.
scaffold_role
(
role_type
=
'teacher'
,
course
=
test_course2
)
tc2_owner
=
factory
.
scaffold_role
(
role_type
=
'owner'
,
course
=
test_course2
)
tc2_workers
=
factory
.
scaffold_role
(
role_type
=
'worker'
,
course
=
test_course2
)
# relationships
teacher_student
=
teacher1
teacher_both
=
teacher2
student_tc1
=
student1
student_both
=
student2
# roles
lecturer1
.
roles
.
append
(
tc1_owner
)
lecturer1
.
roles
.
append
(
tc2_owner
)
teacher_student
.
roles
.
append
(
tc1_teacher
)
teacher_student
.
roles
.
append
(
tc2_student
)
teacher_both
.
roles
.
append
(
tc1_teacher
)
teacher_both
.
roles
.
append
(
tc2_teacher
)
student_tc1
.
roles
.
append
(
tc1_student
)
student_both
.
roles
.
append
(
tc1_student
)
student_both
.
roles
.
append
(
tc2_student
)
# groups
teacher_student
.
groups
.
append
(
tc1_teachers
)
teacher_student
.
groups
.
append
(
tc2_students
)
teacher_both
.
groups
.
append
(
tc1_teachers
)
teacher_both
.
groups
.
append
(
tc2_teachers
)
student_tc1
.
groups
.
append
(
tc1_students
)
student_both
.
groups
.
append
(
tc1_students
)
student_both
.
groups
.
append
(
tc2_students
)
# submissions
tc2_sub1
=
factory
.
create_submission
(
user
=
teacher_student
,
project
=
tc2_hw01
,
parameters
=
{})
tc2_sub1
.
state
=
SubmissionState
.
FINISHED
tc2_sub2
=
factory
.
create_submission
(
user
=
student_both
,
project
=
tc2_hw01
,
parameters
=
{})
tc2_sub2
.
state
=
SubmissionState
.
ABORTED
tc1_sub_p1_cancel
=
factory
.
create_submission
(
user
=
student_both
,
project
=
tc1_hw01
,
parameters
=
{})
tc1_sub_p1_cancel
.
state
=
SubmissionState
.
CANCELLED
tc1_sub_p1_abort
=
factory
.
create_submission
(
user
=
student_tc1
,
project
=
tc1_hw01
,
parameters
=
{})
tc1_sub_p1_abort
.
state
=
SubmissionState
.
ABORTED
tc1_sub_p1_finished
=
factory
.
create_submission
(
user
=
student_both
,
project
=
tc1_hw01
,
parameters
=
{})
tc1_sub_p1_finished
.
state
=
SubmissionState
.
FINISHED
tc1_sub_p1_in_progress
=
factory
.
create_submission
(
user
=
student_tc1
,
project
=
tc1_hw01
,
parameters
=
{})
tc1_sub_p1_in_progress
.
state
=
SubmissionState
.
IN_PROGRESS
# Projects in groups
tc1_students
.
projects
.
append
(
tc1_hw01
)
tc2_students
.
projects
.
append
(
tc2_hw01
)
db
.
session
.
add_all
(
[
tc2_sub1
,
tc2_sub2
,
tc1_sub_p1_cancel
,
tc1_sub_p1_abort
,
tc1_sub_p1_finished
,
tc1_sub_p1_in_progress
])
# reviews
review
=
Review
(
tc2_sub1
)
factory
.
create_review_item
(
author
=
teacher_both
,
review
=
review
,
line
=
1
,
content
=
"problem here"
)
factory
.
create_review_item
(
author
=
teacher_both
,
review
=
review
,
line
=
5
,
content
=
"good stuff"
)
factory
.
create_review_item
(
author
=
teacher_student
,
review
=
review
,
line
=
1
,
content
=
"oops"
)
db
.
session
.
add_all
([
review
])
# workers
executor
=
factory
.
create_worker
(
name
=
'executor'
,
url
=
"foo/url"
)
executor
.
secrets
.
append
(
Secret
(
'executor_secret'
,
"executor_secret"
,
time
.
current_time
()
+
timedelta
(
hours
=
1
)))
processing
=
factory
.
create_worker
(
name
=
'processing'
,
url
=
"bar/url"
)
processing
.
secrets
.
append
(
Secret
(
'processing_secret'
,
"processing_secret"
,
time
.
current_time
()
+
timedelta
(
hours
=
1
)))
executor
.
roles
.
append
(
tc1_workers
)
processing
.
roles
.
append
(
tc2_workers
)
db
.
session
.
add_all
([
executor
,
processing
])
# Commit to the DB
db
.
session
.
commit
()
portal/database/__init__.py
View file @
1f81087f
"""
Database layer module
"""
from
.models
import
User
,
Group
,
Project
,
ProjectState
,
ProjectConfig
,
Role
,
Course
,
Worker
,
\
Submission
,
SubmissionState
,
Review
,
ReviewItem
from
.exceptions
import
PortalDbError
"""
Database layer module
"""
from
.models
import
User
,
Group
,
Project
,
ProjectState
,
ProjectConfig
,
Role
,
Course
,
Worker
,
\
Submission
,
SubmissionState
,
Review
,
ReviewItem
from
.exceptions
import
PortalDbError
portal/database/exceptions.py
View file @
1f81087f
"""
Database specific exceptions
"""
from
sqlalchemy.exc
import
SQLAlchemyError
class
PortalDbError
(
SQLAlchemyError
):
"""Raised when a forbidden operation on portal's database is attempted.
"""
"""
Database specific exceptions
"""
from
sqlalchemy.exc
import
SQLAlchemyError
class
PortalDbError
(
SQLAlchemyError
):
"""Raised when a forbidden operation on portal's database is attempted.
"""
portal/database/mixins.py
View file @
1f81087f
"""
A collection of Mixins specifying common behaviour and attributes of database entities.
"""
import
datetime
from
sqlalchemy.ext.hybrid
import
hybrid_property
from
portal
import
db
# maybe use server_default, server_onupdate instead of default, onupdate; crashes tests
# (https://stackoverflow.com/questions/13370317/sqlalchemy-default-datetime)
# pylint: disable=too-few-public-methods
from
portal.tools.naming
import
sanitize_code_name
class
EntityBase
(
object
):
"""Entity mixin for the models
Attributes:
created_at(Column): Date when the entity has been created
updated_at(Column): Date when the entity has been updated
"""
EXCLUDED
=
[]
created_at
=
db
.
Column
(
db
.
TIMESTAMP
,
default
=
db
.
func
.
now
())
updated_at
=
db
.
Column
(
db
.
TIMESTAMP
,
default
=
db
.
func
.
now
(),
onupdate
=
db
.
func
.
now
())
class
NamedMixin
(
object
):
_name
=
db
.
Column
(
'name'
,
db
.
String
(
50
),
nullable
=
False
)
_codename
=
db
.
Column
(
'codename'
,
db
.
String
(
30
),
nullable
=
False
)
description
=
db
.
Column
(
db
.
Text
)
@
hybrid_property
def
codename
(
self
):
return
self
.
_codename
@
codename
.
setter
def
codename
(
self
,
value
:
str
):
"""Sets codename of the entity only the value is provided
Codename setter will set codename only if value is provided.
It will also set the name to the value, if the name is empty (None)
Codename has to be sanitized - it means that it is slugyfied and shortened
Args:
value: Codename of the entity
"""
if
value
:
self
.
_codename
=
sanitize_code_name
(
value
)
if
self
.
_name
is
None
:
self
.
_name
=
self
.
_codename
@
hybrid_property
def
name
(
self
)
->
str
:
"""Gets name
Returns (str): Name of the entity
"""
return
self
.
_name
@
name
.
setter
def
name
(
self
,
value
):
"""Sets the name of the entity
Args:
value(str): Name of the entity
"""
if
self
.
_codename
is
None
:
self
.
codename
=
value
self
.
_name
=
value
# pylint: enable=too-few-public-methods
"""
A collection of Mixins specifying common behaviour and attributes of database entities.
"""
from
typing
import
List
from
sqlalchemy.ext.hybrid
import
hybrid_property
from
portal
import
db
# maybe use server_default, server_onupdate instead of default, onupdate; crashes tests
# (https://stackoverflow.com/questions/13370317/sqlalchemy-default-datetime)
# pylint: disable=too-few-public-methods
from
portal.tools.meta
import
bound_update_class_var
from
portal.tools.naming
import
sanitize_code_name
def
_repr
(
instance
)
->
str
:
"""Repr helper function
Args:
instance: Instance of the model
Returns(str): String representation of the model
"""
if
isinstance
(
instance
,
EntityBase
):
params
=
{
key
:
getattr
(
instance
,
key
)
for
key
in
instance
.
base_params
()}
return
str
(
params
)
return
instance
.
__dict__
def
_str
(
instance
)
->
str
:
if
hasattr
(
instance
,
'log_name'
):
return
instance
.
log_name
return
_repr
(
instance
=
instance
)
class
EntityBase
:
"""Entity mixin for the models
Class attributes:
created_at(Column): Date when the entity has been created
updated_at(Column): Date when the entity has been updated
"""
BASE_PARAMS
=
[
'created_at'
,
'updated_at'
]
LISTABLE
=
[]
UPDATABLE
=
[]
@
classmethod
def
base_params
(
cls
)
->
List
[
str
]:
return
bound_update_class_var
(
cls
,
'BASE_PARAMS'
)
@
classmethod
def
updatable_params
(
cls
)
->
List
[
str
]:
return
bound_update_class_var
(
cls
,
'UPDATABLE'
)
@
classmethod
def
listable_params
(
cls
)
->
List
[
str
]:
return
bound_update_class_var
(
cls
,
'LISTABLE'
)
created_at
=
db
.
Column
(
db
.
TIMESTAMP
,
default
=
db
.
func
.
now
())
updated_at
=
db
.
Column
(
db
.
TIMESTAMP
,
default
=
db
.
func
.
now
(),
onupdate
=
db
.
func
.
now
())
def
__repr__
(
self
):
return
_repr
(
self
)
def
__str__
(
self
):
return
_str
(
self
)
class
NamedMixin
:
LISTABLE
=
[
'name'
,
'codename'
]
UPDATABLE
=
[
*
LISTABLE
,
'description'
]
BASE_PARAMS
=
[
*
UPDATABLE
]
_name
=
db
.
Column
(
'name'
,
db
.
String
(
50
),
nullable
=
False
)
_codename
=
db
.
Column
(
'codename'
,
db
.
String
(
30
),
nullable
=
False
)
description
=
db
.
Column
(
db
.
Text
)
@
hybrid_property
def
codename
(
self
):
return
self
.
_codename
@
codename
.
setter
def
codename
(
self
,
value
:
str
):
"""Sets codename of the entity only the value is provided
Codename setter will set codename only if value is provided.
It will also set the name to the value, if the name is empty (None)
Codename has to be sanitized - it means that it is slugyfied and shortened
Args:
value: Codename of the entity
"""
if
value
:
self
.
_codename
=
sanitize_code_name
(
value
)
if
self
.
_name
is
None
:
self
.
_name
=
self
.
_codename
@
hybrid_property
def
name
(
self
)
->
str
:
"""Gets name
Returns (str): Name of the entity
"""
return
self
.
_name
@
name
.
setter
def
name
(
self
,
value
):
"""Sets the name of the entity
Args:
value(str): Name of the entity
"""
if
self
.
_codename
is
None
:
self
.
codename
=
value
self
.
_name
=
value
# pylint: enable=too-few-public-methods
portal/database/models.py
View file @
1f81087f
...
...
@@ -7,10 +7,12 @@ import logging
import
uuid
from
typing
import
List
import
sqlalchemy
as
sa
from
flask_sqlalchemy
import
BaseQuery
from
sqlalchemy
import
event
from
sqlalchemy.ext.associationproxy
import
association_proxy
from
sqlalchemy.ext.hybrid
import
hybrid_property
from
sqlalchemy_continuum
import
make_versioned
from
werkzeug.security
import
check_password_hash
,
generate_password_hash
from
portal
import
db
...
...
@@ -19,34 +21,11 @@ from portal.database.mixins import EntityBase, NamedMixin
from
portal.database.types
import
JSONEncodedDict
,
YAMLEncodedDict
from
portal.tools
import
time
from
portal.tools.time
import
normalize_time
from
sqlalchemy_continuum
import
make_versioned
import
sqlalchemy
as
sa
make_versioned
(
user_cls
=
None
)
log
=
logging
.
getLogger
(
__name__
)
def
_repr
(
instance
)
->
str
:
"""Repr helper function
Args:
instance: Instance of the model
Returns(str): String representation of the model
"""
no_include
=
{
'password_hash'
,
'review'
,
'course'
,
'user'
,
'project'
,
'group'
,
'role'
,
'review_items'
,
'client'
,
'EXCLUDED'
,
'query'
,
'groups'
,
'roles'
,
'secrets'
,
'courses'
,
'projects'
}
if
'EXCLUDED'
in
vars
(
instance
.
__class__
):
no_include
.
update
(
instance
.
__class__
.
EXCLUDED
)
result
=
f
"
{
instance
.
__class__
.
__name__
}
: "
for
key
in
dir
(
instance
):
if
not
key
.
startswith
(
"_"
)
and
key
not
in
no_include
:
value
=
getattr
(
instance
,
key
)
if
not
callable
(
value
):
result
+=
f
"
{
key
}
=
{
value
}
"
return
result
class
ClientType
(
enum
.
Enum
):
"""All known client types
"""
...
...
@@ -54,12 +33,13 @@ class ClientType(enum.Enum):
WORKER
=
'worker'
class
Client
(
db
.
Model
):
class
Client
(
db
.
Model
,
EntityBase
):
"""Client entity model
"""
__tablename__
=
'client'
id
=
db
.
Column
(
db
.
String
(
length
=
36
),
default
=
lambda
:
str
(
uuid
.
uuid4
()),
primary_key
=
True
)
LISTABLE
=
[
'id'
,
'type'
,
'codename'
]
UPDATABLE
=
[
'codename'
]
id
=
db
.
Column
(
db
.
String
(
length
=
36
),
default
=
lambda
:
str
(
uuid
.
uuid4
()),
primary_key
=
True
)
type
=
db
.
Column
(
db
.
Enum
(
ClientType
,
name
=
'ClientType'
),
nullable
=
False
)
codename
=
db
.
Column
(
db
.
String
(
30
),
unique
=
True
,
nullable
=
False
)
secrets
=
db
.
relationship
(
'Secret'
,
back_populates
=
'client'
,
uselist
=
True
,
...
...
@@ -141,9 +121,10 @@ class Client(db.Model):
class
Secret
(
db
.
Model
,
EntityBase
):
__tablename__
=
'secret'
EXCLUDED
=
[
'value'
,
'client'
]
id
=
db
.
Column
(
db
.
String
(
length
=
36
),
default
=
lambda
:
str
(
uuid
.
uuid4
()),
primary_key
=
True
)
UPDATABLE
=
[
'name'
,
'expires_at'
]
BASE_PARAMS
=
[
'id'
,
*
UPDATABLE
]
id
=
db
.
Column
(
db
.
String
(
length
=
36
),
default
=
lambda
:
str
(
uuid
.
uuid4
()),
primary_key
=
True
)
name
=
db
.
Column
(
db
.
String
(
40
),
nullable
=
False
)
value
=
db
.
Column
(
db
.
String
(
120
))
expires_at
=
db
.
Column
(
db
.
TIMESTAMP
,
nullable
=
True
)
...
...
@@ -176,7 +157,7 @@ class Secret(db.Model, EntityBase):
return
time
.
normalize_time
(
self
.
expires_at
)
<
time
.
current_time
()
class
User
(
EntityBase
,
Client
):
class
User
(
Client
):
"""User entity model
Attributes:
...
...
@@ -192,10 +173,11 @@ class User(EntityBase, Client):
review_items: Collection of review_items created by user
groups: Collection of groups the user belongs to
"""
EXCLUDED
=
[
'password_hash'