Ejemplo n.º 1
0
    def test_recommend_self_report_after_activity_if_there_is_no_check_on_learning_assessment(
            self):
        # Given a learner
        john_superlearner = self.name_to_learners['John Superlearner']

        # Who's last activity is not an assessment
        last_activity = self._get_activity(
            mongo_id_from_url(
                get_last_activity_url_identifier(john_superlearner)))

        self.assertEqual(
            self.name_to_activity['Mastering Cookie Making'].identifier,
            last_activity.identifier)
        self.assertNotIn(
            last_activity.learning_resource_type,
            MagicStrings.LearningResourceTypes.get_assessment_types())

        # And who's last activity has no check on learning assessment
        # (Remove the check on learning alignment object which is last elem in list... hacky)
        last_activity.educational_alignment.pop(-1)

        # When get mandatory assignment is called
        assignment = self._default_assignment_request(john_superlearner)

        # Then the recommendation assignment is the self report
        self.assertEqual(
            MagicStrings.UnitTestAssertions.SELF_REPORT_URL_IDENTIFIER,
            assignment.assignment.activity_id)
Ejemplo n.º 2
0
def all_get(learnerId):  # noqa: E501
    """Get all activities

    Returns all activities. # noqa: E501

    :param learnerId: Id of the learner to get a recommendation for
    :type learnerId: str

    :rtype: Recommendation
    """
    print("INFO: Received request for all activities recommendation for learner with id {}".format(learnerId))
    query_cacher = QueryCacher()
    learner, etag = query_cacher.get_learner(mongo_id_from_url(learnerId))
    if learner is None:
        return {"error_msg": "Learner cannot be found"}, 404
    print("INFO: Detected goals for learner with id {}. Goals={}".format(learnerId, learner.goals))
    cass_graph = load_or_instantiate('recommenderserver.resources', CassGraph, '603d5ac2-fa9e-43c3-9c50-87ff65804ccd')

    recommendation = Recommender(learner=learner,
                                 etag=etag,
                                 query_cacher=query_cacher,
                                 elo_row_strategy_classes=[],
                                 activity_row_strategy_classes=[AllActivities],
                                 not_enough_content_elo_row_strategy_classes=[],
                                 not_enough_content_activity_row_strategy_classes=[],
                                 min_content_threshold=-1,
                                 cass_graph=cass_graph,
                                 filter_locked=False).get_recommendation()

    print(
        'INFO: All activities recommendation made for learner with id {}. Recommendation: {}'.format(learnerId,
                                                                                               recommendation.to_dict()))
    return recommendation
Ejemplo n.º 3
0
    def test_shiny_strategy(self):
        # Given a learner
        john_superlearner = self.name_to_learners['John Superlearner']
        john_superlearner = self._clear_user(john_superlearner)

        # With an active ELO
        active_elos = get_current_elos(john_superlearner, self.cass_graph)
        self.assertEqual(len(active_elos), 1)
        elo = active_elos[0]

        # When the Shiny strategy is called
        query_mock = self._default_query_mock()
        strategy = Shiny(john_superlearner, query_mock, self.cass_graph, False)
        applicable_elos = strategy.can_instantiate(active_elos)
        self.assertEqual(len(applicable_elos), 1)
        applicable_elo = applicable_elos[0]
        self.assertEqual(elo, applicable_elo)

        # Then it returns shiny activities
        row = strategy.instantiate_row(applicable_elo)
        activities = [
            query_mock.get_activity(
                mongo_id_from_url(recommended_activity.activity_id))
            for recommended_activity in row.activities
        ]
        self.assertGreater(len(activities), 0)
        self.assertTrue(all(is_shiny(activity) for activity in activities))
Ejemplo n.º 4
0
    def get_recommendation(self) -> Recommendation:
        utils.print_with_time("Info: Instantiating Rows")
        rows = self._instantiate_all_rows()
        utils.print_with_time("Info: END Instantiating Rows")

        utils.print_with_time("Info: START Capstone check.")
        capstone_rows = self.get_capstone_if_able()
        for row in capstone_rows:
            rows.append(row)
        utils.print_with_time("Info: END Capstone check.")
        if self.deduplicate_rows:
            self.logic_steps.append("Deduplicated rows")
            self._deduplicate(rows)

        try:
            xapi_sender = XAPISender(
                base_url='http://' + LoggingLRSConfig.BASE_URL,
                x_experience_version=LoggingLRSConfig.X_EXPERIENCE_VERSION,
                basic_auth_user=LoggingLRSConfig.CLIENT_USR,
                basic_auth_pwd=LoggingLRSConfig.CLIENT_PWD)

            produced_statement = XApiStatement(
                actor_name=self.learner.identifier,
                account_name=self.learner.identifier,
                agent_url_id=Config.LEARNER_INFERENCE_BASE_URL + '/' +
                self.learner.identifier,
                verb_name="produced",
                object={
                    "objectType": "Activity",
                    "definition": {
                        "name": {
                            "en": "Recommendation"
                        }
                    },
                    'id': Config.RECOMMENDER_URL
                },
                context_extensions={
                    Config.RECOMMENDER_URL + "/logic": self.logic_steps
                })

            if self.send_log_to_lrs:
                response = xapi_sender.statements_post(produced_statement)
            if self.send_log_to_file:
                utils.print_with_time_split(
                    "{}".format(json.dumps(produced_statement.statement)),
                    'XAPI LOG')

            utils.print_with_time("Info: Logging statement sent.")
        except Exception as e:
            utils.print_with_time(
                'WARN: the following error occurred when trying to send to the logging LRS: {}'
                .format(e.args))

        return Recommendation(type="Recommendation",
                              timestamp=datetime.datetime.utcnow().replace(
                                  tzinfo=datetime.timezone.utc),
                              learner=utils.mongo_id_from_url(
                                  self.learner.identifier),
                              assignment=None,
                              recommendations=rows)
Ejemplo n.º 5
0
 def _update_learner(self, identifier, learner_patch: Learner):
     learner = self.id_to_learners[mongo_id_from_url(identifier)]
     if learner_patch.goals is not None:
         learner.goals = learner_patch.goals
         learner.past_goals = learner_patch.past_goals
     if learner_patch.mastery_estimates is not None:
         learner.past_mastery_estimates = learner_patch.past_mastery_estimates
         learner.mastery_estimates = learner_patch.mastery_estimates
Ejemplo n.º 6
0
    def test_something_new_strategy_returns_activities_for_an_elo_where_the_user_has_no_competency_attempts(
            self):
        # Given a learner
        john_superlearner = self.name_to_learners['John Superlearner']

        john_superlearner = self._clear_user(john_superlearner)

        target_parent_id = 'https://insertCassUrl/api/data/insertCassSchemaUrl.0.3.Competency/6bdc90f5-6d6c-4884-9581-dc2e0c9c07f1'
        john_superlearner.goals.append(
            Goal(context='http://insertCassSchemaUrl/0.3',
                 competency_id=target_parent_id,
                 type='Goal'))

        self._clear_user(john_superlearner)

        target_elo_id = 'https://insertCassUrl/api/data/insertCassSchemaUrl.0.3.Competency/86b50eaa-74e2-412f-b858-9d5610b52bef'

        # Who only has one non-mastered and attempted elo
        for competency in self.cass_graph.get_entire_chain():
            if competency.id != target_elo_id and competency.id != target_parent_id:
                # print(competency.id)
                self._master_competency(competency, john_superlearner)
                self._add_competency_attempt(competency, john_superlearner)

        active_elos = get_current_elos(john_superlearner, self.cass_graph)
        self.assertEqual(len(active_elos), 1)
        active_elo = active_elos[0]

        self.assertTrue(CassConfig.ILL_DEFINED_CONCEPT_TERM in
                        active_elo.ceasnconcept_term)
        self.assertEqual(active_elo.id, target_elo_id)

        # Then the SomethingNew strategy can only instantiate that ELO
        query_mock = self._default_query_mock()
        strategy = NewCompetency(john_superlearner, query_mock,
                                 self.cass_graph, False)
        applicable_elos = strategy.can_instantiate(active_elos)
        self.assertTrue(len(applicable_elos) == 1)
        applicable_elo = applicable_elos[0]
        self.assertEqual(applicable_elo, active_elo)

        # And when a recommendation is made with that elo
        row = strategy.instantiate_row(applicable_elo)

        # Then the recommendation has activities for only that ELO
        activities = [
            query_mock.get_activity(
                mongo_id_from_url(recommended_activity.activity_id))
            for recommended_activity in row.activities
        ]
        self.assertTrue(
            all(
                get_aligned_elo(activity) == applicable_elo.id
                for activity in activities))
Ejemplo n.º 7
0
def load_test_activities() -> Tuple[Dict[str, LearningActivity], Dict[str, LearningActivity]]:
    with open(activities_json_file, "r") as f:
        activities = json.loads(f.read())

    name_to_activity = {}
    id_to_activity = {}

    for activity in activities:
        activity = FakeHttpResponse(data=json.dumps(activity))
        activity = ActivityApiClient().deserialize(response=activity, response_type=LearningActivity)
        name_to_activity[activity.name] = activity
        id_to_activity[mongo_id_from_url(activity.identifier)] = activity

    return name_to_activity, id_to_activity
Ejemplo n.º 8
0
def load_test_learners() -> Tuple[Dict[str, Learner], Dict[str, Learner]]:
    with open(learners_json_file, "r") as f:
        learners = json.loads(f.read())

    name_to_learner = {}
    id_to_learner = {}

    for learner in learners:
        learner = FakeHttpResponse(data=json.dumps(learner))
        learner = LearnerApiClient().deserialize(response=learner, response_type=Learner)
        name_to_learner[learner.name] = learner
        id_to_learner[mongo_id_from_url(learner.identifier)] = learner

    return name_to_learner, id_to_learner
Ejemplo n.º 9
0
    def _get_required_check_on_learning_assessment(
            self) -> Optional[LearningActivity]:
        last_5_activities = utils.last_n_activities_attempted(
            self.learner.activity_attempt_counters, 5, self.query_cacher)
        for activity in last_5_activities:
            if utils.educational_use_in_activity(
                    activity, MagicStrings.EducationalUses.SELF_REPORT):
                continue
            if utils.educational_use_in_activity(
                    activity, MagicStrings.EducationalUses.ASSESSES):
                return None

            assessment_id = utils.get_check_on_learning_assessment_acitivity_id(
                activity)
            if assessment_id is not None:
                return self.query_cacher.get_activity(
                    utils.mongo_id_from_url(assessment_id))
        return None
Ejemplo n.º 10
0
    def get_activity_tokens(self):
        elo_id = get_aligned_elo(self.activity)
        mastery_estimate = get_elo_mastery_estimate(elo_id, self.learner)
        # TODO - Figure out better way around this
        try:
            last_activity = self.query_cacher.get_activity(
                mongo_id_from_url(
                    get_last_activity_url_identifier(self.learner)))
        except:
            last_activity = None

        if TokenConfig.ADVANCED_TOKEN_DECIDER:
            lb_tokens = self.learner_behavior_tokens(self.activity,
                                                     mastery_estimate,
                                                     last_activity)
            am_tokens = self.activity_metadata_tokens(elo_id, self.activity)
            history_multiplier = self.learner_activity_history_multiplier(
                self.learner, self.activity)
            return int(round((lb_tokens + am_tokens) * history_multiplier, 0))
        else:
            return self.simple_token_decider()
Ejemplo n.º 11
0
    def test_recommend_check_on_learning_assessment(self):
        # Given a learner that has taken at least one LearningActivity
        john_superlearner = self.name_to_learners['John Superlearner']

        # And the most recent activity has a check on learning assessment educational alignment object
        last_activity = self._get_activity(
            mongo_id_from_url(
                get_last_activity_url_identifier(john_superlearner)))

        check_on_learning_alignment = last_activity.educational_alignment[-1]
        self.assertEqual(
            MagicStrings.AdditionalTypes.APPROPRIATE_ASSESSMENT_ALIGNMENT,
            check_on_learning_alignment.additional_type)

        # When an assignment is requested from the mandatory assignment maker
        assignment = self._default_assignment_request(john_superlearner)

        # Then a Recommendation object is returned where the assignment id corresponds
        # to a formative assessment that assess the learners last activity
        self.assertEqual(check_on_learning_alignment.target_url,
                         assignment.assignment.activity_id)
Ejemplo n.º 12
0
def upcoming_get(learnerId):  # noqa: E501
    """Get upcoming activities

    Returns an overview of what the learner will be working on (and reflected in upcoming recommendations). It should include current ELO in the sequence that the learner has not yet mastered that they will continue learning. It will also include any ELOs that the learner has forgotten that will be reviewed. # noqa: E501

    :param learnerId: Id of the learner to get a recommendation for
    :type learnerId: str

    :rtype: Recommendation
    """
    print(
        "INFO: Received request for upcoming recommendation for learner with id {}"
        .format(learnerId))
    query_cacher = QueryCacher()
    learner, etag = query_cacher.get_learner(mongo_id_from_url(learnerId))
    if learner is None:
        return {"error_msg": "Learner cannot be found"}, 404
    print("INFO: Detected goals for learner with id {}. Goals={}".format(
        learnerId, learner.goals))
    cass_graph = load_or_instantiate('recommenderserver.resources', CassGraph,
                                     '603d5ac2-fa9e-43c3-9c50-87ff65804ccd')

    recommendation = Recommender(
        learner=learner,
        etag=etag,
        query_cacher=query_cacher,
        elo_row_strategy_classes=RowStrategyConfig.
        UPCOMING_COMPETENCY_ROW_STRATEGIES,
        activity_row_strategy_classes=[],
        not_enough_content_elo_row_strategy_classes=RowStrategyConfig.
        NOT_ENOUGH_CONTENT_ELO_ROW_STRATEGIES,
        not_enough_content_activity_row_strategy_classes=[],
        cass_graph=cass_graph,
        min_content_threshold=-1,
        filter_locked=RowStrategyConfig.FILTER_LOCKED_ACTIVITIES,
        use_prerequisties=False).get_recommendation()
    print_with_time(
        'INFO: Upcoming recommendation made for learner with id {}.'.format(
            learnerId))
    return recommendation
Ejemplo n.º 13
0
    def instantiate_row(self, active_elos: List[Competency],
                        filter_obj: bool) -> Optional[RecommendationRow]:
        if len(active_elos) == 0:
            return None

        elo_index = randint(0, len(active_elos) - 1)
        chosen_elo = active_elos[elo_index]
        past_activities = self.learner.activity_attempt_counters
        past_activities = [
            self.query_cacher.get_activity(
                utils.mongo_id_from_url(attempt.activity_id))
            for attempt in past_activities
        ]

        past_activities = [
            activity for activity in past_activities if activity is not None
        ]

        return self.recommendation_row(
            self.filter_activities_by_chosen_elo(chosen_elo.id,
                                                 past_activities),
            "Take Again")
Ejemplo n.º 14
0
    def non_move_to_next_section_strategies_do_not_return_any_assessments(
            self):
        # Given a user
        learner = self.name_to_learners['John Superlearner']
        # Who starts with nothing (You know nothing John Superlearner Snow)
        learner = self._clear_user(learner)

        # When the the strategy is not MoveToNextSection
        query_mock = self._default_query_mock()
        all_activities = AllActivities(learner, query_mock, self.cass_graph,
                                       True)

        # Then John is given a recommendation for the assessments of the first ELO
        elo_1_1 = self.cass_graph.competency_objs[
            'http://insertCassUrl/api/data/insertCassSchemaUrl.0.3.Competency/2fef4ebf-a526-4037-b9f4-fe53383db51a']
        rec_row = all_activities.instantiate_row(elo_1_1, True)

        self.assertGreater(len(rec_row.activities), 0)

        for recommended_activity in rec_row.activities:
            activity = query_mock.get_activity(
                mongo_id_from_url(recommended_activity.activity_id))
            self.assertFalse(is_assessment_for_elo(activity, elo_1_1))
Ejemplo n.º 15
0
 def _get_last_activity(self) -> Optional[LearningActivity]:
     return self.query_cacher.get_activity(
         utils.mongo_id_from_url(
             utils.get_last_activity_url_identifier(self.learner)))
Ejemplo n.º 16
0
def recommendation_get(learnerId: str, focusedCompetencies=None):  # noqa: E501
    """Get a new recommendation

     # noqa: E501

    :param learnerId: Id of the learner to get a recommendation for
    :type learnerId: str

    :rtype: Recommendation
    """
    print("INFO: Received request for recommendation for learner with id {}".format(learnerId))
    if focusedCompetencies != None:
        print("INFO: Getting recommendations with focused competencies: {}".format(focusedCompetencies))
    else:
        focusedCompetencies = False

    query_cacher = QueryCacher()
    learner, etag = query_cacher.get_learner(mongo_id_from_url(learnerId))
    if learner is None:
        return {"error_msg": "Learner cannot be found"}, 404
    print("INFO: Detected goals for learner with id {}. Goals={}".format(learnerId, learner.goals))
    cass_graph = load_or_instantiate('recommenderserver.resources', CassGraph, '603d5ac2-fa9e-43c3-9c50-87ff65804ccd')

    if MandatoryAssignmentMakerConfig.USE_MANDATORY_ASSIGNMENTS:
        mandatory_assignment_maker = MandatoryAssignmentMaker(learner, etag, query_cacher, cass_graph)
        assignment = mandatory_assignment_maker.get_mandatory_assignment()

        if assignment is not None:
            print(
                'INFO: Mandatory Assignment Recommendation made for learner with id {}. Recommendation: {}'.format(
                    learnerId, assignment.to_dict()))

            assigned_activity = query_cacher.get_activity(assignment.assignment.activity_id)
            aligned_tlo = get_aligned_tlo(assigned_activity)
            # TODO - figure out how to encode user's information into this xAPI statement. Likely either in context or result.
            log_statement = XApiStatement(agent_name="Recommender",
                                          agent_url_id=Config.RECOMMENDER_URL,
                                          verb_id="https://w3id.org/xapi/dod-isd/verbs/assigned",
                                          activity_id=aligned_tlo)

            xapi_sender = XAPISender(base_url='http://'+LoggingLRSConfig.BASE_URL,
                                     x_experience_version=LoggingLRSConfig.X_EXPERIENCE_VERSION,
                                     basic_auth_user=LoggingLRSConfig.CLIENT_USR,
                                     basic_auth_pwd=LoggingLRSConfig.CLIENT_PWD)
            if False:
                xapi_sender.statements_post([log_statement])

            return assignment

    recommendation = Recommender(learner=learner,
                                 etag=etag,
                                 query_cacher=query_cacher,
                                 elo_row_strategy_classes=RowStrategyConfig.PRIORITY_COMPETENCY_ROW_STRATEGIES,
                                 activity_row_strategy_classes=RowStrategyConfig.PRIORITY_ACTIVITY_ROW_STRATEGIES,
                                 not_enough_content_elo_row_strategy_classes=RowStrategyConfig.NOT_ENOUGH_CONTENT_ELO_ROW_STRATEGIES,
                                 not_enough_content_activity_row_strategy_classes=[],
                                 cass_graph=cass_graph,
                                 min_content_threshold=-1,
                                 filter_locked=RowStrategyConfig.FILTER_LOCKED_ACTIVITIES,
                                 use_prerequisties=True,
                                 focus_competencies=focusedCompetencies).get_recommendation()

    print_with_time(
        'INFO: Recommendation made for learner with id {}.'.format(learnerId))
    return recommendation