Esempio n. 1
0
    def setUp(self):
        self.scored_objects = [
            ScoredObject(key=1,
                         score=0.7,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]

        self.comparisons = []
        self.criterion_scores = {
            1: {
                'c1': 100,
                'c2': 200
            },
            2: {
                'c1': 100,
                'c2': 200
            },
            3: {
                'c1': 100,
                'c2': 200
            },
            4: {
                'c1': 100,
                'c2': 200
            },
        }
        self.criterion_weights = {'c1': 0.4, 'c2': 0.6}
Esempio n. 2
0
 def convert_to_scored_object(self):
     return ScoredObject(key=self.answer_id,
                         score=self.score,
                         variable1=self.variable1,
                         variable2=self.variable2,
                         rounds=self.rounds,
                         wins=self.wins,
                         loses=self.loses,
                         opponents=self.opponents)
Esempio n. 3
0
    def setUp(self):
        self.scored_objects = [
            ScoredObject(key=1,
                         score=0.7,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]

        self.comparisons = []
Esempio n. 4
0
    def _get_new_comparison_pair(cls, course_id, assignment_id, user_id,
                                 pairing_algorithm, comparisons):
        from . import Assignment, UserCourse, CourseRole, Answer, AnswerScore, PairingAlgorithm

        # ineligible authors - eg. instructors, TAs, dropped student, current user
        non_students = UserCourse.query \
            .filter(and_(
                UserCourse.course_id == course_id,
                UserCourse.course_role != CourseRole.student
            ))
        ineligible_user_ids = [non_student.user_id \
            for non_student in non_students]
        ineligible_user_ids.append(user_id)

        answers_with_score = Answer.query \
            .with_entities(Answer, AnswerScore.score ) \
            .outerjoin(AnswerScore) \
            .filter(and_(
                Answer.user_id.notin_(ineligible_user_ids),
                Answer.assignment_id == assignment_id,
                Answer.active == True,
                Answer.practice == False,
                Answer.draft == False
            )) \
            .all()

        scored_objects = []
        for answer_with_score in answers_with_score:
            scored_objects.append(
                ScoredObject(key=answer_with_score.Answer.id,
                             score=answer_with_score.score,
                             rounds=answer_with_score.Answer.round,
                             variable1=None,
                             variable2=None,
                             wins=None,
                             loses=None,
                             opponents=None))

        comparison_pairs = [
            comparison.convert_to_comparison_pair()
            for comparison in comparisons
        ]

        comparison_pair = generate_pair(package_name=pairing_algorithm.value,
                                        scored_objects=scored_objects,
                                        comparison_pairs=comparison_pairs,
                                        log=current_app.logger)

        return comparison_pair
Esempio n. 5
0
    def update_scores_1vs1(cls, comparison):
        from . import AnswerScore, AnswerCriterionScore, \
            ComparisonCriterion, ScoringAlgorithm

        assignment_id = comparison.assignment_id
        answer1_id = comparison.answer1_id
        answer2_id = comparison.answer2_id

        # get all other comparisons for the answers not including the ones being calculated
        other_comparisons = Comparison.query \
            .options(load_only('winner', 'answer1_id', 'answer2_id')) \
            .filter(and_(
                Comparison.assignment_id == assignment_id,
                Comparison.id != comparison.id,
                or_(
                    Comparison.answer1_id.in_([answer1_id, answer2_id]),
                    Comparison.answer2_id.in_([answer1_id, answer2_id])
                )
            )) \
            .all()

        scores = AnswerScore.query \
            .filter( AnswerScore.answer_id.in_([answer1_id, answer2_id]) ) \
            .all()

        # get all other criterion comparisons for the answers not including the ones being calculated
        other_criterion_comparisons = ComparisonCriterion.query \
            .join("comparison") \
            .filter(and_(
                Comparison.assignment_id == assignment_id,
                ~Comparison.id == comparison.id,
                or_(
                    Comparison.answer1_id.in_([answer1_id, answer2_id]),
                    Comparison.answer2_id.in_([answer1_id, answer2_id])
                )
            )) \
            .all()

        criteria_scores = AnswerCriterionScore.query \
            .filter( AnswerCriterionScore.answer_id.in_([answer1_id, answer2_id]) ) \
            .all()

        #update answer criterion scores
        updated_criteria_scores = []
        for comparison_criterion in comparison.comparison_criteria:
            criterion_id = comparison_criterion.criterion_id

            score1 = next((criterion_score
                           for criterion_score in criteria_scores
                           if criterion_score.answer_id == answer1_id
                           and criterion_score.criterion_id == criterion_id),
                          AnswerCriterionScore(assignment_id=assignment_id,
                                               answer_id=answer1_id,
                                               criterion_id=criterion_id))
            updated_criteria_scores.append(score1)
            key1_scored_object = score1.convert_to_scored_object(
            ) if score1 != None else ScoredObject(
                key=answer1_id,
                score=None,
                variable1=None,
                variable2=None,
                rounds=0,
                wins=0,
                opponents=0,
                loses=0,
            )

            score2 = next((criterion_score
                           for criterion_score in criteria_scores
                           if criterion_score.answer_id == answer2_id
                           and criterion_score.criterion_id == criterion_id),
                          AnswerCriterionScore(assignment_id=assignment_id,
                                               answer_id=answer2_id,
                                               criterion_id=criterion_id))
            updated_criteria_scores.append(score2)
            key2_scored_object = score2.convert_to_scored_object(
            ) if score2 != None else ScoredObject(
                key=answer2_id,
                score=None,
                variable1=None,
                variable2=None,
                rounds=0,
                wins=0,
                opponents=0,
                loses=0,
            )

            result_1, result_2 = calculate_score_1vs1(
                package_name=ScoringAlgorithm.elo.value,
                key1_scored_object=key1_scored_object,
                key2_scored_object=key2_scored_object,
                winner=comparison_criterion.comparison_pair_winner(),
                other_comparison_pairs=[
                    c.convert_to_comparison_pair()
                    for c in other_criterion_comparisons
                    if c.criterion_id == criterion_id
                ],
                log=current_app.logger)

            for score, result in [(score1, result_1), (score2, result_2)]:
                score.score = result.score
                score.variable1 = result.variable1
                score.variable2 = result.variable2
                score.rounds = result.rounds
                score.wins = result.wins
                score.loses = result.loses
                score.opponents = result.opponents

        updated_scores = []
        score1 = next(
            (score for score in scores if score.answer_id == answer1_id),
            AnswerScore(assignment_id=assignment_id, answer_id=answer1_id))
        updated_scores.append(score1)
        key1_scored_object = score1.convert_to_scored_object(
        ) if score1 != None else ScoredObject(
            key=answer1_id,
            score=None,
            variable1=None,
            variable2=None,
            rounds=0,
            wins=0,
            opponents=0,
            loses=0,
        )

        score2 = next(
            (score for score in scores if score.answer_id == answer2_id),
            AnswerScore(assignment_id=assignment_id, answer_id=answer2_id))
        updated_scores.append(score2)
        key2_scored_object = score2.convert_to_scored_object(
        ) if score2 != None else ScoredObject(
            key=answer2_id,
            score=None,
            variable1=None,
            variable2=None,
            rounds=0,
            wins=0,
            opponents=0,
            loses=0,
        )

        result_1, result_2 = calculate_score_1vs1(
            package_name=ScoringAlgorithm.elo.value,
            key1_scored_object=key1_scored_object,
            key2_scored_object=key2_scored_object,
            winner=comparison.comparison_pair_winner(),
            other_comparison_pairs=[
                c.convert_to_comparison_pair() for c in other_comparisons
            ],
            log=current_app.logger)

        for score, result in [(score1, result_1), (score2, result_2)]:
            score.score = result.score
            score.variable1 = result.variable1
            score.variable2 = result.variable2
            score.rounds = result.rounds
            score.wins = result.wins
            score.loses = result.loses
            score.opponents = result.opponents

        db.session.add_all(updated_criteria_scores)
        db.session.add_all(updated_scores)
        db.session.commit()

        return updated_scores
Esempio n. 6
0
    #    ScoringAlgorithm.comparative_judgement.value,
    ScoringAlgorithm.elo.value,
    #    ScoringAlgorithm.true_skill.value
]
for pairing_package_name in pairing_packages:
    for scoring_package_name in scoring_packages:
        results = []

        for runtime in range(1, TIMES_TO_RUN_PER_ALGORITHM + 1):
            answers = []
            for key in ANSWER_RANGE:
                answers.append(
                    ScoredObject(key=key,
                                 score=0,
                                 variable1=None,
                                 variable2=None,
                                 rounds=0,
                                 opponents=0,
                                 wins=0,
                                 loses=0))

            students = []
            for key in STUDENT_RANGE:
                students.append({
                    'key': key,
                    'comparisons_left': NUMBER_OF_ROUNDS / 2,
                    'comparisons_completed': []
                })

            comparisons = []

            for round in range(1, NUMBER_OF_ROUNDS + 1):
    def test_generate_pair(self, mock_shuffle, mock_random):
        ##
        # empty scored objects set
        scored_objects = []
        comparisons = []
        criterion_scores = {}
        criterion_weights = {'c1': 0.4, 'c2': 0.6}

        with self.assertRaises(InsufficientObjectsForPairException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons, criterion_scores,
                criterion_weights)

        ##
        # not enough scored objects for comparison (only 1 scored object)
        scored_objects = [
            ScoredObject(key=1,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        criterion_scores = {1: {'c1': 100, 'c2': 200}}
        criterion_weights = {'c1': 0.4, 'c2': 0.6}

        with self.assertRaises(InsufficientObjectsForPairException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons, criterion_scores,
                criterion_weights)

        ##
        # User compard all objects error
        scored_objects = [
            ScoredObject(key=1,
                         score=0.7,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = [ComparisonPair(1, 2, None)]
        criterion_scores = {
            1: {
                'c1': 100,
                'c2': 200
            },
            2: {
                'c1': 110,
                'c2': 220
            }
        }
        criterion_weights = {'c1': 0.7, 'c2': 0.3}

        with self.assertRaises(UserComparedAllObjectsException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons, criterion_scores,
                criterion_weights)

        ##
        # User compared all objects error
        scored_objects = [
            ScoredObject(key=1,
                         score=0.7,
                         variable1=None,
                         variable2=None,
                         rounds=4,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=4,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        criterion_scores = {
            1: {
                'c1': 100,
                'c2': 200
            },
            2: {
                'c1': 100,
                'c2': 200
            },
            3: {
                'c1': 100,
                'c2': 200
            },
            4: {
                'c1': 100,
                'c2': 200
            }
        }
        criterion_weights = {'c1': 0.7, 'c2': 0.3}

        max_comparisons = 6  # n(n-1)/2 = 4*3/2
        for i in range(0, max_comparisons):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons, criterion_scores,
                criterion_weights)
            comparisons.append(
                ComparisonPair(results.key1, results.key2, results.key1))

        # if trying to run one more time, should run into all objects compared error
        with self.assertRaises(UserComparedAllObjectsException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons, criterion_scores,
                criterion_weights)

        ##
        # Returns a comparison pair
        scored_objects = [
            ScoredObject(key=1,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        criterion_scores = {
            1: {
                'c1': 100,
                'c2': 200
            },
            2: {
                'c1': 110,
                'c2': 220
            }
        }
        criterion_weights = {'c1': 0.7, 'c2': 0.3}
        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons,
                                                    criterion_scores,
                                                    criterion_weights)

        self.assertIsInstance(results, ComparisonPair)
        min_key = min([results.key1, results.key2])
        max_key = max([results.key1, results.key2])

        self.assertEqual(min_key, 1)
        self.assertEqual(max_key, 2)
        self.assertEqual(results.winner, None)

        ##
        # Selects lowest round objects first
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=5,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=6,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        criterion_scores = {
            1: {
                'c1': 100,
                'c2': 200
            },
            2: {
                'c1': 100,
                'c2': 200
            },
            3: {
                'c1': 100,
                'c2': 200
            },
            4: {
                'c1': 100,
                'c2': 200
            },
            5: {
                'c1': 100,
                'c2': 200
            },
            6: {
                'c1': 100,
                'c2': 200
            }
        }
        criterion_weights = {'c1': 0.7, 'c2': 0.3}

        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons,
                                                    criterion_scores,
                                                    criterion_weights)

        # 5 & 6 should be selected as the lowest valid round objects
        min_key = min([results.key1, results.key2])
        max_key = max([results.key1, results.key2])
        self.assertEqual(min_key, 5)
        self.assertEqual(max_key, 6)

        ##
        # Selects lowest criterion score delta
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.9,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        criterion_scores = {
            1: {
                'c1': 100,
                'c2': 100
            },
            2: {
                'c1': 110,
                'c2': 110
            },
            3: {
                'c1': 115,
                'c2': 100
            }
        }
        criterion_weights = {'c1': 0.7, 'c2': 0.3}
        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons,
                                                    criterion_scores,
                                                    criterion_weights)

        # 1 should be selected first as it has lowest round.
        # it should be paired with 2 as the criterion score delta is lowest.
        # (10 * 0.7 + 10 * 0.3 = 10 which is less than 15 * 0.7 = 10.5)
        min_key = min([results.key1, results.key2])
        max_key = max([results.key1, results.key2])
        self.assertEqual(min_key, 1)
        self.assertEqual(max_key, 2)

        ##
        # Selects closest score when criterion score deta are the same
        scored_objects = [
            ScoredObject(key=1,
                         score=0.9,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.9,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.4,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        criterion_scores = {
            1: {
                'c1': 11,
                'c2': 21
            },
            2: {
                'c1': 11,
                'c2': 21
            },
            3: {
                'c1': 10.1,
                'c2': 20.2
            },
            4: {
                'c1': 11,
                'c2': 21
            }
        }
        criterion_weights = {'c1': 0.4, 'c2': 0.6}

        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons,
                                                    criterion_scores,
                                                    criterion_weights)

        # 3 should be selected first as it has lowest round.
        # it should be paired with 4 as the score (0.4) is closer to 0.5.
        min_key = min([results.key1, results.key2])
        max_key = max([results.key1, results.key2])
        self.assertEqual(min_key, 3)
        self.assertEqual(max_key, 4)

        ##
        # Selects "randomly" if both criterion scores and scores are the same
        scored_objects = [
            ScoredObject(key=1,
                         score=0.9,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.9,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.9,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.9,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        criterion_scores = {
            1: {
                'c1': 11,
                'c2': 21
            },
            2: {
                'c1': 11,
                'c2': 21
            },
            3: {
                'c1': 11,
                'c2': 21
            },
            4: {
                'c1': 11,
                'c2': 21
            }
        }
        criterion_weights = {'c1': 0.4, 'c2': 0.6}

        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons,
                                                    criterion_scores,
                                                    criterion_weights)

        # 4 should be selected first as it has lowest round.
        # it should be paired with either 1, 2 or 3.
        min_key = min([results.key1, results.key2])
        max_key = max([results.key1, results.key2])
        # 1 should alwasys be selected because of mock random.random
        self.assertEqual(min_key, 1)
        self.assertEqual(max_key, 4)

        ##
        # Can select previously compared object but not with same opponent
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = [ComparisonPair(1, 2, None)]
        criterion_scores = {
            1: {
                'c1': 10.1,
                'c2': 20.2
            },
            2: {
                'c1': 10.1,
                'c2': 20.2
            },
            3: {
                'c1': 10.1,
                'c2': 20.2
            }
        }
        criterion_weights = {'c1': 0.4, 'c2': 0.6}

        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons,
                                                    criterion_scores,
                                                    criterion_weights)

        min_key = min([results.key1, results.key2])
        max_key = max([results.key1, results.key2])
        self.assertEqual(min_key, 1)
        self.assertEqual(max_key, 3)

        ##
        # Select opponent with closest score (mock shuffle order)
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.7,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.4,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        criterion_scores = {
            1: {
                'c1': 10.1,
                'c2': 20.2
            },
            2: {
                'c1': 10.1,
                'c2': 20.2
            },
            3: {
                'c1': 10.1,
                'c2': 20.2
            },
            4: {
                'c1': 10.1,
                'c2': 20.2
            }
        }
        criterion_weights = {'c1': 0.4, 'c2': 0.6}
        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons,
                                                    criterion_scores,
                                                    criterion_weights)

        # object 1 should be selected since random shuffle is disabled
        self.assertEqual(results.key1, 1)
        # object 4 should be selected as 0.4 is the closest value to 0.5
        self.assertEqual(results.key2, 4)

        ##
        # Select opponent with closest score (mock shuffle order)
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.4,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = [ComparisonPair(1, 2, None), ComparisonPair(3, 4, None)]
        criterion_scores = {
            1: {
                'c1': 10.1,
                'c2': 20.2
            },
            2: {
                'c1': 10.1,
                'c2': 20.2
            },
            3: {
                'c1': 10.1,
                'c2': 20.2
            },
            4: {
                'c1': 10.1,
                'c2': 20.2
            }
        }
        criterion_weights = {'c1': 0.4, 'c2': 0.6}

        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons,
                                                    criterion_scores,
                                                    criterion_weights)

        # object 1 should be selected since random shuffle is disabled
        self.assertEqual(results.key1, 1)
        # object 4 should be selected as 0.4 is the closest value to 0.5 (object 2 already compared, so wont be considered)
        self.assertEqual(results.key2, 4)

        ##
        # Select opponent with min delta
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        criterion_scores = {
            1: {
                'c1': 10.1,
                'c2': 20.2
            },
            2: {
                'c1': 10.2,
                'c2': 20.3
            },
            3: {
                'c1': 20.1,
                'c2': 20.2
            },
            4: {
                'c1': 10.1,
                'c2': 30.2
            }
        }
        criterion_weights = {'c1': 0.4, 'c2': 0.6}
        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons,
                                                    criterion_scores,
                                                    criterion_weights)

        # object 1 should be selected since random shuffle is disabled
        self.assertEqual(results.key1, 1)
        # object 2 should be selected as it has the min weighted criterion score delta
        self.assertEqual(results.key2, 2)

        ##
        # Select opponent with closest min delta
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = [ComparisonPair(1, 2, None), ComparisonPair(3, 4, None)]
        criterion_scores = {
            1: {
                'c1': 10.1,
                'c2': 20.2
            },
            2: {
                'c1': 10.2,
                'c2': 20.3
            },
            3: {
                'c1': 20.1,
                'c2': 20.2
            },
            4: {
                'c1': 10.1,
                'c2': 30.2
            }
        }
        criterion_weights = {'c1': 0.4, 'c2': 0.6}

        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons,
                                                    criterion_scores,
                                                    criterion_weights)

        # object 1 should be selected since random shuffle is disabled
        self.assertEqual(results.key1, 1)
        # object 3 should be selected as it has the min weighted criterion scores delta
        # after object 2 (which is already compared)
        self.assertEqual(results.key2, 3)

        ##
        # Select from long list
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=500,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        for keyNum in range(2, 500):
            scored_objects.append(
                ScoredObject(key=keyNum,
                             score=0.5,
                             variable1=None,
                             variable2=None,
                             rounds=2,
                             wins=None,
                             loses=None,
                             opponents=None))
        comparisons = []
        criterion_scores = {
            1: {
                'c1': 10,
                'c2': 20
            },
            500: {
                'c1': 15,
                'c2': 24.9
            }
        }
        for keyNum in range(2, 500):
            criterion_scores[keyNum] = {'c1': 15, 'c2': 25}
        criterion_weights = {'c1': 0.4, 'c2': 0.6}

        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons,
                                                    criterion_scores,
                                                    criterion_weights)

        # object 1 should be selected since it has lowest round
        self.assertEqual(results.key1, 1)
        # object 500 should be select as it as the lowest criterion score delta
        self.assertEqual(results.key2, 500)

        # ensure that all scored objects are seen only once until they have almost all been seen
        # (even number of scored objects)
        scored_objects = [
            ScoredObject(key=index,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None) for index in range(30)
        ]
        comparisons = []
        criterion_scores = {}
        for key in range(30):
            criterion_scores[key] = {'c1': None, 'c2': None}
        criterion_weights = {'c1': 0.4, 'c2': 0.6}

        used_keys = set()
        all_keys = set(range(30))

        # n/2 comparisons = 15
        for _ in range(15):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons, criterion_scores,
                criterion_weights)
            # neither key should have been seen before
            self.assertNotIn(results.key1, used_keys)
            self.assertNotIn(results.key2, used_keys)

            comparisons.append(results)
            used_keys.add(results.key1)
            used_keys.add(results.key2)

        self.assertEqual(used_keys, all_keys)

        # remaining comparisons for n(n-1)/2 = 435
        for _ in range(435 - 15):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons, criterion_scores,
                criterion_weights)
            # both keys should have been seen before
            self.assertIn(results.key1, used_keys)
            self.assertIn(results.key2, used_keys)
            comparisons.append(results)

        # next comparison should be an UserComparedAllObjectsException error
        with self.assertRaises(UserComparedAllObjectsException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons, criterion_scores,
                criterion_weights)

        # make sure all pairs are distinct
        self.assertEqual(
            len(comparisons),
            len(set([tuple(sorted([c.key1, c.key2])) for c in comparisons])))

        # ensure that all scored objects are seen only once until they have almost all been seen
        # (odd number of scored objects)
        scored_objects = [
            ScoredObject(key=index,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None) for index in range(31)
        ]
        comparisons = []
        criterion_scores = {}
        for key in range(30):
            criterion_scores[key] = {'c1': None, 'c2': None}
        criterion_weights = {'c1': 0.4, 'c2': 0.6}

        used_keys = set()
        all_keys = set(range(31))

        # floor(n/2) comparisons = 15
        for _ in range(15):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons, criterion_scores,
                criterion_weights)
            # neither key should have been seen before
            self.assertNotIn(results.key1, used_keys)
            self.assertNotIn(results.key2, used_keys)

            comparisons.append(results)
            used_keys.add(results.key1)
            used_keys.add(results.key2)

        # there should be only one key missing
        self.assertEqual(len(all_keys - used_keys), 1)

        # remaining comparisons for n(n-1)/2 = 435
        for _ in range(465 - 15):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons, criterion_scores,
                criterion_weights)
            # both keys should have been seen before
            self.assertIn(results.key1, all_keys)
            self.assertIn(results.key2, all_keys)
            comparisons.append(results)

        # next comparison should be an UserComparedAllObjectsException error
        with self.assertRaises(UserComparedAllObjectsException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons, criterion_scores,
                criterion_weights)

        # make sure all pairs are distinct
        self.assertEqual(
            len(comparisons),
            len(set([tuple(sorted([c.key1, c.key2])) for c in comparisons])))
Esempio n. 8
0
def create(assignment_id):
    """Creates report"""
    if not assignment_id:
        raise RuntimeError(
            "Assignment with ID {} is not found.".format(assignment_id))

    assignment = Assignment.query.filter_by(id=assignment_id).first()
    if not assignment:
        raise RuntimeError(
            "Assignment with ID {} is not found.".format(assignment_id))
    criteria = assignment.criteria

    file_name = assignment.course.name.replace('"',
                                               '') + '_' + assignment_id + '_'

    answers = Answer.query \
        .options(joinedload('score')) \
        .options(joinedload('criteria_scores')) \
        .filter(and_(
            Answer.assignment_id == assignment.id,
            Answer.active == True,
            Answer.draft == False,
            Answer.practice == False
        )) \
        .order_by(Answer.assignment_id, Answer.user_id) \
        .all()

    scores = []
    for answer in answers:
        score = answer.score
        if score:
            scores.append([
                answer.user_id, answer.id, None, 'Overall', score.score,
                score.rounds, score.wins, score.loses, score.opponents
            ])

        criteria_scores = answer.criteria_scores
        criteria_scores.sort(key=lambda x: x.criterion_id)

        for criterion_score in criteria_scores:
            criterion = next(criterion for criterion in criteria
                             if criterion.id == criterion_score.criterion_id)

            scores.append([
                answer.user_id, answer.id, criterion.id, criterion.name,
                criterion_score.score, criterion_score.rounds,
                criterion_score.wins, criterion_score.loses,
                criterion_score.opponents
            ])

    write_csv(file_name + 'scores_final.csv', [
        'User Id', 'Answer Id', 'Criterion Id', 'Criterion', 'Score', 'Rounds',
        'Wins', 'Loses', 'Opponents'
    ], scores)

    # replay comparisons for real scores at every step
    comparisons_output = []
    scores = {criterion.id: {} for criterion in criteria}
    scores['overall'] = {}
    past_comparisons = {criterion.id: [] for criterion in criteria}
    past_comparisons['overall'] = []

    comparisons = Comparison.query \
        .options(joinedload('comparison_criteria')) \
        .filter_by(
            assignment_id=assignment.id,
            completed=True
        ) \
        .order_by(Comparison.modified, Comparison.user_id) \
        .all()

    round_length = float(len(answers)) / 2
    round_number = 0
    for index, comparison in enumerate(comparisons):
        answer1_id = comparison.answer1_id
        answer2_id = comparison.answer2_id

        # overall
        answer1_score_before = scores['overall'].get(
            answer1_id,
            ScoredObject(key=answer1_id,
                         score=elo.INITIAL,
                         variable1=elo.INITIAL,
                         variable2=None,
                         rounds=0,
                         wins=0,
                         loses=0,
                         opponents=0))
        answer2_score_before = scores['overall'].get(
            answer2_id,
            ScoredObject(key=answer2_id,
                         score=elo.INITIAL,
                         variable1=elo.INITIAL,
                         variable2=None,
                         rounds=0,
                         wins=0,
                         loses=0,
                         opponents=0))

        other_comparisons = []
        for pc in past_comparisons['overall']:
            if pc.key1 in [answer1_id, answer2_id
                           ] or pc.key2 in [answer1_id, answer2_id]:
                other_comparisons.append(pc)

        result_1, result_2 = calculate_score_1vs1(
            package_name=ScoringAlgorithm.elo.value,
            key1_scored_object=answer1_score_before,
            key2_scored_object=answer2_score_before,
            winner=comparison.comparison_pair_winner(),
            other_comparison_pairs=[c for c in other_comparisons])
        scores['overall'][result_1.key] = result_1
        scores['overall'][result_2.key] = result_2

        answer1_score_after = scores['overall'][answer1_id]
        answer2_score_after = scores['overall'][answer2_id]

        past_comparisons['overall'].append(
            comparison.convert_to_comparison_pair())

        winner_id = None
        if comparison.winner == WinningAnswer.answer1:
            winner_id = answer1_id
        elif comparison.winner == WinningAnswer.answer2:
            winner_id = answer2_id
        elif comparison.winner == WinningAnswer.draw:
            winner_id = "draw"

        comparisons_output.append([
            comparison.user_id, None, 'Overall', answer1_id,
            answer1_score_before.score, answer1_score_after.score, answer2_id,
            answer2_score_before.score, answer2_score_after.score, winner_id,
            comparison.modified
        ])

        # each criterion
        comparison_criteria = comparison.comparison_criteria
        comparison_criteria.sort(key=lambda x: x.criterion_id)
        for comparison_criterion in comparison_criteria:
            criterion = next(
                criterion for criterion in criteria
                if criterion.id == comparison_criterion.criterion_id)

            answer1_score_before = scores[criterion.id].get(
                answer1_id,
                ScoredObject(key=answer1_id,
                             score=elo.INITIAL,
                             variable1=elo.INITIAL,
                             variable2=None,
                             rounds=0,
                             wins=0,
                             loses=0,
                             opponents=0))
            answer2_score_before = scores[criterion.id].get(
                answer2_id,
                ScoredObject(key=answer2_id,
                             score=elo.INITIAL,
                             variable1=elo.INITIAL,
                             variable2=None,
                             rounds=0,
                             wins=0,
                             loses=0,
                             opponents=0))

            other_comparisons = []
            for pc in past_comparisons[criterion.id]:
                if pc.key1 in [answer1_id, answer2_id
                               ] or pc.key2 in [answer1_id, answer2_id]:
                    other_comparisons.append(pc)

            result_1, result_2 = calculate_score_1vs1(
                package_name=ScoringAlgorithm.elo.value,
                key1_scored_object=answer1_score_before,
                key2_scored_object=answer2_score_before,
                winner=comparison_criterion.comparison_pair_winner(),
                other_comparison_pairs=[c for c in other_comparisons])
            scores[criterion.id][result_1.key] = result_1
            scores[criterion.id][result_2.key] = result_2

            answer1_score_after = scores[criterion.id][answer1_id]
            answer2_score_after = scores[criterion.id][answer2_id]

            past_comparisons[criterion.id].append(
                comparison.convert_to_comparison_pair())

            winner_id = None
            if comparison_criterion.winner == WinningAnswer.answer1:
                winner_id = answer1_id
            elif comparison_criterion.winner == WinningAnswer.answer2:
                winner_id = answer2_id

            comparisons_output.append([
                comparison.user_id, criterion.id, criterion.name, answer1_id,
                answer1_score_before.score, answer1_score_after.score,
                answer2_id, answer2_score_before.score,
                answer2_score_after.score, winner_id,
                comparison_criterion.modified
            ])

        if (index + 1) % round_length < 1:
            round_number += 1

            round_scores = []
            for answer in answers:
                score = scores['overall'].get(
                    answer.id,
                    ScoredObject(key=answer2_id,
                                 score=elo.INITIAL,
                                 variable1=elo.INITIAL,
                                 variable2=None,
                                 rounds=0,
                                 wins=0,
                                 loses=0,
                                 opponents=0))

                round_scores.append([
                    answer.user_id, answer.id, None, 'Overall', score.score,
                    score.rounds, score.wins, score.loses, score.opponents
                ])

                comparison_criteria = comparison.comparison_criteria
                comparison_criteria.sort(key=lambda x: x.criterion_id)
                for comparison_criterion in comparison_criteria:
                    criterion = next(
                        criterion for criterion in criteria
                        if criterion.id == comparison_criterion.criterion_id)
                    criterion_score = scores[criterion.id].get(
                        answer.id,
                        ScoredObject(key=answer2_id,
                                     score=elo.INITIAL,
                                     variable1=elo.INITIAL,
                                     variable2=None,
                                     rounds=0,
                                     wins=0,
                                     loses=0,
                                     opponents=0))

                    round_scores.append([
                        answer.user_id, answer.id, criterion.id,
                        criterion.name, criterion_score.score,
                        criterion_score.rounds, criterion_score.wins,
                        criterion_score.loses, criterion_score.opponents
                    ])

            write_csv(file_name + 'scores_round_' + str(round_number) + '.csv',
                      [
                          'User Id', 'Answer Id', 'Criterion Id', 'Criterion',
                          'Score', 'Rounds', 'Wins', 'Loses', 'Opponents'
                      ], round_scores)

    write_csv(file_name + 'comparisons.csv', [
        'User Id', 'Criterion Id', 'Criterion', 'Answer 1', 'Score 1 Before',
        'Score 1 After', 'Answer 2', 'Score 2 Before', 'Score 2 After',
        'Winner', 'Timestamp'
    ], comparisons_output)


    query = User.query \
        .join(User.user_courses) \
        .with_entities(User.id, User.student_number) \
        .filter(UserCourse.course_id == assignment.course_id) \
        .order_by(User.id)

    users = query.all()

    write_csv(file_name + 'users.csv', ['User Id', 'Student #'], users)

    print('Done.')
Esempio n. 9
0
    def test_generate_pair(self, mock_shuffle):
        # empty scored objects set
        scored_objects = []
        comparisons = []

        with self.assertRaises(InsufficientObjectsForPairException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)

        # not enough scored objects for comparison (only 1 scored object)
        scored_objects = [
            ScoredObject(key=1,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []

        with self.assertRaises(InsufficientObjectsForPairException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)

        # User compard all objects error
        scored_objects = [
            ScoredObject(key=1,
                         score=0.7,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = [ComparisonPair(1, 2, None)]

        with self.assertRaises(UserComparedAllObjectsException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)

        # User compard all objects error
        scored_objects = [
            ScoredObject(key=1,
                         score=0.7,
                         variable1=None,
                         variable2=None,
                         rounds=4,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=4,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        max_comparisons = 6  # n(n-1)/2 = 4*3/2
        for i in range(0, max_comparisons):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)
            comparisons.append(
                ComparisonPair(results.key1, results.key2, results.key1))

        # if trying to run one more time, should run into all objects compared error
        with self.assertRaises(UserComparedAllObjectsException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)

        # Returns a comparison pair
        scored_objects = [
            ScoredObject(key=1,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons)

        self.assertIsInstance(results, ComparisonPair)
        min_key = min([results.key1, results.key2])
        max_key = max([results.key1, results.key2])

        self.assertEqual(min_key, 1)
        self.assertEqual(max_key, 2)
        self.assertEqual(results.winner, None)

        # Selects lowest round objects first
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=5,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=6,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []

        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons)

        # 5 & 6 should be selected as the lowest valid round objects
        min_key = min([results.key1, results.key2])
        max_key = max([results.key1, results.key2])
        self.assertEqual(min_key, 5)
        self.assertEqual(max_key, 6)

        # Can select previously compared object but not with same opponent
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = [ComparisonPair(1, 2, None)]
        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons)

        self.assertEqual(results.key1, 1)
        self.assertEqual(results.key2, 3)

        # Select opponent with closest score (mock shuffle order)
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.7,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.4,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons)

        # object 1 should be selected since random shuffle is disabled
        self.assertEqual(results.key1, 1)
        # object 4 should be selected as 0.4 is the closest value to 0.5
        self.assertEqual(results.key2, 4)

        # Select opponent with closest score (mock shuffle order)
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.4,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = [ComparisonPair(1, 2, None)]

        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons)

        # object 1 should be selected since random shuffle is disabled
        self.assertEqual(results.key1, 1)
        # object 4 should be selected as 0.4 is the closest value to 0.5
        self.assertEqual(results.key2, 4)
Esempio n. 10
0
    def _get_new_comparison_pair(cls, course_id, assignment_id, user_id,
                                 group_id, pairing_algorithm, comparisons):
        from . import Assignment, UserCourse, CourseRole, Answer, AnswerScore, \
            PairingAlgorithm, AnswerCriterionScore, AssignmentCriterion, Group

        # exclude current user and those without a proper role.
        # note that sys admin (not enrolled in the course and thus no course role) can create answers.
        # they are considered eligible
        ineligibles = UserCourse.query \
            .with_entities(UserCourse.user_id) \
            .filter(and_(
                UserCourse.course_id == course_id,
                UserCourse.course_role == CourseRole.dropped
            )) \
            .all()

        ineligible_user_ids = [
            ineligible.user_id for ineligible in ineligibles
        ]
        ineligible_user_ids.append(user_id)

        query = Answer.query \
            .with_entities(Answer, AnswerScore.score) \
            .outerjoin(AnswerScore, AnswerScore.answer_id == Answer.id) \
            .filter(and_(
                or_(
                    ~Answer.user_id.in_(ineligible_user_ids),
                    Answer.user_id == None # don't filter out group answers
                ),
                Answer.assignment_id == assignment_id,
                Answer.active == True,
                Answer.practice == False,
                Answer.draft == False,
                Answer.comparable == True
            ))

        if group_id:
            query = query.filter(Answer.group_id != group_id)

        answers_with_score = query.all()

        scored_objects = []
        for answer_with_score in answers_with_score:
            scored_objects.append(
                ScoredObject(key=answer_with_score.Answer.id,
                             score=answer_with_score.score,
                             rounds=answer_with_score.Answer.round,
                             variable1=None,
                             variable2=None,
                             wins=None,
                             loses=None,
                             opponents=None))

        comparison_pairs = [
            comparison.convert_to_comparison_pair()
            for comparison in comparisons
        ]

        # adaptive min delta algo requires extra criterion specific parameters
        if pairing_algorithm == PairingAlgorithm.adaptive_min_delta:
            # retrieve extra criterion score data
            answer_criterion_scores = AnswerCriterionScore.query \
                .with_entities(AnswerCriterionScore.answer_id,
                    AnswerCriterionScore.criterion_id, AnswerCriterionScore.score) \
                .join(Answer) \
                .filter(and_(
                    Answer.user_id.notin_(ineligible_user_ids),
                    Answer.assignment_id == assignment_id,
                    Answer.active == True,
                    Answer.practice == False,
                    Answer.draft == False
                )) \
                .all()

            assignment_criterion_weights = AssignmentCriterion.query \
                .with_entities(AssignmentCriterion.criterion_id, AssignmentCriterion.weight) \
                .filter(and_(
                    AssignmentCriterion.assignment_id == assignment_id,
                    AssignmentCriterion.active == True
                )) \
                .all()

            criterion_scores = {}
            for criterion_score in answer_criterion_scores:
                scores = criterion_scores.setdefault(criterion_score.answer_id,
                                                     {})
                scores[criterion_score.criterion_id] = criterion_score.score

            criterion_weights = {}
            for the_weight in assignment_criterion_weights:
                criterion_weights[the_weight.criterion_id] = \
                    the_weight.weight

            comparison_pair = generate_pair(
                package_name=pairing_algorithm.value,
                scored_objects=scored_objects,
                comparison_pairs=comparison_pairs,
                criterion_scores=criterion_scores,
                criterion_weights=criterion_weights,
                log=current_app.logger)
        else:
            comparison_pair = generate_pair(
                package_name=pairing_algorithm.value,
                scored_objects=scored_objects,
                comparison_pairs=comparison_pairs,
                log=current_app.logger)

        return comparison_pair
Esempio n. 11
0
def _run(file_path, pairing_package_name, scoring_package_name,
         winner_selector, correct_rate, actual_grades, repetition_count):
    random.seed()
    numpy.random.seed()

    while repetition_count < REPETITIONS:
        grade_by_answer_key = {}
        answers = []
        results = []
        for key, grade in enumerate(actual_grades):
            grade_by_answer_key[key + 1] = grade
            answers.append(
                ScoredObject(key=key + 1,
                             score=0,
                             variable1=None,
                             variable2=None,
                             rounds=0,
                             opponents=0,
                             wins=0,
                             loses=0))

        students = []
        for key in range(NUMBER_OF_STUDENTS):
            students.append({
                'key': key,
                'comparisons_left': NUMBER_OF_COMPARISONS_PER_STUDENT,
                'comparisons_completed': []
            })

        comparisons = []

        for round_count in range(1, NUMBER_OF_ROUNDS + 1):
            if len(students) == 0:
                break

            for comparison_in_round in range(ROUND_LENGTH):
                if len(students) == 0:
                    break

                student = random.choice(students)
                student_comparisons = student['comparisons_completed']

                comparison_pair = generate_pair(
                    package_name=pairing_package_name,
                    scored_objects=answers,
                    comparison_pairs=student_comparisons)
                key1 = comparison_pair.key1
                key1_grade = grade_by_answer_key[key1]
                key2 = comparison_pair.key2
                key2_grade = grade_by_answer_key[key2]

                winner = _decide_winner(winner_selector, correct_rate,
                                        key1_grade, key2_grade)
                comparison_pair = comparison_pair._replace(winner=winner)

                comparisons.append(comparison_pair)
                student['comparisons_completed'].append(comparison_pair)
                student['comparisons_left'] -= 1
                if student['comparisons_left'] <= 0:
                    indexes = [
                        i for i, s in enumerate(students)
                        if student['key'] == s['key']
                    ]
                    del students[indexes[0]]

                index1 = next(index for index, answer in enumerate(answers)
                              if answer.key == key1)
                index2 = next(index for index, answer in enumerate(answers)
                              if answer.key == key2)

                result1, results2 = calculate_score_1vs1(
                    package_name=scoring_package_name,
                    key1_scored_object=answers[index1],
                    key2_scored_object=answers[index2],
                    winner=winner,
                    other_comparison_pairs=comparisons)
                answers[index1] = result1
                answers[index2] = results2

            current_scores = [answer.score for answer in answers]

            r_value, pearsonr_p_value = pearsonr(ACTUAL_GRADES, current_scores)
            results.append(str(r_value))
            #print("Round {} ----------- pearsonr={} value=={}".format(
            #    round_count, r_value, pearsonr_p_value
            #))

        with open(file_path, "a") as csvfile:
            out = csv.writer(csvfile)
            out.writerow(results)

        # prepare for next run
        repetition_count += 1
        actual_grades = [answer.score for answer in answers]
Esempio n. 12
0
    def test_calculate_score_1vs1(self):
        # no winning key error raised
        key1_scored_object = ScoredObject(key=1,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = None
        comparisons = []

        with self.assertRaises(InvalidWinnerException):
            self.score_algorithm.calculate_score_1vs1(key1_scored_object,
                                                      key2_scored_object,
                                                      winner, comparisons)

        # empty comparison set
        key1_scored_object = ScoredObject(key=1,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = ComparisonWinner.key1
        comparisons = []
        key1_results, key2_results = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        self.assertIsInstance(key1_results, ScoredObject)
        self.assertIsInstance(key1_results.score, float)
        self.assertIsInstance(key1_results.variable1, float)
        self.assertIsNone(key1_results.variable2)
        self.assertEqual(key1_results.rounds, 1)
        self.assertEqual(key1_results.opponents, 1)
        self.assertEqual(key1_results.wins, 1)
        self.assertEqual(key1_results.loses, 0)

        self.assertIsInstance(key2_results, ScoredObject)
        self.assertIsInstance(key2_results.score, float)
        self.assertIsInstance(key2_results.variable1, float)
        self.assertIsNone(key2_results.variable2)
        self.assertEqual(key2_results.rounds, 1)
        self.assertEqual(key2_results.opponents, 1)
        self.assertEqual(key2_results.wins, 0)
        self.assertEqual(key2_results.loses, 1)

        self.assertGreater(key1_results.score, key2_results.score)
        self.assertGreater(key2_results.score, 0)

        self.assertGreater(key1_results.variable1, key2_results.variable1)
        self.assertGreater(key2_results.variable1, 0)

        # No value given for variables
        key1_scored_object = ScoredObject(key=1,
                                          score=None,
                                          variable1=None,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=None,
                                          variable1=None,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = ComparisonWinner.key1
        comparisons = []
        key1_results, key2_results = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        self.assertEqual(key1_results.rounds, 1)
        self.assertEqual(key1_results.opponents, 1)
        self.assertEqual(key1_results.wins, 1)
        self.assertEqual(key1_results.loses, 0)

        self.assertEqual(key2_results.rounds, 1)
        self.assertEqual(key2_results.opponents, 1)
        self.assertEqual(key2_results.wins, 0)
        self.assertEqual(key2_results.loses, 1)

        self.assertGreater(key1_results.score, key2_results.score)
        self.assertGreater(key2_results.score, 0)

        self.assertGreater(key1_results.variable1, key2_results.variable1)
        self.assertGreater(key2_results.variable1, 0)

        # comparison set without winners
        key1_scored_object = ScoredObject(key=1,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = ComparisonWinner.key1
        comparisons = [
            ComparisonPair(key1=1, key2=2, winner=None),
            ComparisonPair(key1=1, key2=2, winner=None),
            ComparisonPair(key1=1, key2=2, winner=None)
        ]
        key1_results, key2_results = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        self.assertEqual(key1_results.rounds, 4)
        self.assertEqual(key1_results.opponents, 1)
        self.assertEqual(key1_results.wins, 1)
        self.assertEqual(key1_results.loses, 0)

        self.assertEqual(key2_results.rounds, 4)
        self.assertEqual(key2_results.opponents, 1)
        self.assertEqual(key2_results.wins, 0)
        self.assertEqual(key2_results.loses, 1)

        self.assertGreater(key1_results.score, key2_results.score)
        self.assertGreater(key2_results.score, 0)

        self.assertGreater(key1_results.variable1, key2_results.variable1)
        self.assertGreater(key2_results.variable1, 0)

        # one comparison draw
        key1_scored_object = ScoredObject(key=1,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = ComparisonWinner.draw
        comparisons = []
        key1_results, key2_results = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        self.assertIsInstance(key1_results, ScoredObject)
        self.assertIsInstance(key1_results.score, float)
        self.assertIsInstance(key1_results.variable1, float)
        self.assertIsNone(key1_results.variable2)
        self.assertEqual(key1_results.rounds, 1)
        self.assertEqual(key1_results.opponents, 1)
        self.assertEqual(key1_results.wins, 0)
        self.assertEqual(key1_results.loses, 0)

        self.assertIsInstance(key2_results, ScoredObject)
        self.assertIsInstance(key2_results.score, float)
        self.assertIsInstance(key2_results.variable1, float)
        self.assertIsNone(key2_results.variable2)
        self.assertEqual(key2_results.rounds, 1)
        self.assertEqual(key2_results.opponents, 1)
        self.assertEqual(key2_results.wins, 0)
        self.assertEqual(key2_results.loses, 0)

        self.assertAlmostEqual(key1_results.score, key2_results.score)
        self.assertGreater(key2_results.score, 0)

        self.assertAlmostEqual(key1_results.variable1, key2_results.variable1)
        self.assertGreater(key2_results.variable1, 0)

        # comparison set with scores 1 > 2 ~= 3 > 4
        key1_scored_object = ScoredObject(key=1,
                                          score=1405,
                                          variable1=1405,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=1405,
                                          variable1=1405,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = ComparisonWinner.key1
        comparisons = [
            ComparisonPair(key1=1, key2=3, winner=ComparisonWinner.key1),
            ComparisonPair(key1=2, key2=4, winner=ComparisonWinner.key1)
        ]
        key1_results, key2_results = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        self.assertEqual(key1_results.rounds, 2)
        self.assertEqual(key1_results.opponents, 2)
        self.assertEqual(key1_results.wins, 2)
        self.assertEqual(key1_results.loses, 0)

        self.assertEqual(key2_results.rounds, 2)
        self.assertEqual(key2_results.opponents, 2)
        self.assertEqual(key2_results.wins, 1)
        self.assertEqual(key2_results.loses, 1)

        self.assertGreater(key1_results.score, key2_results.score)
        self.assertGreater(key2_results.score, 0)

        self.assertGreater(key1_results.variable1, key2_results.variable1)
        self.assertGreater(key2_results.variable1, 0)

        # comparison set with scores 1 ~= 2 ~= 3 > 4 (with draw)
        key1_scored_object = ScoredObject(key=1,
                                          score=1405,
                                          variable1=1405,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=1405,
                                          variable1=1405,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = ComparisonWinner.draw
        comparisons = [
            ComparisonPair(key1=1, key2=3, winner=ComparisonWinner.key1),
            ComparisonPair(key1=2, key2=4, winner=ComparisonWinner.key1)
        ]
        key1_results, key2_results = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        self.assertEqual(key1_results.rounds, 2)
        self.assertEqual(key1_results.opponents, 2)
        self.assertEqual(key1_results.wins, 1)
        self.assertEqual(key1_results.loses, 0)

        self.assertEqual(key2_results.rounds, 2)
        self.assertEqual(key2_results.opponents, 2)
        self.assertEqual(key2_results.wins, 1)
        self.assertEqual(key2_results.loses, 0)

        self.assertAlmostEqual(key1_results.score, key2_results.score)
        self.assertGreater(key2_results.score, 0)

        self.assertAlmostEqual(key1_results.variable1, key2_results.variable1)
        self.assertGreater(key2_results.variable1, 0)

        # multiple comparisons between same pairs
        key1_scored_object = ScoredObject(key=1,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = ComparisonWinner.key1
        comparisons = []
        key1_results_1, key2_results_1 = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        key1_scored_object = ScoredObject(key=1,
                                          score=key1_results_1.score,
                                          variable1=key1_results_1.variable1,
                                          variable2=key1_results_1.variable2,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=key2_results_1.score,
                                          variable1=key2_results_1.variable1,
                                          variable2=key2_results_1.variable2,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = ComparisonWinner.key2
        comparisons = [
            ComparisonPair(key1=1, key2=2, winner=ComparisonWinner.key1)
        ]
        key1_results_2, key2_results_2 = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        # 1 win should have a higher score than 1 win & 1 lose against same opponent
        self.assertGreater(key1_results_1.score, key1_results_2.score)
        # 1 lose should have a lower score than 1 win & 1 lose against same opponent
        self.assertLess(key2_results_1.score, key2_results_2.score)

        # 1 win should have a higher expected score than 1 win & 1 lose against same opponent
        self.assertGreater(key1_results_1.variable1, key1_results_2.variable1)
        # 1 lose should have a lower expected score than 1 win & 1 lose against same opponent
        self.assertLess(key2_results_1.variable1, key2_results_2.variable1)

        key1_scored_object = ScoredObject(key=1,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=1400,
                                          variable1=1400,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = ComparisonWinner.key1
        comparisons = []
        key1_results_1, key2_results_1 = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        key1_scored_object = ScoredObject(key=1,
                                          score=key1_results_1.score,
                                          variable1=key1_results_1.variable1,
                                          variable2=key1_results_1.variable2,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=key2_results_1.score,
                                          variable1=key2_results_1.variable1,
                                          variable2=key2_results_1.variable2,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = ComparisonWinner.key1
        comparisons = [
            ComparisonPair(key1=1, key2=2, winner=ComparisonWinner.key1)
        ]
        key1_results_2, key2_results_2 = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        # 1 win should have a lower score than 2 wins against same opponent
        self.assertLess(key1_results_1.score, key1_results_2.score)
        # 1 lose should have a higher score than 2 loses against same opponent
        self.assertGreater(key2_results_1.score, key2_results_2.score)

        # 1 win should have a lower expected score than 2 wins against same opponent
        self.assertLess(key1_results_1.variable1, key1_results_2.variable1)
        # 1 lose should have a higher expected score than 2 loses against same opponent
        self.assertGreater(key2_results_1.variable1, key2_results_2.variable1)

        # adding unrelated comparisons should not effect 1 vs 1 stats results
        key1_scored_object = ScoredObject(key=1,
                                          score=1405,
                                          variable1=1405,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        key2_scored_object = ScoredObject(key=2,
                                          score=1405,
                                          variable1=1405,
                                          variable2=None,
                                          rounds=None,
                                          wins=None,
                                          loses=None,
                                          opponents=None)
        winner = ComparisonWinner.key1
        comparisons = [
            ComparisonPair(key1=1, key2=3, winner=ComparisonWinner.key1),
            ComparisonPair(key1=2, key2=4, winner=ComparisonWinner.key1)
        ]
        key1_results_1, key2_results_1 = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        comparisons = [
            ComparisonPair(key1=3, key2=4, winner=ComparisonWinner.key1),
            ComparisonPair(key1=1, key2=3, winner=ComparisonWinner.key1),
            ComparisonPair(key1=2, key2=42, winner=ComparisonWinner.key1)
        ]
        key1_results_2, key2_results_2 = self.score_algorithm.calculate_score_1vs1(
            key1_scored_object, key2_scored_object, winner, comparisons)

        self.assertAlmostEqual(key1_results_1.score, key1_results_2.score)
        self.assertAlmostEqual(key1_results_1.variable1,
                               key1_results_2.variable1)
        self.assertAlmostEqual(key1_results_1.variable2,
                               key1_results_2.variable2)
        self.assertEqual(key1_results_1.rounds, key1_results_2.rounds)
        self.assertEqual(key1_results_1.opponents, key1_results_2.opponents)
        self.assertEqual(key1_results_1.wins, key1_results_2.wins)
        self.assertEqual(key1_results_1.loses, key1_results_2.loses)

        self.assertAlmostEqual(key2_results_1.score, key2_results_2.score)
        self.assertAlmostEqual(key2_results_1.variable1,
                               key2_results_2.variable1)
        self.assertAlmostEqual(key2_results_1.variable2,
                               key2_results_2.variable2)
        self.assertEqual(key2_results_1.rounds, key2_results_2.rounds)
        self.assertEqual(key2_results_1.opponents, key2_results_2.opponents)
        self.assertEqual(key2_results_1.wins, key2_results_2.wins)
        self.assertEqual(key2_results_1.loses, key2_results_2.loses)
Esempio n. 13
0
    def test_generate_pair(self, mock_shuffle):
        # empty scored objects set
        scored_objects = []
        comparisons = []

        with self.assertRaises(InsufficientObjectsForPairException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)

        # not enough scored objects for comparison (only 1 scored object)
        scored_objects = [
            ScoredObject(key=1,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []

        with self.assertRaises(InsufficientObjectsForPairException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)

        # User compard all objects error
        scored_objects = [
            ScoredObject(key=1,
                         score=0.7,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = [ComparisonPair(1, 2, None)]

        with self.assertRaises(UserComparedAllObjectsException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)

        # User compard all objects error
        scored_objects = [
            ScoredObject(key=1,
                         score=0.7,
                         variable1=None,
                         variable2=None,
                         rounds=4,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=4,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        max_comparisons = 6  # n(n-1)/2 = 4*3/2
        for i in range(0, max_comparisons):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)
            comparisons.append(
                ComparisonPair(results.key1, results.key2, results.key1))

        # if trying to run one more time, should run into all objects compared error
        with self.assertRaises(UserComparedAllObjectsException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)

        # Returns a comparison pair
        scored_objects = [
            ScoredObject(key=1,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons)

        self.assertIsInstance(results, ComparisonPair)
        min_key = min([results.key1, results.key2])
        max_key = max([results.key1, results.key2])

        self.assertEqual(min_key, 1)
        self.assertEqual(max_key, 2)
        self.assertEqual(results.winner, None)

        # Selects lowest round objects first
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=5,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=6,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=1,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []

        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons)

        # 5 & 6 should be selected as the lowest valid round objects
        min_key = min([results.key1, results.key2])
        max_key = max([results.key1, results.key2])
        self.assertEqual(min_key, 5)
        self.assertEqual(max_key, 6)

        # Can select previously compared object but not with same opponent
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=2,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = [ComparisonPair(1, 2, None)]
        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons)

        self.assertEqual(results.key1, 1)
        self.assertEqual(results.key2, 3)

        # Select opponent with closest score (mock shuffle order)
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.7,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.4,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = []
        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons)

        # object 1 should be selected since random shuffle is disabled
        self.assertEqual(results.key1, 1)
        # object 4 should be selected as 0.4 is the closest value to 0.5
        self.assertEqual(results.key2, 4)

        # Select opponent with closest score (mock shuffle order)
        scored_objects = [
            ScoredObject(key=1,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=2,
                         score=0.5,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=3,
                         score=0.2,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None),
            ScoredObject(key=4,
                         score=0.4,
                         variable1=None,
                         variable2=None,
                         rounds=3,
                         wins=None,
                         loses=None,
                         opponents=None)
        ]
        comparisons = [ComparisonPair(1, 2, None), ComparisonPair(3, 4, None)]

        results = self.pair_algorithm.generate_pair(scored_objects,
                                                    comparisons)

        # object 1 should be selected since random shuffle is disabled
        self.assertEqual(results.key1, 1)
        # object 4 should be selected as 0.4 is the closest value to 0.5
        self.assertEqual(results.key2, 4)

        # ensure that all scored objects are seen only once until they have almost all been seen
        # (even number of scored objects)
        scored_objects = [
            ScoredObject(key=index,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None) for index in range(30)
        ]
        comparisons = []

        used_keys = set()
        all_keys = set(range(30))

        # n/2 comparisons = 15
        for _ in range(15):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)
            # neither key should have been seen before
            self.assertNotIn(results.key1, used_keys)
            self.assertNotIn(results.key2, used_keys)

            comparisons.append(results)
            used_keys.add(results.key1)
            used_keys.add(results.key2)

        self.assertEqual(used_keys, all_keys)

        # remaining comparisons for n(n-1)/2 = 435
        for _ in range(435 - 15):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)
            # both keys should have been seen before
            self.assertIn(results.key1, used_keys)
            self.assertIn(results.key2, used_keys)
            comparisons.append(results)

        # next comparison should be an UserComparedAllObjectsException error
        with self.assertRaises(UserComparedAllObjectsException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)

        # make sure all pairs are distinct
        self.assertEqual(
            len(comparisons),
            len(set([tuple(sorted([c.key1, c.key2])) for c in comparisons])))

        # ensure that all scored objects are seen only once until they have almost all been seen
        # (odd number of scored objects)
        scored_objects = [
            ScoredObject(key=index,
                         score=None,
                         variable1=None,
                         variable2=None,
                         rounds=0,
                         wins=None,
                         loses=None,
                         opponents=None) for index in range(31)
        ]
        comparisons = []

        used_keys = set()
        all_keys = set(range(31))

        # floor(n/2) comparisons = 15
        for _ in range(15):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)
            # neither key should have been seen before
            self.assertNotIn(results.key1, used_keys)
            self.assertNotIn(results.key2, used_keys)

            comparisons.append(results)
            used_keys.add(results.key1)
            used_keys.add(results.key2)

        # there should be only one key missing
        self.assertEqual(len(all_keys - used_keys), 1)

        # remaining comparisons for n(n-1)/2 = 435
        for _ in range(465 - 15):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)
            # both keys should have been seen before
            self.assertIn(results.key1, all_keys)
            self.assertIn(results.key2, all_keys)
            comparisons.append(results)

        # next comparison should be an UserComparedAllObjectsException error
        with self.assertRaises(UserComparedAllObjectsException):
            results = self.pair_algorithm.generate_pair(
                scored_objects, comparisons)

        # make sure all pairs are distinct
        self.assertEqual(
            len(comparisons),
            len(set([tuple(sorted([c.key1, c.key2])) for c in comparisons])))
Esempio n. 14
0
    def test_calculate_score_1vs1(self):
        self.comparisons = [
            ComparisonPair(key1=1,key2=2, winner=ComparisonWinner.key1),
            ComparisonPair(key1=1,key2=3, winner=ComparisonWinner.key1)
        ]

        # test comparative judgement score algorithm
        self.package_name = "comparative_judgement"

        key1_scored_object = ScoredObject(
            key=2, score=None, variable1=None, variable2=None,
            rounds=None, wins=None, loses=None, opponents=None
        )
        key2_scored_object = ScoredObject(
            key=3, score=None, variable1=None, variable2=None,
            rounds=None, wins=None, loses=None, opponents=None
        )
        winner = ComparisonWinner.key1

        key2_results, key3_results = calculate_score_1vs1(
            package_name=self.package_name,
            key1_scored_object=key1_scored_object,
            key2_scored_object=key2_scored_object,
            winner=winner,
            other_comparison_pairs=self.comparisons
        )

        self.assertIsInstance(key2_results, ScoredObject)
        self.assertIsInstance(key2_results.score, float)
        self.assertIsInstance(key2_results.variable1, float)
        self.assertIsNone(key2_results.variable2)
        self.assertEqual(key2_results.rounds, 2)
        self.assertEqual(key2_results.opponents, 2)
        self.assertEqual(key2_results.wins, 1)
        self.assertEqual(key2_results.loses, 1)

        self.assertIsInstance(key3_results, ScoredObject)
        self.assertIsInstance(key3_results.score, float)
        self.assertIsInstance(key3_results.variable1, float)
        self.assertIsNone(key3_results.variable2)
        self.assertEqual(key3_results.rounds, 2)
        self.assertEqual(key3_results.opponents, 2)
        self.assertEqual(key3_results.wins, 0)
        self.assertEqual(key3_results.loses, 2)

        # test elo rating algorithm
        self.package_name = "elo_rating"

        key1_scored_object = ScoredObject(
            key=2, score=1395, variable1=1395, variable2=None,
            rounds=None, wins=None, loses=None, opponents=None
        )
        key2_scored_object = ScoredObject(
            key=3, score=1396, variable1=1396, variable2=None,
            rounds=None, wins=None, loses=None, opponents=None
        )
        winner = ComparisonWinner.key1

        key2_results, key3_results = calculate_score_1vs1(
            package_name=self.package_name,
            key1_scored_object=key1_scored_object,
            key2_scored_object=key2_scored_object,
            winner=winner,
            other_comparison_pairs=self.comparisons
        )

        self.assertIsInstance(key2_results, ScoredObject)
        self.assertIsInstance(key2_results.score, float)
        self.assertIsInstance(key2_results.variable1, float)
        self.assertIsNone(key2_results.variable2)
        self.assertEqual(key2_results.rounds, 2)
        self.assertEqual(key2_results.opponents, 2)
        self.assertEqual(key2_results.wins, 1)
        self.assertEqual(key2_results.loses, 1)

        self.assertIsInstance(key3_results, ScoredObject)
        self.assertIsInstance(key3_results.score, float)
        self.assertIsInstance(key3_results.variable1, float)
        self.assertIsNone(key3_results.variable2)
        self.assertEqual(key3_results.rounds, 2)
        self.assertEqual(key3_results.opponents, 2)
        self.assertEqual(key3_results.wins, 0)
        self.assertEqual(key3_results.loses, 2)

        # test true skill rating algorithm
        self.package_name = "true_skill_rating"


        key1_scored_object = ScoredObject(
            key=2, score=-0.909, variable1=20.604, variable2=7.171,
            rounds=None, wins=None, loses=None, opponents=None
        )
        key2_scored_object = ScoredObject(
            key=3, score=-0.058, variable1=21.542, variable2=7.200,
            rounds=None, wins=None, loses=None, opponents=None
        )
        winner = ComparisonWinner.key1

        key2_results, key3_results = calculate_score_1vs1(
            package_name=self.package_name,
            key1_scored_object=key1_scored_object,
            key2_scored_object=key2_scored_object,
            winner=winner,
            other_comparison_pairs=self.comparisons
        )

        self.assertIsInstance(key2_results, ScoredObject)
        self.assertIsInstance(key2_results.score, float)
        self.assertIsInstance(key2_results.variable1, float)
        self.assertIsInstance(key2_results.variable2, float)
        self.assertEqual(key2_results.rounds, 2)
        self.assertEqual(key2_results.opponents, 2)
        self.assertEqual(key2_results.wins, 1)
        self.assertEqual(key2_results.loses, 1)

        self.assertIsInstance(key3_results, ScoredObject)
        self.assertIsInstance(key3_results.score, float)
        self.assertIsInstance(key3_results.variable1, float)
        self.assertIsInstance(key3_results.variable2, float)
        self.assertEqual(key3_results.rounds, 2)
        self.assertEqual(key3_results.opponents, 2)
        self.assertEqual(key3_results.wins, 0)
        self.assertEqual(key3_results.loses, 2)