Loading .gitignore +1 −0 Original line number Diff line number Diff line Loading @@ -5,3 +5,4 @@ __pycache__/ .pytest_cache/ portal.local.cfg Pipfile.lock devel.db No newline at end of file management/commands.py 0 → 100644 +61 −0 Original line number Diff line number Diff line import click import logging from flask import Flask from flask.cli import AppGroup from portal import db from management import data from management.data import shared from portal.service.general import find_user log = logging.getLogger(__name__) data_cli = AppGroup('data') users_cli = AppGroup('users') flask_app: Flask = None def register_commands(app: Flask): global flask_app app.cli.add_command(data_cli) app.cli.add_command(users_cli) flask_app = app return app @data_cli.command('init') def cli_init_data(): log.info("[CMD] Initializing data") log.debug(f"[CONFIG] DB: {flask_app.config['SQLALCHEMY_DATABASE_URI']}") data.init_data(db=db, app=flask_app) @users_cli.command('create') @click.argument('name') def cli_users_create(name): with flask_app.app_context(): log.info(f"[CMD] Create user: {name}") password = click.prompt('Password', hide_input=True, confirmation_prompt=True) creator = shared.DataCreator(db) user = creator.create_user( username=name, name=name, admin=True, password=password ) db.session.commit() log.info(f"[CMD] Created user: {user}") @users_cli.command('set-password') @click.argument('name') def cli_users_create(name): with flask_app.app_context(): log.info(f"[CMD] Update password for user: {name}") password = click.prompt('Password', hide_input=True, confirmation_prompt=True) user = find_user(name) user.set_password(password=password) db.session.add(user) db.session.commit() log.info(f"[CMD] Updated password for user: {user}") management/data/__init__.py +5 −4 Original line number Diff line number Diff line Loading @@ -5,8 +5,9 @@ from management.data.data_dev import init_development_data from management.data.data_prod import init_prod_data def init_data(app: Flask, db: SQLAlchemy): if app.config.get('PORTAL_ENV', 'dev') == 'prod': init_prod_data(app, db) def init_data(app: Flask, db: SQLAlchemy, env=None): env = env if env == 'prod': init_prod_data(db=db, app=app) else: init_development_data(app, db) init_development_data(db=db, app=app) management/data/data_dev.py +9 −4 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ def init_development_data(app: Flask, db: SQLAlchemy): with app.app_context(): creator = DataCreator(db) # users create_stub_users(app, creator, db) create_stub_users(app, creator) # courses cpp = creator.create_course(codename='PB161', name='C++') Loading @@ -25,6 +25,7 @@ def init_development_data(app: Flask, db: SQLAlchemy): c_students = creator.create_group(course=c, name="c_seminar01") c_teachers = creator.create_group(course=c, name="c_teachers") # projects cpp_p1 = creator.create_project( course=cpp, name="HW01", config=dict( Loading Loading @@ -137,6 +138,10 @@ def init_development_data(app: Flask, db: SQLAlchemy): cpp_sub_p1_in_progress = Submission(user=student_cpp, project=cpp_p1, parameters="") cpp_sub_p1_in_progress.state = SubmissionState.IN_PROGRESS # Projects in groups cpp_students.projects.append(cpp_p1) c_students.projects.append(c_p1) db.session.add_all( [c_sub1, c_sub2, cpp_sub_p1_cancel, cpp_sub_p1_abort, cpp_sub_p1_finished, cpp_sub_p1_in_progress]) Loading @@ -153,11 +158,11 @@ def init_development_data(app: Flask, db: SQLAlchemy): db.session.add_all([review]) # components create_stub_components(creator, db) create_stub_components(creator) db.session.commit() def create_stub_users(app, creator, db): def create_stub_users(app, creator): creator.create_default_admin_user(app) creator.create_user(username='student1', uco=111) creator.create_user(username='student2', uco=222) Loading @@ -167,6 +172,6 @@ def create_stub_users(app, creator, db): name='Teacher Only', uco=1002) def create_stub_components(creator, db): def create_stub_components(creator): creator.create_component(name='executor') creator.create_component(name='processing', type='processing') portal/database/models.py +30 −8 Original line number Diff line number Diff line Loading @@ -171,6 +171,23 @@ class User(db.Model, EntityBase): """ return self.query_permissions_for_course(course=course).all() def query_projects_by_course(self, course: 'Course') -> BaseQuery: groups = self.get_groups_in_course(course=course) pids = [] for group in groups: for p in group.projects: pids.append(p.id) return Project.query.filter(Project.id.in_(pids)) def get_projects_by_course(self, course: 'Course') -> List['Project']: """Gets projects from the course Args: course(Course): Course instance Returns(List[Project]): filtered list of projects """ return self.query_projects_by_course(course=course).all() def __init__(self, uco: int = None, email: str = None, username: str = None, is_admin: bool = False) -> None: """Creates user instance Loading Loading @@ -231,7 +248,7 @@ class Course(db.Model, EntityBase): """Gets all users in the course based on their role Args: role(Role): The role to filter by Returns(list[User]): List of users that have the role Returns(List[User]): List of users that have the role """ return User.query.join(User.roles) \ .filter((Role.id == role.id) & (Role.course == self)).all() Loading @@ -240,18 +257,26 @@ class Course(db.Model, EntityBase): """Gets all users in the group Args: group(Group): The group to find users in Returns(list[User]): List of users that are in the group Returns(List[User]): List of users that are in the group """ return User.query.join(User.groups) \ .filter((Group.id == group.id) & (Group.course == self)).all() def get_users_by_group_and_role(self, group: 'Group', role: 'Role'): """Gets users filtered by group and role Args: group(Group): the group to find users in role(Role): the role to filter by Returns(List[User]: """ return User.query.join(User.groups) \ .filter((Group.id == group.id) & (Group.course == self)) \ .join(User.roles).filter((Role.id == role.id) & (Role.course == self)) \ .all() def get_users_filtered(self, groups: List['Group'], roles: List['Role']): # projects: List['Project']? def get_users_filtered(self, groups: List['Group'], roles: List['Role']): """Gets all users in the course who are members of at least one of the provided groups and have at least one of the specified roles. Loading @@ -262,17 +287,14 @@ class Course(db.Model, EntityBase): Returns: Users in the course that satisfy constraints (are in at least one group AND have at least one role). """ q = Course.query.join(Course.roles).join(Role.users).filter(User.id == self.id) q2 = User.query.join(Role.users).join(Course.roles).filter(Role.course_id == self.id) q3 = User.query.join(User.roles).join(Role.course) query = User.query.join(User.roles).filter(Role.course_id == self.id) if roles: role_ids = [role.id for role in roles] query = query.filter(Role.id.in_(role_ids)) if groups: group_ids = [group.id for group in groups] query = query.join(User.groups).filter(Group.id.in_(group_ids)).filter(Group.course_id == self.id) query = query.join(User.groups).filter(Group.id.in_(group_ids)).filter( Group.course_id == self.id) return query.all() Loading Loading
.gitignore +1 −0 Original line number Diff line number Diff line Loading @@ -5,3 +5,4 @@ __pycache__/ .pytest_cache/ portal.local.cfg Pipfile.lock devel.db No newline at end of file
management/commands.py 0 → 100644 +61 −0 Original line number Diff line number Diff line import click import logging from flask import Flask from flask.cli import AppGroup from portal import db from management import data from management.data import shared from portal.service.general import find_user log = logging.getLogger(__name__) data_cli = AppGroup('data') users_cli = AppGroup('users') flask_app: Flask = None def register_commands(app: Flask): global flask_app app.cli.add_command(data_cli) app.cli.add_command(users_cli) flask_app = app return app @data_cli.command('init') def cli_init_data(): log.info("[CMD] Initializing data") log.debug(f"[CONFIG] DB: {flask_app.config['SQLALCHEMY_DATABASE_URI']}") data.init_data(db=db, app=flask_app) @users_cli.command('create') @click.argument('name') def cli_users_create(name): with flask_app.app_context(): log.info(f"[CMD] Create user: {name}") password = click.prompt('Password', hide_input=True, confirmation_prompt=True) creator = shared.DataCreator(db) user = creator.create_user( username=name, name=name, admin=True, password=password ) db.session.commit() log.info(f"[CMD] Created user: {user}") @users_cli.command('set-password') @click.argument('name') def cli_users_create(name): with flask_app.app_context(): log.info(f"[CMD] Update password for user: {name}") password = click.prompt('Password', hide_input=True, confirmation_prompt=True) user = find_user(name) user.set_password(password=password) db.session.add(user) db.session.commit() log.info(f"[CMD] Updated password for user: {user}")
management/data/__init__.py +5 −4 Original line number Diff line number Diff line Loading @@ -5,8 +5,9 @@ from management.data.data_dev import init_development_data from management.data.data_prod import init_prod_data def init_data(app: Flask, db: SQLAlchemy): if app.config.get('PORTAL_ENV', 'dev') == 'prod': init_prod_data(app, db) def init_data(app: Flask, db: SQLAlchemy, env=None): env = env if env == 'prod': init_prod_data(db=db, app=app) else: init_development_data(app, db) init_development_data(db=db, app=app)
management/data/data_dev.py +9 −4 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ def init_development_data(app: Flask, db: SQLAlchemy): with app.app_context(): creator = DataCreator(db) # users create_stub_users(app, creator, db) create_stub_users(app, creator) # courses cpp = creator.create_course(codename='PB161', name='C++') Loading @@ -25,6 +25,7 @@ def init_development_data(app: Flask, db: SQLAlchemy): c_students = creator.create_group(course=c, name="c_seminar01") c_teachers = creator.create_group(course=c, name="c_teachers") # projects cpp_p1 = creator.create_project( course=cpp, name="HW01", config=dict( Loading Loading @@ -137,6 +138,10 @@ def init_development_data(app: Flask, db: SQLAlchemy): cpp_sub_p1_in_progress = Submission(user=student_cpp, project=cpp_p1, parameters="") cpp_sub_p1_in_progress.state = SubmissionState.IN_PROGRESS # Projects in groups cpp_students.projects.append(cpp_p1) c_students.projects.append(c_p1) db.session.add_all( [c_sub1, c_sub2, cpp_sub_p1_cancel, cpp_sub_p1_abort, cpp_sub_p1_finished, cpp_sub_p1_in_progress]) Loading @@ -153,11 +158,11 @@ def init_development_data(app: Flask, db: SQLAlchemy): db.session.add_all([review]) # components create_stub_components(creator, db) create_stub_components(creator) db.session.commit() def create_stub_users(app, creator, db): def create_stub_users(app, creator): creator.create_default_admin_user(app) creator.create_user(username='student1', uco=111) creator.create_user(username='student2', uco=222) Loading @@ -167,6 +172,6 @@ def create_stub_users(app, creator, db): name='Teacher Only', uco=1002) def create_stub_components(creator, db): def create_stub_components(creator): creator.create_component(name='executor') creator.create_component(name='processing', type='processing')
portal/database/models.py +30 −8 Original line number Diff line number Diff line Loading @@ -171,6 +171,23 @@ class User(db.Model, EntityBase): """ return self.query_permissions_for_course(course=course).all() def query_projects_by_course(self, course: 'Course') -> BaseQuery: groups = self.get_groups_in_course(course=course) pids = [] for group in groups: for p in group.projects: pids.append(p.id) return Project.query.filter(Project.id.in_(pids)) def get_projects_by_course(self, course: 'Course') -> List['Project']: """Gets projects from the course Args: course(Course): Course instance Returns(List[Project]): filtered list of projects """ return self.query_projects_by_course(course=course).all() def __init__(self, uco: int = None, email: str = None, username: str = None, is_admin: bool = False) -> None: """Creates user instance Loading Loading @@ -231,7 +248,7 @@ class Course(db.Model, EntityBase): """Gets all users in the course based on their role Args: role(Role): The role to filter by Returns(list[User]): List of users that have the role Returns(List[User]): List of users that have the role """ return User.query.join(User.roles) \ .filter((Role.id == role.id) & (Role.course == self)).all() Loading @@ -240,18 +257,26 @@ class Course(db.Model, EntityBase): """Gets all users in the group Args: group(Group): The group to find users in Returns(list[User]): List of users that are in the group Returns(List[User]): List of users that are in the group """ return User.query.join(User.groups) \ .filter((Group.id == group.id) & (Group.course == self)).all() def get_users_by_group_and_role(self, group: 'Group', role: 'Role'): """Gets users filtered by group and role Args: group(Group): the group to find users in role(Role): the role to filter by Returns(List[User]: """ return User.query.join(User.groups) \ .filter((Group.id == group.id) & (Group.course == self)) \ .join(User.roles).filter((Role.id == role.id) & (Role.course == self)) \ .all() def get_users_filtered(self, groups: List['Group'], roles: List['Role']): # projects: List['Project']? def get_users_filtered(self, groups: List['Group'], roles: List['Role']): """Gets all users in the course who are members of at least one of the provided groups and have at least one of the specified roles. Loading @@ -262,17 +287,14 @@ class Course(db.Model, EntityBase): Returns: Users in the course that satisfy constraints (are in at least one group AND have at least one role). """ q = Course.query.join(Course.roles).join(Role.users).filter(User.id == self.id) q2 = User.query.join(Role.users).join(Course.roles).filter(Role.course_id == self.id) q3 = User.query.join(User.roles).join(Role.course) query = User.query.join(User.roles).filter(Role.course_id == self.id) if roles: role_ids = [role.id for role in roles] query = query.filter(Role.id.in_(role_ids)) if groups: group_ids = [group.id for group in groups] query = query.join(User.groups).filter(Group.id.in_(group_ids)).filter(Group.course_id == self.id) query = query.join(User.groups).filter(Group.id.in_(group_ids)).filter( Group.course_id == self.id) return query.all() Loading