diff --git a/learntools/__init__.py b/learntools/__init__.py index 710c86703..6c13d734c 100644 --- a/learntools/__init__.py +++ b/learntools/__init__.py @@ -2,4 +2,4 @@ machine_learning, ml_explainability, ml_insights, ml_intermediate, python, \ sql -__version__ = '0.3.4' +__version__ = '0.3.5' diff --git a/learntools/core/tracking.py b/learntools/core/tracking.py index cad62b6c7..e86a8e4d4 100644 --- a/learntools/core/tracking.py +++ b/learntools/core/tracking.py @@ -1,12 +1,18 @@ import enum from IPython.display import display, Javascript import json - import learntools +import os # If set to True, then echo logged events as output. DEBUG = False +USE_KAGGLESDK = os.environ.get('LEARN_USE_KAGGLE_SDK') == 'True' +if USE_KAGGLESDK: + from kagglesdk import KaggleClient + from kagglesdk.education.types.education_api_service import ApiTrackExerciseInteractionRequest + from kagglesdk.education.types.education_service import LearnExerciseInteractionType, LearnExerciseOutcomeType, LearnExerciseQuestionType + class InteractionType(enum.Enum): CHECK = 1 HINT = 2 @@ -32,7 +38,67 @@ class QuestionType(enum.Enum): trace = '', ) -def track(event): +def interaction_type_to_kagglesdk(event): + switch = { + InteractionType.CHECK: LearnExerciseInteractionType.CHECK, + InteractionType.HINT: LearnExerciseInteractionType.HINT, + InteractionType.SOLUTION: LearnExerciseInteractionType.SOLUTION, + } + value = event['interactionType'] + assert value in switch + return switch.get(value) + +def outcome_type_to_kagglesdk(interaction_type, event): + switch = { + OutcomeType.PASS: LearnExerciseOutcomeType.PASS, + OutcomeType.FAIL: LearnExerciseOutcomeType.FAIL, + OutcomeType.EXCEPTION: LearnExerciseOutcomeType.EXCEPTION, + OutcomeType.UNATTEMPTED: LearnExerciseOutcomeType.UNATTEMPTED, + } + + value = event.get('outcomeType', None) + if value: + assert value in switch + return switch.get(value) + else: + assert interaction_type != LearnExerciseInteractionType.CHECK, "Check events must have an OutcomeType set: {!r}".format(event) + return LearnExerciseOutcomeType.LEARN_EXERCISE_OUTCOME_TYPE_UNSPECIFIED + +def question_type_to_kagglesdk(event): + switch = { + QuestionType.EQUALITYCHECKPROBLEM: LearnExerciseQuestionType.EQUALITY_CHECK_PROBLEM, + QuestionType.CODINGPROBLEM: LearnExerciseQuestionType.CODING_PROBLEM, + QuestionType.FUNCTIONPROBLEM: LearnExerciseQuestionType.FUNCTION_PROBLEM, + QuestionType.THOUGHTEXPERIMENT: LearnExerciseQuestionType.THOUGHT_EXPERIMENT, + } + + question_type = event.get('questionType', None) + if question_type: + assert question_type in switch + return switch.get(question_type) + return None + +def track_using_kagglesdk(event): + request = ApiTrackExerciseInteractionRequest() + request.learn_tools_version = str(learntools.__version__) + request.value_towards_completion = event.get('valueTowardsCompletion', 0.0) + request.interaction_type = interaction_type_to_kagglesdk(event) + request.outcome_type = outcome_type_to_kagglesdk(request.interaction_type, event) + + question_type = question_type_to_kagglesdk(event) + if question_type: + request.question_type = question_type + + # TODO(b/379083750): the following items are still TBD + # - set request.fork_parent_kernel_session_id + # - automatically handle authorization in KaggleClient + # - post the nudge information back to the client + + client = KaggleClient() + result = client.education.education_api_client.track_exercise_interaction(request) + + +def track_using_iframe(event): # TODO: could be nice to put some validation logic here. for k, v in _EVENT_DEFAULTS.items(): event.setdefault(k, v) @@ -65,3 +131,9 @@ def track(event): display(Javascript(debug_js)) display(message) +def track(event): + if USE_KAGGLESDK: + track_using_kagglesdk(event) + else: + track_using_iframe(event) + \ No newline at end of file