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):
tc1_student = factory.scaffold_role(role_type='student', 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_workers = factory.scaffold_role(role_type='worker', course=test_course1)
tc2_student = factory.scaffold_role(role_type='student', 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_workers = factory.scaffold_role(role_type='worker', course=test_course2)
# relationships
teacher_student = teacher1
......@@ -111,6 +113,8 @@ def init_test_data(app: Flask, db: SQLAlchemy):
processing = factory.create_worker(name='processing', url="bar/url")
processing.secrets.append(Secret('processing_secret', "processing_secret",
time.current_time() + timedelta(hours=1)))
executor.roles.append(tc1_workers)
processing.roles.append(tc2_workers)
db.session.add_all([executor, processing])
# Commit to the DB
db.session.commit()
......@@ -310,6 +310,13 @@ class User(EntityBase, Client):
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):
"""Course model
Attributes:
......@@ -392,11 +399,13 @@ class Course(db.Model, EntityBase, NamedMixin):
.join(User.roles).filter((Role.id == role.id) & (Role.course == self)) \
.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
the provided groups and have at least one of the specified roles.
Args:
client_type:
groups(List['Group']): A list of groups. A user must be part of at least one to
be included in the result.
roles(List['Role']): A list of roles. A user must be part of at
......@@ -406,13 +415,17 @@ class Course(db.Model, EntityBase, NamedMixin):
(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:
role_ids = [role.id for role in roles]
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]
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)
return query.all()
......
......@@ -3,6 +3,7 @@ from flask_jwt_extended import jwt_required
from flask_restplus import Namespace
from portal import logger
from portal.database.models import ClientType
from portal.rest import rest_helpers
from portal.rest.custom_resource import CustomResource
from portal.rest.schemas import SCHEMAS
......@@ -138,17 +139,63 @@ class CourseImport(CustomResource):
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.param('cid', 'Course id')
@courses_namespace.response(404, 'Course not found')
class CourseClients(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')
@courses_namespace.response(403, 'Not allowed to see clients 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)
return SCHEMAS.dump('users', users)
client_type = extract_client_type()
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:
PARAMS = {
'user_reduced': (*ALWAYS_ALLOWED, 'username', 'uco', 'email', 'name'),
'users': (*ALWAYS_ALLOWED, 'username', 'uco', 'email'),
'clients': (*ALWAYS_ALLOWED, 'codename', 'type'),
'submissions': (*ALWAYS_ALLOWED, 'course', 'state', 'project', 'scheduled_for', 'user'),
'submission_state': (*ALWAYS_ALLOWED, 'state', 'parameters', 'scheduled_for'),
'roles': ENT_W_COURSE,
......@@ -391,8 +392,11 @@ class Schemas:
'courses': (*CODENAME_W_DESC,),
'config_reduced': (*ALWAYS_ALLOWED, 'project', 'submissions_allowed_from',
'submissions_allowed_to', 'file_whitelist'),
'secrets': (*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')
'secrets': (
*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):
......@@ -414,6 +418,14 @@ class Schemas:
def users(self, **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
def user_reduced(self, **kwargs):
return self.__get_schema(UserSchema, **kwargs)
......
......@@ -76,7 +76,7 @@ class CourseService(GeneralService):
log.info(f"[UPDATE] Notes access token {self.course.log_name}: {token}")
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
Args:
groups(list): Group names list
......@@ -85,4 +85,5 @@ class CourseService(GeneralService):
"""
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
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):
assert len(cpp_roles) == 2
assert teacher_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)
assert len(java_roles) == 1
......
......@@ -79,19 +79,37 @@ def test_read(client):
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()
response = rest_tools.make_request(client, f"/courses/{course.id}/clients")
assert_response(response, 200)
users = rest_tools.extract_data(response)
course_users = set()
for role in course.roles:
course_users.update(role.clients)
course_users.update([c.id for c in role.clients])
assert users
assert len(users) == 4
assert len(users) == 5
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):
c = Course.query.filter_by(codename="testcourse1").first()
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