Abhinav commited on
Commit
484bee1
·
1 Parent(s): f2f372e

Refactor AI integration and enhance quiz functionality with validation

Browse files
Files changed (5) hide show
  1. .gitignore +3 -1
  2. ai.py +85 -0
  3. app.py +80 -76
  4. requirements.txt +0 -0
  5. stylesheet.py +176 -3
.gitignore CHANGED
@@ -2,4 +2,6 @@ env/
2
  .env
3
  .env.*
4
  .env.local
5
- __pycache__/
 
 
 
2
  .env
3
  .env.*
4
  .env.local
5
+ __pycache__/
6
+ .vscode/
7
+ test.py
ai.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from openai import OpenAI
3
+ import modal
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+
8
+ class Colors:
9
+ """ANSI color codes for terminal output formatting."""
10
+ GREEN = "\033[0;32m"
11
+ RED = "\033[0;31m"
12
+ BLUE = "\033[0;34m"
13
+ GRAY = "\033[0;90m"
14
+ BOLD = "\033[1m"
15
+ END = "\033[0m"
16
+
17
+
18
+ def ask_ai(
19
+ prompt,
20
+ system_prompt,
21
+ temperature=0.7,
22
+ max_tokens=None,
23
+ stream=True,
24
+ verbose=False
25
+ ):
26
+ """
27
+ Send a prompt to the AI model and get a response.
28
+
29
+ Args:
30
+ prompt (str): The user prompt to send to the AI
31
+ system_prompt (str): The system instructions for the AI
32
+ model (str): The model name to use
33
+ temperature (float): Controls randomness (0.0-1.0)
34
+ max_tokens (int): Maximum tokens in the response
35
+ stream (bool): Whether to stream the response
36
+ verbose (bool): Whether to print status messages
37
+
38
+ Returns:
39
+ str: The AI's response text
40
+ """
41
+
42
+ # Create OpenAI client and set up the connection to Modal
43
+ API_KEY = os.getenv("Modal_API_KEY")
44
+ client = OpenAI(api_key=API_KEY)
45
+
46
+ # Set base URL to point to our Modal-deployed endpoint
47
+ client.base_url = f"https://abhinav77642--llama-3-1-8b-instruct-serve.modal.run/v1"
48
+
49
+ # Set up the messages for the conversation
50
+ messages = [
51
+ {"role": "system", "content": system_prompt},
52
+ {"role": "user", "content": prompt}
53
+ ]
54
+
55
+ # Set up the completion parameters
56
+ completion_args = {
57
+ "model": "neuralmagic/Meta-Llama-3.1-8B-Instruct-quantized.w4a16",
58
+ "messages": messages,
59
+ "temperature": temperature,
60
+ "max_tokens": max_tokens,
61
+ "stream": stream
62
+ }
63
+
64
+ # Remove None values
65
+ completion_args = {k: v for k, v in completion_args.items() if v is not None}
66
+
67
+ try:
68
+ response = client.chat.completions.create(**completion_args)
69
+
70
+ # Handle the response based on streaming or non-streaming mode
71
+ if stream:
72
+ result = ""
73
+ for chunk in response:
74
+ if chunk.choices and chunk.choices[0].delta.content:
75
+ content = chunk.choices[0].delta.content
76
+ result += content
77
+
78
+ return result
79
+ else:
80
+ result = response.choices[0].message.content
81
+ return result
82
+
83
+ except Exception as e:
84
+ error_msg = f"Error during API call: {e}"
85
+ return f"Error: {error_msg}"
app.py CHANGED
@@ -1,42 +1,16 @@
1
- import os
2
  import json
3
  import gradio as gr
4
  import PyPDF2 as pypdf
5
- from prompts import prompts
6
  from stylesheet import style
7
  from dotenv import load_dotenv
8
- from azure.ai.inference import ChatCompletionsClient
9
- from azure.ai.inference.models import SystemMessage, UserMessage
10
- from azure.core.credentials import AzureKeyCredential
11
 
12
  load_dotenv()
13
 
14
  css, js = style()
15
 
16
 
17
- # AI Response Generation Function
18
- def ai(system_prompt, user_prompt):
19
- endpoint = "https://abhin-marwbf2g-eastus2.cognitiveservices.azure.com/openai/deployments/gpt-4o"
20
- model_name = "gpt-4o"
21
- api_key = os.getenv("API_KEY")
22
- client = ChatCompletionsClient(
23
- endpoint=endpoint,
24
- credential=AzureKeyCredential(api_key),
25
- )
26
- response = client.complete(
27
- messages=[
28
- SystemMessage(content=system_prompt),
29
- UserMessage(content=user_prompt),
30
- ],
31
- max_tokens=4096,
32
- temperature=1.0,
33
- top_p=1.0,
34
- model=model_name,
35
- )
36
- # print("---------AI RESPONSE RECEIVED-----------")
37
- return response.choices[0].message.content
38
-
39
-
40
  # PDF To Text Conversion Function
41
  def convertPdfIntoText(file):
42
  if file is None:
@@ -50,79 +24,109 @@ def convertPdfIntoText(file):
50
 
51
 
52
  # Gradio Interface Setup
53
- with gr.Blocks(title="Quizy", css=css, head=js) as demo:
54
  gr.Markdown("# Quizy: Quiz Generator")
55
  gr.Markdown("Upload a PDF file to generate a quiz based on its content.")
56
 
57
  file_input = gr.File(label="Upload PDF File", file_types=[".pdf"])
58
- submit_btn = gr.Button("Generate Quiz", visible=True, elem_id="generate-btn")
59
 
60
  with gr.Accordion("Quiz", open=False, visible=False) as quiz_accordion:
61
  output_container = gr.HTML()
 
62
 
63
- # Main Function
64
- def generate_quiz(file):
65
- if file is None:
66
- return "<p>No file uploaded.</p>", gr.Accordion(visible=False)
67
 
68
- inputData = convertPdfIntoText(file)
69
 
70
- systemPrompt, userPrompt = prompts(inputData)
71
- response = ai(systemPrompt, userPrompt)
72
 
73
- clean_response = response.strip().removeprefix("```json").removesuffix("```")
74
- clean_response = clean_response.strip()
75
- quiz_data = json.loads(clean_response)
 
 
76
 
77
- html = "<div class='quiz-container'>"
 
78
 
79
- # Store the answers in a separate div for JavaScript access
80
- answers_json = {
81
- str(i): question["answer"] for i, question in enumerate(quiz_data["quiz"])
82
- }
83
- html += f"<div id='quiz-answers' style='display:none;'>{json.dumps(answers_json)}</div>"
84
 
85
- for i, question in enumerate(quiz_data["quiz"]):
86
- question_type = question["type"]
 
87
 
88
- answer_json = json.dumps(question["answer"]).replace('"', "&quot;")
89
- html += f"<div class='question' data-id='{i}' data-type='{question_type}' data-answer='{answer_json}'>"
90
- html += f"<h3>Question {i+1}: {question['question']}</h3>"
91
-
92
- if question_type == "single_choice":
93
- if "options" in question:
 
94
  html += "<div class='options'>"
95
- for option in question["options"]:
96
- html += f"<div><input type='radio' name='q{i}' value='{option}'> {option}</div>"
 
 
97
  html += "</div>"
98
- if question_type == "true_false":
99
- html += "<div class='options'>"
100
- html += "<div><input type='radio' name='q{i}' value='True'> True</div>"
101
- html += (
102
- "<div><input type='radio' name='q{i}' value='False'> False</div>"
103
- )
 
 
 
 
 
104
  html += "</div>"
105
- elif question_type == "multiple_choice":
106
- if "options" in question:
107
- html += "<div class='options'>"
108
- for option in question["options"]:
109
- html += f"<div><input type='checkbox' name='q{i}[]' value='{option}'> {option}</div>"
110
- html += "</div>"
111
- elif question_type == "fill_in_the_blank":
112
- html += "<input type='text' placeholder='Enter your answer here...'>"
113
 
 
 
 
 
114
  html += "</div>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
- html += "<button class='submit-btn' onclick='checkAnswers()'>Submit</button>"
117
- html += "<div id='result'></div>"
118
- html += "</div>"
119
 
120
- return html, gr.Accordion(visible=True)
121
 
122
- submit_btn.click(
123
  fn=generate_quiz,
124
  inputs=file_input,
125
  outputs=[output_container, quiz_accordion],
126
  )
127
 
128
- demo.launch()
 
 
1
  import json
2
  import gradio as gr
3
  import PyPDF2 as pypdf
 
4
  from stylesheet import style
5
  from dotenv import load_dotenv
6
+ from prompts import prompts
7
+ from ai import ask_ai
 
8
 
9
  load_dotenv()
10
 
11
  css, js = style()
12
 
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  # PDF To Text Conversion Function
15
  def convertPdfIntoText(file):
16
  if file is None:
 
24
 
25
 
26
  # Gradio Interface Setup
27
+ with gr.Blocks(title="Quizy", css=css, head=js) as app:
28
  gr.Markdown("# Quizy: Quiz Generator")
29
  gr.Markdown("Upload a PDF file to generate a quiz based on its content.")
30
 
31
  file_input = gr.File(label="Upload PDF File", file_types=[".pdf"])
32
+ generate_btn = gr.Button("Generate Quiz", visible=True, elem_id="generate-btn")
33
 
34
  with gr.Accordion("Quiz", open=False, visible=False) as quiz_accordion:
35
  output_container = gr.HTML()
36
+ submit_btn = gr.Button("Submit", elem_id="submit-btn", visible=False)
37
 
38
+ def generate_quiz(file):
39
+ if file is None:
40
+ return "<p>No file uploaded.</p>", gr.Accordion(visible=False)
 
41
 
42
+ inputData = convertPdfIntoText(file)
43
 
44
+ systemPrompt, userPrompt = prompts(inputData)
45
+ response = ask_ai(systemPrompt, userPrompt)
46
 
47
+ clean_response = (
48
+ response.strip().removeprefix("```json").removesuffix("```")
49
+ )
50
+ clean_response = clean_response.strip()
51
+ quiz_data = json.loads(clean_response)
52
 
53
+ html = "<div class='quiz-container'>"
54
+ html += "<form id='quiz-form' method='post' onsubmit='return false;'>"
55
 
56
+ for i, question in enumerate(quiz_data["quiz"]):
57
+ question_type = question["type"]
 
 
 
58
 
59
+ answer_json = json.dumps(question["answer"]).replace('"', "&quot;")
60
+ html += f"<div class='question' data-id='{i}' data-type='{question_type}' data-answer='{answer_json}'>"
61
+ html += f"<h3>Question {i+1}: {question['question']}</h3>"
62
 
63
+ if question_type == "single_choice":
64
+ if "options" in question:
65
+ html += "<div class='options'>"
66
+ for option in question["options"]:
67
+ html += f"<div><input type='radio' name='q{i}' value='{option}'> {option}</div>"
68
+ html += "</div>"
69
+ elif question_type == "true_false":
70
  html += "<div class='options'>"
71
+ html += (
72
+ f"<div><input type='radio' name='q{i}' value='True'> True</div>"
73
+ )
74
+ html += f"<div><input type='radio' name='q{i}' value='False'> False</div>"
75
  html += "</div>"
76
+ elif question_type == "multiple_choice":
77
+ if "options" in question:
78
+ html += "<div class='options'>"
79
+ for option in question["options"]:
80
+ html += f"<div><input type='checkbox' name='q{i}[]' value='{option}'> {option}</div>"
81
+ html += "</div>"
82
+ elif question_type == "fill_in_the_blank":
83
+ html += (
84
+ "<input type='text' placeholder='Enter your answer here...'>"
85
+ )
86
+
87
  html += "</div>"
 
 
 
 
 
 
 
 
88
 
89
+ # Updated button to use validateForm first and then call checkAnswers if validation passes
90
+ html += "<button type='button' class='submit-btn' onclick='if(validateForm()) checkAnswers(event);'>Submit Quiz</button>"
91
+ html += "</form>" # Close the form
92
+ html += "<div id='result'></div>"
93
  html += "</div>"
94
+ html += "<details id='quiz-answers' style='display:none;'>"
95
+ html += "<summary><h2>Solutions:</h2></summary>"
96
+ html += "<div class='answers-container'>"
97
+
98
+ for i, question in enumerate(quiz_data["quiz"]):
99
+ question_type = question["type"]
100
+ html += "<div class='answer-item'>"
101
+ html += f"<h3>Question {i+1}: {question['question']}</h3>"
102
+
103
+ # Format the answer based on question type
104
+ if question_type == "multiple_choice":
105
+ if isinstance(question["answer"], list):
106
+ answer_text = ", ".join(question["answer"])
107
+ html += f"<p><strong>Correct Answer:</strong> {answer_text}</p>"
108
+ else:
109
+ html += f"<p><strong>Correct Answer:</strong> {question['answer']}</p>"
110
+ elif question_type == "fill_in_the_blank":
111
+ html += (
112
+ f"<p><strong>Correct Answer:</strong> {question['answer']}</p>"
113
+ )
114
+ else: # single_choice or true_false
115
+ html += (
116
+ f"<p><strong>Correct Answer:</strong> {question['answer']}</p>"
117
+ )
118
+
119
+ html += "</div>"
120
 
121
+ html += "</div>" # Close answers-container
122
+ html += "</details>" # Close quiz-answers
 
123
 
124
+ return html, gr.Accordion(visible=True)
125
 
126
+ generate_btn.click(
127
  fn=generate_quiz,
128
  inputs=file_input,
129
  outputs=[output_container, quiz_accordion],
130
  )
131
 
132
+ app.launch()
requirements.txt CHANGED
Binary files a/requirements.txt and b/requirements.txt differ
 
stylesheet.py CHANGED
@@ -7,6 +7,34 @@ def style():
7
  border: 1px solid #ddd;
8
  border-radius: 5px;
9
  margin-top: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  }
11
  #result {
12
  display: none;
@@ -22,10 +50,143 @@ def style():
22
  color: #333333;
23
  border-radius: 5px;
24
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  """
26
  js = """
27
  <script>
28
- function checkAnswers() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  let score = 0;
30
  let total = 0;
31
  const questions = document.querySelectorAll(".question");
@@ -75,8 +236,18 @@ def style():
75
  score++;
76
  }
77
  }
 
 
 
 
 
 
 
 
 
 
78
  });
79
-
80
  // Display the result
81
  resultElement.style.display = "block";
82
  if (score === total) {
@@ -94,7 +265,9 @@ def style():
94
  resultElement.style.border = "1px solid #ddd";
95
  resultElement.style.borderRadius = "5px";
96
  resultElement.style.marginTop = "20px";
 
 
97
  }
98
  </script>
99
  """
100
- return css, js
 
7
  border: 1px solid #ddd;
8
  border-radius: 5px;
9
  margin-top: 20px;
10
+ cursor: pointer;
11
+ transition: all 0.3s ease;
12
+ }
13
+ .submit-btn.disabled {
14
+ background-color: #cccccc;
15
+ color: #666666;
16
+ cursor: not-allowed;
17
+ opacity: 0.7;
18
+ }
19
+ .validation-alert {
20
+ background-color: #ffdddd;
21
+ color: #990000;
22
+ padding: 10px;
23
+ margin-bottom: 15px;
24
+ border-radius: 5px;
25
+ font-weight: bold;
26
+ text-align: center;
27
+ animation: fadeIn 0.5s;
28
+ }
29
+ .invalid-question {
30
+ border: 2px solid #ff0000;
31
+ padding: 10px;
32
+ border-radius: 5px;
33
+ margin-bottom: 10px;
34
+ }
35
+ @keyframes fadeIn {
36
+ 0% { opacity: 0; }
37
+ 100% { opacity: 1; }
38
  }
39
  #result {
40
  display: none;
 
50
  color: #333333;
51
  border-radius: 5px;
52
  }
53
+ #quiz-answers {
54
+ margin-top: 30px;
55
+ padding: 20px;
56
+ background-color: #dfdfdf;
57
+ border: 1px solid #ddd;
58
+ border-radius: 5px;
59
+ }
60
+ #quiz-answers summary {
61
+ display: inline-block;
62
+ padding-bottom: 5px;
63
+ cursor: pointer;
64
+ }
65
+ #quiz-answers summary, #quiz-answers h2 {
66
+ color: #383838;
67
+ }
68
+ .answers-container {
69
+ display: flex;
70
+ flex-direction: column;
71
+ gap: 20px;
72
+ }
73
+
74
+ .answer-item {
75
+ padding: 15px;
76
+ background-color: #e1ece4;
77
+ border: 2px solid lightgreen;
78
+ border-radius: 5px;
79
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
80
+ }
81
+
82
+ .answer-item h3 {
83
+ margin-top: 0;
84
+ color: #333;
85
+ font-size: 18px;
86
+ }
87
+
88
+ .answer-item p {
89
+ margin-bottom: 0;
90
+ color: #009214;
91
+ font-size: 16px;
92
+ font-weight: 500;
93
+ }
94
+ .answer-item p strong {
95
+ color: #1636d5;
96
+ }
97
  """
98
  js = """
99
  <script>
100
+ function validateForm() {
101
+ const questions = document.querySelectorAll(".question");
102
+ let isValid = true;
103
+
104
+ questions.forEach((question) => {
105
+ const questionType = question.getAttribute("data-type");
106
+ const inputs = question.querySelectorAll("input");
107
+
108
+ if (questionType === "single_choice" || questionType === "true_false") {
109
+ // Check if at least one radio button is selected
110
+ let hasChecked = false;
111
+ inputs.forEach((input) => {
112
+ if (input.checked) {
113
+ hasChecked = true;
114
+ }
115
+ });
116
+ if (!hasChecked) {
117
+ isValid = false;
118
+ // Highlight the question that needs attention
119
+ question.style.border = "2px solid red";
120
+ question.style.padding = "10px";
121
+ question.style.borderRadius = "5px";
122
+ } else {
123
+ question.style.border = "none";
124
+ question.style.padding = "0";
125
+ }
126
+ } else if (questionType === "multiple_choice") {
127
+ // Check if at least one checkbox is selected
128
+ let hasChecked = false;
129
+ inputs.forEach((input) => {
130
+ if (input.checked) {
131
+ hasChecked = true;
132
+ }
133
+ });
134
+ if (!hasChecked) {
135
+ isValid = false;
136
+ question.style.border = "2px solid red";
137
+ question.style.padding = "10px";
138
+ question.style.borderRadius = "5px";
139
+ } else {
140
+ question.style.border = "none";
141
+ question.style.padding = "0";
142
+ }
143
+ } else if (questionType === "fill_in_the_blank") {
144
+ // Check if the text input has a value
145
+ if (inputs[0].value.trim() === "") {
146
+ isValid = false;
147
+ question.style.border = "2px solid red";
148
+ question.style.padding = "10px";
149
+ question.style.borderRadius = "5px";
150
+ } else {
151
+ question.style.border = "none";
152
+ question.style.padding = "0";
153
+ }
154
+ }
155
+ });
156
+
157
+ return isValid;
158
+ }
159
+
160
+ function checkAnswers(event) {
161
+ event.preventDefault();
162
+
163
+ // First validate the form
164
+ if (!validateForm()) {
165
+ // Show an alert message
166
+ const alertDiv = document.createElement('div');
167
+ alertDiv.className = 'validation-alert';
168
+ alertDiv.innerHTML = '<p>Please answer all questions before submitting!</p>';
169
+ alertDiv.style.backgroundColor = '#ffdddd';
170
+ alertDiv.style.color = '#990000';
171
+ alertDiv.style.padding = '10px';
172
+ alertDiv.style.borderRadius = '5px';
173
+ alertDiv.style.marginBottom = '15px';
174
+ alertDiv.style.fontWeight = 'bold';
175
+ alertDiv.style.textAlign = 'center';
176
+
177
+ // Insert at the top of the form
178
+ const form = document.getElementById('quiz-form');
179
+ const existingAlert = document.querySelector('.validation-alert');
180
+ if (existingAlert) {
181
+ form.removeChild(existingAlert);
182
+ }
183
+ form.insertBefore(alertDiv, form.firstChild);
184
+
185
+ // Scroll to the top
186
+ window.scrollTo({ top: 0, behavior: 'smooth' });
187
+ return;
188
+ }
189
+
190
  let score = 0;
191
  let total = 0;
192
  const questions = document.querySelectorAll(".question");
 
236
  score++;
237
  }
238
  }
239
+ }); // Remove any validation alerts once submitted successfully
240
+ const existingAlert = document.querySelector('.validation-alert');
241
+ if (existingAlert) {
242
+ existingAlert.remove();
243
+ }
244
+
245
+ // Remove any highlight on questions
246
+ document.querySelectorAll('.question').forEach(q => {
247
+ q.style.border = 'none';
248
+ q.style.padding = '0';
249
  });
250
+
251
  // Display the result
252
  resultElement.style.display = "block";
253
  if (score === total) {
 
265
  resultElement.style.border = "1px solid #ddd";
266
  resultElement.style.borderRadius = "5px";
267
  resultElement.style.marginTop = "20px";
268
+ answerContainer = document.getElementById('quiz-answers');
269
+ answerContainer.style.display = 'block';
270
  }
271
  </script>
272
  """
273
+ return css, js