Spaces:
Running
Running
File size: 11,816 Bytes
eba7c64 92bfe31 eba7c64 92bfe31 eba7c64 92bfe31 eba7c64 92bfe31 eba7c64 92bfe31 eba7c64 92bfe31 eba7c64 92bfe31 eba7c64 | 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 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | """
Curriculum Service - Firestore-backed curriculum data.
Fetches subjects, topics, and modules from Firestore.
Falls back to static data if Firestore is unavailable.
"""
import logging
import os
from typing import Any, Dict, List, Optional
logger = logging.getLogger(__name__)
# Static curriculum data as fallback
_STATIC_SUBJECTS = [
{
"id": "gen-math",
"code": "GEN MATH",
"name": "General Mathematics",
"gradeLevel": "Grade 11",
"semester": "1st Semester",
"color": "from-blue-500 to-cyan-500",
"pdfAvailable": True,
"topics": [
{"id": "gen-math-001", "name": "Patterns and Real-Life Relationships", "unit": "Patterns, Relations, and Functions"},
{"id": "gen-math-002", "name": "Functions as Mathematical Models", "unit": "Patterns, Relations, and Functions"},
{"id": "gen-math-003", "name": "Function Notation and Evaluation", "unit": "Patterns, Relations, and Functions"},
{"id": "gen-math-004", "name": "Domain and Range of Functions", "unit": "Patterns, Relations, and Functions"},
{"id": "gen-math-005", "name": "Operations on Functions", "unit": "Patterns, Relations, and Functions"},
{"id": "gen-math-006", "name": "Composite Functions", "unit": "Patterns, Relations, and Functions"},
{"id": "gen-math-007", "name": "Inverse Functions", "unit": "Patterns, Relations, and Functions"},
{"id": "gen-math-008", "name": "Graphs of Rational Functions", "unit": "Patterns, Relations, and Functions"},
{"id": "gen-math-009", "name": "Graphs of Exponential Functions", "unit": "Patterns, Relations, and Functions"},
{"id": "gen-math-010", "name": "Graphs of Logarithmic Functions", "unit": "Patterns, Relations, and Functions"},
{"id": "gen-math-011", "name": "Simple and Compound Interest", "unit": "Financial Mathematics"},
{"id": "gen-math-012", "name": "Simple and General Annuities", "unit": "Financial Mathematics"},
{"id": "gen-math-013", "name": "Present and Future Value", "unit": "Financial Mathematics"},
{"id": "gen-math-014", "name": "Loans, Amortization, and Sinking Funds", "unit": "Financial Mathematics"},
{"id": "gen-math-015", "name": "Stocks, Bonds, and Market Indices", "unit": "Financial Mathematics"},
{"id": "gen-math-016", "name": "Business Decision-Making with Mathematical Models", "unit": "Financial Mathematics"},
{"id": "gen-math-017", "name": "Propositions and Logical Connectives", "unit": "Logic and Mathematical Reasoning"},
{"id": "gen-math-018", "name": "Truth Values and Truth Tables", "unit": "Logic and Mathematical Reasoning"},
{"id": "gen-math-019", "name": "Logical Equivalence and Implication", "unit": "Logic and Mathematical Reasoning"},
{"id": "gen-math-020", "name": "Quantifiers and Negation", "unit": "Logic and Mathematical Reasoning"},
{"id": "gen-math-021", "name": "Validity of Arguments", "unit": "Logic and Mathematical Reasoning"},
]
},
{
"id": "stats-prob",
"code": "STAT&PROB",
"name": "Statistics and Probability",
"gradeLevel": "Grade 11",
"semester": "2nd Semester",
"color": "from-sky-500 to-cyan-500",
"pdfAvailable": True,
"topics": [
{"id": "stat-001", "name": "Random Variables", "unit": "Random Variables"},
{"id": "stat-002", "name": "Discrete Probability Distributions", "unit": "Random Variables"},
{"id": "stat-003", "name": "Mean and Variance of Discrete RV", "unit": "Random Variables"},
{"id": "stat-004", "name": "Normal Distribution", "unit": "Normal Distribution"},
{"id": "stat-005", "name": "Standard Normal Distribution and Z-scores", "unit": "Normal Distribution"},
{"id": "stat-006", "name": "Areas Under the Normal Curve", "unit": "Normal Distribution"},
{"id": "stat-007", "name": "Sampling Distributions", "unit": "Sampling and Estimation"},
{"id": "stat-008", "name": "Central Limit Theorem", "unit": "Sampling and Estimation"},
{"id": "stat-009", "name": "Point Estimation", "unit": "Sampling and Estimation"},
{"id": "stat-010", "name": "Confidence Intervals", "unit": "Sampling and Estimation"},
{"id": "stat-011", "name": "Hypothesis Testing Concepts", "unit": "Hypothesis Testing"},
{"id": "stat-012", "name": "T-test", "unit": "Hypothesis Testing"},
{"id": "stat-013", "name": "Z-test", "unit": "Hypothesis Testing"},
{"id": "stat-014", "name": "Correlation and Regression", "unit": "Correlation and Regression"},
]
},
{
"id": "pre-calc",
"code": "PRE-CALC",
"name": "Pre-Calculus",
"gradeLevel": "Grade 12",
"semester": "1st Semester",
"color": "from-orange-500 to-red-500",
"pdfAvailable": False,
"topics": [
{"id": "pre-calc-001", "name": "Conic Sections - Parabola", "unit": "Analytic Geometry"},
{"id": "pre-calc-002", "name": "Conic Sections - Ellipse", "unit": "Analytic Geometry"},
{"id": "pre-calc-003", "name": "Conic Sections - Hyperbola", "unit": "Analytic Geometry"},
{"id": "pre-calc-004", "name": "Conic Sections - Circle", "unit": "Analytic Geometry"},
{"id": "pre-calc-005", "name": "Systems of Nonlinear Equations", "unit": "Analytic Geometry"},
{"id": "pre-calc-006", "name": "Sequences and Series", "unit": "Series and Induction"},
{"id": "pre-calc-007", "name": "Arithmetic Sequences", "unit": "Series and Induction"},
{"id": "pre-calc-008", "name": "Geometric Sequences", "unit": "Series and Induction"},
{"id": "pre-calc-009", "name": "Mathematical Induction", "unit": "Series and Induction"},
{"id": "pre-calc-010", "name": "Binomial Theorem", "unit": "Series and Induction"},
{"id": "pre-calc-011", "name": "Angles and Unit Circle", "unit": "Trigonometry"},
{"id": "pre-calc-012", "name": "Trigonometric Functions", "unit": "Trigonometry"},
{"id": "pre-calc-013", "name": "Trigonometric Identities", "unit": "Trigonometry"},
{"id": "pre-calc-014", "name": "Sum and Difference Formulas", "unit": "Trigonometry"},
{"id": "pre-calc-015", "name": "Inverse Trigonometric Functions", "unit": "Trigonometry"},
{"id": "pre-calc-016", "name": "Polar Coordinates", "unit": "Trigonometry"},
]
},
{
"id": "basic-calc",
"code": "BASIC CALC",
"name": "Basic Calculus",
"gradeLevel": "Grade 12",
"semester": "2nd Semester",
"color": "from-green-500 to-teal-500",
"pdfAvailable": True,
"topics": [
{"id": "calc-001", "name": "Limits of Functions", "unit": "Limits"},
{"id": "calc-002", "name": "Limit Theorems", "unit": "Limits"},
{"id": "calc-003", "name": "One-Sided Limits", "unit": "Limits"},
{"id": "calc-004", "name": "Infinite Limits and Limits at Infinity", "unit": "Limits"},
{"id": "calc-005", "name": "Continuity of Functions", "unit": "Limits"},
{"id": "calc-006", "name": "Definition of the Derivative", "unit": "Derivatives"},
{"id": "calc-007", "name": "Differentiation Rules", "unit": "Derivatives"},
{"id": "calc-008", "name": "Chain Rule", "unit": "Derivatives"},
{"id": "calc-009", "name": "Implicit Differentiation", "unit": "Derivatives"},
{"id": "calc-010", "name": "Higher-Order Derivatives", "unit": "Derivatives"},
{"id": "calc-011", "name": "Related Rates", "unit": "Derivatives"},
{"id": "calc-012", "name": "Extrema and the First Derivative Test", "unit": "Derivatives"},
{"id": "calc-013", "name": "Concavity and the Second Derivative Test", "unit": "Derivatives"},
{"id": "calc-014", "name": "Optimization Problems", "unit": "Derivatives"},
{"id": "calc-015", "name": "Antiderivatives and Indefinite Integrals", "unit": "Integration"},
{"id": "calc-016", "name": "Definite Integrals and the FTC", "unit": "Integration"},
{"id": "calc-017", "name": "Integration by Substitution", "unit": "Integration"},
{"id": "calc-018", "name": "Area Under a Curve", "unit": "Integration"},
]
},
]
_firestore_db = None
def _get_firestore_db():
"""Initialize Firestore client."""
global _firestore_db
if _firestore_db is not None:
return _firestore_db
try:
import firebase_admin
from firebase_admin import firestore
if not firebase_admin._apps:
# Try service account from env or default credentials
import json
svc_account = os.getenv("FIREBASE_SERVICE_ACCOUNT_JSON")
if svc_account:
sa_creds = json.loads(svc_account)
firebase_admin.initialize_app(firebase_admin.Certificate(sa_creds))
else:
firebase_admin.initialize_app()
_firestore_db = firestore.client()
return _firestore_db
except Exception as e:
logger.warning(f"Could not initialize Firestore: {e}")
return None
def get_subjects(grade_level: Optional[str] = None) -> List[Dict[str, Any]]:
"""
Fetch all subjects from Firestore.
Falls back to static data if Firestore unavailable.
Defaults to Grade 11 (SHS) if no grade specified.
"""
# Default to Grade 11 (SHS) - only serve Grade 11 students for now
if grade_level is None:
grade_level = "Grade 11"
db = _get_firestore_db()
if db is not None:
try:
subjects_ref = db.collection("subjects")
if grade_level:
subjects_ref = subjects_ref.where("gradeLevel", "==", grade_level)
docs = subjects_ref.stream()
subjects = []
for doc in docs:
data = doc.to_dict()
if data:
data["id"] = doc.id
subjects.append(data)
if subjects:
logger.info(f"Loaded {len(subjects)} subjects from Firestore")
return subjects
except Exception as e:
logger.warning(f"Firestore fetch failed, using static data: {e}")
# Static fallback
if grade_level:
return [s for s in _STATIC_SUBJECTS if s.get("gradeLevel") == grade_level]
return list(_STATIC_SUBJECTS)
def get_subject(subject_id: str) -> Optional[Dict[str, Any]]:
"""Fetch a single subject by ID."""
db = _get_firestore_db()
if db is not None:
try:
doc = db.collection("subjects").document(subject_id).get()
if doc.exists:
data = doc.to_dict()
data["id"] = doc.id
return data
except Exception as e:
logger.warning(f"Firestore fetch failed for {subject_id}: {e}")
# Static fallback
for subject in _STATIC_SUBJECTS:
if subject["id"] == subject_id:
return dict(subject)
return None
def get_topics(subject_id: str) -> List[Dict[str, Any]]:
"""Fetch all topics for a subject."""
subject = get_subject(subject_id)
if subject:
return subject.get("topics", [])
return []
def get_topic(subject_id: str, topic_id: str) -> Optional[Dict[str, Any]]:
"""Fetch a single topic."""
topics = get_topics(subject_id)
for topic in topics:
if topic["id"] == topic_id:
return topic
return None |