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
c7c7bb6e
Verified
Commit
c7c7bb6e
authored
Apr 13, 2019
by
Peter Stanko
Browse files
Schemas - namespace added + _in expression
parent
249fa6d6
Pipeline
#31372
passed with stage
in 7 minutes and 12 seconds
Changes
5
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
app.py
View file @
c7c7bb6e
...
...
@@ -24,6 +24,7 @@ devel_cli = AppGroup('devel', help='Development management')
submissions_cli
=
AppGroup
(
'submissions'
,
help
=
'Submissions management'
)
projects_cli
=
AppGroup
(
'projects'
,
help
=
'Projects management'
)
cfg_cli
=
AppGroup
(
'cfg'
,
help
=
'Configuration management'
)
management_cli
=
AppGroup
(
'mgmt'
,
help
=
'Portal management'
)
app
:
Flask
=
create_app
()
...
...
@@ -33,20 +34,45 @@ app.cli.add_command(courses_cli)
app
.
cli
.
add_command
(
devel_cli
)
app
.
cli
.
add_command
(
submissions_cli
)
app
.
cli
.
add_command
(
projects_cli
)
app
.
cli
.
add_command
(
management_cli
)
manager
=
DataManagement
(
app
,
db
)
celery
=
portal
.
get_celery
(
app
)
"""
MANAGEMENT
"""
@
cfg_cli
.
command
(
'env'
,
help
=
'Print flask configuration'
)
@
click
.
pass_context
def
cli_print_cfg
(
ctx
):
@
management_cli
.
command
(
'archive-submissions'
)
def
cli_mgmt_arch_submissions
():
print
(
"[MGMT] Archiving submissions - NOW"
)
from
portal.async_celery.periodic_tasks
import
archive_submissions_in_arch_project
with
app
.
app_context
():
archive_submissions_in_arch_project
()
@
management_cli
.
command
(
'delete-cancelled'
)
def
cli_mgmt_arch_submissions
():
print
(
"[MGMT] Deleting cancelled submissions - NOW"
)
from
portal.async_celery.periodic_tasks
import
delete_cancelled_submissions
with
app
.
app_context
():
delete_cancelled_submissions
()
@
management_cli
.
command
(
'env'
,
help
=
'Print flask configuration'
)
def
cli_print_cfg
():
log
.
info
(
"[CFG] Configuration:"
)
for
(
key
,
val
)
in
app
.
config
.
items
():
print
(
f
"
{
key
}
:
{
val
}
"
)
"""
DATA
"""
@
devel_cli
.
command
(
'run'
,
help
=
'Runs the devel server with initializes db'
)
@
click
.
option
(
'-p'
,
'--port'
,
default
=
8000
)
@
click
.
pass_context
...
...
@@ -153,8 +179,7 @@ def cli_courses_list():
manager
.
list_courses
()
@
courses_cli
.
command
(
'create-role'
,
help
=
"Creates role by it's type in the course"
)
@
courses_cli
.
command
(
'create-role'
,
help
=
"Creates role by it's type in the course"
)
@
click
.
argument
(
'course'
)
@
click
.
argument
(
'type'
)
@
click
.
argument
(
'name'
,
required
=
False
)
...
...
@@ -163,8 +188,7 @@ def cli_course_role_creates(course, type, name=None):
manager
.
create_role
(
course
,
role_type
=
type
,
name
=
name
)
@
courses_cli
.
command
(
'is-import-teachers'
,
help
=
"Import teachers for course"
)
@
courses_cli
.
command
(
'is-import-teachers'
,
help
=
"Import teachers for course"
)
@
click
.
argument
(
'course'
)
@
click
.
argument
(
'role_name'
)
def
cli_course_role_creates
(
course
,
role_name
):
...
...
@@ -172,6 +196,14 @@ def cli_course_role_creates(course, role_name):
manager
.
is_import_users
(
course
,
role_name
=
role_name
,
users_type
=
'teachers'
)
@
courses_cli
.
command
(
'is-import-students'
,
help
=
"Import students for course"
)
@
click
.
argument
(
'course'
)
@
click
.
argument
(
'role_name'
)
def
cli_course_role_creates
(
course
,
role_name
):
log
.
info
(
f
"[CMD] Import students ->
\"
{
role_name
}
\"
in the
\"
{
course
}
\"
"
)
manager
.
is_import_users
(
course
,
role_name
=
role_name
,
users_type
=
'students'
)
"""
PROJECTS
"""
...
...
portal/async_celery/periodic_tasks.py
View file @
c7c7bb6e
...
...
@@ -16,6 +16,7 @@ def setup_periodic_tasks(sender, **kwargs):
archive_submissions_in_arch_project
.
s
(),
name
=
"Archive all submissions in archived project"
)
@
celery_app
.
task
def
abort_non_proc_submissions
(
**
kwargs
):
from
portal.async_celery.async_manager
import
AsyncManager
...
...
portal/database/models.py
View file @
c7c7bb6e
...
...
@@ -351,9 +351,13 @@ class Course(db.Model, EntityBase, NamedMixin):
db
.
UniqueConstraint
(
'codename'
,
name
=
'course_unique_codename'
),
)
@
hybrid_property
def
namespace
(
self
)
->
str
:
return
self
.
codename
@
property
def
log_name
(
self
):
return
f
"
{
self
.
id
}
(
{
self
.
codenam
e
}
)"
return
f
"
{
self
.
id
}
(
{
self
.
namespac
e
}
)"
def
is_api_enabled
(
self
)
->
bool
:
return
self
.
notes_access_token
is
not
None
and
self
.
notes_access_token
!=
""
...
...
@@ -459,9 +463,13 @@ class Project(db.Model, EntityBase, NamedMixin):
db
.
UniqueConstraint
(
'course_id'
,
'codename'
,
name
=
'p_course_unique_name'
),
)
@
hybrid_property
def
namespace
(
self
)
->
str
:
return
f
"
{
self
.
course
.
codename
}
/
{
self
.
codename
}
"
@
property
def
log_name
(
self
):
return
f
"
{
self
.
id
}
(
{
self
.
course
.
codename
}
/
{
self
.
codenam
e
}
)"
def
log_name
(
self
)
->
str
:
return
f
"
{
self
.
id
}
(
{
self
.
namespac
e
}
)"
def
state
(
self
,
timestamp
=
time
.
current_time
())
->
ProjectState
:
"""Gets project state based on the timestamp
...
...
@@ -798,9 +806,13 @@ class Group(db.Model, EntityBase, NamedMixin):
db
.
UniqueConstraint
(
'course_id'
,
'codename'
,
name
=
'g_course_unique_name'
),
)
@
hybrid_property
def
namespace
(
self
)
->
str
:
return
f
"
{
self
.
course
.
codename
}
/
{
self
.
codename
}
"
@
property
def
log_name
(
self
):
return
f
"
{
self
.
id
}
(
{
self
.
course
.
codename
}
/
{
self
.
codenam
e
}
)"
return
f
"
{
self
.
id
}
(
{
self
.
namespac
e
}
)"
def
__init__
(
self
,
course
:
Course
,
name
:
str
=
None
,
description
:
str
=
None
,
codename
:
str
=
None
):
...
...
@@ -896,6 +908,10 @@ class Submission(db.Model, EntityBase):
}
return
params
@
hybrid_property
def
namespace
(
self
)
->
str
:
return
f
"
{
self
.
project
.
namespace
}
/
{
self
.
user
.
codename
}
/
{
self
.
created_at
}
"
@
property
def
log_name
(
self
):
return
f
"
{
self
.
id
}
(
{
self
.
course
.
codename
}
/
{
self
.
project
.
codename
}
for "
\
...
...
portal/database/queries.py
View file @
c7c7bb6e
...
...
@@ -136,9 +136,9 @@ def cancelled_submissions_for_deletion(time_period: datetime.timedelta) -> BaseQ
Returns(BaseQuery): Query to get the result
"""
delete_from
=
datetime
.
datetime
.
now
()
-
time_period
return
Submission
.
query
.
filter
(
Submission
.
state
.
in_
([
Submission
S
tate
.
CANCELLED
,
SubmissionState
.
ABORTED
]
)
&
Submission
.
created_at
<
delete_from
)
states
=
[
SubmissionState
.
CANCELLED
,
SubmissionState
.
ABORTED
]
return
Submission
.
query
.
filter
(
Submission
.
s
tate
.
in_
(
states
)
&
(
Submission
.
created_at
<
delete_from
)
)
def
effective_permissions_for_course
(
client
:
Client
,
course
:
Course
)
->
BaseQuery
:
...
...
@@ -147,8 +147,10 @@ def effective_permissions_for_course(client: Client, course: Course) -> BaseQuer
return
agg_func
(
getattr
(
Role
,
name
)).
label
(
name
)
props
=
[
_m
(
prop
)
for
prop
in
Role
.
PERMISSIONS
]
result
=
db
.
session
.
query
(
*
props
).
filter
(
Role
.
course
==
course
).
join
(
Role
.
clients
).
filter
(
Client
.
id
==
client
.
id
)
result
=
db
.
session
.
query
(
*
props
)
\
.
filter
(
Role
.
course
==
course
)
\
.
join
(
Role
.
clients
)
\
.
filter
(
Client
.
id
==
client
.
id
)
return
result
...
...
portal/rest/schemas.py
View file @
c7c7bb6e
...
...
@@ -2,6 +2,7 @@
Schemas used to serialize/deserialize and validate of the models in the DB
"""
import
functools
from
typing
import
List
from
marshmallow
import
Schema
,
ValidationError
,
fields
,
validates_schema
from
marshmallow_enum
import
EnumField
...
...
@@ -9,22 +10,26 @@ from marshmallow_enum import EnumField
from
portal.database.models
import
ClientType
,
ProjectState
,
SubmissionState
,
WorkerState
def
_in
(
prefix
,
params
)
->
List
[
str
]:
return
[
f
'
{
prefix
}
.
{
p
}
'
for
p
in
params
]
class
NestedCollection
:
ENTITIES
=
{
'Role'
:
(
'id'
,
'codename'
,
'course.id'
,
'course.codename'
),
'Group'
:
(
'id'
,
'codename'
,
'course.id'
,
'course.codename'
),
'Project'
:
(
'id'
,
'codename'
,
'course.id'
,
'state'
,
'submit_configurable'
),
'Course'
:
(
'id'
,
'codename'
),
DEFAULTS
=
(
'id'
,
'codename'
,
'namespace'
)
NESTED_REPR
=
{
'Role'
:
(
*
DEFAULTS
,
*
_in
(
'course'
,
DEFAULTS
)),
'Group'
:
(
*
DEFAULTS
,
*
_in
(
'course'
,
DEFAULTS
)),
'Project'
:
(
*
DEFAULTS
,
'state'
,
'submit_configurable'
,
*
_in
(
'course'
,
DEFAULTS
)),
'Course'
:
(
*
DEFAULTS
,),
'User'
:
(
'id'
,
'username'
,
'uco'
,
'codename'
,
'name'
),
'Worker'
:
(
'id'
,
'codename'
,
'name'
),
'Client'
:
(
'id'
,
'type'
,
'name'
,
'codename'
),
'Component'
:
(
'id'
,
'name'
,
'type'
),
'Submission'
:
(
'id'
,
'state'
,
'user.id'
,
'user.username'
,
'user.name'
,
'project.id'
,
'project.codename'
,
'course.id'
,
'course.codename'
,
'created_at'
,
'updated_at'
,
'result'
,
'points'
),
'ReviewItem'
:
(
'id'
,
'review.id'
,
'line'
,
'content'
,
'file'
,
'user.id'
),
'Submission'
:
(
'id'
,
'state'
,
'created_at'
,
'updated_at'
,
'result'
,
'points'
,
*
_in
(
'user'
,
[
'username'
,
'codename'
,
'name'
,
'uco'
,
'id'
]),
*
_in
(
'project'
,
DEFAULTS
),
*
_in
(
'course'
,
DEFAULTS
)),
'ReviewItem'
:
(
'id'
,
'review.id'
,
'line'
,
'content'
,
'file'
,
*
_in
(
'user'
,
[
'id'
,
'username'
,
'uco'
,
'name'
])),
'Secret'
:
(
'id'
,
'name'
,
'expires_at'
)
}
...
...
@@ -67,7 +72,7 @@ class NestedCollection:
@
property
def
entities
(
self
):
return
self
.
__class__
.
ENTITIES
return
self
.
__class__
.
NESTED_REPR
def
get
(
self
,
name
):
return
self
.
collection
[
name
]
...
...
@@ -90,6 +95,7 @@ class NamedSchema(object):
name
=
fields
.
Str
(
required
=
True
)
codename
=
fields
.
Str
(
required
=
True
)
description
=
fields
.
Str
(
required
=
False
,
allow_none
=
True
)
namespace
=
fields
.
Str
(
required
=
False
,
allow_none
=
True
)
class
UserSchema
(
BaseSchema
,
Schema
):
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment