def _check_project_unhappy(project, student):
    """Determine whether ``project`` is unhappy because either:
    - they and their supervisor are under-subscribed;
    - they are under-subscribed, their supervisor is full, and either
      ``student`` is in the supervisor's matching or the supervisor prefers
      ``student`` to their worst current matching;
    - ``project`` is full and their supervisor prefers ``student`` to the
      worst student in the matching of ``project``.
    """

    supervisor = project.supervisor

    project_undersubscribed = len(project.matching) < project.capacity
    both_undersubscribed = (
        project_undersubscribed
        and len(supervisor.matching) < supervisor.capacity
    )

    supervisor_full = len(supervisor.matching) == supervisor.capacity

    swap_available = (
        student in supervisor.matching and student.matching != project
    ) or supervisor.prefers(student, supervisor.get_worst_match())

    project_upsetting_supervisor = len(
        project.matching
    ) == project.capacity and supervisor.prefers(
        student, project.get_worst_match()
    )

    return (
        both_undersubscribed
        or (project_undersubscribed and supervisor_full and swap_available)
        or project_upsetting_supervisor
    )