Unverified Commit 7c079b31 authored by Peter Stanko's avatar Peter Stanko
Browse files

Submissions list - single query

parent c06ebf39
Loading
Loading
Loading
Loading
+0 −5
Original line number Diff line number Diff line
@@ -364,11 +364,6 @@ sú maximálnou hodnotou povolení jeho rolí.
* Komponentom sa prístupové práva nekontrolujú.

=== Zoznam povolení
* archive_projects:
** povoľuje archiváciu projektov v kurze
** typicky vhodná pre vlastníka kurzu
** zatiaľ nevyužitá hodnota (archivácia projektov nie je implementovaná)

* create_submissions:
** povoľuje vytváranie odovzdaní pod vlastnou identitou s ohľadom na časové obmedzenia projektov (submissions allowed from-to)
** typické povolenie pre študenta
+0 −2
Original line number Diff line number Diff line
@@ -44,7 +44,6 @@ PERM_TEACHER = dict(
    handle_notes_access_token=False,
    write_groups=True,
    write_projects=True,
    archive_projects=True,
    create_submissions_other=True,
    create_submissions=True,
    resubmit_submissions=True,
@@ -68,7 +67,6 @@ PERM_OWNER = dict(
    write_roles=True,
    write_groups=True,
    write_projects=True,
    archive_projects=True,
    resubmit_submissions=True,
    evaluate_submissions=True,
    read_submissions_all=True,
+12 −2
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ 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.sql.base import ImmutableColumnCollection
from werkzeug.security import check_password_hash, generate_password_hash

from portal import db
@@ -93,7 +94,7 @@ class Client(db.Model):
        Returns: Query for the permissions
        """
        from . import queries
        return queries.client_permissions_for_course(self, course)
        return queries.client_roles_in_course(self, course)

    def get_permissions_for_course(self, course: 'Course') -> List['Role']:
        """Gets list of permissions for the course
@@ -698,6 +699,15 @@ class Role(db.Model, EntityBase, NamedMixin):
        'course.id', ondelete='cascade'), nullable=False)
    course = db.relationship("Course", back_populates="roles", uselist=False)

    PERMISSIONS = ('create_submissions', 'create_submissions_other',
                   'view_course_limited', 'view_course_full', 'update_course',
                   'handle_notes_access_token', 'write_roles', 'write_groups',
                   'write_projects', 'resubmit_submissions', 'evaluate_submissions',
                   'read_submissions_all', 'read_submissions_groups',
                   'read_submissions_own', 'read_reviews_all', 'read_reviews_groups',
                   'read_reviews_own', 'write_reviews_all', 'write_reviews_group',
                   'write_reviews_own')

    # all default to False, explicit setting via role.set_permissions is
    # required
    view_course_limited = db.Column(db.Boolean, default=False, nullable=False)
@@ -709,7 +719,6 @@ class Role(db.Model, EntityBase, NamedMixin):
    write_roles = db.Column(db.Boolean, default=False, nullable=False)
    write_groups = db.Column(db.Boolean, default=False, nullable=False)
    write_projects = db.Column(db.Boolean, default=False, nullable=False)
    archive_projects = db.Column(db.Boolean, default=False, nullable=False)

    create_submissions = db.Column(db.Boolean, default=False, nullable=False)
    create_submissions_other = db.Column(db.Boolean, default=False, nullable=False)
@@ -876,6 +885,7 @@ class Submission(db.Model, EntityBase):
    points = db.Column(db.Numeric(precision=10, scale=4), default=0.0)
    result = db.Column(db.String(length=10), default='none')
    course = association_proxy('project', 'course')
    course_id = association_proxy('project', 'course_id')

    ALLOWED_TRANSITIONS = {
        SubmissionState.CANCELLED: [SubmissionState.READY, SubmissionState.CREATED,
+82 −5
Original line number Diff line number Diff line
import datetime
import logging

from flask_sqlalchemy import BaseQuery
from sqlalchemy import func

from portal import db
from portal.database.models import Client, ClientType, Course, Group, Project, Role, Submission, \
    SubmissionState, User, Worker

log = logging.getLogger(__name__)


def _get_class_based_on_client_type(client_type):
    klass = Client
@@ -13,11 +18,6 @@ def _get_class_based_on_client_type(client_type):
    return klass


def client_permissions_for_course(client, course: Course = None) -> BaseQuery:
    return Role.query.filter_by(course=course) \
        .join(Role.clients).filter(Client.id == client.id)


def client_courses(client) -> BaseQuery:
    return Course.query.join(Course.roles).join(Role.clients).filter(Client.id == client.id)

@@ -43,6 +43,7 @@ def user_projects(user: User, course: Course = None) -> BaseQuery:


def user_projects_by_course(user, course: 'Course') -> BaseQuery:
    # TODO: To query
    groups = user_groups_in_course(user, course=course).all()
    pids = []
    for group in groups:
@@ -139,3 +140,79 @@ def cancelled_submissions_for_deletion(time_period: datetime.timedelta) -> BaseQ
    return Submission.query.filter(
        Submission.state.in_(SubmissionState.CANCELLED, SubmissionState.ABORTED) &
        Submission.created_at < delete_from)


def effective_permissions_for_course(client: Client, course: Course) -> BaseQuery:
    def _m(name): return func.max(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)
    return result


def effective_permissions_for_client(client: Client) -> BaseQuery:
    def _m(name): return func.max(getattr(Role, name)).label(name)
    props = [_m(prop) for prop in Role.PERMISSIONS]
    query = db.session.query(Role.course_id, *props)\
        .filter(Role.clients.contains(client))\
        .group_by(Role.course_id)
    return query


def list_submissions_for_user(client: User, user_ids=None,
                              course_id=None, role_ids=None,
                              group_ids=None, project_ids=None,
                              states=None, archived=False) -> BaseQuery:
    query: BaseQuery = Submission.query

    if user_ids:
        user_filter = (User.id.in_(user_ids)) | (User.codename.in_(user_ids))
        query = query.join(Submission.user).filter(user_filter)

    if not archived:
        query = query.filter(Submission.state != SubmissionState.ARCHIVED)

    if states:
        query = query.filter(Submission.state.in_(states))

    if course_id:
        query = _filter_by_course_groups_roles(query, course_id, group_ids, project_ids, role_ids)

    if not client.is_admin:
        query = _filter_by_client(query, client)

    log.debug(f"[QUERY] find submissions: {query}")
    return query


def _filter_by_course_groups_roles(query, course_id, group_ids, project_ids, role_ids):
    course_filter = (Course.id == course_id) | (Course.codename == course_id)
    query = query.join(Submission.project).join(Project.course).filter(course_filter)

    if project_ids:
        project_filter = (Project.id.in_(project_ids) | (Project.codename.in_(project_ids)))
        query = query.filter(project_filter)

    if role_ids:
        role_filter = (Role.id.in_(role_ids) | (Role.codename.in_(role_ids)))
        query = query.join(Course.roles).filter(role_filter)

    if group_ids:
        group_filter = (Group.id.in_(group_ids) | (Group.codename.in_(group_ids)))
        query = query.join(Course.groups).filter(group_filter)

    return query


def _filter_by_client(query: BaseQuery, client: User) -> BaseQuery:
    eff_client = effective_permissions_for_client(client).subquery()
    check_course_id = eff_client.c.course_id == Project.course_id
    from sqlalchemy import true
    check_read_all = (check_course_id & (eff_client.c.read_submissions_all == true()))
    check_read_own = (check_course_id & (eff_client.c.read_submissions_own == true()) &
                      (Submission.user_id == client.id))
    query = query.join(Submission.project)\
        .join(eff_client, eff_client.c.course_id == Project.course_id)\
        .filter(check_read_all | check_read_own)
    return query
+9 −3
Original line number Diff line number Diff line
@@ -103,7 +103,13 @@ class SubmissionsFacade(GeneralCRUDFacade):
        return self._service(submission).cancel_submission()

    def find_all(self, *args, **kwargs):
        role_ids = self._request.args.get('roles')
        group_ids = self._request.args.get('groups')
        return super(SubmissionsFacade, self).find_all(*args, **kwargs,
        role_ids = self._request.args.getlist('roles')
        group_ids = self._request.args.getlist('groups')
        project_ids = self._request.args.getlist('projects')
        states = self._request.args.getlist('states')
        user_ids = self._request.args.getlist('users')
        course = self._request.args.get('course')
        return super(SubmissionsFacade, self).find_all(*args, **kwargs, client=self.client,
                                                       course_id=course, states=states,
                                                       user_ids=user_ids, project_ids=project_ids,
                                                       role_ids=role_ids, group_ids=group_ids)
Loading