Unverified Commit aab29ca1 authored by Peter Stanko's avatar Peter Stanko
Browse files

New migrations

parent d5e29371
"""empty message
Revision ID: 3401e67c571b
Revises: c21ce0e65d64
Create Date: 2018-09-11 09:45:04.606844
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '3401e67c571b'
down_revision = 'c21ce0e65d64'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('projectConfig', sa.Column('test_files_subdir', sa.String(length=50), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('projectConfig', 'test_files_subdir')
# ### end Alembic commands ###
"""empty message
Revision ID: 8c3c07862a02
Revises: 0ad81fc4487e
Create Date: 2018-09-04 14:24:06.598289
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '8c3c07862a02'
down_revision = '0ad81fc4487e'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('submission', sa.Column('async_task_id', sa.String(length=36), nullable=True))
op.drop_column('submission', 'process_task_id')
op.drop_column('submission', 'storage_task_id')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('submission', sa.Column('storage_task_id', sa.VARCHAR(length=36), autoincrement=False, nullable=True))
op.add_column('submission', sa.Column('process_task_id', sa.VARCHAR(length=36), autoincrement=False, nullable=True))
op.drop_column('submission', 'async_task_id')
# ### end Alembic commands ###
"""empty message
Revision ID: 0ad81fc4487e
Revision ID: bd2121c14eaa
Revises:
Create Date: 2018-09-02 17:09:55.862167
Create Date: 2018-09-16 16:10:06.408159
"""
from alembic import op
import sqlalchemy as sa
import portal.database.types
# revision identifiers, used by Alembic.
revision = '0ad81fc4487e'
revision = 'bd2121c14eaa'
down_revision = None
branch_labels = None
depends_on = None
......@@ -21,7 +22,9 @@ def upgrade():
op.create_table('client',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('type', sa.Enum('USER', 'WORKER', name='ClientType'), nullable=False),
sa.PrimaryKeyConstraint('id')
sa.Column('codename', sa.String(length=30), nullable=False),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('codename')
)
op.create_table('course',
sa.Column('created_at', sa.TIMESTAMP(), nullable=True),
......@@ -87,27 +90,23 @@ def upgrade():
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('uco', sa.Integer(), nullable=True),
sa.Column('email', sa.String(length=50), nullable=False),
sa.Column('username', sa.String(length=30), nullable=False),
sa.Column('name', sa.String(length=50), nullable=True),
sa.Column('is_admin', sa.Boolean(), nullable=True),
sa.Column('password_hash', sa.String(length=120), nullable=True),
sa.ForeignKeyConstraint(['id'], ['client.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('email'),
sa.UniqueConstraint('username')
sa.UniqueConstraint('email')
)
op.create_table('worker',
sa.Column('created_at', sa.TIMESTAMP(), nullable=True),
sa.Column('updated_at', sa.TIMESTAMP(), nullable=True),
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('name', sa.String(length=30), nullable=False),
sa.Column('url', sa.String(length=250), nullable=False),
sa.Column('tags', sa.Text(), nullable=True),
sa.Column('portal_secret', sa.String(length=64), nullable=True),
sa.Column('state', sa.Enum('CREATED', 'READY', 'STOPPED', name='WorkerState'), nullable=False),
sa.ForeignKeyConstraint(['id'], ['client.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('name')
sa.PrimaryKeyConstraint('id')
)
op.create_table('clients_roles',
sa.Column('client_id', sa.String(length=36), nullable=True),
......@@ -122,6 +121,7 @@ def upgrade():
sa.Column('project_id', sa.String(length=36), nullable=False),
sa.Column('submissions_cancellation_period', sa.Integer(), nullable=True),
sa.Column('test_files_source', sa.String(length=600), nullable=True),
sa.Column('test_files_subdir', sa.String(length=50), nullable=True),
sa.Column('test_files_commit_hash', sa.String(length=64), nullable=True),
sa.Column('file_whitelist', sa.Text(), nullable=True),
sa.Column('pre_submit_script', sa.Text(), nullable=True),
......@@ -158,6 +158,7 @@ def upgrade():
sa.Column('read_projects', sa.Boolean(), nullable=False),
sa.Column('archive_projects', sa.Boolean(), nullable=False),
sa.Column('create_submissions', sa.Boolean(), nullable=False),
sa.Column('create_submissions_other', sa.Boolean(), nullable=False),
sa.Column('resubmit_submissions', sa.Boolean(), nullable=False),
sa.Column('evaluate_submissions', sa.Boolean(), nullable=False),
sa.Column('read_submissions_all', sa.Boolean(), nullable=False),
......@@ -174,22 +175,21 @@ def upgrade():
sa.PrimaryKeyConstraint('id')
)
op.create_table('submission',
sa.Column('created_at', sa.TIMESTAMP(), nullable=True),
sa.Column('updated_at', sa.TIMESTAMP(), nullable=True),
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('scheduled_for', sa.TIMESTAMP(timezone=True), nullable=True),
sa.Column('parameters', sa.Text(), nullable=False),
sa.Column('state', sa.Enum('CREATED', 'READY', 'QUEUED', 'IN_PROGRESS', 'FINISHED', 'CANCELLED', 'ABORTED', 'ARCHIVED', name='SubmissionState'), nullable=False),
sa.Column('note', sa.Text(), nullable=True),
sa.Column('source_hash', sa.String(length=64), nullable=True),
sa.Column('user_id', sa.String(length=36), nullable=False),
sa.Column('project_id', sa.String(length=36), nullable=False),
sa.Column('storage_task_id', sa.String(length=36), nullable=True),
sa.Column('process_task_id', sa.String(length=36), nullable=True),
sa.ForeignKeyConstraint(['project_id'], ['project.id'], ondelete='cascade'),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='cascade'),
sa.PrimaryKeyConstraint('id')
)
sa.Column('created_at', sa.TIMESTAMP(), nullable=True),
sa.Column('updated_at', sa.TIMESTAMP(), nullable=True),
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('scheduled_for', sa.TIMESTAMP(timezone=True), nullable=True),
sa.Column('parameters', portal.database.types.JSONEncodedDict(), nullable=True),
sa.Column('state', sa.Enum('CREATED', 'READY', 'QUEUED', 'IN_PROGRESS', 'FINISHED', 'CANCELLED', 'ABORTED', 'ARCHIVED', name='SubmissionState'), nullable=False),
sa.Column('note', portal.database.types.JSONEncodedDict(), nullable=True),
sa.Column('source_hash', sa.String(length=64), nullable=True),
sa.Column('user_id', sa.String(length=36), nullable=False),
sa.Column('project_id', sa.String(length=36), nullable=False),
sa.Column('async_task_id', sa.String(length=36), nullable=True),
sa.ForeignKeyConstraint(['project_id'], ['project.id'], ondelete='cascade'),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ondelete='cascade'),
sa.PrimaryKeyConstraint('id')
)
op.create_table('users_groups',
sa.Column('user_id', sa.String(length=36), nullable=True),
sa.Column('group_id', sa.String(length=36), nullable=True),
......
"""empty message
Revision ID: c21ce0e65d64
Revises: 8c3c07862a02
Create Date: 2018-09-10 20:45:32.588254
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'c21ce0e65d64'
down_revision = '8c3c07862a02'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('submission', 'parameters',
existing_type=sa.TEXT(),
nullable=True)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('submission', 'parameters',
existing_type=sa.TEXT(),
nullable=False)
# ### end Alembic commands ###
......@@ -3,42 +3,25 @@ Models module where all of the models are specified
"""
import enum
import json
import logging
import uuid
from typing import List
import sqlalchemy
from flask_sqlalchemy import BaseQuery
from sqlalchemy import TypeDecorator, event
from sqlalchemy import event
from sqlalchemy.ext.hybrid import hybrid_property
from werkzeug.security import check_password_hash, generate_password_hash
from portal import db
from portal.database.exceptions import PortalDbError
from portal.database.mixins import EntityBase, NamedMixin
from portal.database.types import JSONEncodedDict
from portal.tools import time
from portal.tools.time import normalize_time
log = logging.getLogger(__name__)
class JSONEncodedDict(TypeDecorator):
"Represents an immutable structure as a json-encoded string."
impl = sqlalchemy.Text
def process_bind_param(self, value, dialect):
if value is not None:
value = json.dumps(value)
return value
def process_result_value(self, value, dialect):
if value is not None:
value = json.loads(value)
return value
def _repr(instance) -> str:
"""Repr helper function
Args:
......
import json
import sqlalchemy
from sqlalchemy import TypeDecorator
class JSONEncodedDict(TypeDecorator):
"Represents an immutable structure as a json-encoded string."
impl = sqlalchemy.Text
def process_bind_param(self, value, dialect):
if value is not None:
value = json.dumps(value)
return value
def process_result_value(self, value, dialect):
if value is not None:
value = json.loads(value)
return value
\ No newline at end of file
......@@ -302,7 +302,7 @@ class SecretSchema(BaseSchema, Schema):
"""
name = fields.Str()
expires_at = fields.LocalDateTime(dump_only=True)
client = NESTED('permissions')
client = NESTED('client')
class CourseImportConfigSchema(Schema):
......@@ -346,15 +346,6 @@ class ProjectImportSchema(Schema):
source_project = fields.Str()
class NotificationSendToSchema(Schema):
"""Notification Send To Schema
"""
role = fields.Str()
users = fields.List(fields.Str())
group = fields.Str()
course = fields.Str()
class SubmissionResultTemplateSchema(Schema):
"""Submission Result Template Schema
"""
......
......@@ -68,7 +68,7 @@ def login_secret(identifier: str, secret: str) -> Client:
def validate_gitlab_token(token: str, username: str, throws: bool = True):
"""Validates gitlab access token using the gitlab permissions
"""Validates gitlab access token using the gitlab client
Args:
token(str): Gitlab access token
username(str): Username
......
......@@ -68,7 +68,7 @@ class DataMissingError(PortalAPIError):
resource(str): Name of the resource
"""
message = dict(action=action, resource=resource,
message='Data is missing!')
message='Data is missing')
super(DataMissingError, self).__init__(code=400, message=message)
......@@ -96,9 +96,7 @@ class ResourceNotFoundError(PortalAPIError):
class UnauthorizedError(PortalAPIError):
def __init__(self, note=None):
message = dict(
message=f"You are not authorized.",
)
message = dict(message=f"You are not authorized.",)
if note:
message['note'] = note
......@@ -108,7 +106,7 @@ class UnauthorizedError(PortalAPIError):
class ForbiddenError(PortalAPIError):
# could use a resource identification (like 404)
def __init__(self, client=None, note=None):
user_message = f"Forbidden for {client.type}: {client.id} ({client.codename})!" if client else \
user_message = f"Forbidden for {client.type}: {client.id} ({client.codename})" if client else \
'Forbidden action.'
message = dict(uid=client.id, message=user_message)
if note:
......
......@@ -182,7 +182,6 @@ class GroupService:
"""List all groups
Args:
course(Course): Course instance
permissions: Client instance
Returns(list): List of all groups
......
......@@ -189,14 +189,14 @@ class PermissionsService:
return self._course
def get_effective_permissions(self, course_id: str = None) -> dict:
"""Gets effective permissions for a permissions in course.
If no course is specified, returns the permissions's
"""Gets effective permissions for a course in course.
If no course is specified, returns the client's
effective permissions in all courses he is a part of.
Args:
course_id(str): Course ID
Returns(dict): Effective permissions for the permissions. Keys are course IDs, values
Returns(dict): Effective permissions for the course. Keys are course IDs, values
dictionaries of permissions with their values.
"""
course = general.find_course(course_id, throws=False)
......@@ -204,7 +204,7 @@ class PermissionsService:
return self.build_effective_permissions(*courses)
def build_effective_permissions(self, *courses) -> dict:
"""Builds effective permissions in a list of courses for the permissions
"""Builds effective permissions in a list of courses for the client
Args:
*courses: List of courses
......@@ -217,7 +217,7 @@ class PermissionsService:
return result
def effective_permissions_for_course(self, course: Course = None) -> dict:
"""Extracts effective permissions for a permissions in one course
"""Extracts effective permissions for a client in one course
Returns(dict): Effective permissions dictionary
"""
......
......@@ -145,10 +145,7 @@ class ProjectService:
"""List of all projects
Args:
course(Course): Course instance
permissions: Client instance
Returns(list): list of projects
"""
perm_service = permissions.PermissionsService(course=course)
if perm_service.check.permissions(['view_course_full']):
......
......@@ -101,14 +101,14 @@ class RoleService:
"""Finds all clients based on their ids
Args:
ids(List[str]): List of permissions ids
ids(List[str]): List of clients ids
Returns(List[Client]): List of clients
"""
return [find_client(i) for i in ids]
def update_clients_membership(self, data: dict) -> Role:
"""Updates permissions membership in the role
"""Updates client membership in the role
Args:
data(dict): Data provided to update role membership
......@@ -155,7 +155,6 @@ class RoleService:
def remove_client(self, client: Client) -> Role:
"""Removes single client from the role
Args:
role(Role): Role instance
client(Client): Client instance
Returns(Role): Updated role
"""
......@@ -175,8 +174,6 @@ class RoleService:
"""List of all roles
Args:
course(Course): Course instance
permissions(Client): Client instance
Returns(list): List of roles
"""
......
......@@ -110,7 +110,7 @@ class UserService:
return self.user
def update_password(self, data: dict):
log.info(f"[UPDATE] User password: {self.user.id} by {self.permissions.id}")
log.info(f"[UPDATE] User password: {self.user.id} by {self.client.id}")
self.__require_param(data, 'new_password')
if self.client == self.user and not self.user.is_admin:
self.__check_old_password(data)
......@@ -172,7 +172,6 @@ class UserService:
"""Find all groups for the user, optionally filtered by course.
Args:
user(User): User instance
course_id(str): Course id (optional)
Returns(List[Group]): List of all groups
......@@ -185,7 +184,6 @@ class UserService:
"""Get all roles for the user, optionally filtered by course
Args:
user(User): User instance
course_id(str): Course id (optional)
Returns(List[Role]):
......@@ -198,9 +196,6 @@ class UserService:
def find_reviews(self) -> List[Review]:
"""Gets reviews the user has contributed to.
Args:
user(User): User instance
Returns(List[Review]):
"""
reviews = []
......@@ -214,7 +209,6 @@ class UserService:
"""Get all submissions of a user, optionally filtered by course and project.
Args:
user(User):
course_id(str): Course id (optional)
project_ids(List[str]): List of project ids (optional)
......
"""
Gitlab permissions module
Gitlab client module
"""
import gitlab
......@@ -7,7 +7,7 @@ from flask import Flask
class GitlabFactory(object):
"""Gitlab permissions wrapper for flask
"""Gitlab client wrapper for flask
DOC: http://python-gitlab.readthedocs.io/en/stable/api-usage.html
"""
......@@ -19,19 +19,19 @@ class GitlabFactory(object):
self.app: Flask = app
def init_app(self, app: Flask):
"""Initializes the permissions with a flask application
"""Initializes the client with a flask application
Args:
app(Flask): Flask application
"""
self.app = app
def instance(self, *args, **kwargs) -> gitlab.Gitlab:
"""Creates instance of the Gitlab permissions
"""Creates instance of the Gitlab client
Args:
*args:
**kwargs:
Returns(gitlab.Gitlab): Gitlab permissions instance
Returns(gitlab.Gitlab): Gitlab client instance
"""
gitlab_url = self.app.config.get('GITLAB_URL')
return gitlab.Gitlab(gitlab_url, *args, **kwargs)
......@@ -228,7 +228,7 @@ def test_submission_create_valid(session):
user = User(uco=123, email='foo', username='xfoo')
course = Course(name="C++", codename="testcourse1")
project = Project(course=course, name="p1")
submission = Submission(user=user, project=project, parameters="")
submission = Submission(user=user, project=project, parameters={})
session.add(submission)
session.flush()
......@@ -243,7 +243,7 @@ def test_submission_update_user(session):
user2 = User(uco=456, email='bar', username='xbar')
course = Course(name="C++", codename="testcourse1")
project = Project(course=course, codename="p1")
submission = Submission(user=user1, project=project, parameters="")
submission = Submission(user=user1, project=project, parameters={})
session.add_all([submission, user2])
session.flush()
......@@ -581,7 +581,7 @@ def test_relation_course_project_delete_project_entity(session):
assert len(Project.query.all()) == 2
# permissions-role-course
# client-role-course
def test_relation_client_role(session):
user1 = User(uco=123, email="foo", username="xfoo")
user2 = User(uco=456, email="bar", username="xbar")
......@@ -833,7 +833,7 @@ def test_user_submission_review(session):
course = Course(name="C++", codename="PB161")
project = Project(course=course, name="p1")
submission = Submission(user=user, project=project, parameters="")
submission = Submission(user=user, project=project, parameters={})
session.add(submission)
session.flush()
assert submission in user.submissions
......
......@@ -177,7 +177,7 @@ def test_create_submission(client):
p_submissions = len(p.submissions)
request_dict = {
"project_params": "data for Kontr",
"project_params": {},
"file_params": {
"source": {
"type": "git",
......@@ -187,7 +187,7 @@ def test_create_submission(client):
}
}
}
request_json = json.dumps(request_dict, cls=utils.DateTimeEncoder)
request_json = json.dumps(request_dict)
path = f"/courses/{cpp.codename}/projects/{p.name}/submissions"
response = utils.make_request(
......@@ -212,7 +212,7 @@ def test_create_submission_as_different_user(client):
p_submissions = len(p.submissions)
request_dict = {
"project_params": "data for Kontr",
"project_params": {},
"file_params": {
"source": {
"type": "git",
......@@ -222,7 +222,7 @@ def test_create_submission_as_different_user(client):
}
}
}
request_json = json.dumps(request_dict, cls=utils.DateTimeEncoder)
request_json = json.dumps(request_dict)
path = f"/courses/{cpp.codename}/projects/{p.name}/submissions?user=student1"
response = utils.make_request(client, path, data=request_json, method='post')
......
......@@ -31,7 +31,7 @@ def make_request(client: FlaskClient, url: str, method: str,
""" Creates an authenticated request to an endpoint.
Args:
client: Flask's test permissions
client: Flask's test client
url: request url
method: get, post, put
credentials: json data for login
......
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