|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import os |
|
|
import json |
|
|
from datetime import datetime |
|
|
import gradio as gr |
|
|
import PyPDF2 |
|
|
import time |
|
|
import re |
|
|
from PIL import Image |
|
|
import io |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
import google.generativeai as genai |
|
|
genai.configure(api_key=os.getenv("GEMINI_API_KEY")) |
|
|
gemini_model = genai.GenerativeModel('gemini-2.5-pro') |
|
|
print("โ
Gemini AI initialized successfully (PRIMARY)") |
|
|
except Exception as e: |
|
|
print(f"โ Error initializing Gemini: {e}") |
|
|
gemini_model = None |
|
|
|
|
|
|
|
|
try: |
|
|
import cohere |
|
|
cohere_client = cohere.Client(os.getenv("COHERE_API_KEY")) |
|
|
print("โ
Cohere initialized successfully (SECONDARY)") |
|
|
except Exception as e: |
|
|
print(f"โ Error initializing Cohere: {e}") |
|
|
cohere_client = None |
|
|
|
|
|
|
|
|
try: |
|
|
from huggingface_hub import InferenceClient |
|
|
zai_client = InferenceClient( |
|
|
provider="novita", |
|
|
api_key=os.environ.get("HF_TOKEN"), |
|
|
) |
|
|
print("โ
Z.ai GLM-4.6 initialized successfully (TERTIARY)") |
|
|
except Exception as e: |
|
|
print(f"โ Error initializing Z.ai: {e}") |
|
|
zai_client = None |
|
|
|
|
|
|
|
|
try: |
|
|
minimax_client = InferenceClient( |
|
|
provider="novita", |
|
|
api_key=os.environ.get("HF_TOKEN"), |
|
|
) |
|
|
print("โ
MiniMax AI initialized successfully (FINAL FALLBACK)") |
|
|
except Exception as e: |
|
|
print(f"โ Error initializing MiniMax: {e}") |
|
|
minimax_client = None |
|
|
|
|
|
|
|
|
def ask_ai(prompt, temperature=0.7, max_retries=2): |
|
|
""" |
|
|
Try models in order: Gemini โ Cohere โ Z.ai โ MiniMax |
|
|
Returns: (response_text, source_name) |
|
|
""" |
|
|
last_error = None |
|
|
|
|
|
|
|
|
if gemini_model: |
|
|
for attempt in range(max_retries): |
|
|
try: |
|
|
response = gemini_model.generate_content( |
|
|
prompt, |
|
|
generation_config=genai.types.GenerationConfig( |
|
|
temperature=temperature, |
|
|
) |
|
|
) |
|
|
return response.text, "gemini" |
|
|
except Exception as e: |
|
|
last_error = e |
|
|
print(f"โ Gemini attempt {attempt+1} failed: {str(e)}") |
|
|
if attempt < max_retries - 1: |
|
|
time.sleep(1) |
|
|
|
|
|
|
|
|
if cohere_client: |
|
|
for attempt in range(max_retries): |
|
|
try: |
|
|
response = cohere_client.chat( |
|
|
model="command-r-plus-08-2024", |
|
|
message=prompt, |
|
|
temperature=temperature |
|
|
) |
|
|
return response.text, "cohere" |
|
|
except Exception as e: |
|
|
last_error = e |
|
|
print(f"โ Cohere attempt {attempt+1} failed: {str(e)}") |
|
|
if attempt < max_retries - 1: |
|
|
time.sleep(1) |
|
|
|
|
|
|
|
|
if zai_client: |
|
|
for attempt in range(max_retries): |
|
|
try: |
|
|
completion = zai_client.chat.completions.create( |
|
|
model="zai-org/GLM-4.6", |
|
|
messages=[{"role": "user", "content": prompt}], |
|
|
temperature=temperature |
|
|
) |
|
|
return completion.choices[0].message.content, "zai" |
|
|
except Exception as e: |
|
|
last_error = e |
|
|
print(f"โ Z.ai attempt {attempt+1} failed: {str(e)}") |
|
|
if attempt < max_retries - 1: |
|
|
time.sleep(1) |
|
|
|
|
|
|
|
|
if minimax_client: |
|
|
try: |
|
|
completion = minimax_client.chat.completions.create( |
|
|
model="MiniMaxAI/MiniMax-M2", |
|
|
messages=[{"role": "user", "content": prompt}], |
|
|
temperature=temperature |
|
|
) |
|
|
return completion.choices[0].message.content, "minimax" |
|
|
except Exception as e: |
|
|
last_error = e |
|
|
print(f"โ MiniMax fallback failed: {str(e)}") |
|
|
|
|
|
|
|
|
error_msg = f"โ Error: All AI services failed. Last error: {str(last_error)}" |
|
|
return error_msg, "error" |
|
|
|
|
|
|
|
|
papers_storage = [] |
|
|
pdf_content_storage = {} |
|
|
insert_storage = {} |
|
|
questions_index = [] |
|
|
ADMIN_PASSWORD = "@mikaelJ46" |
|
|
|
|
|
|
|
|
chemistry_topics = [ |
|
|
|
|
|
"States of Matter", "Atoms, Elements & Compounds", "Mixtures & Separation Techniques", |
|
|
"Atomic Structure", "Electronic Configuration", "Periodic Table", |
|
|
"Chemical Bonding: Ionic", "Chemical Bonding: Covalent", "Chemical Bonding: Metallic", |
|
|
"Structure & Properties of Materials", "Nanoparticles", |
|
|
|
|
|
|
|
|
"Group 1: Alkali Metals", "Group 7: Halogens", "Group 0: Noble Gases", |
|
|
"Transition Metals", "Reactivity Series", "Extraction of Metals", |
|
|
"Corrosion & Rusting", "Alloys", |
|
|
|
|
|
|
|
|
"Chemical Reactions", "Exothermic & Endothermic Reactions", "Energy Changes", |
|
|
"Rates of Reaction", "Catalysts", "Reversible Reactions", "Equilibrium", |
|
|
"Redox Reactions", "Electrolysis", "Electrochemistry", |
|
|
|
|
|
|
|
|
"Acids & Alkalis", "pH Scale", "Neutralization", "Making Salts", |
|
|
"Titrations", "Strong & Weak Acids", |
|
|
|
|
|
|
|
|
"Hydrocarbons: Alkanes", "Hydrocarbons: Alkenes", "Crude Oil & Fractional Distillation", |
|
|
"Polymers", "Alcohols", "Carboxylic Acids", "Organic Synthesis", |
|
|
|
|
|
|
|
|
"Air Composition", "Air Pollution", "Greenhouse Effect & Climate Change", |
|
|
"Water Treatment", "Sustainable Chemistry", |
|
|
|
|
|
|
|
|
"Relative Formula Mass", "Moles & Molar Mass", "Empirical & Molecular Formulae", |
|
|
"Reacting Masses", "Limiting Reactants", "Percentage Yield", |
|
|
"Gas Volumes", "Concentration Calculations", |
|
|
|
|
|
|
|
|
"Laboratory Safety", "Experimental Techniques", "Analysis & Evaluation" |
|
|
] |
|
|
|
|
|
biology_topics = [ |
|
|
|
|
|
"Cell Structure & Function", "Specialised Cells", "Microscopy", |
|
|
"Cell Division: Mitosis", "Cell Division: Meiosis", "Stem Cells", |
|
|
"Diffusion", "Osmosis", "Active Transport", |
|
|
|
|
|
|
|
|
"Organisation of Organisms", "Enzymes", "Digestive System", |
|
|
"Circulatory System: Heart", "Circulatory System: Blood Vessels", "Blood Components", |
|
|
"Respiratory System", "Gas Exchange", "Breathing Mechanism", |
|
|
|
|
|
|
|
|
"Communicable Diseases", "Pathogens: Bacteria & Viruses", "Disease Prevention", |
|
|
"Immune System", "Vaccination", "Antibiotics & Painkillers", |
|
|
"Developing New Medicines", "Monoclonal Antibodies", |
|
|
|
|
|
|
|
|
"Photosynthesis", "Factors Affecting Photosynthesis", "Uses of Glucose", |
|
|
"Respiration: Aerobic", "Respiration: Anaerobic", "Metabolism", |
|
|
|
|
|
|
|
|
"Homeostasis Principles", "Nervous System", "Reflex Actions", "Brain Structure", |
|
|
"Eye Structure & Function", "Body Temperature Control", |
|
|
"Endocrine System", "Hormones", "Blood Glucose Regulation", |
|
|
"Diabetes", "Water & Nitrogen Balance", "Kidneys & Dialysis", |
|
|
|
|
|
|
|
|
"DNA Structure", "Protein Synthesis", "Genetic Inheritance", |
|
|
"Inherited Disorders", "Sex Determination", "Genetic Diagrams", |
|
|
"Variation", "Evolution", "Natural Selection", "Selective Breeding", |
|
|
"Genetic Engineering", "Cloning", "Classification", |
|
|
|
|
|
|
|
|
"Ecosystems", "Food Chains & Webs", "Energy Transfer", |
|
|
"Nutrient Cycles: Carbon", "Nutrient Cycles: Water", "Nutrient Cycles: Nitrogen", |
|
|
"Biodiversity", "Habitat Loss", "Conservation", |
|
|
"Population Dynamics", "Competition", "Adaptations", |
|
|
"Waste Management", "Pollution", "Global Warming Impact", |
|
|
"Deforestation", "Sustainable Development", |
|
|
|
|
|
|
|
|
"Scientific Method", "Variables & Controls", "Data Analysis", |
|
|
"Biological Techniques", "Field Studies" |
|
|
] |
|
|
|
|
|
|
|
|
def extract_text_from_pdf(pdf_file): |
|
|
"""Extract text from uploaded PDF file""" |
|
|
if pdf_file is None: |
|
|
return "" |
|
|
try: |
|
|
pdf_reader = PyPDF2.PdfReader(pdf_file) |
|
|
text = "" |
|
|
for page in pdf_reader.pages: |
|
|
text += page.extract_text() + "\n" |
|
|
return text |
|
|
except Exception as e: |
|
|
return f"Error extracting PDF: {e}" |
|
|
|
|
|
def identify_paper_details(text, filename): |
|
|
"""Use AI to identify paper year, series, variant, and subject from content""" |
|
|
sample_text = text[:2000] if len(text) > 2000 else text |
|
|
|
|
|
prompt = f"""Analyze this IGCSE science past paper and identify its details. |
|
|
|
|
|
Filename: {filename} |
|
|
Paper Text Sample: |
|
|
{sample_text} |
|
|
|
|
|
Identify and return ONLY a JSON object with: |
|
|
- subject: "Chemistry" or "Biology" |
|
|
- year: The year (e.g., "2023", "2022") |
|
|
- series: The exam series (e.g., "June", "November", "May/June", "October/November") |
|
|
- variant: The paper variant (e.g., "1", "2", "3" or "11", "12", "21", "22") |
|
|
- paper_number: The paper number (e.g., "1", "2", "3", "4", "6") |
|
|
- syllabus_code: If visible (e.g., "0620" for Chemistry, "0610" for Biology) |
|
|
|
|
|
Look for clues like "Cambridge IGCSE", subject codes, dates, paper numbers. |
|
|
|
|
|
Return ONLY valid JSON (no markdown): |
|
|
{{"subject": "...", "year": "...", "series": "...", "variant": "...", "paper_number": "...", "syllabus_code": "..."}}""" |
|
|
|
|
|
try: |
|
|
response, _ = ask_ai(prompt, temperature=0.1) |
|
|
clean_txt = response.replace("```json", "").replace("```", "").strip() |
|
|
details = json.loads(clean_txt) |
|
|
return details |
|
|
except Exception as e: |
|
|
print(f"Error identifying paper details: {e}") |
|
|
return parse_filename_for_details(filename) |
|
|
|
|
|
def parse_filename_for_details(filename): |
|
|
"""Fallback: Parse filename for paper details""" |
|
|
details = { |
|
|
"subject": "Unknown", |
|
|
"year": "Unknown", |
|
|
"series": "Unknown", |
|
|
"variant": "Unknown", |
|
|
"paper_number": "Unknown", |
|
|
"syllabus_code": "Unknown" |
|
|
} |
|
|
|
|
|
|
|
|
year_match = re.search(r'(20\d{2})|(\d{2}(?=_[wsmj]|[WS]))', filename) |
|
|
if year_match: |
|
|
year = year_match.group(1) or ("20" + year_match.group(2)) |
|
|
details["year"] = year |
|
|
|
|
|
|
|
|
if re.search(r'[Jj]une?|[Mm]ay[_/-]?[Jj]une?|mj|MJ', filename): |
|
|
details["series"] = "May/June" |
|
|
elif re.search(r'[Nn]ov(ember)?|[Oo]ct(ober)?|ON', filename): |
|
|
details["series"] = "October/November" |
|
|
elif re.search(r'[Mm]ar(ch)?|[Ff]eb(ruary)?|FM', filename): |
|
|
details["series"] = "February/March" |
|
|
|
|
|
|
|
|
variant_match = re.search(r'[Vv]ariant[_\s]?(\d)|[Pp]aper[_\s]?(\d{1,2})|_qp_(\d{1,2})', filename) |
|
|
if variant_match: |
|
|
details["variant"] = variant_match.group(1) or variant_match.group(2) or variant_match.group(3) |
|
|
|
|
|
|
|
|
code_match = re.search(r'\b(0\d{3})\b', filename) |
|
|
if code_match: |
|
|
details["syllabus_code"] = code_match.group(1) |
|
|
code_subject_map = { |
|
|
'0620': 'Chemistry', '0610': 'Biology' |
|
|
} |
|
|
details["subject"] = code_subject_map.get(code_match.group(1), "Unknown") |
|
|
|
|
|
return details |
|
|
|
|
|
def extract_questions_from_text(text, paper_id, paper_title, subject, paper_details): |
|
|
"""Use AI to intelligently extract questions from past paper text""" |
|
|
if not text or len(text) < 100: |
|
|
return [] |
|
|
|
|
|
prompt = f"""Analyze this IGCSE {subject} past paper and extract ALL questions. |
|
|
|
|
|
Paper Details: |
|
|
- Subject: {subject} |
|
|
- Year: {paper_details.get('year', 'Unknown')} |
|
|
- Series: {paper_details.get('series', 'Unknown')} |
|
|
- Paper: {paper_details.get('paper_number', 'Unknown')} |
|
|
- Variant: {paper_details.get('variant', 'Unknown')} |
|
|
|
|
|
Paper Text: |
|
|
{text[:8000]} |
|
|
|
|
|
Extract each question and return as JSON array. For each question include: |
|
|
- question_number (e.g., "1(a)", "2(b)(i)") |
|
|
- question_text (the complete question) |
|
|
- marks (number of marks) |
|
|
- topic (specific IGCSE {subject} topic) |
|
|
- requires_insert (true/false - references diagrams, figures, data?) |
|
|
- question_type (e.g., "multiple choice", "structured", "practical", "calculation", "explanation") |
|
|
|
|
|
Return ONLY valid JSON array (no markdown): |
|
|
[{{"question_number": "1(a)", "question_text": "...", "marks": 2, "topic": "...", "requires_insert": false, "question_type": "..."}}]""" |
|
|
|
|
|
try: |
|
|
response, _ = ask_ai(prompt, temperature=0.2) |
|
|
clean_txt = response.replace("```json", "").replace("```", "").strip() |
|
|
questions = json.loads(clean_txt) |
|
|
|
|
|
for q in questions: |
|
|
q['paper_id'] = paper_id |
|
|
q['paper_title'] = paper_title |
|
|
q['subject'] = subject |
|
|
q['year'] = paper_details.get('year', 'Unknown') |
|
|
q['series'] = paper_details.get('series', 'Unknown') |
|
|
q['variant'] = paper_details.get('variant', 'Unknown') |
|
|
q['paper_number'] = paper_details.get('paper_number', 'Unknown') |
|
|
q['syllabus_code'] = paper_details.get('syllabus_code', 'Unknown') |
|
|
|
|
|
return questions |
|
|
except Exception as e: |
|
|
print(f"Error extracting questions: {e}") |
|
|
return extract_questions_fallback(text, paper_id, paper_title, subject, paper_details) |
|
|
|
|
|
def extract_questions_fallback(text, paper_id, paper_title, subject, paper_details): |
|
|
"""Fallback method using regex patterns""" |
|
|
questions = [] |
|
|
pattern = r'(\d+(?:\([a-z]\))?(?:\([ivx]+\))?)\s+(.{20,500}?)\[(\d+)\]' |
|
|
matches = re.finditer(pattern, text, re.IGNORECASE) |
|
|
|
|
|
for match in matches: |
|
|
q_num = match.group(1) |
|
|
q_text = match.group(2).strip() |
|
|
marks = int(match.group(3)) |
|
|
|
|
|
questions.append({ |
|
|
'question_number': q_num, |
|
|
'question_text': q_text, |
|
|
'marks': marks, |
|
|
'topic': 'General', |
|
|
'requires_insert': bool(re.search(r'Fig\.|diagram|table|graph|data|shown', q_text, re.IGNORECASE)), |
|
|
'question_type': 'structured', |
|
|
'paper_id': paper_id, |
|
|
'paper_title': paper_title, |
|
|
'subject': subject, |
|
|
'year': paper_details.get('year', 'Unknown'), |
|
|
'series': paper_details.get('series', 'Unknown'), |
|
|
'variant': paper_details.get('variant', 'Unknown'), |
|
|
'paper_number': paper_details.get('paper_number', 'Unknown'), |
|
|
'syllabus_code': paper_details.get('syllabus_code', 'Unknown') |
|
|
}) |
|
|
|
|
|
return questions |
|
|
|
|
|
def process_insert_file(insert_file): |
|
|
"""Process insert file (PDF or image)""" |
|
|
if insert_file is None: |
|
|
return None, None |
|
|
|
|
|
try: |
|
|
file_name = insert_file.name |
|
|
file_ext = file_name.lower().split('.')[-1] |
|
|
|
|
|
if file_ext == 'pdf': |
|
|
text = extract_text_from_pdf(insert_file) |
|
|
return text, "pdf" |
|
|
elif file_ext in ['jpg', 'jpeg', 'png', 'gif']: |
|
|
image = Image.open(insert_file) |
|
|
return image, "image" |
|
|
else: |
|
|
return None, None |
|
|
except Exception as e: |
|
|
print(f"Error processing insert: {e}") |
|
|
return None, None |
|
|
|
|
|
|
|
|
def ai_tutor_chat(message, history, subject, topic): |
|
|
"""AI tutor focused on deep understanding and conceptual clarity""" |
|
|
if not message.strip(): |
|
|
return history |
|
|
|
|
|
subject_context = { |
|
|
"Chemistry": """You are an expert IGCSE Chemistry tutor who prioritizes DEEP UNDERSTANDING over memorization. |
|
|
|
|
|
Your teaching approach: |
|
|
- Always explain the WHY behind chemical phenomena (not just the what) |
|
|
- Connect microscopic (atomic/molecular) behavior to macroscopic observations |
|
|
- Use real-world examples and applications to make concepts tangible |
|
|
- Break down complex reactions into step-by-step mechanisms |
|
|
- Emphasize patterns and relationships (e.g., periodic trends, reaction types) |
|
|
- Address common misconceptions directly |
|
|
- Use analogies and visual descriptions to clarify abstract concepts |
|
|
- Encourage students to predict outcomes based on understanding, not memorization |
|
|
- Link different topics together (e.g., bonding โ properties โ reactivity) |
|
|
|
|
|
Key teaching principles: |
|
|
- Particle theory underlies everything (structure determines properties) |
|
|
- Energy changes drive chemical processes |
|
|
- Conservation laws (mass, charge, energy) are fundamental |
|
|
- Equilibrium and rates are about competing processes""", |
|
|
|
|
|
"Biology": """You are an expert IGCSE Biology tutor who emphasizes DEEP UNDERSTANDING and interconnected thinking. |
|
|
|
|
|
Your teaching approach: |
|
|
- Always explain biological processes in terms of structure-function relationships |
|
|
- Connect molecular/cellular processes to organism-level phenomena |
|
|
- Use real-world health, ecology, and evolution examples |
|
|
- Explain mechanisms step-by-step (don't just list facts) |
|
|
- Emphasize the REASONS for biological adaptations and processes |
|
|
- Address common misconceptions about evolution, genetics, and body systems |
|
|
- Use analogies to make complex processes accessible (but explain their limits) |
|
|
- Show how different biological systems interact and depend on each other |
|
|
- Encourage students to apply knowledge to novel situations |
|
|
- Link topics together (e.g., respiration โ transport โ gas exchange) |
|
|
|
|
|
Key teaching principles: |
|
|
- Evolution by natural selection explains adaptations |
|
|
- Enzymes control the rate of life processes |
|
|
- Homeostasis maintains stable internal conditions |
|
|
- Energy flow and nutrient cycling connect ecology |
|
|
- DNA โ RNA โ protein โ trait (central dogma)""" |
|
|
} |
|
|
|
|
|
system = f"""{subject_context[subject]} |
|
|
|
|
|
Current focus: {topic or 'any topic'} |
|
|
|
|
|
When answering: |
|
|
1. Check for understanding gaps before giving the full answer |
|
|
2. Use the Socratic method - guide thinking with questions |
|
|
3. Provide detailed step-by-step explanations with reasoning |
|
|
4. Include diagrams descriptions when helpful |
|
|
5. Give practice examples for students to try |
|
|
6. Connect to exam skills (command words, mark schemes) |
|
|
7. Celebrate curiosity and deeper questions |
|
|
|
|
|
Remember: Understanding beats memorization. Help students THINK like scientists.""" |
|
|
|
|
|
|
|
|
conversation = "" |
|
|
for user_msg, bot_msg in history[-6:]: |
|
|
if user_msg: |
|
|
conversation += f"Student: {user_msg}\n" |
|
|
if bot_msg: |
|
|
clean_msg = bot_msg.replace("๐ต ", "").replace("๐ข ", "").replace("๐ฃ ", "") |
|
|
conversation += f"Tutor: {clean_msg}\n" |
|
|
|
|
|
conversation += f"Student: {message}\nTutor:" |
|
|
full_prompt = f"{system}\n\nConversation:\n{conversation}" |
|
|
|
|
|
bot_response, source = ask_ai(full_prompt, temperature=0.7) |
|
|
|
|
|
|
|
|
if source == "cohere": |
|
|
bot_response = f"๐ต {bot_response}" |
|
|
elif source == "zai": |
|
|
bot_response = f"๐ข {bot_response}" |
|
|
elif source == "minimax": |
|
|
bot_response = f"๐ฃ {bot_response}" |
|
|
|
|
|
history.append((message, bot_response)) |
|
|
return history |
|
|
|
|
|
def clear_chat(): |
|
|
return [] |
|
|
|
|
|
|
|
|
def explain_concept(subject, concept): |
|
|
"""Deep dive explanation of scientific concepts""" |
|
|
if not concept: |
|
|
return "Enter a concept to explain!" |
|
|
|
|
|
prompt = f"""Provide a COMPREHENSIVE explanation of this IGCSE {subject} concept: "{concept}" |
|
|
|
|
|
Structure your explanation as follows: |
|
|
|
|
|
**1. CORE IDEA** (In simple terms - what IS it?) |
|
|
|
|
|
**2. DEEPER UNDERSTANDING** (Why does it work this way? What's the mechanism?) |
|
|
|
|
|
**3. KEY DETAILS & FACTS** (Important specifics students need to know) |
|
|
|
|
|
**4. COMMON MISCONCEPTIONS** (What do students often get wrong?) |
|
|
|
|
|
**5. REAL-WORLD CONNECTIONS** (Where do we see this? Why does it matter?) |
|
|
|
|
|
**6. EXAM TIPS** (What questions test this? How to approach them?) |
|
|
|
|
|
**7. PRACTICE THINKING** (A question to test understanding) |
|
|
|
|
|
Use clear language, step-by-step reasoning, and helpful analogies. |
|
|
Make connections to other topics. Focus on UNDERSTANDING, not just facts.""" |
|
|
|
|
|
response, source = ask_ai(prompt, temperature=0.5) |
|
|
|
|
|
if source in ["cohere", "zai", "minimax"]: |
|
|
response = f"{response}\n\n_[Explained by {source.title()}]_" |
|
|
|
|
|
return response |
|
|
|
|
|
|
|
|
def solve_calculation(subject, problem, show_steps): |
|
|
"""Step-by-step calculation solver with conceptual explanation""" |
|
|
if not problem.strip(): |
|
|
return "Enter a calculation problem!" |
|
|
|
|
|
steps_instruction = "Show EVERY step with full working" if show_steps else "Show key steps" |
|
|
|
|
|
prompt = f"""Solve this IGCSE {subject} calculation problem with DEEP EXPLANATION: |
|
|
|
|
|
Problem: {problem} |
|
|
|
|
|
Provide: |
|
|
1. **What we're finding**: Identify what the question asks for |
|
|
2. **What we know**: List given information and its meaning |
|
|
3. **Formula/Concept**: Which formula/principle applies and WHY |
|
|
4. **Step-by-step solution**: {steps_instruction} with units |
|
|
5. **Checking**: Does the answer make sense? Why? |
|
|
6. **Concept explanation**: What does this result mean scientifically? |
|
|
7. **Common mistakes**: What errors do students typically make? |
|
|
8. **Related problems**: Similar question types to practice |
|
|
|
|
|
Use clear formatting. Explain the reasoning at each step, not just the math.""" |
|
|
|
|
|
response, source = ask_ai(prompt, temperature=0.3) |
|
|
|
|
|
if source in ["cohere", "zai", "minimax"]: |
|
|
response = f"{response}\n\n_[Solved by {source.title()}]_" |
|
|
|
|
|
return response |
|
|
|
|
|
|
|
|
def analyze_experiment(subject, experiment_description, question): |
|
|
"""Analyze experiments and practical work with scientific reasoning""" |
|
|
if not experiment_description.strip(): |
|
|
return "Describe the experiment!" |
|
|
|
|
|
prompt = f"""Analyze this IGCSE {subject} experiment with focus on SCIENTIFIC THINKING: |
|
|
|
|
|
Experiment: {experiment_description} |
|
|
|
|
|
Question: {question if question else "Analyze this experiment comprehensively"} |
|
|
|
|
|
Provide: |
|
|
1. **Aim**: What is being investigated and why? |
|
|
2. **Science Behind It**: What principles/concepts does this test? |
|
|
3. **Method Analysis**: Why is it done this way? What makes it valid? |
|
|
4. **Variables**: Independent, dependent, control variables and why they matter |
|
|
5. **Expected Results**: What should happen and WHY (predict using theory) |
|
|
6. **Safety & Practical Tips**: Important precautions and techniques |
|
|
7. **Possible Errors**: What could go wrong? How to minimize errors? |
|
|
8. **Results Analysis**: How to interpret data scientifically |
|
|
9. **Evaluation**: How could this experiment be improved? |
|
|
10. **Exam Connection**: How might this be tested? |
|
|
|
|
|
Think like a scientist - connect method to theory.""" |
|
|
|
|
|
response, source = ask_ai(prompt, temperature=0.4) |
|
|
|
|
|
if source in ["cohere", "zai", "minimax"]: |
|
|
response = f"{response}\n\n_[Analyzed by {source.title()}]_" |
|
|
|
|
|
return response |
|
|
|
|
|
|
|
|
def generate_question(subject, topic, difficulty): |
|
|
"""Generate practice questions with focus on understanding""" |
|
|
if not topic: |
|
|
return "Select a topic!", "", "" |
|
|
|
|
|
difficulty_guide = { |
|
|
"Easy": "Test basic understanding and recall. Simple calculations or describe questions.", |
|
|
"Medium": "Test application and analysis. Require explanations and connections.", |
|
|
"Hard": "Test evaluation and synthesis. Multi-step problems, novel scenarios." |
|
|
} |
|
|
|
|
|
pdf_context = "" |
|
|
for paper_id, content in pdf_content_storage.items(): |
|
|
paper = next((p for p in papers_storage if p['id'] == paper_id), None) |
|
|
if paper and paper['subject'] == subject: |
|
|
pdf_context += f"\n\nReference: {paper['title']}:\n{content[:2000]}" |
|
|
|
|
|
prompt = f"""Create ONE high-quality IGCSE {subject} exam question on: "{topic}" |
|
|
|
|
|
Difficulty: {difficulty} - {difficulty_guide[difficulty]} |
|
|
{f"Base style on: {pdf_context[:1500]}" if pdf_context else "Create authentic exam-style question."} |
|
|
|
|
|
The question should: |
|
|
- Test UNDERSTANDING, not just recall |
|
|
- Use appropriate command words (describe, explain, evaluate, calculate, etc.) |
|
|
- Be worth 4-8 marks |
|
|
- Include context/data if relevant |
|
|
- Test ability to apply knowledge to new situations |
|
|
|
|
|
Return ONLY valid JSON (no markdown): |
|
|
{{ |
|
|
"question": "complete question with all context", |
|
|
"marks": 6, |
|
|
"command_word": "explain/describe/calculate/etc", |
|
|
"expectedAnswer": "detailed key points with scientific reasoning", |
|
|
"markScheme": "specific mark allocations and what earns each mark", |
|
|
"understandingTips": "what concepts students need to understand to answer this" |
|
|
}}""" |
|
|
|
|
|
response, source = ask_ai(prompt, temperature=0.4) |
|
|
|
|
|
try: |
|
|
clean_txt = response.replace("```json", "").replace("```", "").strip() |
|
|
data = json.loads(clean_txt) |
|
|
|
|
|
question_text = f"**[{data['marks']} marks] - {data['command_word'].upper()}**\n\n{data['question']}" |
|
|
expected = f"**Understanding Required:**\n{data.get('understandingTips', '')}\n\n**Key Points:**\n{data['expectedAnswer']}" |
|
|
marks = data['markScheme'] |
|
|
|
|
|
return question_text, expected, marks |
|
|
except: |
|
|
return response, "", "Error parsing response" |
|
|
|
|
|
def check_answer(question, expected, user_answer, subject): |
|
|
"""Check answers with focus on understanding and reasoning""" |
|
|
if not user_answer.strip(): |
|
|
return "Write your answer first!" |
|
|
|
|
|
prompt = f"""Evaluate this IGCSE {subject} answer focusing on UNDERSTANDING and SCIENTIFIC REASONING: |
|
|
|
|
|
Question: {question} |
|
|
|
|
|
Expected answer points: {expected} |
|
|
|
|
|
Student's answer: |
|
|
{user_answer} |
|
|
|
|
|
Assess: |
|
|
1. Scientific accuracy |
|
|
2. Depth of understanding (not just memorization) |
|
|
3. Use of scientific terminology |
|
|
4. Logical reasoning and explanations |
|
|
5. Answering the specific command word |
|
|
6. Completeness |
|
|
|
|
|
Return JSON (no markdown): |
|
|
{{ |
|
|
"score": 0-100, |
|
|
"marks": "X/8", |
|
|
"understanding_level": "surface/developing/strong/excellent", |
|
|
"feedback": "detailed feedback on scientific understanding", |
|
|
"strengths": "what shows good understanding", |
|
|
"improvements": "how to deepen understanding", |
|
|
"misconceptions": "any misunderstandings evident", |
|
|
"examTips": "exam technique advice", |
|
|
"followUpQuestion": "a question to test/extend understanding further" |
|
|
}}""" |
|
|
|
|
|
response, source = ask_ai(prompt, temperature=0.3) |
|
|
|
|
|
try: |
|
|
clean_txt = response.replace("```json", "").replace("```", "").strip() |
|
|
fb = json.loads(clean_txt) |
|
|
|
|
|
result = f"""๐ **Score: {fb['score']}% ({fb['marks']})** |
|
|
**Understanding Level:** {fb['understanding_level'].upper()} |
|
|
|
|
|
๐ **Detailed Feedback:** |
|
|
{fb['feedback']} |
|
|
|
|
|
โ
**Your Strengths:** |
|
|
{fb['strengths']} |
|
|
|
|
|
๐ **How to Deepen Understanding:** |
|
|
{fb['improvements']} |
|
|
|
|
|
โ ๏ธ **Misconceptions to Address:** |
|
|
{fb.get('misconceptions', 'None identified')} |
|
|
|
|
|
๐ก **Exam Tips:** |
|
|
{fb['examTips']} |
|
|
|
|
|
๐ค **Think Further:** |
|
|
{fb.get('followUpQuestion', 'Keep practicing!')}""" |
|
|
|
|
|
if source in ["cohere", "zai", "minimax"]: |
|
|
result += f"\n\n_[Graded by {source.title()}]_" |
|
|
|
|
|
return result |
|
|
except: |
|
|
return response |
|
|
|
|
|
|
|
|
def search_questions_by_topic(subject, topic): |
|
|
"""Search for questions matching a specific topic""" |
|
|
if not questions_index: |
|
|
return "๐ญ No questions available yet. Admin needs to upload past papers first!" |
|
|
|
|
|
matching = [q for q in questions_index |
|
|
if q['subject'] == subject and |
|
|
(topic.lower() in q['topic'].lower() or topic.lower() in q['question_text'].lower())] |
|
|
|
|
|
if not matching: |
|
|
return f"๐ญ No questions found for {topic} in {subject}. Try a different topic or broader search." |
|
|
|
|
|
result = f"### ๐ฏ Found {len(matching)} question(s) on '{topic}' in {subject}\n\n" |
|
|
|
|
|
for i, q in enumerate(matching, 1): |
|
|
insert_note = " ๐ผ๏ธ **[Requires Insert]**" if q.get('requires_insert') else "" |
|
|
q_type = f" ({q.get('question_type', 'structured')})" if q.get('question_type') else "" |
|
|
|
|
|
paper_info = f"**{q['year']} {q['series']}** - Paper {q['paper_number']}" |
|
|
if q.get('variant') != 'Unknown': |
|
|
paper_info += f" Variant {q['variant']}" |
|
|
if q.get('syllabus_code') != 'Unknown': |
|
|
paper_info += f" ({q['syllabus_code']})" |
|
|
|
|
|
result += f"""**Question {i}** - {paper_info} |
|
|
๐ **{q['question_number']}** [{q['marks']} marks]{q_type}{insert_note} |
|
|
{q['question_text']} |
|
|
|
|
|
{'โ'*80} |
|
|
""" |
|
|
|
|
|
return result |
|
|
|
|
|
def view_papers_student(subject): |
|
|
"""View all papers for a subject""" |
|
|
filtered = [p for p in papers_storage if p["subject"] == subject] |
|
|
if not filtered: |
|
|
return f"๐ญ No {subject} papers available." |
|
|
|
|
|
result = "" |
|
|
for p in filtered: |
|
|
insert_note = " ๐ผ๏ธ Insert Available" if p['id'] in insert_storage else "" |
|
|
q_count = len([q for q in questions_index if q['paper_id'] == p['id']]) |
|
|
|
|
|
paper_details = p.get('paper_details', {}) |
|
|
year = paper_details.get('year', 'Unknown') |
|
|
series = paper_details.get('series', 'Unknown') |
|
|
variant = paper_details.get('variant', 'Unknown') |
|
|
paper_num = paper_details.get('paper_number', 'Unknown') |
|
|
syllabus = paper_details.get('syllabus_code', 'Unknown') |
|
|
|
|
|
paper_info = f"**{year} {series}** - Paper {paper_num}" |
|
|
if variant != 'Unknown': |
|
|
paper_info += f" Variant {variant}" |
|
|
if syllabus != 'Unknown': |
|
|
paper_info += f" ({syllabus})" |
|
|
|
|
|
result += f"""**{p['title']}** {'๐ PDF' if p.get('has_pdf') else ''}{insert_note} |
|
|
{paper_info} |
|
|
โฐ Uploaded: {p['uploaded_at']} | ๐ {q_count} questions extracted |
|
|
{p['content'][:200]}... |
|
|
|
|
|
{'โ'*80} |
|
|
""" |
|
|
|
|
|
return result |
|
|
|
|
|
|
|
|
def verify_admin_password(password): |
|
|
if password == ADMIN_PASSWORD: |
|
|
return gr.update(visible=True), gr.update(visible=False), "โ
Access granted!" |
|
|
return gr.update(visible=False), gr.update(visible=True), "โ Incorrect password!" |
|
|
|
|
|
def upload_paper(title, subject, content, pdf_file, insert_file): |
|
|
"""Upload past papers with AI extraction""" |
|
|
if not all([title, subject, content]): |
|
|
return "โ Please fill all required fields!", get_papers_list(), "๐ Status: Waiting for upload" |
|
|
|
|
|
paper_id = len(papers_storage) + 1 |
|
|
|
|
|
pdf_text = "" |
|
|
paper_details = {} |
|
|
if pdf_file is not None: |
|
|
pdf_text = extract_text_from_pdf(pdf_file) |
|
|
if pdf_text and not pdf_text.startswith("Error"): |
|
|
paper_details = identify_paper_details(pdf_text, pdf_file.name) |
|
|
pdf_content_storage[paper_id] = pdf_text |
|
|
|
|
|
detail_str = f"\n\n๐ **Paper Details:**" |
|
|
detail_str += f"\n- Year: {paper_details.get('year', 'Unknown')}" |
|
|
detail_str += f"\n- Series: {paper_details.get('series', 'Unknown')}" |
|
|
detail_str += f"\n- Paper: {paper_details.get('paper_number', 'Unknown')}" |
|
|
detail_str += f"\n- Variant: {paper_details.get('variant', 'Unknown')}" |
|
|
if paper_details.get('syllabus_code') != 'Unknown': |
|
|
detail_str += f"\n- Syllabus Code: {paper_details.get('syllabus_code')}" |
|
|
content += detail_str |
|
|
content += f"\n[๐ PDF extracted: {len(pdf_text)} characters]" |
|
|
|
|
|
insert_data = None |
|
|
insert_type = None |
|
|
if insert_file is not None: |
|
|
insert_data, insert_type = process_insert_file(insert_file) |
|
|
if insert_data: |
|
|
insert_storage[paper_id] = (insert_data, insert_type) |
|
|
content += f"\n[๐ผ๏ธ Insert attached: {insert_type}]" |
|
|
|
|
|
papers_storage.append({ |
|
|
"id": paper_id, |
|
|
"title": title, |
|
|
"subject": subject, |
|
|
"content": content, |
|
|
"has_pdf": bool(pdf_text and not pdf_text.startswith("Error")), |
|
|
"has_insert": bool(insert_data), |
|
|
"paper_details": paper_details, |
|
|
"uploaded_at": datetime.now().strftime("%Y-%m-%d %H:%M") |
|
|
}) |
|
|
|
|
|
status_msg = "โ
Paper uploaded!" |
|
|
if pdf_text and not pdf_text.startswith("Error"): |
|
|
status_msg += "\nโณ AI is extracting questions..." |
|
|
questions = extract_questions_from_text(pdf_text, paper_id, title, subject, paper_details) |
|
|
questions_index.extend(questions) |
|
|
|
|
|
paper_info = f"{paper_details.get('year', 'Unknown')} {paper_details.get('series', 'Unknown')}" |
|
|
if paper_details.get('variant') != 'Unknown': |
|
|
paper_info += f" Variant {paper_details.get('variant')}" |
|
|
|
|
|
status_msg += f"\nโ
Extracted {len(questions)} questions from **{paper_info}**!" |
|
|
status_msg += f"\n๐ Identified as: {subject} Paper {paper_details.get('paper_number', 'Unknown')}" |
|
|
|
|
|
return status_msg, get_papers_list(), f"๐ Total papers: {len(papers_storage)} | Total questions: {len(questions_index)}" |
|
|
|
|
|
def get_papers_list(): |
|
|
"""Get formatted list of all papers""" |
|
|
if not papers_storage: |
|
|
return "No papers yet." |
|
|
|
|
|
result = [] |
|
|
for p in papers_storage: |
|
|
paper_details = p.get('paper_details', {}) |
|
|
year = paper_details.get('year', 'Unknown') |
|
|
series = paper_details.get('series', 'Unknown') |
|
|
variant = paper_details.get('variant', 'Unknown') |
|
|
paper_num = paper_details.get('paper_number', 'Unknown') |
|
|
|
|
|
paper_info = f"{year} {series} - Paper {paper_num}" |
|
|
if variant != 'Unknown': |
|
|
paper_info += f" V{variant}" |
|
|
|
|
|
insert_icon = '๐ผ๏ธ Insert' if p.get('has_insert') else '' |
|
|
pdf_icon = '๐ PDF' if p.get('has_pdf') else '' |
|
|
|
|
|
result.append(f"**{p['title']}** ({p['subject']}) {pdf_icon} {insert_icon}\n{paper_info}\nโฐ {p['uploaded_at']}\n{p['content'][:120]}...\n{'โ'*60}") |
|
|
|
|
|
return "\n".join(result) |
|
|
|
|
|
|
|
|
with gr.Blocks(theme=gr.themes.Soft(), title="IGCSE Science Platform") as app: |
|
|
gr.Markdown(""" |
|
|
# ๐ฌ IGCSE Science Learning Platform |
|
|
Chemistry โ๏ธ | Biology ๐งฌ |
|
|
_Deep Understanding Through AI-Powered Learning_ |
|
|
""") |
|
|
|
|
|
with gr.Tabs(): |
|
|
|
|
|
with gr.Tab("๐จโ๐ Student Portal"): |
|
|
with gr.Tabs(): |
|
|
|
|
|
with gr.Tab("๐ค AI Tutor - Deep Understanding"): |
|
|
gr.Markdown("""### Chat with Your AI Science Tutor |
|
|
*Focus on understanding WHY, not just memorizing facts* |
|
|
|
|
|
**Tips for getting the most from your tutor:** |
|
|
- Ask "why" and "how" questions |
|
|
- Request step-by-step explanations |
|
|
- Ask for real-world examples |
|
|
- Challenge yourself with "what if" scenarios""") |
|
|
|
|
|
with gr.Row(): |
|
|
subj = gr.Radio(["Chemistry", "Biology"], label="Subject", value="Chemistry") |
|
|
topc = gr.Dropdown(chemistry_topics, label="Topic (optional)", allow_custom_value=True) |
|
|
|
|
|
def update_topics(s): |
|
|
topics = {"Chemistry": chemistry_topics, "Biology": biology_topics} |
|
|
return gr.Dropdown(choices=topics[s], value=None) |
|
|
subj.change(update_topics, subj, topc) |
|
|
|
|
|
chat = gr.Chatbot(height=500, show_label=False) |
|
|
txt = gr.Textbox(placeholder="Ask anything... e.g., 'Why do ionic compounds conduct electricity when molten but not when solid?'", label="Message") |
|
|
with gr.Row(): |
|
|
send = gr.Button("Send ๐ค", variant="primary") |
|
|
clr = gr.Button("Clear ๐") |
|
|
|
|
|
send.click(ai_tutor_chat, [txt, chat, subj, topc], chat) |
|
|
txt.submit(ai_tutor_chat, [txt, chat, subj, topc], chat) |
|
|
clr.click(clear_chat, outputs=chat) |
|
|
|
|
|
|
|
|
with gr.Tab("๐ก Concept Explainer"): |
|
|
gr.Markdown("""### Deep Dive into Scientific Concepts |
|
|
*Get comprehensive explanations that build real understanding*""") |
|
|
|
|
|
with gr.Row(): |
|
|
ce_subj = gr.Radio(["Chemistry", "Biology"], label="Subject", value="Chemistry") |
|
|
ce_concept = gr.Textbox(label="Concept to Explain", |
|
|
placeholder="e.g., 'covalent bonding', 'osmosis', 'enzyme action'") |
|
|
|
|
|
ce_output = gr.Markdown(label="Explanation") |
|
|
gr.Button("๐ Explain Concept", variant="primary", size="lg").click( |
|
|
explain_concept, [ce_subj, ce_concept], ce_output |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("๐งฎ Calculation Helper"): |
|
|
gr.Markdown("""### Step-by-Step Problem Solving |
|
|
*Understand the reasoning, not just the answer*""") |
|
|
|
|
|
calc_subj = gr.Radio(["Chemistry", "Biology"], label="Subject", value="Chemistry") |
|
|
calc_problem = gr.Textbox(lines=4, label="Problem", |
|
|
placeholder="e.g., 'Calculate the mass of calcium carbonate needed to produce 22g of carbon dioxide'") |
|
|
calc_steps = gr.Checkbox(label="Show detailed steps", value=True) |
|
|
calc_output = gr.Markdown(label="Solution") |
|
|
|
|
|
gr.Button("โ๏ธ Solve Problem", variant="primary", size="lg").click( |
|
|
solve_calculation, [calc_subj, calc_problem, calc_steps], calc_output |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("๐ฌ Experiment Analyzer"): |
|
|
gr.Markdown("""### Understand Scientific Investigations |
|
|
*Connect practical work to theory*""") |
|
|
|
|
|
exp_subj = gr.Radio(["Chemistry", "Biology"], label="Subject", value="Chemistry") |
|
|
exp_desc = gr.Textbox(lines=5, label="Experiment Description", |
|
|
placeholder="Describe the experiment setup and procedure...") |
|
|
exp_q = gr.Textbox(label="Specific Question (optional)", |
|
|
placeholder="e.g., 'Why must we use excess acid in this experiment?'") |
|
|
exp_output = gr.Markdown(label="Analysis") |
|
|
|
|
|
gr.Button("๐ Analyze Experiment", variant="primary", size="lg").click( |
|
|
analyze_experiment, [exp_subj, exp_desc, exp_q], exp_output |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("๐ Past Papers Browser"): |
|
|
gr.Markdown("""### ๐ฏ Search Real Exam Questions by Topic |
|
|
*Practice with actual IGCSE questions*""") |
|
|
|
|
|
with gr.Row(): |
|
|
pp_subject = gr.Radio(["Chemistry", "Biology"], label="Subject", value="Chemistry") |
|
|
pp_topic = gr.Dropdown(chemistry_topics, label="Select Topic") |
|
|
|
|
|
pp_subject.change(update_topics, pp_subject, pp_topic) |
|
|
|
|
|
search_btn = gr.Button("๐ Search Questions", variant="primary", size="lg") |
|
|
questions_output = gr.Markdown(label="Questions Found", value="Select a topic and click Search") |
|
|
|
|
|
search_btn.click(search_questions_by_topic, [pp_subject, pp_topic], questions_output) |
|
|
|
|
|
gr.Markdown("---\n### ๐ Browse All Papers") |
|
|
browse_subject = gr.Radio(["Chemistry", "Biology"], label="Subject", value="Chemistry") |
|
|
papers_display = gr.Markdown(label="Available Papers") |
|
|
gr.Button("๐ Show All Papers").click(view_papers_student, browse_subject, papers_display) |
|
|
|
|
|
|
|
|
with gr.Tab("โ Practice Questions"): |
|
|
gr.Markdown("""### Generate & Practice Exam Questions |
|
|
*Focus on understanding, not just correct answers*""") |
|
|
|
|
|
with gr.Row(): |
|
|
ps = gr.Radio(["Chemistry", "Biology"], label="Subject", value="Chemistry") |
|
|
pt = gr.Dropdown(chemistry_topics, label="Topic") |
|
|
diff = gr.Radio(["Easy", "Medium", "Hard"], label="Difficulty", value="Medium") |
|
|
|
|
|
ps.change(update_topics, ps, pt) |
|
|
|
|
|
q = gr.Textbox(label="๐ Question", lines=8, interactive=False) |
|
|
exp = gr.Textbox(label="Understanding Required & Expected Points", lines=6, interactive=False) |
|
|
mark = gr.Textbox(label="๐ Mark Scheme", lines=5, interactive=False) |
|
|
ans = gr.Textbox(lines=12, label="โ Your Answer", |
|
|
placeholder="Write your answer here. Focus on explaining your reasoning...") |
|
|
fb = gr.Textbox(lines=18, label="๐ Detailed Feedback", interactive=False) |
|
|
|
|
|
with gr.Row(): |
|
|
gr.Button("๐ฒ Generate Question", variant="primary").click( |
|
|
generate_question, [ps, pt, diff], [q, exp, mark] |
|
|
) |
|
|
gr.Button("โ
Check Answer", variant="secondary").click( |
|
|
check_answer, [q, exp, ans, ps], fb |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("๐ Admin Panel"): |
|
|
with gr.Column() as login_section: |
|
|
gr.Markdown("### ๐ Admin Login") |
|
|
pwd = gr.Textbox(label="Password", type="password", placeholder="Enter admin password") |
|
|
login_btn = gr.Button("๐ Login", variant="primary") |
|
|
login_status = gr.Textbox(label="Status", interactive=False) |
|
|
|
|
|
with gr.Column(visible=False) as admin_section: |
|
|
gr.Markdown("""### ๐ค Upload Past Papers & Resources |
|
|
|
|
|
**Instructions:** |
|
|
1. **Title**: e.g., "Paper 2 Chemistry - June 2023" |
|
|
2. **Subject**: Select Chemistry or Biology |
|
|
3. **Content**: Add description, syllabus code (0620 Chemistry, 0610 Biology), or notes |
|
|
4. **PDF**: Upload the actual past paper (questions will be auto-extracted) |
|
|
5. **Insert**: Upload any accompanying insert/resource booklet |
|
|
|
|
|
The AI will automatically: |
|
|
- Identify paper details (year, series, variant) |
|
|
- Extract all questions with topics |
|
|
- Index them for student search |
|
|
- Store insert materials for reference |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
t = gr.Textbox(label="๐ Title", placeholder="e.g., Paper 2 Chemistry - October/November 2023") |
|
|
s = gr.Radio(["Chemistry", "Biology"], label="Subject", value="Chemistry") |
|
|
c = gr.Textbox(lines=5, label="Content/Description", |
|
|
placeholder="Add notes, syllabus code (0620/0610), or instructions...") |
|
|
pdf = gr.File(label="๐ Past Paper PDF (questions will be extracted)", file_types=[".pdf"]) |
|
|
insert = gr.File(label="๐ผ๏ธ Insert/Resource Booklet (optional)", |
|
|
file_types=[".pdf", ".jpg", ".jpeg", ".png"]) |
|
|
|
|
|
up = gr.Button("โฌ Upload Paper", variant="primary", size="lg") |
|
|
st = gr.Textbox(label="Upload Status", lines=4) |
|
|
stats = gr.Textbox(label="๐ Database Statistics", value="๐ Status: No papers uploaded yet") |
|
|
|
|
|
with gr.Column(): |
|
|
gr.Markdown("### ๐ All Uploaded Papers") |
|
|
lst = gr.Textbox(lines=26, label="Papers Database", value=get_papers_list(), |
|
|
interactive=False, show_label=False) |
|
|
|
|
|
up.click(upload_paper, [t, s, c, pdf, insert], [st, lst, stats]) |
|
|
|
|
|
login_btn.click(verify_admin_password, [pwd], [admin_section, login_section, login_status]) |
|
|
|
|
|
gr.Markdown(""" |
|
|
--- |
|
|
**System Status:** ๐ข Gemini AI (Primary) | ๐ต Cohere (Secondary) | ๐ข Z.ai (Tertiary) | ๐ฃ MiniMax (Fallback) |
|
|
|
|
|
**Key Features:** |
|
|
- ๐ง **Deep Understanding Focus**: AI emphasizes WHY, not just WHAT |
|
|
- ๐ฏ Smart question extraction and topic-based search |
|
|
- ๐ผ๏ธ Insert/resource support for diagrams and data |
|
|
- ๐ Comprehensive concept explanations |
|
|
- ๐งฎ Step-by-step calculation support |
|
|
- ๐ฌ Experiment analysis with theory connections |
|
|
- ๐ค Multi-AI fallback system for reliability |
|
|
|
|
|
**Teaching Philosophy:** |
|
|
- Structure determines function |
|
|
- Understanding beats memorization |
|
|
- Connect concepts across topics |
|
|
- Apply knowledge to novel situations |
|
|
""") |
|
|
|
|
|
app.launch() |