Spaces:
Sleeping
Sleeping
| import os | |
| import base64 | |
| import tempfile | |
| import html | |
| from datetime import datetime | |
| import gradio as gr | |
| from llama_index.core import VectorStoreIndex, SimpleDirectoryReader | |
| from llama_index.embeddings.openai import OpenAIEmbedding | |
| from llama_index.llms.openai import OpenAI | |
| DATA_DIR = "data" | |
| LOGO_PATH = "utas_logo.png" | |
| COURSE_NAME = "EGETS5241 - Energy Data Analytics" | |
| PROGRAMME = "MSc Energy Transition and Sustainability" | |
| UNIVERSITY = "University of Technology and Applied Sciences, Muscat" | |
| MODEL_NAME = "gpt-4o-mini" | |
| TEMPERATURE = 0.2 | |
| TOP_K = 2 | |
| GLOBAL_INDEX = None | |
| BASE_RULES = """ | |
| You are an interactive Energy Data Analytics tutor for Master students at UTAS Muscat. | |
| Course: EGETS5241 - Energy Data Analytics | |
| Programme: MSc Energy Transition and Sustainability | |
| Help students prepare for exams using the uploaded PPT/PDF/handouts. | |
| You can help with: | |
| - Detailed explanation | |
| - Short answer questions | |
| - Long answer questions | |
| - Numerical questions | |
| - MCQs and quizzes | |
| - Case studies | |
| - Flashcards | |
| - Important final exam questions with answers | |
| Use simple, clear English. | |
| Ground answers mainly in the uploaded course material. | |
| If something is not found in the uploaded course material, say: | |
| "This point is not directly found in the uploaded course material, so I will explain it using general Energy Data Analytics knowledge." | |
| """ | |
| def get_logo_html(): | |
| if not os.path.exists(LOGO_PATH): | |
| return "<div class='logo-box'><b>UTAS</b></div>" | |
| with open(LOGO_PATH, "rb") as f: | |
| encoded = base64.b64encode(f.read()).decode("utf-8") | |
| return f""" | |
| <div class="logo-box"> | |
| <img src="data:image/png;base64,{encoded}" class="logo-img"> | |
| </div> | |
| """ | |
| def get_files(): | |
| os.makedirs(DATA_DIR, exist_ok=True) | |
| return [ | |
| f for f in os.listdir(DATA_DIR) | |
| if not f.startswith(".") | |
| and f.lower().endswith((".pdf", ".pptx", ".docx", ".txt", ".md")) | |
| ] | |
| def load_index(): | |
| global GLOBAL_INDEX | |
| if GLOBAL_INDEX is not None: | |
| return GLOBAL_INDEX, "" | |
| if not os.getenv("OPENAI_API_KEY"): | |
| return None, "OPENAI_API_KEY is missing. Add it in Hugging Face Space settings." | |
| files = get_files() | |
| if not files: | |
| return None, "No course file found. Upload your PPT/PDF inside the data folder." | |
| try: | |
| documents = SimpleDirectoryReader(DATA_DIR).load_data() | |
| embed_model = OpenAIEmbedding(model="text-embedding-3-small") | |
| GLOBAL_INDEX = VectorStoreIndex.from_documents( | |
| documents, | |
| embed_model=embed_model, | |
| show_progress=False | |
| ) | |
| return GLOBAL_INDEX, "" | |
| except Exception as e: | |
| return None, f"Error loading course files: {str(e)}" | |
| def build_prompt(question, mode): | |
| mode_instruction = { | |
| "Explain in detail": "Explain the topic in detail with headings, simple examples, and energy-sector relevance.", | |
| "Short answer": "Give a short exam-style answer in 5-7 clear lines.", | |
| "Long answer": "Give a long exam-style answer with introduction, main points, example, and conclusion.", | |
| "Numerical question": "Create or solve a numerical question. Show formula, values, substitution, calculation, final answer, and interpretation.", | |
| "MCQ quiz": "Create 5 MCQs with 4 options each. Give correct answer and short explanation.", | |
| "Case study": "Create or answer a case-study question using energy analytics context.", | |
| "Flashcards": "Generate 10 flashcards in Q/A format.", | |
| "Important exam questions": "Generate a complete exam preparation question bank with short-answer, long-answer, MCQ, numerical, and case-study questions with model answers.", | |
| "Teacher may ask": "Give high-probability exam-focused questions from this topic and provide model answers." | |
| }[mode] | |
| return f""" | |
| {BASE_RULES} | |
| Selected mode: | |
| {mode_instruction} | |
| Student question/topic: | |
| {question} | |
| """ | |
| def answer_question(question, mode): | |
| index, error = load_index() | |
| if index is None: | |
| return error | |
| llm = OpenAI(model=MODEL_NAME, temperature=TEMPERATURE) | |
| query_engine = index.as_query_engine( | |
| llm=llm, | |
| similarity_top_k=TOP_K, | |
| response_mode="compact" | |
| ) | |
| response = query_engine.query(build_prompt(question, mode)) | |
| return str(response) | |
| def update_mode(mode): | |
| labels = { | |
| "Explain in detail": "๐ Explain in detail", | |
| "Short answer": "๐ Short answer", | |
| "Long answer": "๐ Long answer", | |
| "Numerical question": "๐งฎ Numerical question", | |
| "MCQ quiz": "โ MCQ quiz", | |
| "Case study": "๐ญ Case study", | |
| "Flashcards": "๐ง Flashcards", | |
| "Important exam questions": "๐ Question Bank", | |
| "Teacher may ask": "๐ฏ Likely Exam Questions" | |
| } | |
| return mode, f"<div class='mode-chip'>{labels[mode]}</div>" | |
| def respond(message, history, mode): | |
| history = history or [] | |
| if not message or not message.strip(): | |
| return history, "" | |
| history.append({"role": "user", "content": message}) | |
| try: | |
| reply = answer_question(message, mode) | |
| except Exception as e: | |
| reply = f"Error: {str(e)}" | |
| history.append({"role": "assistant", "content": reply}) | |
| return history, "" | |
| def clean_text(content): | |
| """Clean Gradio chatbot message content for reports.""" | |
| if content is None: | |
| return "" | |
| if isinstance(content, list): | |
| parts = [] | |
| for item in content: | |
| if isinstance(item, dict): | |
| parts.append(str(item.get("text", item.get("content", "")))) | |
| else: | |
| parts.append(str(item)) | |
| return " ".join([p for p in parts if p]).strip() | |
| if isinstance(content, dict): | |
| return str(content.get("text", content.get("content", ""))).strip() | |
| return str(content).strip() | |
| def _history_pairs(history): | |
| history = history or [] | |
| pairs = [] | |
| current_user = None | |
| for item in history: | |
| if isinstance(item, dict): | |
| role = item.get("role", "") | |
| content = clean_text(item.get("content", "")) | |
| if role == "user": | |
| current_user = content | |
| elif role == "assistant": | |
| pairs.append((current_user or "", content)) | |
| current_user = None | |
| elif isinstance(item, (list, tuple)) and len(item) == 2: | |
| pairs.append((clean_text(item[0]), clean_text(item[1]))) | |
| return pairs | |
| def export_premium_report(history): | |
| pairs = _history_pairs(history) | |
| if not pairs: | |
| return None | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| filepath = os.path.join(tempfile.gettempdir(), f"EGETS5241_Premium_Study_Report_{timestamp}.html") | |
| report_time = datetime.now().strftime("%d %B %Y, %H:%M") | |
| logo_tag = "" | |
| if os.path.exists(LOGO_PATH): | |
| try: | |
| with open(LOGO_PATH, "rb") as f: | |
| logo_encoded = base64.b64encode(f.read()).decode("utf-8") | |
| logo_tag = f'<img src="data:image/png;base64,{logo_encoded}" class="report-logo" alt="UTAS Logo">' | |
| except Exception: | |
| logo_tag = "" | |
| toc_items = "" | |
| sections = "" | |
| for i, (q, a) in enumerate(pairs, start=1): | |
| q_clean = clean_text(q) | |
| a_clean = clean_text(a) | |
| q_safe = html.escape(q_clean).replace("\n", "<br>") | |
| a_safe = html.escape(a_clean).replace("\n", "<br>") | |
| short_title = html.escape(q_clean[:80] + ("..." if len(q_clean) > 80 else "")) | |
| toc_items += f'<li><a href="#q{i}">Question {i}: {short_title}</a></li>' | |
| sections += f""" | |
| <section class="qa-card" id="q{i}"> | |
| <div class="qa-header"> | |
| <span class="qa-number">Q{i}</span> | |
| <span class="qa-title">Student Question</span> | |
| </div> | |
| <div class="question-text">{q_safe}</div> | |
| <details open> | |
| <summary>Tutor Answer</summary> | |
| <div class="answer-text">{a_safe}</div> | |
| </details> | |
| </section> | |
| """ | |
| html_content = f"""<!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>EGETS5241 Study Report</title> | |
| <style> | |
| :root {{ | |
| --burgundy: #850047; | |
| --burgundy-dark: #5a0030; | |
| --gold: #d6b94b; | |
| --cream: #fff8dc; | |
| --paper: #ffffff; | |
| --text: #1f2933; | |
| }} | |
| body {{ | |
| font-family: Arial, sans-serif; | |
| background: #f6f1e7; | |
| color: var(--text); | |
| margin: 0; | |
| padding: 28px; | |
| }} | |
| .container {{ | |
| max-width: 1050px; | |
| margin: auto; | |
| background: var(--paper); | |
| border-radius: 20px; | |
| padding: 30px; | |
| border: 2px solid var(--gold); | |
| box-shadow: 0 12px 30px rgba(90,0,48,0.12); | |
| }} | |
| .header {{ | |
| background: linear-gradient(135deg, var(--burgundy-dark), var(--burgundy), #b8860b); | |
| color: white; | |
| padding: 28px; | |
| border-radius: 18px; | |
| border-top: 8px solid #f0c84b; | |
| display: flex; | |
| gap: 22px; | |
| align-items: center; | |
| margin-bottom: 24px; | |
| }} | |
| .report-logo {{ | |
| width: 160px; | |
| background: white; | |
| padding: 12px; | |
| border-radius: 14px; | |
| border: 1px solid var(--gold); | |
| }} | |
| .header-title h1 {{ | |
| margin: 0 0 8px 0; | |
| font-size: 30px; | |
| line-height: 1.2; | |
| }} | |
| .header-title p {{ | |
| margin: 4px 0; | |
| font-size: 15px; | |
| }} | |
| .meta {{ | |
| background: var(--cream); | |
| border-left: 7px solid #b8860b; | |
| padding: 14px 18px; | |
| border-radius: 12px; | |
| margin-bottom: 22px; | |
| font-size: 15px; | |
| line-height: 1.6; | |
| }} | |
| .toc {{ | |
| background: #fffdf4; | |
| border: 1.5px solid var(--gold); | |
| border-radius: 14px; | |
| padding: 18px 22px; | |
| margin-bottom: 24px; | |
| }} | |
| .toc h2 {{ | |
| color: var(--burgundy); | |
| margin-top: 0; | |
| }} | |
| .toc a {{ | |
| color: var(--burgundy); | |
| text-decoration: none; | |
| font-weight: bold; | |
| }} | |
| .toc li {{ | |
| margin-bottom: 8px; | |
| line-height: 1.45; | |
| }} | |
| .qa-card {{ | |
| border: 1.5px solid var(--gold); | |
| border-radius: 16px; | |
| margin-bottom: 20px; | |
| overflow: hidden; | |
| page-break-inside: avoid; | |
| background: white; | |
| }} | |
| .qa-header {{ | |
| background: var(--burgundy); | |
| color: white; | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| padding: 12px 16px; | |
| font-weight: bold; | |
| }} | |
| .qa-number {{ | |
| background: #f0c84b; | |
| color: var(--burgundy-dark); | |
| padding: 5px 9px; | |
| border-radius: 999px; | |
| }} | |
| .question-text {{ | |
| background: var(--cream); | |
| padding: 16px; | |
| font-size: 16px; | |
| line-height: 1.65; | |
| font-weight: bold; | |
| }} | |
| summary {{ | |
| cursor: pointer; | |
| background: #f0e5bf; | |
| color: var(--burgundy); | |
| font-weight: bold; | |
| padding: 12px 16px; | |
| font-size: 16px; | |
| }} | |
| .answer-text {{ | |
| padding: 18px; | |
| font-size: 16px; | |
| line-height: 1.75; | |
| white-space: normal; | |
| }} | |
| .footer {{ | |
| margin-top: 28px; | |
| padding-top: 14px; | |
| border-top: 1px solid #e5d9ad; | |
| color: #4b5563; | |
| font-size: 13px; | |
| text-align: center; | |
| }} | |
| .print-note {{ | |
| background: #edf7fb; | |
| border: 1px solid #b7dbe8; | |
| padding: 12px 16px; | |
| border-radius: 12px; | |
| margin-bottom: 20px; | |
| font-size: 14px; | |
| }} | |
| @media print {{ | |
| body {{ | |
| background: white; | |
| padding: 0; | |
| }} | |
| .container {{ | |
| box-shadow: none; | |
| border: none; | |
| padding: 10px; | |
| }} | |
| .print-note {{ | |
| display: none; | |
| }} | |
| summary {{ | |
| list-style: none; | |
| }} | |
| summary::-webkit-details-marker {{ | |
| display: none; | |
| }} | |
| }} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| {logo_tag} | |
| <div class="header-title"> | |
| <h1>โก EGETS5241 - Energy Data Analytics Study Report</h1> | |
| <p><b>MSc Energy Transition and Sustainability</b></p> | |
| <p>University of Technology and Applied Sciences, Muscat</p> | |
| </div> | |
| </div> | |
| <div class="meta"> | |
| <b>Generated on:</b> {report_time}<br> | |
| <b>Total student interactions:</b> {len(pairs)}<br> | |
| <b>Purpose:</b> Exam preparation and self-study record. | |
| </div> | |
| <div class="print-note"> | |
| <b>Tip:</b> To save this report as a polished PDF, open this file in your browser and press <b>Ctrl + P</b>, then choose <b>Save as PDF</b>. | |
| </div> | |
| <div class="toc"> | |
| <h2>Table of Contents</h2> | |
| <ol> | |
| {toc_items} | |
| </ol> | |
| </div> | |
| {sections} | |
| <div class="footer"> | |
| Generated by the EGETS5241 Energy Data Analytics Tutor. | |
| </div> | |
| </div> | |
| </body> | |
| </html>""" | |
| with open(filepath, "w", encoding="utf-8") as f: | |
| f.write(html_content) | |
| return filepath | |
| def export_excel(history): | |
| pairs = _history_pairs(history) | |
| if not pairs: | |
| return None | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| filepath = os.path.join(tempfile.gettempdir(), f"EGETS5241_Chat_Report_{timestamp}.xlsx") | |
| try: | |
| from openpyxl import Workbook | |
| from openpyxl.styles import Font, PatternFill, Alignment, Border, Side | |
| from openpyxl.utils import get_column_letter | |
| wb = Workbook() | |
| ws = wb.active | |
| ws.title = "Chat Report" | |
| ws.merge_cells("A1:C1") | |
| ws["A1"] = "EGETS5241 - Energy Data Analytics Chat Report" | |
| ws["A1"].font = Font(bold=True, size=18, color="850047") | |
| ws["A1"].alignment = Alignment(horizontal="center") | |
| ws.merge_cells("A2:C2") | |
| ws["A2"] = "MSc Energy Transition and Sustainability | UTAS Muscat" | |
| ws["A2"].font = Font(italic=True, size=12, color="1F2933") | |
| ws["A2"].alignment = Alignment(horizontal="center") | |
| ws.append([]) | |
| ws.append(["No.", "Student Question", "Tutor Answer"]) | |
| header_row = 4 | |
| header_fill = PatternFill("solid", fgColor="850047") | |
| header_font = Font(color="FFFFFF", bold=True) | |
| border = Border( | |
| left=Side(style="thin", color="D6B94B"), | |
| right=Side(style="thin", color="D6B94B"), | |
| top=Side(style="thin", color="D6B94B"), | |
| bottom=Side(style="thin", color="D6B94B"), | |
| ) | |
| for cell in ws[header_row]: | |
| cell.fill = header_fill | |
| cell.font = header_font | |
| cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True) | |
| cell.border = border | |
| for i, (q, a) in enumerate(pairs, start=1): | |
| ws.append([i, q, a]) | |
| for row in ws.iter_rows(min_row=5): | |
| for cell in row: | |
| cell.alignment = Alignment(wrap_text=True, vertical="top") | |
| cell.border = border | |
| fill_alt = PatternFill("solid", fgColor="FFF8DC") | |
| for r in range(5, ws.max_row + 1): | |
| if r % 2 == 1: | |
| for c in range(1, 4): | |
| ws.cell(r, c).fill = fill_alt | |
| widths = [8, 45, 100] | |
| for idx, width in enumerate(widths, start=1): | |
| ws.column_dimensions[get_column_letter(idx)].width = width | |
| ws.freeze_panes = "A5" | |
| ws.auto_filter.ref = f"A4:C{ws.max_row}" | |
| wb.save(filepath) | |
| return filepath | |
| except Exception: | |
| fallback = filepath.replace(".xlsx", ".csv") | |
| import csv | |
| with open(fallback, "w", newline="", encoding="utf-8") as f: | |
| writer = csv.writer(f) | |
| writer.writerow(["No.", "Student Question", "Tutor Answer"]) | |
| for i, (q, a) in enumerate(pairs, start=1): | |
| writer.writerow([i, q, a]) | |
| return fallback | |
| def export_pdf(history): | |
| pairs = _history_pairs(history) | |
| if not pairs: | |
| return None | |
| timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| filepath = os.path.join(tempfile.gettempdir(), f"EGETS5241_Chat_Report_{timestamp}.pdf") | |
| try: | |
| from reportlab.lib.pagesizes import A4 | |
| from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle | |
| from reportlab.lib import colors | |
| from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle | |
| from reportlab.lib.units import cm | |
| doc = SimpleDocTemplate( | |
| filepath, | |
| pagesize=A4, | |
| rightMargin=1.4 * cm, | |
| leftMargin=1.4 * cm, | |
| topMargin=1.3 * cm, | |
| bottomMargin=1.3 * cm | |
| ) | |
| styles = getSampleStyleSheet() | |
| title_style = ParagraphStyle( | |
| "TitleCustom", | |
| parent=styles["Title"], | |
| textColor=colors.HexColor("#850047"), | |
| fontSize=20, | |
| leading=24, | |
| alignment=1 | |
| ) | |
| sub_style = ParagraphStyle( | |
| "SubTitleCustom", | |
| parent=styles["BodyText"], | |
| textColor=colors.HexColor("#1F2933"), | |
| fontSize=11, | |
| leading=15, | |
| alignment=1 | |
| ) | |
| q_style = ParagraphStyle( | |
| "Question", | |
| parent=styles["BodyText"], | |
| textColor=colors.HexColor("#850047"), | |
| fontSize=11, | |
| leading=15, | |
| fontName="Helvetica-Bold" | |
| ) | |
| a_style = ParagraphStyle( | |
| "Answer", | |
| parent=styles["BodyText"], | |
| textColor=colors.HexColor("#1F2933"), | |
| fontSize=10, | |
| leading=14 | |
| ) | |
| story = [] | |
| story.append(Paragraph("EGETS5241 - Energy Data Analytics Chat Report", title_style)) | |
| story.append(Paragraph("MSc Energy Transition and Sustainability | UTAS Muscat", sub_style)) | |
| story.append(Paragraph(f"Generated on: {datetime.now().strftime('%d %B %Y, %H:%M')}", sub_style)) | |
| story.append(Spacer(1, 14)) | |
| for i, (q, a) in enumerate(pairs, start=1): | |
| question = Paragraph(f"<b>Student Question {i}:</b><br/>{html.escape(str(q))}", q_style) | |
| answer_text = html.escape(str(a)).replace("\n", "<br/>") | |
| answer = Paragraph(f"<b>Tutor Answer:</b><br/>{answer_text}", a_style) | |
| table = Table([[question], [answer]], colWidths=[17.5 * cm]) | |
| table.setStyle(TableStyle([ | |
| ("BACKGROUND", (0, 0), (-1, 0), colors.HexColor("#FFF4C7")), | |
| ("BACKGROUND", (0, 1), (-1, 1), colors.white), | |
| ("BOX", (0, 0), (-1, -1), 0.75, colors.HexColor("#D6B94B")), | |
| ("INNERGRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#E8D89A")), | |
| ("LEFTPADDING", (0, 0), (-1, -1), 8), | |
| ("RIGHTPADDING", (0, 0), (-1, -1), 8), | |
| ("TOPPADDING", (0, 0), (-1, -1), 8), | |
| ("BOTTOMPADDING", (0, 0), (-1, -1), 8), | |
| ])) | |
| story.append(table) | |
| story.append(Spacer(1, 10)) | |
| doc.build(story) | |
| return filepath | |
| except Exception: | |
| fallback = filepath.replace(".pdf", ".txt") | |
| with open(fallback, "w", encoding="utf-8") as f: | |
| f.write("EGETS5241 - Energy Data Analytics Chat Report\n\n") | |
| for i, (q, a) in enumerate(pairs, start=1): | |
| f.write(f"Student Question {i}:\n{q}\n\nTutor Answer:\n{a}\n\n{'-'*70}\n\n") | |
| return fallback | |
| CSS = """ | |
| body, .gradio-container { | |
| background: #f6f1e7 !important; | |
| color: #1f2933 !important; | |
| font-family: Arial, sans-serif !important; | |
| font-size: 18px !important; | |
| } | |
| .gradio-container { | |
| max-width: 1320px !important; | |
| margin: auto !important; | |
| } | |
| .header { | |
| background: linear-gradient(135deg, #5a0030, #850047, #b8860b); | |
| color: white; | |
| padding: 30px; | |
| border-radius: 22px; | |
| border-top: 7px solid #f0c84b; | |
| box-shadow: 0 10px 25px rgba(90,0,48,0.25); | |
| min-height: 165px; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| } | |
| .header h1 { | |
| font-size: 36px !important; | |
| margin-bottom: 10px !important; | |
| } | |
| .header h3 { | |
| font-size: 21px !important; | |
| margin: 4px 0 !important; | |
| } | |
| .header p { | |
| font-size: 16px !important; | |
| margin: 4px 0 !important; | |
| } | |
| .header h1, .header h3, .header p { | |
| color: white !important; | |
| } | |
| .logo-box { | |
| background: white; | |
| border: 2px solid #d6b94b; | |
| border-radius: 20px; | |
| padding: 18px; | |
| text-align: center; | |
| min-height: 165px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .logo-img { | |
| width: 340px; | |
| max-width: 100%; | |
| display: block; | |
| margin: auto; | |
| } | |
| .info-box { | |
| background: #fff8dc; | |
| border: 2px solid #d6b94b; | |
| border-left: 7px solid #b8860b; | |
| padding: 16px; | |
| border-radius: 14px; | |
| color: #1f2933 !important; | |
| font-size: 18px; | |
| line-height: 1.65; | |
| } | |
| .info-box, .info-box * { | |
| color: #1f2933 !important; | |
| opacity: 1 !important; | |
| } | |
| .example-box { | |
| background: #fff8dc; | |
| border: 2px solid #d6b94b; | |
| padding: 16px; | |
| border-radius: 14px; | |
| margin-bottom: 10px; | |
| font-size: 18px; | |
| } | |
| .example-box, .example-box * { | |
| color: #1f2933 !important; | |
| opacity: 1 !important; | |
| } | |
| .mode-chip { | |
| background: #850047; | |
| color: white !important; | |
| padding: 14px; | |
| border-radius: 12px; | |
| font-weight: bold; | |
| text-align: center; | |
| margin: 10px 0; | |
| font-size: 18px; | |
| } | |
| button { | |
| border-radius: 12px !important; | |
| font-weight: bold !important; | |
| font-size: 17px !important; | |
| } | |
| textarea, input { | |
| background: white !important; | |
| color: #1f2933 !important; | |
| font-size: 18px !important; | |
| } | |
| #chatbox { | |
| background: white !important; | |
| border: 2px solid #d6b94b !important; | |
| border-radius: 16px !important; | |
| font-size: 18px !important; | |
| line-height: 1.8 !important; | |
| } | |
| #chatbox * { | |
| font-size: 18px !important; | |
| line-height: 1.8 !important; | |
| } | |
| footer { | |
| display: none !important; | |
| } | |
| """ | |
| def main(): | |
| with gr.Blocks(css=CSS, title="EGETS5241 Energy Data Analytics Tutor") as demo: | |
| with gr.Row(equal_height=True): | |
| with gr.Column(scale=1, min_width=320): | |
| gr.HTML(get_logo_html()) | |
| with gr.Column(scale=4): | |
| gr.HTML(f""" | |
| <div class="header"> | |
| <h1>โก {COURSE_NAME}</h1> | |
| <h3>{PROGRAMME}</h3> | |
| <p>{UNIVERSITY}</p> | |
| </div> | |
| """) | |
| gr.HTML(""" | |
| <div class="info-box"> | |
| <b>How to use:</b> Type your topic or question directly in the question box below, for example | |
| <b>Smart Grid</b>, <b>SCADA</b>, <b>ARIMA</b>, or <b>Missing Values</b>. | |
| Then choose the question type and click <b>Send</b>. | |
| </div> | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1, min_width=330): | |
| gr.Markdown("### Choose Question Type") | |
| with gr.Row(): | |
| btn_explain = gr.Button("๐ Explain", variant="primary") | |
| btn_short = gr.Button("๐ Short") | |
| with gr.Row(): | |
| btn_long = gr.Button("๐ Long") | |
| btn_num = gr.Button("๐งฎ Numerical") | |
| with gr.Row(): | |
| btn_mcq = gr.Button("โ MCQ") | |
| btn_case = gr.Button("๐ญ Case Study") | |
| with gr.Row(): | |
| btn_flash = gr.Button("๐ง Flashcards") | |
| btn_exam = gr.Button("๐ Question Bank", variant="primary") | |
| mode = gr.State("Explain in detail") | |
| mode_display = gr.HTML("<div class='mode-chip'>๐ Explain in detail</div>") | |
| with gr.Column(scale=3): | |
| gr.HTML(""" | |
| <div class="example-box"> | |
| <b>Example questions:</b> | |
| <ul> | |
| <li>Give me important final exam questions for Smart Grid.</li> | |
| <li>Explain SCADA in simple words.</li> | |
| <li>Create MCQs from data preprocessing.</li> | |
| <li>Give one numerical question on load forecasting with answer.</li> | |
| </ul> | |
| </div> | |
| """) | |
| chatbot = gr.Chatbot( | |
| label="Energy Data Analytics Chat", | |
| height=660, | |
| elem_id="chatbox" | |
| ) | |
| msg = gr.Textbox( | |
| label="Your topic or question", | |
| placeholder="Example: Explain SARIMA in simple words / Give important exam questions for Smart Grid", | |
| lines=3 | |
| ) | |
| with gr.Row(): | |
| send = gr.Button("Send", variant="primary") | |
| clear = gr.Button("Clear") | |
| gr.Markdown("### Download Interaction Report") | |
| report_download = gr.DownloadButton( | |
| label="๐ Generate Premium Study Report", | |
| value=None, | |
| variant="primary" | |
| ) | |
| send.click(respond, inputs=[msg, chatbot, mode], outputs=[chatbot, msg]) | |
| msg.submit(respond, inputs=[msg, chatbot, mode], outputs=[chatbot, msg]) | |
| clear.click(lambda: [], inputs=[], outputs=[chatbot]) | |
| report_download.click(export_premium_report, inputs=[chatbot], outputs=[report_download]) | |
| btn_explain.click(lambda: update_mode("Explain in detail"), inputs=[], outputs=[mode, mode_display]) | |
| btn_short.click(lambda: update_mode("Short answer"), inputs=[], outputs=[mode, mode_display]) | |
| btn_long.click(lambda: update_mode("Long answer"), inputs=[], outputs=[mode, mode_display]) | |
| btn_num.click(lambda: update_mode("Numerical question"), inputs=[], outputs=[mode, mode_display]) | |
| btn_mcq.click(lambda: update_mode("MCQ quiz"), inputs=[], outputs=[mode, mode_display]) | |
| btn_case.click(lambda: update_mode("Case study"), inputs=[], outputs=[mode, mode_display]) | |
| btn_flash.click(lambda: update_mode("Flashcards"), inputs=[], outputs=[mode, mode_display]) | |
| btn_exam.click(lambda: update_mode("Important exam questions"), inputs=[], outputs=[mode, mode_display]) | |
| try: | |
| load_index() | |
| except Exception: | |
| pass | |
| demo.launch() | |
| if __name__ == "__main__": | |
| main() | |