File size: 6,158 Bytes
75075b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
import os
from typing import Dict, List, Optional, NamedTuple, Tuple
from datetime import datetime

from datasets import load_dataset
from huggingface_hub import HfApi

# Environment variables and constants
EXAM_DATASET_ID = os.getenv(
    "EXAM_DATASET_ID", "agents-course/unit_1_quiz_student_responses"
)
CERTIFICATE_MODELS_DIR = os.getenv("CERTIFICATE_MODELS_DIR", "./certificate_models")
CERTIFICATE_PATH = os.path.join(CERTIFICATE_MODELS_DIR, "certificate.png")

PASSING_THRESHOLD = float(os.getenv("PASSING_THRESHOLD", "0.8"))  # 80%
MIN_QUESTIONS = int(os.getenv("MIN_QUESTIONS", "1"))


class CertificateResult(NamedTuple):
    """Stores the result of a certificate check"""

    message: str
    certificate_path: Optional[str]
    pass_percentage: float
    passed: bool
    results_df: Optional[object] = None


def get_user_results(username: str) -> List[Dict]:
    """
    Get user's quiz results from the dataset.

    Args:
        username: The Hugging Face username to check

    Returns:
        List of user's quiz results
    """
    try:
        ds = load_dataset(EXAM_DATASET_ID, split="train")

        # Filter for this user's results
        user_results = ds.filter(lambda x: x["username"] == username)

        results = user_results.to_list()
        print(f"Found {len(results)} results for user {username}")
        return results

    except Exception as e:
        print(f"Error in get_user_results: {str(e)}")
        raise


def calculate_pass_percentage(results: List[Dict]) -> Tuple[float, int]:
    """
    Calculate the user's pass percentage and number of questions from their results.

    The dataset structure has:
    - is_correct: bool indicating if answer was correct
    - grade: float64 indicating overall grade
    - datetime: string of attempt timestamp

    Args:
        results: List of quiz results

    Returns:
        Tuple of (highest grade achieved, number of questions answered)
    """
    try:
        if not results:
            return 0.0, 0

        # Group results by datetime to get distinct attempts
        attempts = {}
        for result in results:
            timestamp = result["datetime"]
            if timestamp not in attempts:
                attempts[timestamp] = {
                    "correct": 0,
                    "total": 0,
                    "grade": result.get("grade", 0.0),
                }

            attempts[timestamp]["total"] += 1
            if result["is_correct"]:
                attempts[timestamp]["correct"] += 1

        # Find the best attempt
        best_attempt = max(
            attempts.values(),
            key=lambda x: x["grade"]
            if x["grade"] is not None
            else (x["correct"] / x["total"] if x["total"] > 0 else 0),
        )

        # If grade is available, use it; otherwise calculate from correct/total
        if best_attempt["grade"] is not None and best_attempt["grade"] > 0:
            pass_percentage = float(best_attempt["grade"])
        else:
            pass_percentage = (
                best_attempt["correct"] / best_attempt["total"]
                if best_attempt["total"] > 0
                else 0.0
            )

        return pass_percentage, best_attempt["total"]

    except Exception as e:
        print(f"Error in calculate_pass_percentage: {str(e)}")
        raise


def has_passed(pass_percentage: float, num_questions: int) -> bool:
    """
    Check if user has passed based on percentage and minimum questions.

    Args:
        pass_percentage: User's highest quiz score
        num_questions: Number of questions answered

    Returns:
        Boolean indicating if user passed
    """
    return pass_percentage >= PASSING_THRESHOLD and num_questions >= MIN_QUESTIONS


def get_certificate_result(
    pass_percentage: float, num_questions: int
) -> CertificateResult:
    """
    Determine if user passed and create appropriate message.

    Args:
        pass_percentage: User's highest quiz score
        num_questions: Number of questions answered

    Returns:
        CertificateResult with pass status and details
    """
    passed = has_passed(pass_percentage, num_questions)

    if passed:
        return CertificateResult(
            message="""
            Congratulations, you successfully completed the course! 🎉 \n
            You can download your certificate below ⬇️ \n    
            You are now an <a href="https://huggingface.co/agents-course-finishers">Agent Course Finisher</a>!
            """,
            certificate_path=CERTIFICATE_PATH,
            pass_percentage=pass_percentage,
            passed=True,
        )
    else:
        return CertificateResult(
            message="""
            You haven't completed all the requirements yet. \n
            Keep trying! 💪
            """,
            certificate_path=None,
            pass_percentage=pass_percentage,
            passed=False,
        )


def check_certification(username: str) -> CertificateResult:
    """
    Check if a user has completed the certification requirements.

    Args:
        username: The Hugging Face username to check

    Returns:
        CertificateResult containing pass status and details
    """
    try:
        # Get user's quiz results
        results = get_user_results(username)
        if not results:
            return CertificateResult(
                message="No quiz results found. Please complete the quiz first.",
                certificate_path=None,
                pass_percentage=0.0,
                passed=False,
            )

        # Calculate pass percentage and get appropriate certificate result
        pass_percentage, num_questions = calculate_pass_percentage(results)
        return get_certificate_result(pass_percentage, num_questions)

    except Exception as e:
        error_msg = """
        There was an error checking your certification status.
        Please try again later or contact support if the issue persists.
        """
        return CertificateResult(
            message=error_msg,
            certificate_path=None,
            pass_percentage=0.0,
            passed=False,
        )