Spaces:
Sleeping
Sleeping
| # app.py | |
| import os | |
| import gradio as gr | |
| from text_extractor import extract_text_from_file | |
| from embedder import get_embeddings | |
| from vector_store import create_faiss_index, search_similar_cvs | |
| from groq_api import summarize_match | |
| # Global state | |
| cv_texts = [] | |
| cv_names = [] | |
| cv_vectors = [] | |
| faiss_index = None | |
| def upload_cvs(files): | |
| global cv_texts, cv_names, cv_vectors, faiss_index | |
| try: | |
| cv_texts = [extract_text_from_file(f.name) for f in files] | |
| cv_names = [f.name for f in files] | |
| cv_vectors = get_embeddings(cv_texts) | |
| import numpy as np | |
| if cv_vectors is None or np.array(cv_vectors).size == 0: | |
| return "β No valid CVs extracted or embedded." | |
| faiss_index = create_faiss_index(cv_vectors) | |
| return f"β Uploaded and indexed {len(files)} CVs." | |
| except Exception as e: | |
| return f"β Error during upload: {e}" | |
| def match_jd(jd_text): | |
| global faiss_index | |
| try: | |
| if not faiss_index: | |
| return "β Please upload CVs first." | |
| if not jd_text.strip(): | |
| return "β Job description is empty." | |
| jd_vector = get_embeddings([jd_text])[0] | |
| top_k_indices = search_similar_cvs(jd_vector, faiss_index, k=3) | |
| import os | |
| matched_names = [os.path.basename(cv_names[i]) for i in top_k_indices] | |
| matched_texts = [ | |
| cv_texts[i][:500] if cv_texts[i].strip() else "[No CV content]" | |
| for i in top_k_indices | |
| ] | |
| summary = summarize_match(jd_text, matched_names, matched_texts) | |
| return f""" | |
| β <span style='color:#16a34a; font-weight:bold;'>Top Matches:</span><br>{matched_names}<br><br> | |
| π <span style='color:#3b82f6; font-weight:bold;'>Summary:</span><br>{summary} | |
| """ | |
| except Exception as e: | |
| return f"<span style='color:red;'>β Error during matching: {e}</span>" | |
| def clear_data(): | |
| global cv_texts, cv_names, cv_vectors, faiss_index | |
| cv_texts, cv_names, cv_vectors, faiss_index = [], [], [], None | |
| return "π§Ή All data cleared. You can now start fresh." | |
| # ====================== | |
| # TABBED PROFESSIONAL UI | |
| # ====================== | |
| with gr.Blocks(css=""" | |
| .gr-button { background-color: #2563eb; color: white; font-weight: bold; } | |
| .gr-button:hover { background-color: #1d4ed8; } | |
| textarea, input[type='file'] { border: 2px solid #3b82f6 !important; } | |
| .gr-textbox label { color: #111827; font-weight: 600; } | |
| """) as upload_tab: | |
| gr.Markdown("## π€ Upload CVs") | |
| gr.Markdown("Upload candidate CVs in PDF or DOCX format.") | |
| cv_upload = gr.File(label="Upload CVs", file_types=[".pdf", ".docx"], file_count="multiple") | |
| upload_button = gr.Button("π Upload & Index CVs") | |
| upload_status = gr.Textbox(label="Status", interactive=False) | |
| upload_button.click(upload_cvs, inputs=[cv_upload], outputs=[upload_status]) | |
| with gr.Blocks() as match_tab: | |
| gr.Markdown("## π Match Job Description to CVs") | |
| jd_input = gr.Textbox(label="Paste Job Description", lines=8, placeholder="e.g. Looking for a Python Data Analyst...") | |
| match_button = gr.Button("π Match CVs") | |
| match_result = gr.HTML() | |
| match_button.click(match_jd, inputs=[jd_input], outputs=[match_result]) | |
| with gr.Blocks() as reset_tab: | |
| gr.Markdown("## π§Ή Clear All Data") | |
| gr.Markdown("Click below to reset the app and upload new CVs.") | |
| clear_button = gr.Button("Reset App") | |
| clear_output = gr.Textbox(label="Reset Status", interactive=False) | |
| clear_button.click(clear_data, inputs=[], outputs=[clear_output]) | |
| # Menu Bar Style Tabs | |
| app = gr.TabbedInterface( | |
| interface_list=[upload_tab, match_tab, reset_tab], | |
| tab_names=["π€ Upload CVs", "π Match JD", "π§Ή Reset"] | |
| ) | |
| if __name__ == "__main__": | |
| app.launch() | |