Example #1
0
    def _finalized_exchange_pair_checks(self, exchange_left, exchange_right):
        """Perform common checks for the consistency of two completed exchanges."""
        exchange_left.refresh_from_db()
        exchange_right.refresh_from_db()

        self.assertTrue(exchange_left.is_finalized())
        self.assertTrue(exchange_right.is_finalized())
        self.assertEqual(exchange_left.finalizer_exchange, exchange_right)
        self.assertEqual(exchange_right.finalized_exchange, exchange_left)
        self.assertIsNone(exchange_left.get_match())
        self.assertIsNone(exchange_right.get_match())

        # check if student allocations have been exchanged as requested
        # but only when we actually have student objects available (not for FREE_CHANGE, TEACHER_OFFER)
        if exchange_left.initiator_student is not None:
            self.assertEqual(exchange_right.allocation_from,
                             get_current_student_subject_allocation(self.timetable, exchange_left.initiator_student,
                                                                    Activity.from_timetable_activity(
                                                                        exchange_right.allocation_from.activityRealization.activity).subject,
                                                                    activity_types=["LAB", "LV", "AV"]))

        if exchange_right.initiator_student is not None:
            self.assertEqual(exchange_left.allocation_from,
                             get_current_student_subject_allocation(self.timetable, exchange_right.initiator_student,
                                                                    Activity.from_timetable_activity(
                                                                        exchange_left.allocation_from.activityRealization.activity).subject,
                                                                    activity_types=["LAB", "LV", "AV"]))
Example #2
0
 def test_get_current_student_subject_allocation(self):
     lab = get_current_student_subject_allocation(self.timetable, self.students[0], self.subjects[0],
                                                  activity_types=["LAB", "LV", "AV"])
     lec = get_current_student_subject_allocation(self.timetable, self.students[0], self.subjects[0],
                                                  activity_types=["P"])
     self.assertEqual(self.timetable, lab.timetable)
     self.assertEqual(self.timetable, lec.timetable)
     self.assertEqual(self.subjects[0],
                      Activity.from_timetable_activity(lab.activityRealization.activity).subject)
     self.assertEqual(self.subjects[0],
                      Activity.from_timetable_activity(lec.activityRealization.activity).subject)
Example #3
0
def render_exchanges(exchanges, show_subject=True, third_person=False, manager_student=None,
                     show_student=True, show_finalized=False, show_cancelled=False, show_cancel_link=True):
    """Directive-like helper.

    Args:
        exchanges (List[Exchange]): A list of exchanges.
        show_subject (bool): Whether to show the subject preceding everything else.
        show_student (bool): Whether to show the student's name, if SPECIFIC_STUDENT.
        third_person (bool): Whether to output third person perspective noun forms.
        manager_student (Student): The student that would accept the exchange. If None, no accept links are generated.
        show_finalized (bool): Whether to display finalized exchanges.
        show_cancelled (bool): Whether to display cancelled exchanges.
        show_cancel_link (bool): Whether to display the cancel button, if available.
    """
    filtered_exchanges = exchanges
    if not show_finalized:
        filtered_exchanges = [e for e in filtered_exchanges if not e.is_finalized()]
    if not show_cancelled:
        filtered_exchanges = [e for e in filtered_exchanges if not e.is_cancelled()]

    # sort exchanges
    sorted_exchanges = sorted(filtered_exchanges, key=_exchange_sorted_helper)

    view_models = []
    vm = namedtuple('ExchangeViewModel', ['type', 'allocation_from', 'allocation_to', 'initiator_student',
                                          'requested_finalizer_student', 'date_created', 'date_finalized',
                                          'cancelled', 'subject', 'has_initiator_student', 'accept_link',
                                          'cancel_link'])
    for ex in sorted_exchanges:
        subject = Activity.from_timetable_activity(ex.allocation_from.activityRealization.activity).subject
        view_models.append(vm(
            subject="{}".format(subject.name),
            type=ex.get_type().name,
            allocation_from="{} at {}".format(ex.allocation_from.day, ex.allocation_from.start),
            allocation_to="{} at {}".format(ex.allocation_to.day, ex.allocation_to.start),
            has_initiator_student=ex.initiator_student is not None,
            initiator_student="{} {}".format(ex.initiator_student.name.title(), ex.initiator_student.surname.title())
                              if ex.initiator_student is not None else None,
            requested_finalizer_student="{}".format(str(ex.requested_finalizer_student)),
            date_created="{}".format(str(ex.date_created)),
            date_finalized="{}".format(str(ex.date_finalized)),
            cancelled=bool(ex.date_cancelled),
            accept_link=reverse("accept_exchange", kwargs={
                "timetable_slug": ex.allocation_from.timetable.slug,
                "exchange_id": ex.id
            }) if manager_student and is_exchange_acceptable(ex, manager_student) else None,
            cancel_link=reverse("cancel_exchange", kwargs={
                "timetable_slug": ex.allocation_from.timetable.slug,
                "exchange_id": ex.id
            }) if manager_student and is_exchange_cancellable(ex, manager_student) else None
        ))

    return {
        "exchanges": view_models,
        "show_subject": show_subject,
        "show_student": show_student,
        "show_cancel_link": show_cancel_link,
        "source_word": "Their" if not third_person else "From",
        "destination_word": "for your" if not third_person else "to"
    }
Example #4
0
 def test_get_student_subject_other_allocations(self):
     labs = get_student_subject_other_allocations(self.timetable, self.students[0], self.subjects[0],
                                                  activity_types=["LAB", "LV", "AV"])
     lecs = get_student_subject_other_allocations(self.timetable, self.students[0], self.subjects[0],
                                                  activity_types=["P"])
     self.assertEqual(5, len(labs))
     self.assertEqual(0, len(lecs))
     self.assertEqual(self.subjects[0], Activity.from_timetable_activity(labs[0].activityRealization.activity).subject)
Example #5
0
 def _cancel_exchange_get_params(self, exchange: Exchange):
     """Cancel an exchange object and return its parameters for end-to-end testing."""
     result = dict(
         timetable=self.timetable,
         source_person=exchange.initiator_student,
         requested_student=exchange.requested_finalizer_student,
         subject_transfer_to_map={Activity.from_timetable_activity(
             exchange.allocation_from.activityRealization.activity).subject.id: exchange.allocation_to},
         force_allocation_from=exchange.allocation_from if exchange.initiator_student is None else None
     )
     exchange.date_cancelled = datetime.utcnow()
     exchange.save()
     return result
Example #6
0
def accept_exchange(request, timetable_slug, exchange_id):
    selected_timetable = get_object_or_404(Timetable, slug=timetable_slug)
    student = Student.from_user(request.user)
    exchange = get_object_or_404(Exchange, id=exchange_id)

    if exchange.is_finalized():
        header = "Exchange already finalized."
        message = "This exchange has already been finalized."
    elif exchange.is_cancelled():
        header = "Exchange cancelled."
        message = "This exchange is cancelled."
    else:
        subject = Activity.from_timetable_activity(
            exchange.allocation_from.activityRealization.activity).subject
        try:
            any_fulfilled = process_new_exchange_request(
                selected_timetable, student, exchange.initiator_student if
                exchange.get_type() == ExchangeType.SPECIFIC_STUDENT else None,
                {subject.id: exchange.allocation_from})
        except FormProcessingError as e:
            return render(
                request, "exchange/exchange_create_result.html", {
                    "selected_timetable": selected_timetable,
                    "header": e.header,
                    "message": e.message
                })
        if any_fulfilled:
            header = "Exchange fulfilled!"
            message = "Your request was fulfilled. Check your new slot on your timetable."
        else:
            header = "Request added to the queue."
            message = "Your request could not be fulfilled immediately and was placed into the queue for it to " \
                      "be fulfilled in the future. Check back at a later date!"
    return render(
        request, "exchange/exchange_create_result.html", {
            "selected_timetable": selected_timetable,
            "header": header,
            "message": message
        })
Example #7
0
 def test_get_available_exchanges(self):
     exchanges = get_available_exchanges(self.timetable, self.students[0])
     self.assertEqual(len(exchanges), 10)
     for e in exchanges:
         self.assertIn(Activity.from_timetable_activity(e.allocation_from.activityRealization.activity).subject,
                       self.subjects)
Example #8
0
    def sync_activities_with_fri_najave(self, timetable, semester, year,
                                        location, update_subject):
        """
        Sinhronizira učitelje v najavah z učitelji v aktivnostih.
        Če je potrebno, ustvari nove aktivnosti.
        Starih aktivnosti, ki jih ni v najavah, ne pobriše.
        Ce dodamo kodo predmeta, se posodobijo aktivnosti samo pri tem predmetu.
        Ce je koda predmeta None, potem se posodobijo vse aktivnosti.
        """
        logger.info("Entering syncActivitiesWithFRINajave")

        def activity_name(subject, lecture_type):
            name = "{0}({1})_{2}".format(subject.name, subject.code,
                                         lecture_type.short_name)
            logger.debug("Generated name {}".format(name))
            return name

        def activity_short_name(subject, lecture_type):
            name = "{0}_{1}".format(subject.short_name,
                                    lecture_type.short_name)
            logger.debug("Generated sname {}".format(name))
            return name

        def get_teacher_codes(cikel, lecture_type_id):
            logger.debug("Processing {}".format(cikel))
            codes = set()
            mapping_urnik_studis = {1: [1, 7], 2: [3], 3: [2], 4: [8], 6: [4]}
            for izvajalec in cikel['izvajalci']:
                if izvajalec['tip_izvajanja']['id'] in mapping_urnik_studis[
                        lecture_type_id]:
                    codes.add(izvajalec['delavec_sifra'])
            logger.debug("Got codes for {}: {}".format(lecture_type_id, codes))
            return codes

        def subject_lecture_types(cikel):
            # id from studis to our system
            # Studis:
            # [{u'id': 1, 'title': {u'en': None, 'sl': 'Predavanja'}},
            # {u'id': 2, 'title': {u'en': None, 'sl': 'Avditorne vaje'}},
            # {u'id': 3, 'title': {u'en': None, 'sl': 'Laboratorijske vaje'}},
            # {u'id': 4, 'title': {u'en': None, 'sl': 'Seminar'}},
            # {u'id': 5, 'title': {u'en': None, 'sl': 'Poobla\u0161\u010denec'}},
            # {u'id': 6, 'title': {u'en': None, 'sl': 'Koordinator'}},
            # {u'id': 7, 'title': {u'en': None, 'sl': 'Nosilec'}},
            # {u'id': 8, 'title': {u'en': None, 'sl': 'Laborant'}}]
            mapping_studis_urnik = {
                1: 1,
                2: 3,
                3: 2,
                4: 6,
                5: None,
                6: None,
                7: 1,
                8: 2
            }
            lecture_types_ids = set(
                mapping_studis_urnik[t['tip_izvajanja']['id']]
                for t in cikel['izvajalci'])
            lecture_types_ids.discard(None)
            return lecture_types_ids

        def get_duration(lecture_type_id, izvajanje):
            """
            Get subject duration. The duration of subject depends on subject and lecture type.
            It is read from Studis database.
            """
            mapping_urnik_studis = {
                1: 'st_ur_predavanj',
                2: 'st_ur_lab_vaj',
                3: 'st_ur_avd_vaj',
                6: 'st_ur_seminarja',
                8: 'st_ur_lab_vaj',
            }
            if izvajanje[mapping_urnik_studis[lecture_type_id]] is None:
                return None

            add_duration = 0
            # Weird fix: sometimes hours are attributed to seminar
            if izvajanje[
                    'st_ur_seminarja'] is not None and lecture_type_id not in [
                        1, 6
                    ]:
                add_duration = izvajanje['st_ur_seminarja']
            total_duration = add_duration + izvajanje[
                mapping_urnik_studis[lecture_type_id]]
            return total_duration / 15

        semester_id = semester['id']
        studij = Studij(year)
        sifranti = Sifranti()
        najave = Najave(year)
        # Only process this semester izvajanja
        izvajanja = [
            i for i in studij.get_izvajanja() if i["semester"] == semester_id
        ]

        izvajanja_subject_ids = defaultdict(list)
        for izvajanje in izvajanja:
            izvajanja_subject_ids[izvajanje['idpredmet']].append(izvajanje)

        for cikel in najave.get_predmeti_cikli():
            subject_code = cikel['predmet_sifra']
            # Skip subjects we should no update
            if update_subject is not None and update_subject != subject_code:
                continue

            found = False
            izvajanja = izvajanja_subject_ids[cikel['predmet_id']]
            for izvajanje in izvajanja:
                izvajanje_id = izvajanje["id"].split("-")[1].strip()
                cikel_izvajanje_id = str(cikel["izvajanje_id"])
                if cikel_izvajanje_id == izvajanje_id:
                    found = True
            if not found:
                logger.info(
                    "No matching izvajanje for cikel {0}, skipping".format(
                        cikel).encode("utf-8"))
                continue

            izvajanje = izvajanja[0]
            if izvajanje['izvaja_partnerska_institucija']:
                logger.info('This izvajanje is managed by other faculty, skip')
                continue
            logger.info("Looking for subject code {0}".format(
                subject_code).encode("utf-8"))
            try:
                subject = Subject.objects.get(code=subject_code)
            except Exception:
                logger.Exception(
                    f"Error retrieving subject with code {subject_code}")
                continue
            logger.debug("Processing subject {}".format(subject))
            lecture_types = subject_lecture_types(cikel)
            logger.debug("Got lecture types: {}".format(lecture_types))

            for lecture_type_id in lecture_types:
                logger.debug("Processing lt {}".format(lecture_type_id))
                lecture_type = LectureType.objects.get(pk=lecture_type_id)
                logger.debug("lt {}".format(lecture_type))
                activities = subject.activities.filter(
                    lecture_type=lecture_type,
                    activityset=timetable.activityset)
                logger.debug("Got activities {}".format(activities))
                teacher_codes = get_teacher_codes(cikel, lecture_type_id)
                logger.debug("Got teacher codes {}".format(teacher_codes))
                duration = get_duration(lecture_type_id, izvajanje)
                logger.debug("Got duration {}".format(duration))

                teachers = []
                for code in teacher_codes:
                    try:
                        teacher = Teacher.objects.get(code=code)
                        teachers.append(teacher)
                    except ObjectDoesNotExist as e:
                        logger.exception(
                            "Teacher with code {0} on subject {1} does not exist"
                            .format(code, subject.code))

                if activities.count() == 0:
                    logger.debug(
                        "Activity of type {0} for {1} not found. Creating one."
                        .format(lecture_type, subject))
                    if duration is None:
                        logger.debug(
                            'Duration for type {0} is 0, skipping.'.format(
                                lecture_type))
                        continue
                    activity = Activity(
                        subject=subject,
                        lecture_type=lecture_type,
                        activityset=timetable.activityset,
                        duration=duration,
                        name=activity_name(subject, lecture_type),
                        short_name=activity_short_name(subject, lecture_type),
                        type=lecture_type.short_name)
                    activity.save()
                    activity.locations.add(location)
                    logger.debug("Created activity {0}.".format(activity))
                    activities = [activity]
                for activity in activities:
                    activity.name = activity_name(subject, lecture_type)
                    activity.short_name = activity_short_name(
                        subject, lecture_type)
                    print("Name, shortname")
                    print(activity.name, activity.short_name)
                    activity.save()
                    activity.teachers.clear()
                    activity.teachers.add(*teachers)
        logger.info("Exiting syncActivitiesWithFRINajave")
Example #9
0
def import_unitime_activities(tt, solution):
    """Read all Unitime allocations for the given solution.
    Read all scheduling subparts for these allocations and create activities and
    corresponding realizations and alocations.
    """
    day_mapping = {
        64: "MON",
        32: "TUE",
        16: "WED",
        8: "THU",
        4: "FRI",
        2: "SAT",
        1: "SUN"
    }
    itype_type_mapping = {10: 'P', 30: 'LV', 20: 'AV'}
    db = Database()
    query = (
        "SELECT c.uniqueid, slot, days, r.external_uid, "
        "di.external_uid, ss.uniqueid, ss.min_per_week, ss.itype "
        "FROM assignment AS a "
        "JOIN assigned_rooms AS ar ON (a.uniqueid = ar.assignment_id) "
        "JOIN assigned_instructors AS ai ON (a.uniqueid = ai.assignment_id) "
        "JOIN  departmental_instructor AS di ON "
        "(di.uniqueid = ai.instructor_id) "
        "JOIN class_ AS c ON (c.uniqueid = a.class_id) "
        "JOIN room AS r ON (r.uniqueid = ar.room_id) "
        "JOIN scheduling_subpart AS ss ON (ss.uniqueid = c.subpart_id) "
        "WHERE solution_id={0}").format(solution)
    db.execute(query)
    allocations = db.fetch_all_rows()
    # Subparts represent allocations
    subpart_data = dict()
    subpart_ids = set()
    for _, _, _, _, teacher_id, subpart_id, mpw, itype in allocations:
        subpart_ids.add(subpart_id)
        if subpart_id not in subpart_data:
            subpart_data[subpart_id] = (itype, mpw, [teacher_id])
        else:
            subpart_data[subpart_id][2].append(teacher_id)
    # For each subpart id we create one activity
    for subpart_id in subpart_ids:
        # First we have to determine the subject for the subpart
        query = """SELECT co.external_uid FROM scheduling_subpart AS ss
        JOIN instr_offering_config AS ioc ON (ss.config_id=ioc.uniqueid)
        JOIN course_offering AS co ON (co.instr_offr_id=ioc.instr_offr_id)
        WHERE ss.uniqueid={0}""".format(subpart_id)
        db.execute(query)
        assert db.rowcount == 1, "There should be exactly one subject per subpart"
        subject = Subject.objects.get(pk=db.fetch_next_row()[0])
        itype, minutes, teacher_ids = subpart_data[subpart_id]
        teachers = [
            Teacher.objects.get(pk=teacher_id) for teacher_id in teacher_ids
        ]
        type_short_name = itype_type_mapping[itype]
        lecture_type = LectureType.objects.get(short_name=type_short_name)
        activity = Activity(
            subject=subject,
            lecture_type=lecture_type,
            name=subject.name + "_" + type_short_name,
            short_name=subject.short_name,
            activityset=tt.activityset,
            type=type_short_name,
            duration=minutes / 60,
        )
        activity.save()
        for teacher in teachers:
            activity.teachers.add(teacher)
        # Get all allocations and iterate through all class ids
        # Classes represent realizations
        activity_allocations = [a for a in allocations if a[5] == subpart_id]
        class_ids = set([a[0] for a in activity_allocations])
        for class_id in class_ids:
            class_allocations = [
                a for a in activity_allocations if a[0] == class_id
            ]
            class_teacher_ids = [a[4] for a in class_allocations]
            class_teachers = [
                Teacher.objects.get(pk=teacher_id)
                for teacher_id in class_teacher_ids
            ]
            slot, day, room_id = class_allocations[1:1 + 3]
            day = day_mapping[day]
            hour = slot / 12
            # minute = slot % 12 * 5
            # V resnici se pri nama vedno zacne ob uri in ne 15 cez
            minute = 0
            allocation_time = "{0:02d}:{1:02d}".format(hour, minute)
            room = Classroom.objects.get(id=room_id)
            realization = ActivityRealization(activity=activity)
            realization.save()
            realization.teachers.add(*class_teachers)
            Allocation(timetable=tt,
                       activityRealization=realization,
                       classroom=room,
                       day=day,
                       start=allocation_time).save()