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

Clients endpoint updated for type selection

parent 6b8f2b86
...@@ -43,10 +43,12 @@ def init_test_data(app: Flask, db: SQLAlchemy): ...@@ -43,10 +43,12 @@ def init_test_data(app: Flask, db: SQLAlchemy):
tc1_student = factory.scaffold_role(role_type='student', course=test_course1) tc1_student = factory.scaffold_role(role_type='student', course=test_course1)
tc1_teacher = factory.scaffold_role(role_type='teacher', course=test_course1) tc1_teacher = factory.scaffold_role(role_type='teacher', course=test_course1)
tc1_owner = factory.scaffold_role(role_type='owner', course=test_course1) tc1_owner = factory.scaffold_role(role_type='owner', course=test_course1)
tc1_workers = factory.scaffold_role(role_type='worker', course=test_course1)
tc2_student = factory.scaffold_role(role_type='student', course=test_course2) tc2_student = factory.scaffold_role(role_type='student', course=test_course2)
tc2_teacher = factory.scaffold_role(role_type='teacher', course=test_course2) tc2_teacher = factory.scaffold_role(role_type='teacher', course=test_course2)
tc2_owner = factory.scaffold_role(role_type='owner', course=test_course2) tc2_owner = factory.scaffold_role(role_type='owner', course=test_course2)
tc2_workers = factory.scaffold_role(role_type='worker', course=test_course2)
# relationships # relationships
teacher_student = teacher1 teacher_student = teacher1
...@@ -111,6 +113,8 @@ def init_test_data(app: Flask, db: SQLAlchemy): ...@@ -111,6 +113,8 @@ def init_test_data(app: Flask, db: SQLAlchemy):
processing = factory.create_worker(name='processing', url="bar/url") processing = factory.create_worker(name='processing', url="bar/url")
processing.secrets.append(Secret('processing_secret', "processing_secret", processing.secrets.append(Secret('processing_secret', "processing_secret",
time.current_time() + timedelta(hours=1))) time.current_time() + timedelta(hours=1)))
executor.roles.append(tc1_workers)
processing.roles.append(tc2_workers)
db.session.add_all([executor, processing]) db.session.add_all([executor, processing])
# Commit to the DB # Commit to the DB
db.session.commit() db.session.commit()
...@@ -310,6 +310,13 @@ class User(EntityBase, Client): ...@@ -310,6 +310,13 @@ class User(EntityBase, Client):
return hash(self.id) return hash(self.id)
def _get_class_based_on_client_type(client_type):
klass = Client
if client_type is not None:
klass = User if client_type == ClientType.USER else Worker
return klass
class Course(db.Model, EntityBase, NamedMixin): class Course(db.Model, EntityBase, NamedMixin):
"""Course model """Course model
Attributes: Attributes:
...@@ -392,11 +399,13 @@ class Course(db.Model, EntityBase, NamedMixin): ...@@ -392,11 +399,13 @@ class Course(db.Model, EntityBase, NamedMixin):
.join(User.roles).filter((Role.id == role.id) & (Role.course == self)) \ .join(User.roles).filter((Role.id == role.id) & (Role.course == self)) \
.all() .all()
def get_clients_filtered(self, groups: List['Group'], roles: List['Role']): def get_clients_filtered(self, groups: List['Group'] = None, roles: List['Role'] = None,
client_type=None):
"""Gets all users in the course who are members of at least one of """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. the provided groups and have at least one of the specified roles.
Args: Args:
client_type:
groups(List['Group']): A list of groups. A user must be part of at least one to groups(List['Group']): A list of groups. A user must be part of at least one to
be included in the result. be included in the result.
roles(List['Role']): A list of roles. A user must be part of at roles(List['Role']): A list of roles. A user must be part of at
...@@ -406,13 +415,17 @@ class Course(db.Model, EntityBase, NamedMixin): ...@@ -406,13 +415,17 @@ class Course(db.Model, EntityBase, NamedMixin):
(are in at least one group AND have at least one role). (are in at least one group AND have at least one role).
""" """
query = Client.query.join(Client.roles).filter(Role.course_id == self.id) klass = _get_class_based_on_client_type(client_type)
query = klass.query.join(klass.roles)
if client_type is not None:
query = query.filter(klass.type == client_type)
query = query.filter(Role.course_id == self.id)
if roles: if roles:
role_ids = [role.id for role in roles] role_ids = [role.id for role in roles]
query = query.filter(Role.id.in_(role_ids)) query = query.filter(Role.id.in_(role_ids))
if groups: if groups and client_type == ClientType.USER:
group_ids = [group.id for group in groups] group_ids = [group.id for group in groups]
query = query.join(Client.groups).filter(Group.id.in_(group_ids)).filter( query = query.join(User.groups).filter(Group.id.in_(group_ids)).filter(
Group.course_id == self.id) Group.course_id == self.id)
return query.all() return query.all()
......
...@@ -3,6 +3,7 @@ from flask_jwt_extended import jwt_required ...@@ -3,6 +3,7 @@ from flask_jwt_extended import jwt_required
from flask_restplus import Namespace from flask_restplus import Namespace
from portal import logger from portal import logger
from portal.database.models import ClientType
from portal.rest import rest_helpers from portal.rest import rest_helpers
from portal.rest.custom_resource import CustomResource from portal.rest.custom_resource import CustomResource
from portal.rest.schemas import SCHEMAS from portal.rest.schemas import SCHEMAS
...@@ -138,17 +139,63 @@ class CourseImport(CustomResource): ...@@ -138,17 +139,63 @@ class CourseImport(CustomResource):
return SCHEMAS.dump('course', copied_course) return SCHEMAS.dump('course', copied_course)
def extract_client_type():
client_type = request.args.get('type')
if client_type is not None:
if client_type == 'worker':
client_type = ClientType.WORKER
elif client_type == 'user':
client_type = ClientType.USER
else:
client_type = None
return client_type
@courses_namespace.route('/<string:cid>/clients') @courses_namespace.route('/<string:cid>/clients')
@courses_namespace.param('cid', 'Course id') @courses_namespace.param('cid', 'Course id')
@courses_namespace.response(404, 'Course not found') @courses_namespace.response(404, 'Course not found')
class CourseClients(CustomResource): class CourseClients(CustomResource):
@jwt_required @jwt_required
# @courses_namespace.response(200, 'Users in the course', model=users_schema) # @courses_namespace.response(200, 'Users in the course', model=users_schema)
@courses_namespace.response(403, 'Not allowed to see users in the course') @courses_namespace.response(403, 'Not allowed to see clients in the course')
def get(self, cid): def get(self, cid):
course = self.find.course(cid) course = self.find.course(cid)
self.permissions(course=course).require.permissions(['view_course_full']) self.permissions(course=course).require.permissions(['view_course_full'])
group_ids = request.args.getlist('group') group_ids = request.args.getlist('group')
role_ids = request.args.getlist('role') role_ids = request.args.getlist('role')
users = self.rest.courses(course).get_clients_filtered(group_ids, role_ids) client_type = extract_client_type()
return SCHEMAS.dump('users', users) clients = self.rest.courses(course).get_clients_filtered(group_ids, role_ids,
client_type=client_type)
return SCHEMAS.dump('clients', clients)
@courses_namespace.route('/<string:cid>/users')
@courses_namespace.param('cid', 'Course id')
@courses_namespace.response(404, 'Course not found')
class CourseUsers(CustomResource):
@jwt_required
# @courses_namespace.response(200, 'Users in the course', model=users_schema)
@courses_namespace.response(403, 'Not allowed to see users in the course')
def get(self, cid):
course = self.find.course(cid)
self.permissions(course=course).require.permissions(['view_course_full'])
group_ids = request.args.getlist('group')
role_ids = request.args.getlist('role')
users = self.rest.courses(course).get_clients_filtered(group_ids, role_ids,
client_type=ClientType.USER)
return SCHEMAS.dump('users', users)
@courses_namespace.route('/<string:cid>/workers')
@courses_namespace.param('cid', 'Course id')
@courses_namespace.response(404, 'Course not found')
class CourseWorkers(CustomResource):
@jwt_required
# @courses_namespace.response(200, 'Users in the course', model=users_schema)
@courses_namespace.response(403, 'Not allowed to see workers in the course')
def get(self, cid):
course = self.find.course(cid)
self.permissions(course=course).require.permissions(['view_course_full'])
group_ids = request.args.getlist('group')
role_ids = request.args.getlist('role')
workers = self.rest.courses(course).get_clients_filtered(group_ids, role_ids,
client_type=ClientType.WORKER)
return SCHEMAS.dump('workers', workers)
...@@ -382,6 +382,7 @@ class Schemas: ...@@ -382,6 +382,7 @@ class Schemas:
PARAMS = { PARAMS = {
'user_reduced': (*ALWAYS_ALLOWED, 'username', 'uco', 'email', 'name'), 'user_reduced': (*ALWAYS_ALLOWED, 'username', 'uco', 'email', 'name'),
'users': (*ALWAYS_ALLOWED, 'username', 'uco', 'email'), 'users': (*ALWAYS_ALLOWED, 'username', 'uco', 'email'),
'clients': (*ALWAYS_ALLOWED, 'codename', 'type'),
'submissions': (*ALWAYS_ALLOWED, 'course', 'state', 'project', 'scheduled_for', 'user'), 'submissions': (*ALWAYS_ALLOWED, 'course', 'state', 'project', 'scheduled_for', 'user'),
'submission_state': (*ALWAYS_ALLOWED, 'state', 'parameters', 'scheduled_for'), 'submission_state': (*ALWAYS_ALLOWED, 'state', 'parameters', 'scheduled_for'),
'roles': ENT_W_COURSE, 'roles': ENT_W_COURSE,
...@@ -391,8 +392,11 @@ class Schemas: ...@@ -391,8 +392,11 @@ class Schemas:
'courses': (*CODENAME_W_DESC,), 'courses': (*CODENAME_W_DESC,),
'config_reduced': (*ALWAYS_ALLOWED, 'project', 'submissions_allowed_from', 'config_reduced': (*ALWAYS_ALLOWED, 'project', 'submissions_allowed_from',
'submissions_allowed_to', 'file_whitelist'), 'submissions_allowed_to', 'file_whitelist'),
'secrets': (*ALWAYS_ALLOWED, 'name', 'expires_at', 'client.id', 'client.type', 'client.name', 'client.codename'), 'secrets': (
'secret': (*ALWAYS_ALLOWED, 'name', 'expires_at', 'client.id', 'client.type', 'client.name', 'client.codename') *ALWAYS_ALLOWED, 'name', 'expires_at', 'client.id', 'client.type', 'client.name',
'client.codename'),
'secret': (*ALWAYS_ALLOWED, 'name', 'expires_at', 'client.id', 'client.type', 'client.name',
'client.codename')
} }
def __get_schema(self, schema_klass, select_params=None, only=None, strict=True, **kwargs): def __get_schema(self, schema_klass, select_params=None, only=None, strict=True, **kwargs):
...@@ -414,6 +418,14 @@ class Schemas: ...@@ -414,6 +418,14 @@ class Schemas:
def users(self, **kwargs): def users(self, **kwargs):
return self.__get_schema(UserSchema, many=True, **kwargs) return self.__get_schema(UserSchema, many=True, **kwargs)
@fn_name
def client(self, **kwargs):
return self.__get_schema(ClientSchema, **kwargs)
@fn_name
def clients(self, **kwargs):
return self.__get_schema(ClientSchema, many=True, **kwargs)
@fn_name @fn_name
def user_reduced(self, **kwargs): def user_reduced(self, **kwargs):
return self.__get_schema(UserSchema, **kwargs) return self.__get_schema(UserSchema, **kwargs)
......
...@@ -76,7 +76,7 @@ class CourseService(GeneralService): ...@@ -76,7 +76,7 @@ class CourseService(GeneralService):
log.info(f"[UPDATE] Notes access token {self.course.log_name}: {token}") log.info(f"[UPDATE] Notes access token {self.course.log_name}: {token}")
return self.course return self.course
def get_clients_filtered(self, groups: List[str], roles: List[str]) -> List[User]: def get_clients_filtered(self, groups: List[str], roles: List[str], client_type=None) -> List[User]:
"""Get all users for course filtered """Get all users for course filtered
Args: Args:
groups(list): Group names list groups(list): Group names list
...@@ -85,4 +85,5 @@ class CourseService(GeneralService): ...@@ -85,4 +85,5 @@ class CourseService(GeneralService):
""" """
groups_entities = Group.query.filter(Group.id.in_(groups)).all() if groups else None groups_entities = Group.query.filter(Group.id.in_(groups)).all() if groups else None
roles_entities = Role.query.filter(Role.id.in_(roles)).all() if roles else None roles_entities = Role.query.filter(Role.id.in_(roles)).all() if roles else None
return self.course.get_clients_filtered(groups_entities, roles_entities) return self.course.get_clients_filtered(groups_entities, roles_entities,
client_type=client_type)
...@@ -869,6 +869,7 @@ def test_user_roles_for_course(session): ...@@ -869,6 +869,7 @@ def test_user_roles_for_course(session):
assert len(cpp_roles) == 2 assert len(cpp_roles) == 2
assert teacher_cpp in cpp_roles assert teacher_cpp in cpp_roles
assert lect_cpp in cpp_roles assert lect_cpp in cpp_roles
assert len(course.get_clients_filtered(client_type=ClientType.USER)) == 1
java_roles = user.get_roles_in_course(course=c_java) java_roles = user.get_roles_in_course(course=c_java)
assert len(java_roles) == 1 assert len(java_roles) == 1
......
...@@ -79,19 +79,37 @@ def test_read(client): ...@@ -79,19 +79,37 @@ def test_read(client):
rest_tools.assert_course(c, course) rest_tools.assert_course(c, course)
def test_read_course_users(client): def test_read_course_clients(client):
course = Course.query.filter_by(codename="testcourse2").first() course = Course.query.filter_by(codename="testcourse2").first()
response = rest_tools.make_request(client, f"/courses/{course.id}/clients") response = rest_tools.make_request(client, f"/courses/{course.id}/clients")
assert_response(response, 200) assert_response(response, 200)
users = rest_tools.extract_data(response) users = rest_tools.extract_data(response)
course_users = set() course_users = set()
for role in course.roles: for role in course.roles:
course_users.update(role.clients) course_users.update([c.id for c in role.clients])
assert users assert users
assert len(users) == 4 assert len(users) == 5
assert len(course_users) == len(users) assert len(course_users) == len(users)
def test_read_course_client_type_workers(client):
course = Course.query.filter_by(codename="testcourse1").first()
response = rest_tools.make_request(client, f"/courses/{course.id}/clients?type=worker")
assert_response(response, 200)
workers = rest_tools.extract_data(response)
assert workers
assert len(workers) == 1
def test_read_course_client_type_users(rest_service, client):
course = Course.query.filter_by(codename="testcourse1").first()
response = rest_tools.make_request(client, f"/courses/{course.id}/clients?type=user")
assert_response(response, 200)
users = rest_tools.extract_data(response)
assert users
assert len(users) == 5
def test_update(client): def test_update(client):
c = Course.query.filter_by(codename="testcourse1").first() c = Course.query.filter_by(codename="testcourse1").first()
request_dict = dict( request_dict = dict(
......
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