Unverified Commit 6aa72543 authored by Peter Stanko's avatar Peter Stanko
Browse files

Group service

parent 5e847e32
......@@ -6,11 +6,8 @@ from portal import logger
from portal.rest import rest_helpers
from portal.rest.schemas import client_list_update_schema, group_import_schema, group_schema, \
groups_schema, projects_schema, users_schema
from portal.service import auth, general, permissions
from portal.service.groups import add_project_to_group, add_single_user_to_group, create_group, \
delete_group, find_projects_in_group, find_users_in_group_by_role, import_group, list_groups, \
remove_project_from_group, remove_single_user_from_group, update_group, \
update_user_group_membership
from portal.service import general, permissions
from portal.service.groups import GroupService
groups_namespace = Namespace('')
......@@ -24,10 +21,9 @@ class GroupsList(Resource):
@jwt_required
# @groups_namespace.response(200, 'List of all groups', model=groups_schema)
def get(self, cid: str):
client = auth.find_client()
course = general.find_course(cid)
return groups_schema.dump(list_groups(course, client=client))
groups = GroupService().list_groups(course)
return groups_schema.dump(groups)[0]
@jwt_required
# @groups_namespace.response(201, 'Group created', model=group_schema)
......@@ -41,7 +37,7 @@ class GroupsList(Resource):
group_schema, action='create', resource='group'
)
new_group = create_group(course, **data)
new_group = GroupService().create_group(course, **data)
return group_schema.dump(new_group)[0], 201
......@@ -71,7 +67,7 @@ class GroupResource(Resource):
permissions.PermissionsService(course=course).require.update_course()
group = general.find_group(course, gid)
delete_group(group)
GroupService(group).delete_group()
return '', 204
@jwt_required
......@@ -87,7 +83,7 @@ class GroupResource(Resource):
)
group = general.find_group(course, gid)
update_group(group, data)
GroupService(group).update_group(data)
return '', 204
......@@ -106,7 +102,7 @@ class GroupUsersList(Resource):
# authorization
permissions.PermissionsService(course=course).require.belongs_to_group(group)
users = find_users_in_group_by_role(course, group, role_id)
users = GroupService(group).find_users_by_role(course, role_id)
return users_schema.dump(users)
@jwt_required
......@@ -124,7 +120,7 @@ class GroupUsersList(Resource):
group = general.find_group(course, gid)
# everything from users_add is added, THEN everything from users_remove
# is removed
update_user_group_membership(group, data)
GroupService(group).update_membership(data)
return '', 204
......@@ -148,7 +144,7 @@ class GroupAddOrDeleteSingleUser(Resource):
group = general.find_group(course, gid)
user = general.find_user(uid)
add_single_user_to_group(group, user)
GroupService(group).add_user(user)
return '', 204
@jwt_required
......@@ -161,7 +157,7 @@ class GroupAddOrDeleteSingleUser(Resource):
group = general.find_group(course, gid)
user = general.find_user(uid)
remove_single_user_from_group(group, user)
GroupService(group).remove_user(user)
return '', 204
......@@ -178,7 +174,7 @@ class GroupProjectsList(Resource):
# authorization
permissions.PermissionsService(course=course).require.belongs_to_group(group)
projects = find_projects_in_group(group)
projects = GroupService(group).find_projects()
return projects_schema.dump(projects)
......@@ -202,7 +198,7 @@ class GroupAddOrDeleteProject(Resource):
group = general.find_group(course, gid)
project = general.find_project(course, pid)
add_project_to_group(group, project)
GroupService(group).add_project(project)
return '', 204
@jwt_required
......@@ -217,7 +213,7 @@ class GroupAddOrDeleteProject(Resource):
group = general.find_group(course, gid)
project = general.find_project(course, pid)
remove_project_from_group(group, project)
GroupService(group).remove_project(project)
return '', 204
......@@ -236,5 +232,5 @@ class GroupImport(Resource):
data = rest_helpers.parse_request_data(
group_import_schema, action='import', resource='group'
)
new_group = import_group(data, target_course)
new_group = GroupService().import_group(data, target_course)
return group_schema.dump(new_group), 201
......@@ -7,10 +7,8 @@ from portal.database.models import ClientType
from portal.rest import rest_helpers
from portal.rest.schemas import client_list_update_schema, permissions_schema, role_schema, \
roles_schema, users_schema
from portal.service import auth, general, permissions
from portal.service.roles import add_single_client_to_role, create_role, delete_role, list_roles, \
remove_single_client_from_role, update_clients_membership_in_role, update_role, \
update_role_permissions
from portal.service import general, permissions
from portal.service.roles import RoleService
roles_namespace = Namespace('')
......@@ -23,9 +21,9 @@ class RoleList(Resource):
@jwt_required
# @roles_namespace.response(200, 'Roles list', model=roles_schema)
def get(self, cid):
client = auth.find_client()
course = general.find_course(cid)
return roles_schema.dump(list_roles(course, client))
roles = RoleService().list_roles(course)
return roles_schema.dump(roles)[0]
@jwt_required
# @roles_namespace.response(201, 'Role created', model=role_schema)
......@@ -37,7 +35,7 @@ class RoleList(Resource):
data = rest_helpers.parse_request_data(
role_schema, action='create', resource='role'
)
new_role = create_role(course, **data)
new_role = RoleService().create_role(course, **data)
return role_schema.dump(new_role)[0], 201
......@@ -65,7 +63,7 @@ class RoleResource(Resource):
permissions.PermissionsService(course=course).require.update_course()
role = general.find_role(course, rid)
delete_role(role)
RoleService(role).delete_role()
return '', 204
@jwt_required
......@@ -80,7 +78,7 @@ class RoleResource(Resource):
)
role = general.find_role(course, rid)
update_role(role, data)
RoleService(role).update_role(data)
return '', 204
......@@ -114,7 +112,7 @@ class RolePermissions(Resource):
)
role = general.find_role(course, rid)
update_role_permissions(role, data)
RoleService(role).update_permissions(data)
return permissions_schema.dump(role.permissions), 204
......@@ -151,7 +149,7 @@ class RoleUsersList(Resource):
# everything from users_add is added, THEN everything from users_remove
# is subtracted
role = general.find_role(course, rid)
update_clients_membership_in_role(role, data)
RoleService(role).update_clients_membership(data)
return '', 204
......@@ -174,7 +172,7 @@ class RoleUser(Resource):
role = general.find_role(course, rid)
client = general.find_client(clid)
add_single_client_to_role(role, client)
RoleService(role).add_client(client)
return '', 204
@jwt_required
......@@ -183,6 +181,6 @@ class RoleUser(Resource):
course = general.find_course(cid)
permissions.PermissionsService(course=course).require.write_roles()
role = general.find_role(course, rid)
user = general.find_client(clid)
remove_single_client_from_role(role, user)
client = general.find_client(clid)
RoleService(role).remove_client(client)
return '', 204
......@@ -7,9 +7,9 @@ from typing import List
from portal import logger
from portal.database.models import Course, Group, Role, User
from portal.service import general
from portal.service.groups import copy_group
from portal.service.projects import copy_project
from portal.service.roles import copy_role
from portal.service.groups import GroupService
from portal.service.projects import ProjectService
from portal.service.roles import RoleService
log = logger.get_logger(__name__)
......@@ -53,13 +53,13 @@ class CourseService:
"""
if config.get('roles'):
for role in self.course.roles:
copy_role(role, target, with_clients=config['roles'])
RoleService(role).copy_role(target, with_clients=config['roles'])
if config.get('groups'):
for group in self.course.groups:
copy_group(group, target, with_users=config['groups'])
GroupService(group).copy_group(target, with_users=config['groups'])
if config.get('projects'):
for project in self.course.projects:
copy_project(project, target)
ProjectService(project).copy_project(target)
general.write_entity(target)
log.info(f"[IMPORT] From {self.course.id} to: {target.id}.")
......
......@@ -3,7 +3,7 @@ Groups service
"""
import logging
from typing import List, Union
from typing import List
from portal.database.models import Course, Group, Project, User
from portal.service import filters, general, permissions
......@@ -14,241 +14,226 @@ from portal.service.users import UserService
log = logging.getLogger(__name__)
def copy_group(source: Group, target: Course, with_users: str):
"""Copies group to a specified course
Args:
source(Group): Source group instance
target(Course): Target course instance
with_users(str): Whether it should copy with users
Returns: the new group
"""
log.info(
f"[COPY] Group: {source.id} to course {target.id} with config: {with_users}")
new_name = get_new_name(source, target)
new_group = Group(target, codename=new_name)
new_group.description = source.description
new_group.name = source.name
if with_users == "with_users":
for user in source.users:
user.groups.append(new_group)
return new_group
def _set_group_data(group: Group, data: dict) -> Group:
return general.update_entity(group, data, allowed=['name', 'description', 'codename'])
def delete_group(group: Group):
"""Deletes group
Args:
group(Group): Group instance
"""
log.info(f"[DELETE] Group {group.id} ({group.codename})")
general.delete_entity(group)
class GroupService:
def __init__(self, group: Group = None):
self._group = group
@property
def group(self) -> Group:
return self._group
def __set_group_data(group: Group, data: dict) -> Group:
return general.update_entity(group, data, allowed=['name', 'description', 'codename'])
def copy_group(self, target: Course, with_users: str):
"""Copies group to a specified course
Args:
target(Course): Target course instance
with_users(str): Whether it should copy with users
def update_group(group: Group, data: dict) -> Group:
"""Updates group
Args:
group(Group): Group instance
data(dict): Group data
Returns(Group): Updated group instance
"""
log.info(f"[UPDATE] Group {group.codename} to {group}.")
return __set_group_data(group, data)
def create_group(course: Course, **data) -> Course:
"""Creates a new group
Args:
course(Course): Course instance
Keyword Args:
name(str): Name of the group
description(str): Description of the group
codename(str): Codename of the group
Returns(Group): Created group instance
"""
new_group = Group(course=course)
__set_group_data(new_group, data)
log.info(
f"[CREATE] Group for course {course.id} ({course.codename}): {new_group}")
return new_group
def update_user_group_membership(group: Group, data: dict):
"""Updates the list of members of the group
Args:
group(Group): Group instance
data(dict): Data
"""
users_old = set(group.users)
users_add = data.get('add')
users_remove = data.get('remove')
users = set()
if users_add:
to_add = UserService().find_users(users_add)
users = users_old.union(to_add)
if users_remove:
to_remove = UserService().find_users(users_remove)
users = users_old.difference(to_remove)
group.users = list(users)
general.write_entity(group)
log.info(f"[UPDATE] Users of group {group.codename} to {group.users}.")
def find_users_in_group_by_role(
course: Course, group: Group, role_id=None) -> List[User]:
"""Finds users in the group by role
Args:
course(Course): Course instance
group(Group): Group instance
role_id(str): Group id
Returns(list[User]): List of the users
"""
role = find_role(course, role_id) if role_id else None
return group.get_users_based_on_role(role=role)
def add_single_user_to_group(group: Group, user: User) -> Group:
"""Adds a single user to the group
Args:
group(Group): Group instance
user(User): User instance
Returns(Group): updated group instance
"""
course = group.course
if user not in group.users:
group.users.append(user)
general.write_entity(group)
Returns: the new group
"""
source = self.group
log.info(
f"[COPY] Group: {source.id} to course {target.id} with config: {with_users}")
new_name = get_new_name(source, target)
new_group = Group(target, codename=new_name)
new_group.description = source.description
new_group.name = source.name
if with_users == "with_users":
for user in source.users:
user.groups.append(new_group)
return new_group
def delete_group(self):
"""Deletes group
"""
log.info(f"[DELETE] Group {self.group.id} ({self.group.codename})")
general.delete_entity(self.group)
def update_group(self, data: dict) -> Group:
"""Updates group
Args:
data(dict): Group data
Returns(Group): Updated group instance
"""
log.info(f"[UPDATE] Group {self.group.codename} to {group}.")
return _set_group_data(self.group, data)
def create_group(self, course: Course, **data) -> Course:
"""Creates a new group
Args:
course(Course): Course instance
Keyword Args:
name(str): Name of the group
description(str): Description of the group
codename(str): Codename of the group
Returns(Group): Created group instance
"""
new_group = Group(course=course)
self._group = new_group
_set_group_data(new_group, data)
log.info(
f"[ADD] Added user {user.username} to group {group.codename} "
f"in course {course.codename}.")
else:
log.info(f"[ADD] User {user.username} is already in group {group.codename} "
f"in course {course.codename}: no change.")
return group
def remove_single_user_from_group(group: Group, user: User) -> Group:
"""Removes single user from the group
Args:
group(Group): Group instance
user(User): User instance
Returns(Group): updated group instance
"""
course = group.course
try:
group.users.remove(user)
general.write_entity(group)
except ValueError:
message = f"[REMOVE] Could not remove user " \
f"{user.username} from group {group.codename} in course " \
f"{course.codename}: group does not contain user."
raise PortalAPIError(400, message=message)
log.info(f"[REMOVE] User {user.username} from group {group.codename}"
f" in course {course.codename}.")
return group
def import_group(data: dict, course: Course) -> Group:
"""Imports Group from a course
Args:
data(dict): import configuration
course(Course): Course instance
"""
source_course = find_course(data['source_course'])
source_group = find_group(source_course, data['source_group'])
new_group = copy_group(source_group, course,
data['with_users'])
general.write_entity(new_group)
log.info(
f"[IMPORT] Group {source_group.name} from course "
f"{source_course.codename} to course {course.codename} "
f"as {new_group.codename}.")
return new_group
def list_groups(course: Course, client: Union[User, Course]) -> List[Group]:
"""List all groups
Args:
course(Course): Course instance
client: Client instance
Returns(list): List of all groups
"""
perm_service = permissions.PermissionsService(course=course, client=client)
if perm_service.check.client(['view_course_full']):
return course.groups
elif perm_service.check.client(['view_course_limited']):
return filters.filter_groups_from_course(course=course, user=client)
raise ForbiddenError(uid=client.id)
def remove_project_from_group(group: Group, project: Project) -> Group:
"""Removes projects from the group
Args:
group(Group): Group instance
project(Project): Project instance
Returns:
"""
course = group.course
try:
group.projects.remove(project)
general.write_entity(group)
except ValueError:
message = f"[REMOVE] Could not remove project " \
f"{project.codename} from group {group.codename} in course " \
f"{course.codename}: group does not contain project."
raise PortalAPIError(400, message=message)
log.info(f"[REMOVE] Project {project.codename} from group {group.codename}"
f" in course {course.codename}.")
return group
def add_project_to_group(group: Group, project: Project) -> Group:
"""Adds project to the group
Args:
group(Group): Group instance
project(Project): Project instance
Returns:
"""
course = group.course
if project not in group.projects:
group.projects.append(project)
general.write_entity(group)
f"[CREATE] Group for course {course.id} ({course.codename}): {new_group}")
return new_group
def update_membership(self, data: dict):
"""Updates the list of members of the group
Args:
data(dict): Data
"""
users_old = set(self.group.users)
users_add = data.get('add')
users_remove = data.get('remove')
users = set()
if users_add:
to_add = UserService().find_users(users_add)
users = users_old.union(to_add)
if users_remove:
to_remove = UserService().find_users(users_remove)
users = users_old.difference(to_remove)
self.group.users = list(users)
general.write_entity(self.group)
log.info(f"[UPDATE] Users of group {self.group.codename} to {self.group.users}.")
def find_users_by_role(self, course: Course, role_id=None) -> List[User]:
"""Finds users in the group by role
Args:
course(Course): Course instance
role_id(str): Group id
Returns(list[User]): List of the users
"""
course = course or self.group.course
role = find_role(course, role_id) if role_id else None
return self.group.get_users_based_on_role(role=role)
def add_user(self, user: User) -> Group:
"""Adds a single user to the group
Args:
user(User): User instance
Returns(Group): updated group instance
"""
# TODO: Check that user belongs to Course using the Role relationship
course = self.group.course
if user not in self.group.users:
self.group.users.append(user)
general.write_entity(self.group)
log.info(
f"[ADD] Added user {user.username} to group {self.group.codename} "
f"in course {course.codename}.")
else:
log.info(f"[ADD] User {user.username} is already in group {self.group.codename} "
f"in course {course.codename}: no change.")
return self.group
def remove_user(self, user: User) -> Group:
"""Removes single user from the group
Args:
user(User): User instance
Returns(Group): updated group instance
"""
course = self.group.course
try:
self.group.users.remove(user)
general.write_entity(self.group)
except ValueError:
message = f"[REMOVE] Could not remove user " \
f"{user.username} from group {self.group.codename} in course " \
f"{course.codename}: group does not contain user."
raise PortalAPIError(400, message=message)
log.info(f"[REMOVE] User {user.username} from group {self.group.codename}"
f" in course {course.codename}.")
return self.group
def import_group(self, data: dict, course: Course = None) -> Group:
"""Imports Group from a course
Args:
data(dict): import configuration