File size: 6,785 Bytes
c35855b 252d356 c35855b 252d356 c35855b 252d356 c35855b 252d356 c35855b | 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 | import os
import re
import pdfplumber
import pytesseract
import cv2
import numpy as np
import gradio as gr
from PIL import Image
from collections import Counter
from assistant.storage import (
generate_doc_id,
save_document,
load_document,
save_metadata,
get_all_docs,
)
# Configure Tesseract path (Windows only)
if os.name == 'nt':
pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"
FALLBACK = "This information is not available in your uploaded notes."
STOP_WORDS = {
"what", "is", "a", "an", "the", "of", "to", "in", "and", "why",
"how", "who", "when", "where", "are", "do", "does", "did", "for",
"on", "it", "this", "that", "tell", "me", "about", "define",
"explain", "give", "meaning", "can", "you", "please", "with",
"was", "will", "has", "have", "had", "be", "been", "by", "or",
"at", "from", "as", "its", "my", "your"
}
FACULTY_MAP = {
"KKD": "Dr. Kailas Devadkar",
"NR": "Dr. Nataasha Raul",
"JS": "Prof. Jignesh Sisodia"
}
# --- TIMETABLE LOGIC ---
def extract_text(file_path):
if file_path is None: return ""
ext = os.path.splitext(file_path)[1].lower()
text = ""
if ext == ".pdf":
with pdfplumber.open(file_path) as pdf:
for page in pdf.pages:
extracted = page.extract_text()
if extracted: text += extracted + "\n"
elif ext in [".png", ".jpg", ".jpeg"]:
img = cv2.imread(file_path)
if img is None:
img = Image.open(file_path)
text = pytesseract.image_to_string(img)
else:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
text = pytesseract.image_to_string(thresh)
return text
def calculate_attendance_logic(raw_list):
"""Internal logic to calculate attendance metrics exactly as requested."""
subject_counts = Counter()
subject_to_faculty = {}
for entry in raw_list:
sub = entry.get("subject")
fac = entry.get("faculty")
subject_counts[sub] += 1
if fac and sub not in subject_to_faculty:
# Resolve actual faculty name if it exists in map, else keep code
subject_to_faculty[sub] = FACULTY_MAP.get(fac, fac)
subjects_data = {}
for subject, count in subject_counts.items():
weekly = count
monthly = weekly * 4
# Calculate safe_bunks (attendance >= 75%)
safe_bunks = 0
while monthly > 0 and ((monthly - (safe_bunks + 1)) / monthly) >= 0.75:
safe_bunks += 1
# Calculate classes_needed (attendance >= 75%)
classes_needed = 0
# If classes_needed/monthly < 0.75, it means we currently have 0% attendance
# Requirement: get to 75% of (monthly + extra_classes)
# However, following the user's previously working logic provided in file:
current_attended = 0 # assuming starting from scratch for simplicity as per previous logic
while monthly > 0 and (classes_needed / monthly) < 0.75:
classes_needed += 1
subjects_data[subject] = {
"faculty": subject_to_faculty.get(subject, "Unknown"),
"weekly": weekly,
"monthly": monthly,
"safe_bunks": safe_bunks,
"classes_needed": classes_needed
}
return subjects_data
def api_process_timetable(file):
if file is None: return {"error": "No file"}
text = extract_text(file.name)
raw_list = []
pattern = r"([A-Z]{2,})\s*/\s*(?:([A-Z]?)\s*/\s*)?([A-Z0-9]{2,})"
matches = re.finditer(pattern, text)
for match in matches:
raw_list.append({
"subject": match.group(1).strip(),
"faculty": match.group(3).strip()
})
subjects_data = calculate_attendance_logic(raw_list)
return {
"raw": raw_list,
"subjects": subjects_data
}
# --- ASSISTANT LOGIC (PURE RAG) ---
def api_upload_notes(file):
if file is None: return {"error": "No file"}
text = extract_text(file.name)
if not text.strip(): return {"error": "Empty text"}
doc_id = generate_doc_id()
save_document(doc_id, text)
save_metadata(doc_id, os.path.basename(file.name))
return {"doc_id": doc_id, "message": "Saved successfully"}
def _find_context(text, keywords):
sentences = re.split(r'[.!?\n]+', text)
matched = []
for i, sentence in enumerate(sentences):
sentence = sentence.strip()
if len(sentence) < 5: continue
sent_lower = sentence.lower()
for kw in keywords:
if kw in sent_lower:
matched.append({"text": sentence.replace('\n', ' '), "line": i+1})
break
if len(matched) >= 3: break
return matched
def api_ask_question(doc_id, question):
text = load_document(doc_id)
if text is None: return {"answer": "Doc not found"}
words = re.findall(r'\b\w+\b', question.lower())
keywords = [w for w in words if w not in STOP_WORDS and len(w) > 1]
keywords = keywords if keywords else words
matches = _find_context(text, keywords)
if not matches: return {"answer": FALLBACK, "source": None}
context = " ".join([m["text"] for m in matches])
first_match = matches[0]
return {
"answer": f"According to your notes: {context}",
"source": {
"line": first_match["line"],
"column": first_match["text"].lower().find(keywords[0]) + 1 if keywords else 1
}
}
# --- GRADIO UI ---
with gr.Blocks(title="Lumina Backend API") as demo:
gr.Markdown("# 🚀 Lumina Backend (Gradio API)")
with gr.Tab("Timetable Parser"):
tt_file = gr.File(label="Upload Timetable")
tt_btn = gr.Button("Parse")
tt_out = gr.JSON(label="Parsed Output")
tt_btn.click(api_process_timetable, inputs=tt_file, outputs=tt_out, api_name="process_timetable")
with gr.Tab("AI Assistant"):
with gr.Row():
note_file = gr.File(label="Upload Notes")
note_btn = gr.Button("Upload & Process")
note_out = gr.JSON(label="Doc ID Output")
with gr.Row():
ask_id = gr.Textbox(label="Document ID")
ask_q = gr.Textbox(label="Question")
ask_btn = gr.Button("Ask AI")
ask_out = gr.JSON(label="AI Answer")
note_btn.click(api_upload_notes, inputs=note_file, outputs=note_out, api_name="upload_notes")
ask_btn.click(api_ask_question, inputs=[ask_id, ask_q], outputs=ask_out, api_name="ask_question")
if __name__ == "__main__":
demo.launch(theme=gr.themes.Soft())
|