Verified Commit c7c7bb6e authored by Peter Stanko's avatar Peter Stanko
Browse files

Schemas - namespace added + _in expression

parent 249fa6d6
Pipeline #31372 passed with stage
in 7 minutes and 12 seconds
......@@ -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
"""
......
......@@ -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
......
......@@ -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.codename})"
return f"{self.id} ({self.namespace})"
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.codename})"
def log_name(self) -> str:
return f"{self.id} ({self.namespace})"
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.codename})"
return f"{self.id} ({self.namespace})"
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 " \
......
......@@ -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_([SubmissionState.CANCELLED, SubmissionState.ABORTED]) &
Submission.created_at < delete_from)
states = [SubmissionState.CANCELLED, SubmissionState.ABORTED]
return Submission.query.filter(Submission.state.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
......
......@@ -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):
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment