<commit_before>"""Group (a.k.a Classroom) management."""
import base64
import csv
import io
import string

import api
from api import (block_before_competition, check_csrf, PicoException,
                 rate_limit, require_login, require_teacher)
from flask import jsonify
from flask_restplus import Namespace, Resource
from marshmallow import (fields, post_load, pre_load, RAISE, Schema, validate,
                         validates_schema, ValidationError)

from .schemas import (batch_registration_req, group_invite_req, group_patch_req,
                      group_remove_team_req, group_req, score_progressions_req,
                      scoreboard_page_req)

ns = Namespace('groups', description='Group management')


@ns.route('')
class GroupList(Resource):
    """Get the list of your groups, or create a new group."""

    @require_login
    @ns.response(200, 'Success')
    @ns.response(401, 'Not logged in')
    def get(self):
        """Get the groups of which you are a member."""
        curr_tid = api.user.get_user()['tid']
        return jsonify(api.team.get_groups(curr_tid))

    @check_csrf
    @require_teacher
    @rate_limit(limit=20, duration=10)
    @ns.response(201, 'Classroom added')
    @ns.response(400, 'Error parsing request')
    @ns.response(401, 'Not logged in')
    @ns.response(403, 'You do not have permission to create a classroom ' +
                      'or CSRF token invalid')
    @ns.response(409, 'You already have a classroom with that name')
    @ns.response(429, 'Too many requests, slow down!')
    @ns.expect(group_req)
    def post(self):
        """Create a new group."""
        req = group_req.parse_args(strict=True)
        curr_user = api.user.get_user()

        # Don't create group if teacher already has one with same name
        if api.group.get_group(
                name=req['name'], owner_tid=curr_user['tid']) is not None:
            raise PicoException(
                'You already have a classroom with that name', 409)
        if not all([
                c in string.digits + string.ascii_lowercase + " ()-,#'&"
                for c in req['name'].lower()]):
            raise PicoException(
                "Classroom names cannot contain special characters other " +
                "than ()-,#'&", status_code=400
            )

        # Make sure this teacher hasn't already created the max no. of groups
        db = api.db.get_conn()
        created_group_count = db.groups.count_documents(
            {'owner': curr_user['tid']})
        settings = api.config.get_settings()
        if (created_group_count >= settings['group_limit']
                and not curr_user.get('admin', False)):
            raise PicoException(
                'You have created the maximum number of classrooms. ' +
                'Please contact an administrator for assistance.',
                status_code=403
            )

        gid = api.group.create_group(curr_user['tid'], req['name'])
        res = jsonify({
            'success': True,
            'gid': gid
        })
        res.status_code = 201
        return res


@ns.response(200, 'Success')
@ns.response(401, 'Not logged in')
@ns.response(403, 'Permission denied')
@ns.response(404, 'Classroom not found')
@ns.route('/<string:group_id>')
class Group(Resource):
    """Get a specific group."""

    def get(self, group_id):
        """Get a specific group."""
        group = api.group.get_group(gid=group_id)
        if not group:
            raise PicoException('Classroom not found', 404)

        group_members = [group['owner']] + group['members'] + group['teachers']
        group_teachers = [group['owner']] + group['teachers']
        if not api.user.is_logged_in():
            # Return group name and settings even if not a member.
            # Used for group invite links.
            return jsonify({
                'name': group['name'],
                'settings': group['settings']
            })
        curr_user = api.user.get_user()
        if curr_user['tid'] not in group_members and not curr_user['admin']:
            return jsonify({
                'name': group['name'],
                'settings': group['settings']
            })

        # Replace the team ids with full team objects if teacher, else remove
        if curr_user['tid'] in group_teachers:
            full_teachers = []
            for tid in group['teachers']:
                full_teachers.append(api.team.get_team_information(tid))
            group['teachers'] = full_teachers
            full_members = []
            for tid in group['members']:
                full_members.append(api.team.get_team_information(tid))
            group['members'] = full_members
        else:
            group.pop('teachers')
            group.pop('members')

        return jsonify(group)

    @require_teacher
    @ns.response(400, 'Error parsing request')
    @ns.response(403, 'CSRF token incorrect')
    @ns.response(422, 'Cannot make a previously hidden classroom public')
    @ns.expect(group_patch_req)
    def patch(self, group_id):
        """Modify a group's settings (other fields are not available)."""
        req = group_patch_req.parse_args(strict=True)

        group = api.group.get_group(gid=group_id)
        if not group:
            raise PicoException('Classroom not found', 404)

        curr_user = api.user.get_user()
        if (curr_user['tid'] not in ([group['owner']] + group['teachers'])
                and not curr_user['admin']):
            raise PicoException(
                'You do not have permission to modify this classroom.', 403
            )

        api.group.change_group_settings(group_id, req['settings'])
        return jsonify({
            'success': True
        })

    @check_csrf
    @require_teacher
    def delete(self, group_id):
        """Delete a group. Must be the owner of the group."""
        group = api.group.get_group(gid=group_id)
        if not group:
            raise PicoException('Classroom not found', 404)

        curr_user = api.user.get_user()
        if (curr_user['tid'] != group['owner']
                and not curr_user['admin']):
            raise PicoException(
                'You do not have permission to delete this classroom.', 403
            )

        api.group.delete_group(group_id)
        return jsonify({
            'success': True
        })


@ns.response(200, 'Success')
@ns.response(401, 'Not logged in')
@ns.response(403, 'Permission denied or CSRF token invalid')
@ns.response(404, 'Classroom not found')
@ns.response(422, 'Specified team is not a member of the classroom')
@ns.route('/<string:group_id>/remove_team')
class RemoveTeamResponse(Resource):
    """
    Remove a team from a group.

    If the specified team is not your own, requires teacher role within
    the group.
    """

    @check_csrf
    @require_login
    def get(self, group_id):
        """Remove your own team from this group."""<commit_msg>Fix bug with deleted groups remaining in UI<commit_after>"""Group (a.k.a Classroom) management."""
import base64
import csv
import io
import string

import api
from api import (block_before_competition, check_csrf, PicoException,
                 rate_limit, require_login, require_teacher)
from flask import jsonify
from flask_restplus import Namespace, Resource
from marshmallow import (fields, post_load, pre_load, RAISE, Schema, validate,
                         validates_schema, ValidationError)

from .schemas import (batch_registration_req, group_invite_req, group_patch_req,
                      group_remove_team_req, group_req, score_progressions_req,
                      scoreboard_page_req)

ns = Namespace('groups', description='Group management')


@ns.route('')
class GroupList(Resource):
    """Get the list of your groups, or create a new group."""

    @require_login
    @ns.response(200, 'Success')
    @ns.response(401, 'Not logged in')
    def get(self):
        """Get the groups of which you are a member."""
        curr_tid = api.user.get_user()['tid']
        return jsonify(api.team.get_groups(curr_tid))

    @check_csrf
    @require_teacher
    @rate_limit(limit=20, duration=10)
    @ns.response(201, 'Classroom added')
    @ns.response(400, 'Error parsing request')
    @ns.response(401, 'Not logged in')
    @ns.response(403, 'You do not have permission to create a classroom ' +
                      'or CSRF token invalid')
    @ns.response(409, 'You already have a classroom with that name')
    @ns.response(429, 'Too many requests, slow down!')
    @ns.expect(group_req)
    def post(self):
        """Create a new group."""
        req = group_req.parse_args(strict=True)
        curr_user = api.user.get_user()

        # Don't create group if teacher already has one with same name
        if api.group.get_group(
                name=req['name'], owner_tid=curr_user['tid']) is not None:
            raise PicoException(
                'You already have a classroom with that name', 409)
        if not all([
                c in string.digits + string.ascii_lowercase + " ()-,#'&"
                for c in req['name'].lower()]):
            raise PicoException(
                "Classroom names cannot contain special characters other " +
                "than ()-,#'&", status_code=400
            )

        # Make sure this teacher hasn't already created the max no. of groups
        db = api.db.get_conn()
        created_group_count = db.groups.count_documents(
            {'owner': curr_user['tid']})
        settings = api.config.get_settings()
        if (created_group_count >= settings['group_limit']
                and not curr_user.get('admin', False)):
            raise PicoException(
                'You have created the maximum number of classrooms. ' +
                'Please contact an administrator for assistance.',
                status_code=403
            )

        gid = api.group.create_group(curr_user['tid'], req['name'])
        res = jsonify({
            'success': True,
            'gid': gid
        })
        res.status_code = 201
        return res


@ns.response(200, 'Success')
@ns.response(401, 'Not logged in')
@ns.response(403, 'Permission denied')
@ns.response(404, 'Classroom not found')
@ns.route('/<string:group_id>')
class Group(Resource):
    """Get a specific group."""

    def get(self, group_id):
        """Get a specific group."""
        group = api.group.get_group(gid=group_id)
        if not group:
            raise PicoException('Classroom not found', 404)

        group_members = [group['owner']] + group['members'] + group['teachers']
        group_teachers = [group['owner']] + group['teachers']
        if not api.user.is_logged_in():
            # Return group name and settings even if not a member.
            # Used for group invite links.
            return jsonify({
                'name': group['name'],
                'settings': group['settings']
            })
        curr_user = api.user.get_user()
        if curr_user['tid'] not in group_members and not curr_user['admin']:
            return jsonify({
                'name': group['name'],
                'settings': group['settings']
            })

        # Replace the team ids with full team objects if teacher, else remove
        if curr_user['tid'] in group_teachers:
            full_teachers = []
            for tid in group['teachers']:
                full_teachers.append(api.team.get_team_information(tid))
            group['teachers'] = full_teachers
            full_members = []
            for tid in group['members']:
                full_members.append(api.team.get_team_information(tid))
            group['members'] = full_members
        else:
            group.pop('teachers')
            group.pop('members')

        return jsonify(group)

    @require_teacher
    @ns.response(400, 'Error parsing request')
    @ns.response(403, 'CSRF token incorrect')
    @ns.response(422, 'Cannot make a previously hidden classroom public')
    @ns.expect(group_patch_req)
    def patch(self, group_id):
        """Modify a group's settings (other fields are not available)."""
        req = group_patch_req.parse_args(strict=True)

        group = api.group.get_group(gid=group_id)
        if not group:
            raise PicoException('Classroom not found', 404)

        curr_user = api.user.get_user()
        if (curr_user['tid'] not in ([group['owner']] + group['teachers'])
                and not curr_user['admin']):
            raise PicoException(
                'You do not have permission to modify this classroom.', 403
            )

        api.group.change_group_settings(group_id, req['settings'])
        return jsonify({
            'success': True
        })

    @check_csrf
    @require_teacher
    def delete(self, group_id):
        """Delete a group. Must be the owner of the group."""
        group = api.group.get_group(gid=group_id)
        if not group:
            raise PicoException('Classroom not found', 404)

        curr_user = api.user.get_user()
        if (curr_user['tid'] != group['owner']
                and not curr_user['admin']):
            raise PicoException(
                'You do not have permission to delete this classroom.', 403
            )

        api.group.delete_group(group_id)
        api.cache.invalidate(api.team.get_groups, curr_user['tid'])
        return jsonify({
            'success': True
        })


@ns.response(200, 'Success')
@ns.response(401, 'Not logged in')
@ns.response(403, 'Permission denied or CSRF token invalid')
@ns.response(404, 'Classroom not found')
@ns.response(422, 'Specified team is not a member of the classroom')
@ns.route('/<string:group_id>/remove_team')
class RemoveTeamResponse(Resource):
    """
    Remove a team from a group.

    If the specified team is not your own, requires teacher role within
    the group.
    """

    @check_csrf
    @require_login
    def get(self, group_id):
        """Remove your own team from this group."""
{'fraction_non_alphanumeric': 0.11434080845593486, 'fraction_numerical': 0.01328381659762891, 'mean_word_length': 2.3532088122605366, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4020114', 'n_tokens_mistral': 4151, 'n_tokens_neox': 3897, 'n_words': 1132}