<commit_before>from aiohttp.web import Request, Response, HTTPTemporaryRedirect
from datetime import datetime

from ._format import JSONResonse, get_match_info_or_error, match_info_to_id, get_params, HTTPError
from cogs.scheduler.constants import GROUP_DEADLINES
from cogs.db.models import ProjectGroup

from cogs.security.middleware import permit


async def get_all(request: Request) -> Response:
    """
    Get information about currently existing rotations

    :param request:
    :return:
    """
    db = request.app["db"]
    rotations = {f"{rotation.series}-{rotation.part}": f"/api/series/{rotation.series}/{rotation.part}"
                 for rotation in db.get_all_series()}
    return JSONResonse(links=rotations)


async def get(request: Request) -> Response:
    """
    Get specific information a rotation

    :param request:
    :return:
    """
    db = request.app["db"]

    rotation = get_match_info_or_error(request, ["group_series", "group_part"], db.get_project_group)
    year = match_info_to_id(request, "group_series")

    user = request["user"]
    if not user.can_view_group(rotation):
        raise HTTPError(status=403,
                        message="Cannot view rotation")

    return JSONResonse(links={"parent": f"/api/series/{year}",
                              "projects": [f"/api/projects/{project.id}" for project in rotation.projects]},
                       data=rotation.serialise())


async def latest(request: Request) -> JSONResonse:
    """
    Redirect to the latest rotation

    :param request:
    :return:
    """
    db = request.app["db"]
    latest = db.get_most_recent_group()
    return HTTPTemporaryRedirect(f"/api/series/{latest.series}/{latest.part}")


@permit("create_project_groups")
async def create(request: Request) -> JSONResonse:
    """
    Create a new rotation

    :param request:
    :return:
    """
    db = request.app["db"]

    rotation_data = await get_params(request, {"supervisor_submit": str,
                                             "student_invite": str,
                                             "student_choice": str,
                                             "student_complete": str,
                                             "marking_complete": str,
                                             "series": int,
                                             "part": int})

    try:
        deadlines = {deadline: datetime.strptime(getattr(rotation_data, deadline), "%Y-%m-%d")
                     for deadline in GROUP_DEADLINES}
    except ValueError:
        raise HTTPError(status=400,
                        message="Not all deadlines follow YYYY-MM-DD format")

    new_group = ProjectGroup(series=rotation_data.series,
                             part=rotation_data.part,
                             student_viewable=False,
                             student_choosable=False,
                             student_uploadable=False,
                             can_finalise=False,
                             read_only=False,
                             manual_supervisor_reminders=None,
                             **deadlines)

    db.add(new_group)
    db.commit()

    return JSONResonse(status=201)


@permit("create_project_groups")
async def edit(request: Request) -> JSONResonse:
    """
    Edit an existing rotation

    :param request:
    :return:
    """
    db = request.app["db"]
    mail = request.app["mailer"]
    scheduler = request.app["scheduler"]

    rotation = get_match_info_or_error(request, ["group_series", "group_part"], db.get_project_group)

    rotation_data = await get_params(request, {"supervisor_submit": str,
                                             "student_invite": str,
                                             "student_choice": str,
                                             "student_complete": str,
                                             "marking_complete": str})

    try:
        deadlines = {deadline: datetime.strptime(getattr(rotation_data, deadline), "%Y-%m-%d")
                     for deadline in GROUP_DEADLINES}
    except ValueError:
        raise HTTPError(status=400,
                        message="Not all deadlines follow YYYY-MM-DD format")

    if deadlines["supervisor_submit"].date() != rotation.supervisor_submit:
        for supervisor in db.get_users_by_permission("create_projects"):
            mail.send(supervisor,
                      f"supervisor_invite_{rotation.part}",
                      new_deadline=deadlines["supervisor_submit"],
                      extension=True)

    for deadline in deadlines:
        if deadlines[deadline].date() != getattr(rotation, deadline):
            scheduler.schedule_deadline(deadlines[deadline], deadline, rotation)

        setattr(rotation, deadline, deadlines[deadline])

    db.commit()

    return JSONResonse(status=204)


@permit("create_project_groups")
async def remind(request: Request) -> JSONResonse:
    """
    Send supervisors an email reminding them to submit a project

    :param request:
    :return:
    """
    db = request.app["db"]
    mail = request.app["mailer"]

    rotation = get_match_info_or_error(request, ["group_series", "group_part"], db.get_project_group)
    rotation.manual_supervisor_reminders = datetime.now().date()
    db.commit()<commit_msg>Modify edit rotation endpoint to take rotation attributes as well as deadline times<commit_after>from aiohttp.web import Request, Response, HTTPTemporaryRedirect
from datetime import datetime

from ._format import JSONResonse, get_match_info_or_error, match_info_to_id, get_params, HTTPError
from cogs.scheduler.constants import GROUP_DEADLINES
from cogs.db.models import ProjectGroup
from typing import Dict

from cogs.security.middleware import permit


async def get_all(request: Request) -> Response:
    """
    Get information about currently existing rotations

    :param request:
    :return:
    """
    db = request.app["db"]
    rotations = {f"{rotation.series}-{rotation.part}": f"/api/series/{rotation.series}/{rotation.part}"
                 for rotation in db.get_all_series()}
    return JSONResonse(links=rotations)


async def get(request: Request) -> Response:
    """
    Get specific information a rotation

    :param request:
    :return:
    """
    db = request.app["db"]

    rotation = get_match_info_or_error(request, ["group_series", "group_part"], db.get_project_group)
    year = match_info_to_id(request, "group_series")

    user = request["user"]
    if not user.can_view_group(rotation):
        raise HTTPError(status=403,
                        message="Cannot view rotation")

    return JSONResonse(links={"parent": f"/api/series/{year}",
                              "projects": [f"/api/projects/{project.id}" for project in rotation.projects]},
                       data=rotation.serialise())


async def latest(request: Request) -> JSONResonse:
    """
    Redirect to the latest rotation

    :param request:
    :return:
    """
    db = request.app["db"]
    latest = db.get_most_recent_group()
    return HTTPTemporaryRedirect(f"/api/series/{latest.series}/{latest.part}")


@permit("create_project_groups")
async def create(request: Request) -> JSONResonse:
    """
    Create a new rotation

    :param request:
    :return:
    """
    db = request.app["db"]

    rotation_data = await get_params(request, {
        "supervisor_submit": str,
        "student_invite": str,
        "student_choice": str,
        "student_complete": str,
        "marking_complete": str,
        "series": int,
        "part": int
    })

    try:
        deadlines = {deadline: datetime.strptime(getattr(rotation_data, deadline), "%Y-%m-%d")
                     for deadline in GROUP_DEADLINES}
    except ValueError:
        raise HTTPError(status=400,
                        message="Not all deadlines follow YYYY-MM-DD format")

    new_group = ProjectGroup(series=rotation_data.series,
                             part=rotation_data.part,
                             student_viewable=False,
                             student_choosable=False,
                             student_uploadable=False,
                             can_finalise=False,
                             read_only=False,
                             manual_supervisor_reminders=None,
                             **deadlines)

    db.add(new_group)
    db.commit()

    return JSONResonse(status=201)


@permit("create_project_groups")
async def edit(request: Request) -> JSONResonse:
    """
    Edit an existing rotation

    :param request:
    :return:
    """
    db = request.app["db"]
    mail = request.app["mailer"]
    scheduler = request.app["scheduler"]

    rotation = get_match_info_or_error(request, ["group_series", "group_part"], db.get_project_group)

    rotation_data = await get_params(request, {"deadlines": Dict[str, str], "attrs": Dict[str, bool]})
    try:
        deadlines = {deadline: datetime.strptime(rotation_data.deadlines[deadline], "%Y-%m-%d")
                     for deadline in GROUP_DEADLINES}
    except ValueError:
        raise HTTPError(status=400,
                        message="Not all deadlines follow YYYY-MM-DD format")

    if deadlines["supervisor_submit"].date() != rotation.supervisor_submit:
        for supervisor in db.get_users_by_permission("create_projects"):
            mail.send(supervisor,
                      f"supervisor_invite_{rotation.part}",
                      new_deadline=deadlines["supervisor_submit"],
                      extension=True)

    for deadline in deadlines:
        if deadlines[deadline].date() != getattr(rotation, deadline):
            scheduler.schedule_deadline(deadlines[deadline], deadline, rotation)
        setattr(rotation, deadline, deadlines[deadline])

    for attr, value in rotation_data.attrs.items():
        setattr(rotation, attr, value)

    db.commit()

    return JSONResonse(links={"parent": f"/api/series/{rotation.series}",
                              "projects": [f"/api/projects/{project.id}" for project in rotation.projects]},
                       data=rotation.serialise())


@permit("create_project_groups")
async def remind(request: Request) -> JSONResonse:
    """
    Send supervisors an email reminding them to submit a project

    :param request:
    :return:
    """
    db = request.app["db"]
    mail = request.app["mailer"]

    rotation = get_match_info_or_error(request, ["group_series", "group_part"], db.get_project_group)
    rotation.manual_supervisor_reminders = datetime.now().date()
    db.commit()
{'fraction_non_alphanumeric': 0.10743255989435956, 'fraction_numerical': 0.0025466893039049238, 'mean_word_length': 2.316546762589928, 'pattern_counts': {'":': 29, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6931855', 'n_tokens_mistral': 2977, 'n_tokens_neox': 2846, 'n_words': 624}