Spaces:
Build error
Build error
| import csv | |
| import datetime as dt | |
| import os | |
| import re | |
| import tempfile | |
| import textwrap | |
| import zipfile | |
| from collections import Counter | |
| from pathlib import Path | |
| import gradio as gr | |
| APP_TITLE = "ASHU AI Mentor Studio" | |
| OUT_DIR = Path("outputs") | |
| OUT_DIR.mkdir(exist_ok=True) | |
| SKILLS = sorted(set(""" | |
| python java c++ javascript typescript sql mysql postgresql mongodb snowflake excel powerbi tableau | |
| pandas numpy scikit-learn sklearn tensorflow pytorch keras opencv yolo ultralytics nlp computer vision | |
| machine learning deep learning llm rag langchain llamaindex openai huggingface transformers gradio streamlit | |
| flask fastapi docker kubernetes git github linux bash aws azure gcp airflow mlflow dvc cicd rest api | |
| microservices prompt engineering data analysis data science statistics regression classification clustering xgboost | |
| lightgbm random forest svm cnn rnn lstm transformer bert gpt vector database faiss chromadb pinecone | |
| elasticsearch html css react nodejs django pytest monitoring grafana prometheus agile scrum communication leadership | |
| documentation research writing latex | |
| """.split())) | |
| STOPWORDS = set(""" | |
| a an the and or but if then else with without for from into onto in on at by to of is are was were be been | |
| being this that these those as it its your you we our they them their candidate role job work experience skills | |
| using use used will can should could would may might about across within strong good excellent ability knowledge | |
| hands-on years year team teams project projects responsibilities requirements preferred required | |
| """.split()) | |
| def clean_text(text): | |
| return re.sub(r"\s+", " ", text or "").strip() | |
| def tokens(text): | |
| return [t.lower() for t in re.findall(r"[A-Za-z][A-Za-z0-9+.#/-]{1,}", text or "")] | |
| def keywords(text, n=30): | |
| toks = [t for t in tokens(text) if t not in STOPWORDS and len(t) > 2] | |
| return [w for w, _ in Counter(toks).most_common(n)] | |
| def detect_skills(text): | |
| hay = " " + re.sub(r"[^a-z0-9+#./-]+", " ", (text or "").lower()) + " " | |
| found = [] | |
| for skill in SKILLS: | |
| pattern = r"(?<![a-z0-9+#./-])" + re.escape(skill.lower()) + r"(?![a-z0-9+#./-])" | |
| if re.search(pattern, hay): | |
| found.append(skill) | |
| return sorted(found) | |
| def read_uploaded_file(file_obj): | |
| if file_obj is None: | |
| return "" | |
| try: | |
| path = Path(file_obj.name if hasattr(file_obj, "name") else str(file_obj)) | |
| suffix = path.suffix.lower() | |
| if suffix in [".txt", ".md", ".csv", ".py", ".json", ".log"]: | |
| return path.read_text(encoding="utf-8", errors="ignore") | |
| return "Unsupported file type for this stable build. Paste resume/JD text in the textbox for PDF/DOCX." | |
| except Exception as exc: | |
| return f"Could not read uploaded file: {exc}" | |
| def timestamp_name(prefix, ext): | |
| ts = dt.datetime.now().strftime("%Y%m%d_%H%M%S") | |
| return OUT_DIR / f"{prefix}_{ts}.{ext}" | |
| def write_text(prefix, content, ext="txt"): | |
| path = timestamp_name(prefix, ext) | |
| path.write_text(content or "", encoding="utf-8") | |
| return str(path) | |
| def write_csv(prefix, rows): | |
| path = timestamp_name(prefix, "csv") | |
| if not rows: | |
| rows = [{"message": "No rows generated"}] | |
| with path.open("w", newline="", encoding="utf-8") as f: | |
| writer = csv.DictWriter(f, fieldnames=list(rows[0].keys())) | |
| writer.writeheader() | |
| writer.writerows(rows) | |
| return str(path) | |
| def zip_outputs(prefix, files): | |
| path = timestamp_name(prefix, "zip") | |
| with zipfile.ZipFile(path, "w", zipfile.ZIP_DEFLATED) as z: | |
| for f in files: | |
| if f and Path(f).exists(): | |
| z.write(f, arcname=Path(f).name) | |
| return str(path) | |
| def similarity_score(a, b): | |
| ka = set(keywords(a, 120)) | |
| kb = set(keywords(b, 120)) | |
| if not ka or not kb: | |
| return 0.0 | |
| return len(ka & kb) / max(1, len(kb)) | |
| def ats_analyze(resume_file, resume_text, jd_text, target_role): | |
| resume = clean_text((resume_text or "") + "\n" + read_uploaded_file(resume_file)) | |
| jd = clean_text(jd_text or "") | |
| if not resume: | |
| return "Paste resume text or upload a TXT/CSV/MD resume file first.", None | |
| if not jd: | |
| return "Paste the job description first.", None | |
| r_skills = detect_skills(resume) | |
| j_skills = detect_skills(jd) | |
| matched = sorted(set(r_skills) & set(j_skills)) | |
| missing = sorted(set(j_skills) - set(r_skills)) | |
| keyword_match = similarity_score(resume, jd) | |
| skill_score = len(matched) / max(1, len(j_skills)) | |
| length_score = min(1.0, len(resume.split()) / 450) | |
| role_bonus = 0.08 if target_role and target_role.lower() in resume.lower() else 0.0 | |
| score = round(100 * min(1.0, 0.55 * skill_score + 0.30 * keyword_match + 0.15 * length_score + role_bonus), 1) | |
| verdict = "Strong match" if score >= 75 else "Moderate match" if score >= 55 else "Needs tailoring" | |
| jd_kw = keywords(jd, 20) | |
| resume_kw = set(keywords(resume, 80)) | |
| missing_kw = [k for k in jd_kw if k not in resume_kw][:15] | |
| report = f"""# ATS Resume + JD Analysis | |
| **Target role:** {target_role or 'Not specified'} | |
| **ATS-style score:** **{score}/100** | |
| **Verdict:** **{verdict}** | |
| ## Skill Coverage | |
| - JD skills detected: {len(j_skills)} | |
| - Resume skills detected: {len(r_skills)} | |
| - Matched skills: {len(matched)} | |
| - Missing JD skills: {len(missing)} | |
| ## Matched Skills | |
| {', '.join(matched) if matched else 'No direct technical skill matches detected.'} | |
| ## Missing Skills to Add/Improve | |
| {', '.join(missing) if missing else 'No major missing skills detected.'} | |
| ## Missing JD Keywords | |
| {', '.join(missing_kw) if missing_kw else 'No important missing JD keywords detected.'} | |
| ## Immediate Improvements | |
| 1. Add a role-aligned summary using the exact job title. | |
| 2. Put matched technical skills in a clear skills section. | |
| 3. Add quantified project bullets: problem, tool/model, metric, business impact. | |
| 4. Add missing JD keywords only where truthful. | |
| 5. Avoid tables/images for ATS-friendly formatting. | |
| """ | |
| rows = [ | |
| {"metric": "ATS score", "value": str(score)}, | |
| {"metric": "verdict", "value": verdict}, | |
| {"metric": "matched skills", "value": ", ".join(matched)}, | |
| {"metric": "missing skills", "value": ", ".join(missing)}, | |
| {"metric": "missing keywords", "value": ", ".join(missing_kw)}, | |
| ] | |
| txt_path = write_text("ashu_ats_report", report, "md") | |
| csv_path = write_csv("ashu_ats_report", rows) | |
| return report, zip_outputs("ashu_ats_outputs", [txt_path, csv_path]) | |
| def generate_cover_letter(name, company, role, resume_file, resume_text, jd_text): | |
| resume = clean_text((resume_text or "") + "\n" + read_uploaded_file(resume_file)) | |
| jd = clean_text(jd_text or "") | |
| matched = sorted(set(detect_skills(resume)) & set(detect_skills(jd)))[:8] | |
| name = name or "Candidate" | |
| company = company or "your organization" | |
| role = role or "the advertised role" | |
| skills_line = ", ".join(matched) if matched else "AI, analytics, problem solving, and practical project execution" | |
| letter = f"""Dear Hiring Team, | |
| I am writing to express my interest in the {role} position at {company}. My background aligns well with the role requirements, especially in {skills_line}. | |
| Across my work, I have focused on building practical solutions that connect technical execution with measurable outcomes. I bring experience in understanding requirements, designing reliable workflows, implementing systems, evaluating results, and communicating insights clearly to stakeholders. | |
| What attracts me to this opportunity is the chance to contribute to a team where ownership, learning, and impact matter. I would be excited to apply my experience to the problems your team is solving. | |
| Thank you for considering my application. I would welcome the opportunity to discuss how my experience can contribute to {company}. | |
| Sincerely, | |
| {name} | |
| """ | |
| return letter, write_text("ashu_cover_letter", letter, "txt") | |
| def interview_questions(role, skills, difficulty, count): | |
| role = role or "AI/ML Engineer" | |
| difficulty = difficulty or "Medium" | |
| skill_list = [s.strip() for s in re.split(r"[,\n]", skills or "") if s.strip()] or ["Python", "Machine Learning", "System Design", "Communication"] | |
| count = int(count) | |
| rows = [] | |
| lines = ["# Interview Questions"] | |
| for i in range(1, count + 1): | |
| skill = skill_list[(i - 1) % len(skill_list)] | |
| if i % 4 == 1: | |
| question = f"Explain a project where you used {skill}. What was the problem, your approach, and the final impact?" | |
| elif i % 4 == 2: | |
| question = f"For a {role}, how would you design a reliable workflow using {skill} in production?" | |
| elif i % 4 == 3: | |
| question = f"What can go wrong while using {skill}, and how would you validate or debug it?" | |
| else: | |
| question = f"Give a {difficulty.lower()}-level scenario where {skill} must be optimized for accuracy, latency, or cost." | |
| rows.append({"No": i, "Skill": skill, "Difficulty": difficulty, "Question": question}) | |
| lines.append(f"{i}. **[{skill}]** {question}") | |
| csv_path = write_csv("ashu_interview_questions", rows) | |
| md_path = write_text("ashu_interview_questions", "\n".join(lines), "md") | |
| return "\n\n".join(lines), zip_outputs("ashu_interview_outputs", [csv_path, md_path]) | |
| def score_answer(question, answer): | |
| q = clean_text(question) | |
| a = clean_text(answer) | |
| if not q or not a: | |
| return "Add both the interview question and your answer." | |
| qk = set(keywords(q, 20)) | |
| ak = set(keywords(a, 80)) | |
| coverage = len(qk & ak) / max(1, len(qk)) | |
| structure = sum(1 for x in ["problem", "approach", "result", "impact", "metric", "learned"] if x in a.lower()) / 6 | |
| length = min(1.0, len(a.split()) / 120) | |
| score = round(100 * (0.45 * coverage + 0.35 * structure + 0.20 * length), 1) | |
| feedback = [] | |
| if structure < 0.5: | |
| feedback.append("Use STAR format: Situation, Task, Action, Result.") | |
| if coverage < 0.5: | |
| feedback.append("Answer the exact question terms more directly.") | |
| if len(a.split()) < 80: | |
| feedback.append("Add more detail and at least one measurable result.") | |
| if not feedback: | |
| feedback.append("Good structure. Add one stronger metric if possible.") | |
| return f"# Answer Score: {score}/100\n\n## Feedback\n- " + "\n- ".join(feedback) | |
| def training_plan(goal, level, weeks, hours_per_week): | |
| goal = goal or "AI/ML and interview preparation" | |
| level = level or "Beginner" | |
| weeks = int(weeks) | |
| hours_per_week = int(hours_per_week) | |
| topics = keywords(goal, 10) or ["python", "machine learning", "projects", "interview"] | |
| rows = [] | |
| lines = [f"# {weeks}-Week Training Plan", f"**Goal:** {goal}", f"**Level:** {level}", f"**Time:** {hours_per_week} hours/week"] | |
| for w in range(1, weeks + 1): | |
| topic = topics[(w - 1) % len(topics)].title() | |
| deliverable = f"Week {w} output: notes + one exercise + 3 interview answers" | |
| rows.append({"Week": w, "Topic": topic, "Hours": hours_per_week, "Deliverable": deliverable}) | |
| lines.append(f"\n## Week {w}: {topic}\n- Learn/revise fundamentals\n- Build one mini exercise\n- Create 5 flashcards\n- Practice 3 interview questions\n- **Deliverable:** {deliverable}") | |
| md = "\n".join(lines) | |
| md_path = write_text("ashu_training_plan", md, "md") | |
| csv_path = write_csv("ashu_training_plan", rows) | |
| return md, zip_outputs("ashu_training_plan", [md_path, csv_path]) | |
| def summarize_document(file_obj, pasted_text, style): | |
| text = clean_text((pasted_text or "") + "\n" + read_uploaded_file(file_obj)) | |
| if not text: | |
| return "Paste text or upload TXT/CSV/MD file first.", None | |
| sentences = re.split(r"(?<=[.!?])\s+", text) | |
| words = keywords(text, 25) | |
| scored = [] | |
| for s in sentences: | |
| sc = sum(1 for w in words if w in s.lower()) | |
| if sc > 0 and len(s.split()) > 5: | |
| scored.append((sc, s)) | |
| selected = [s for _, s in sorted(scored, reverse=True)[:8]] or sentences[:6] | |
| if style == "Study notes": | |
| output = "# Study Notes\n\n## Key Terms\n" + ", ".join(words[:15]) + "\n\n## Notes\n" + "\n".join(f"- {clean_text(s)}" for s in selected) | |
| elif style == "Executive summary": | |
| output = "# Executive Summary\n\n" + " ".join(selected[:5]) + "\n\n## Key Terms\n" + ", ".join(words[:12]) | |
| else: | |
| output = "# Bullet Summary\n" + "\n".join(f"- {clean_text(s)}" for s in selected) | |
| return output, write_text("ashu_summary", output, "md") | |
| def flashcards_mcqs(file_obj, pasted_text, count): | |
| text = clean_text((pasted_text or "") + "\n" + read_uploaded_file(file_obj)) | |
| if not text: | |
| return "Paste text or upload TXT/CSV/MD file first.", None | |
| kws = keywords(text, int(count) + 10) | |
| rows = [] | |
| lines = ["# Flashcards and MCQs"] | |
| for i, kw in enumerate(kws[: int(count)], 1): | |
| rows.append({"type": "flashcard", "question": f"What is {kw}?", "answer": f"Explain {kw} using the given material and add one example."}) | |
| rows.append({"type": "mcq", "question": f"Which statement best relates to {kw}?", "answer": "A"}) | |
| lines.append(f"## {i}. {kw.title()}\n**Flashcard:** What is {kw}?\n\n**MCQ:** Which statement best relates to {kw}?\nA. Most relevant concept from the material\nB. Unrelated term\nC. Random process\nD. None\n\n**Answer:** A") | |
| md = "\n\n".join(lines) | |
| md_path = write_text("ashu_flashcards_mcqs", md, "md") | |
| csv_path = write_csv("ashu_flashcards_mcqs", rows) | |
| return md, zip_outputs("ashu_flashcards_outputs", [md_path, csv_path]) | |
| def presenter_script(topic, audience, minutes): | |
| topic = topic or "AI Mentor Platform" | |
| audience = audience or "students and professionals" | |
| minutes = int(minutes) | |
| section_names = ["Hook", "Problem", "Solution", "Workflow", "Demo", "Benefits", "Closing"] | |
| sections = max(3, min(7, minutes // 2 + 1)) | |
| lines = [f"# Digital Presenter Script: {topic}", f"**Audience:** {audience}", f"**Duration:** {minutes} minutes"] | |
| for i in range(sections): | |
| name = section_names[i] | |
| lines.append(f"\n## Scene {i + 1}: {name}\nPresenter says: Today we discuss **{topic}** for {audience}. Explain this section clearly, show one practical example, and connect it to value.\n\nOn-screen visual: title card, workflow diagram, or short bullet animation.") | |
| script = "\n".join(lines) | |
| return script, write_text("ashu_presenter_script", script, "md") | |
| def candidate_scorecard(name, role, resume_file, resume_text, interview_notes): | |
| text = clean_text((resume_text or "") + "\n" + read_uploaded_file(resume_file) + "\n" + (interview_notes or "")) | |
| found = detect_skills(text) | |
| dimensions = { | |
| "Technical fit": min(100, 30 + len(found) * 5), | |
| "Communication": 75 if len((interview_notes or "").split()) > 60 else 55, | |
| "Project depth": 80 if any(w in text.lower() for w in ["project", "built", "developed", "implemented"]) else 50, | |
| "Role alignment": 85 if role and role.lower() in text.lower() else 60, | |
| "Learning potential": 78, | |
| } | |
| final = round(sum(dimensions.values()) / len(dimensions), 1) | |
| rows = [{"Dimension": k, "Score": v} for k, v in dimensions.items()] | |
| md = f"# Candidate Scorecard\n\n**Name:** {name or 'Candidate'}\n\n**Role:** {role or 'Not specified'}\n\n**Final score:** **{final}/100**\n\n" | |
| for k, v in dimensions.items(): | |
| md += f"- **{k}:** {v}/100\n" | |
| md += "\n## Detected Strengths\n" + (", ".join(found[:20]) if found else "Add more evidence from resume/interview.") | |
| md_path = write_text("ashu_candidate_scorecard", md, "md") | |
| csv_path = write_csv("ashu_candidate_scorecard", rows) | |
| return md, zip_outputs("ashu_candidate_scorecard", [md_path, csv_path]) | |
| def architecture_diagram(system_name, features): | |
| system_name = system_name or "ASHU AI Mentor Studio" | |
| feature_list = [f.strip() for f in re.split(r"[,\n]", features or "") if f.strip()] or ["Resume Analyzer", "Training Engine", "Interview Engine", "Report Generator", "Download Center"] | |
| nodes = [] | |
| for i, feature in enumerate(feature_list, 1): | |
| nodes.append(f"UI --> F{i}[{feature}]") | |
| nodes.append(f"F{i} --> R[Reports and Downloads]") | |
| mermaid = "flowchart TD\nU[User] --> UI[" + system_name + " UI]\n" + "\n".join(nodes) + "\nR --> D[TXT CSV ZIP Outputs]" | |
| md = f"# Architecture Diagram\n\n```mermaid\n{mermaid}\n```\n\n## Components\n" + "\n".join(f"- {f}" for f in feature_list) | |
| return md, write_text("ashu_architecture_mermaid", mermaid, "mmd") | |
| def health_check(): | |
| return "✅ ASHU Gradio Space is running. App loaded successfully." | |
| with gr.Blocks(title=APP_TITLE) as demo: | |
| gr.Markdown(f"# 🚀 {APP_TITLE}\nStable Hugging Face Gradio app with resume/JD analysis, training, interview prep, summaries, quizzes, scripts, scorecards, and diagrams.") | |
| with gr.Tab("Health Check"): | |
| btn = gr.Button("Check app") | |
| out = gr.Markdown() | |
| btn.click(health_check, outputs=out) | |
| with gr.Tab("Resume + JD Analyzer"): | |
| role = gr.Textbox(label="Target Role", placeholder="AI/ML Engineer") | |
| resume_file = gr.File(label="Resume file: TXT/CSV/MD only in this stable build") | |
| resume_text = gr.Textbox(label="Or paste resume text", lines=8) | |
| jd_text = gr.Textbox(label="Paste job description", lines=8) | |
| analyze_btn = gr.Button("Analyze ATS Match") | |
| ats_out = gr.Markdown() | |
| ats_zip = gr.File(label="Download ATS outputs") | |
| analyze_btn.click(ats_analyze, inputs=[resume_file, resume_text, jd_text, role], outputs=[ats_out, ats_zip]) | |
| with gr.Tab("Cover Letter"): | |
| name = gr.Textbox(label="Candidate Name") | |
| company = gr.Textbox(label="Company") | |
| cover_role = gr.Textbox(label="Role") | |
| cover_btn = gr.Button("Generate Cover Letter") | |
| cover_out = gr.Textbox(label="Cover Letter", lines=12) | |
| cover_file = gr.File(label="Download") | |
| cover_btn.click(generate_cover_letter, inputs=[name, company, cover_role, resume_file, resume_text, jd_text], outputs=[cover_out, cover_file]) | |
| with gr.Tab("Interview Prep"): | |
| int_role = gr.Textbox(label="Role", value="AI/ML Engineer") | |
| int_skills = gr.Textbox(label="Skills", value="Python, Machine Learning, SQL, System Design") | |
| difficulty = gr.Dropdown(["Easy", "Medium", "Hard"], value="Medium", label="Difficulty") | |
| q_count = gr.Slider(5, 30, value=12, step=1, label="Number of Questions") | |
| q_btn = gr.Button("Generate Questions") | |
| q_out = gr.Markdown() | |
| q_file = gr.File(label="Download") | |
| q_btn.click(interview_questions, inputs=[int_role, int_skills, difficulty, q_count], outputs=[q_out, q_file]) | |
| gr.Markdown("## Answer Scoring") | |
| question = gr.Textbox(label="Interview Question", lines=3) | |
| answer = gr.Textbox(label="Your Answer", lines=8) | |
| score_btn = gr.Button("Score Answer") | |
| score_out = gr.Markdown() | |
| score_btn.click(score_answer, inputs=[question, answer], outputs=score_out) | |
| with gr.Tab("Training Plan"): | |
| goal = gr.Textbox(label="Learning Goal", value="AI ML job preparation with Python and projects") | |
| level = gr.Dropdown(["Beginner", "Intermediate", "Advanced"], value="Intermediate", label="Level") | |
| weeks = gr.Slider(1, 24, value=8, step=1, label="Weeks") | |
| hours = gr.Slider(1, 20, value=6, step=1, label="Hours per week") | |
| train_btn = gr.Button("Create Plan") | |
| train_out = gr.Markdown() | |
| train_zip = gr.File(label="Download") | |
| train_btn.click(training_plan, inputs=[goal, level, weeks, hours], outputs=[train_out, train_zip]) | |
| with gr.Tab("Summarizer"): | |
| doc_file = gr.File(label="TXT/CSV/MD file") | |
| doc_text = gr.Textbox(label="Or paste document text", lines=10) | |
| summary_style = gr.Dropdown(["Bullet summary", "Study notes", "Executive summary"], value="Bullet summary", label="Style") | |
| sum_btn = gr.Button("Summarize") | |
| sum_out = gr.Markdown() | |
| sum_file = gr.File(label="Download") | |
| sum_btn.click(summarize_document, inputs=[doc_file, doc_text, summary_style], outputs=[sum_out, sum_file]) | |
| with gr.Tab("Flashcards + MCQs"): | |
| card_count = gr.Slider(5, 30, value=10, step=1, label="Count") | |
| card_btn = gr.Button("Generate Flashcards and MCQs") | |
| card_out = gr.Markdown() | |
| card_zip = gr.File(label="Download") | |
| card_btn.click(flashcards_mcqs, inputs=[doc_file, doc_text, card_count], outputs=[card_out, card_zip]) | |
| with gr.Tab("Presenter Script"): | |
| topic = gr.Textbox(label="Topic", value="ASHU AI Mentor Studio") | |
| audience = gr.Textbox(label="Audience", value="students and professionals") | |
| minutes = gr.Slider(2, 30, value=8, step=1, label="Duration in minutes") | |
| script_btn = gr.Button("Generate Script") | |
| script_out = gr.Markdown() | |
| script_file = gr.File(label="Download") | |
| script_btn.click(presenter_script, inputs=[topic, audience, minutes], outputs=[script_out, script_file]) | |
| with gr.Tab("Candidate Scorecard"): | |
| cand_name = gr.Textbox(label="Candidate Name") | |
| cand_role = gr.Textbox(label="Role") | |
| notes = gr.Textbox(label="Interview Notes", lines=8) | |
| scorecard_btn = gr.Button("Generate Scorecard") | |
| scorecard_out = gr.Markdown() | |
| scorecard_zip = gr.File(label="Download") | |
| scorecard_btn.click(candidate_scorecard, inputs=[cand_name, cand_role, resume_file, resume_text, notes], outputs=[scorecard_out, scorecard_zip]) | |
| with gr.Tab("Architecture Diagram"): | |
| sys_name = gr.Textbox(label="System Name", value="ASHU AI Mentor Studio") | |
| feats = gr.Textbox(label="Features, one per line", lines=6, value="Resume Analyzer\nTraining Engine\nInterview Engine\nReport Generator\nDownload Center") | |
| arch_btn = gr.Button("Generate Mermaid Architecture") | |
| arch_out = gr.Markdown() | |
| arch_file = gr.File(label="Download Mermaid") | |
| arch_btn.click(architecture_diagram, inputs=[sys_name, feats], outputs=[arch_out, arch_file]) | |
| if __name__ == "__main__": | |
| demo.launch() | |