MakPr016 commited on
Commit
350fffb
·
0 Parent(s):

FastAPI Space

Browse files
Files changed (4) hide show
  1. Dockerfile +12 -0
  2. README.md +17 -0
  3. app.py +163 -0
  4. requirements.txt +6 -0
Dockerfile ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY app.py .
9
+
10
+ EXPOSE 7860
11
+
12
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: CO-PO Mapping API
3
+ emoji: 🎓
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ # CO-PO Mapping API
11
+
12
+ AI-powered Course Outcome to Program Outcome mapping using fine-tuned sentence transformers.
13
+
14
+ ## Endpoints
15
+
16
+ - `POST /map/semantic` - Pure semantic AI mapping
17
+ - `POST /map/hybrid` - Hybrid (AI + keywords) mapping
app.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ from pydantic import BaseModel
4
+ from typing import List, Optional
5
+ from sentence_transformers import SentenceTransformer
6
+ from sklearn.metrics.pairwise import cosine_similarity
7
+ import os
8
+ import re
9
+
10
+ OFFICIAL_PO_DEFINITIONS = {
11
+ "PO1": "Apply knowledge of mathematics, natural science, computing, engineering fundamentals and an engineering specialization to solve complex engineering problems",
12
+ "PO2": "Identify, formulate, review research literature and analyze complex engineering problems reaching substantiated conclusions",
13
+ "PO3": "Design creative solutions for complex engineering problems and design systems, components or processes to meet identified needs",
14
+ "PO4": "Conduct investigations of complex engineering problems using research-based knowledge including design of experiments, analysis and interpretation of data",
15
+ "PO5": "Create, select and apply appropriate techniques, resources and modern engineering IT tools to solve complex engineering problems",
16
+ "PO6": "Analyze societal, environmental and sustainability aspects while solving complex engineering problems",
17
+ "PO7": "Apply ethical principles and commit to professional ethics and human values",
18
+ "PO8": "Function effectively as an individual and as a member or leader in diverse teams",
19
+ "PO9": "Communicate effectively with the engineering community and society",
20
+ "PO10": "Apply engineering management principles and economic decision-making to manage projects",
21
+ "PO11": "Recognize the need for independent lifelong learning and adaptability to emerging technologies"
22
+ }
23
+
24
+ PO_KEYWORDS = {
25
+ "PO1": ["knowledge", "mathematics", "math", "science", "computing", "engineering", "fundamental", "theory", "concept", "principle", "algorithm", "data structure", "programming", "software", "hardware", "circuit", "system", "analysis", "understand", "explain", "apply", "technical", "computer"],
26
+ "PO2": ["identify", "formulate", "analyze", "analysis", "problem", "research", "investigate", "investigation", "examine", "evaluate", "evaluation", "assess", "assessment", "literature", "study", "review", "complex"],
27
+ "PO3": ["design", "create", "develop", "build", "implement", "implementation", "construct", "architecture", "model", "prototype", "system", "component", "solution", "innovative", "creative", "synthesize"],
28
+ "PO4": ["experiment", "test", "testing", "measure", "measurement", "data", "analysis", "interpret", "interpretation", "validation", "verify", "verification", "research", "investigation", "empirical", "benchmark", "evaluate"],
29
+ "PO5": ["tool", "tools", "technology", "software", "framework", "platform", "library", "IDE", "programming", "language", "modern", "technique", "method", "approach", "implement", "application", "use", "utilize"],
30
+ "PO6": ["society", "social", "environmental", "environment", "sustainability", "sustainable", "impact", "ethical", "responsible", "responsibility", "green", "energy", "carbon", "climate", "eco", "community", "culture", "global"],
31
+ "PO7": ["ethics", "ethical", "professional", "integrity", "responsibility", "responsible", "conduct", "moral", "morality", "values", "principles", "principle", "honesty", "fairness", "accountability", "code of conduct"],
32
+ "PO8": ["team", "teams", "collaborate", "collaboration", "cooperative", "cooperation", "group", "leadership", "leader", "member", "members", "teamwork", "collective", "peer", "diverse", "diversity", "multicultural", "together"],
33
+ "PO9": ["communicate", "communication", "present", "presentation", "document", "documentation", "report", "write", "writing", "speak", "speaking", "explain", "articulate", "technical writing", "stakeholder", "audience"],
34
+ "PO10": ["project", "projects", "management", "manage", "plan", "planning", "schedule", "scheduling", "resource", "resources", "budget", "cost", "timeline", "milestone", "risk", "decision", "economic", "strategy", "organize", "organization"],
35
+ "PO11": ["learning", "learn", "adapt", "adapting", "adaptability", "emerging", "new", "continuous", "lifelong", "skill", "skills", "development", "growth", "technology", "technologies", "trend", "trends", "innovation", "self-learn", "update", "evolve", "change"]
36
+ }
37
+
38
+ class FineTunedCOPOMapper:
39
+ def __init__(self):
40
+ hf_token = os.environ.get("HF_TOKEN")
41
+ print("Loading private model from Hugging Face...")
42
+ self.model = SentenceTransformer("MakPr016/co-po-finetuned-model", use_auth_token=hf_token)
43
+ print("Model loaded successfully!")
44
+ self.po_embeddings = {}
45
+ self._precompute_po_embeddings()
46
+
47
+ def _precompute_po_embeddings(self):
48
+ for po_id, po_text in OFFICIAL_PO_DEFINITIONS.items():
49
+ self.po_embeddings[po_id] = self.model.encode([po_text])[0]
50
+
51
+ def _normalize_text(self, text):
52
+ text = text.lower()
53
+ text = re.sub(r'[^\w\s]', ' ', text)
54
+ return re.sub(r'\s+', ' ', text).strip()
55
+
56
+ def _calculate_keyword_score(self, co_text, po_id):
57
+ co_normalized = self._normalize_text(co_text)
58
+ co_words = set(co_normalized.split())
59
+ keywords = PO_KEYWORDS.get(po_id, [])
60
+ if not keywords:
61
+ return 0.0
62
+ matched_count = 0
63
+ for keyword in keywords:
64
+ keyword_normalized = self._normalize_text(keyword)
65
+ if len(keyword_normalized.split()) == 1:
66
+ if keyword_normalized in co_words:
67
+ matched_count += 1
68
+ else:
69
+ if keyword_normalized in co_normalized:
70
+ matched_count += 2
71
+ if matched_count == 0:
72
+ return 0.0
73
+ elif matched_count <= 3:
74
+ return 0.3
75
+ elif matched_count <= 6:
76
+ return 0.6
77
+ else:
78
+ return min(1.0, matched_count / len(keywords) * 3.0)
79
+
80
+ def map_co_to_pos_semantic(self, co_text):
81
+ co_embedding = self.model.encode([co_text])[0]
82
+ results = []
83
+ for po_id, po_embedding in self.po_embeddings.items():
84
+ similarity = float(cosine_similarity([co_embedding], [po_embedding])[0][0])
85
+ if similarity > 0.7:
86
+ strength, confidence = 3, "high"
87
+ elif similarity > 0.5:
88
+ strength, confidence = 2, "medium"
89
+ elif similarity > 0.3:
90
+ strength, confidence = 1, "low"
91
+ else:
92
+ strength, confidence = 0, "very low"
93
+ results.append({'po_id': po_id, 'score': round(similarity, 3), 'semantic_score': round(similarity, 3), 'strength': strength, 'po_description': OFFICIAL_PO_DEFINITIONS[po_id], 'confidence': confidence, 'method': 'semantic_only'})
94
+ return sorted(results, key=lambda x: x['score'], reverse=True)
95
+
96
+ def map_co_to_pos_hybrid(self, co_text):
97
+ co_embedding = self.model.encode([co_text])[0]
98
+ results = []
99
+ for po_id, po_embedding in self.po_embeddings.items():
100
+ semantic_score = float(cosine_similarity([co_embedding], [po_embedding])[0][0])
101
+ keyword_score = self._calculate_keyword_score(co_text, po_id)
102
+ final_score = (0.7 * semantic_score) + (0.3 * keyword_score)
103
+ if final_score > 0.7:
104
+ strength, confidence = 3, "high"
105
+ elif final_score > 0.5:
106
+ strength, confidence = 2, "medium"
107
+ elif final_score > 0.3:
108
+ strength, confidence = 1, "low"
109
+ else:
110
+ strength, confidence = 0, "very low"
111
+ results.append({'po_id': po_id, 'score': round(final_score, 3), 'semantic_score': round(semantic_score, 3), 'keyword_score': round(keyword_score, 3), 'strength': strength, 'po_description': OFFICIAL_PO_DEFINITIONS[po_id], 'confidence': confidence, 'method': 'hybrid'})
112
+ return sorted(results, key=lambda x: x['score'], reverse=True)
113
+
114
+ app = FastAPI(title="CO-PO Mapping API", version="2.0.0")
115
+ app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
116
+
117
+ mapper = None
118
+
119
+ @app.on_event("startup")
120
+ async def startup():
121
+ global mapper
122
+ mapper = FineTunedCOPOMapper()
123
+
124
+ class CORequest(BaseModel):
125
+ co_text: str
126
+
127
+ class POMapping(BaseModel):
128
+ po_id: str
129
+ score: float
130
+ semantic_score: float
131
+ keyword_score: Optional[float] = None
132
+ strength: int
133
+ po_description: str
134
+ confidence: str
135
+ method: str
136
+
137
+ class MappingResponse(BaseModel):
138
+ co_text: str
139
+ total_pos: int
140
+ method: str
141
+ mappings: List[POMapping]
142
+
143
+ @app.get("/")
144
+ async def root():
145
+ return {"message": "CO-PO Mapping API", "version": "2.0.0", "status": "active"}
146
+
147
+ @app.get("/health")
148
+ async def health():
149
+ return {"status": "healthy", "model_loaded": mapper is not None}
150
+
151
+ @app.post("/map/semantic", response_model=MappingResponse)
152
+ async def map_semantic(request: CORequest):
153
+ if not request.co_text or not request.co_text.strip():
154
+ raise HTTPException(400, "CO text cannot be empty")
155
+ results = mapper.map_co_to_pos_semantic(request.co_text)
156
+ return MappingResponse(co_text=request.co_text, total_pos=len(results), method="semantic_only", mappings=[POMapping(**r) for r in results])
157
+
158
+ @app.post("/map/hybrid", response_model=MappingResponse)
159
+ async def map_hybrid(request: CORequest):
160
+ if not request.co_text or not request.co_text.strip():
161
+ raise HTTPException(400, "CO text cannot be empty")
162
+ results = mapper.map_co_to_pos_hybrid(request.co_text)
163
+ return MappingResponse(co_text=request.co_text, total_pos=len(results), method="hybrid", mappings=[POMapping(**r) for r in results])
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ fastapi==0.104.1
2
+ uvicorn[standard]==0.24.0
3
+ sentence-transformers==2.2.2
4
+ scikit-learn==1.3.2
5
+ torch==2.1.0
6
+ pydantic==2.5.0