Spaces:
Sleeping
Sleeping
""" | |
CV Review Feature | |
""" | |
import json | |
import concurrent.futures | |
from httpx import LocalProtocolError | |
import streamlit as st | |
from cohere.core.api_error import ApiError | |
from utils.backend import produce_report | |
from utils.format import extract_json | |
def generate_markdown_report(REPORT_OBJ: dict) -> str: | |
"""Format the report object dictionary into a markdown readable report""" | |
def add_section(section_title: str, table_contents: list) -> str: | |
"""Utility function to add a section containing a table to the markdown report""" | |
if not table_contents: | |
return "" | |
section = f"### {section_title.title()}\n\n" | |
section += ( | |
"| Job Posting Requirement | CV Details | Explanation | Impact Score |\n" | |
"| ----------------------- | ---------- | ----------- | -------------- |\n" | |
) | |
for table_row in table_contents: | |
section += f"| {table_row.get('jobPostingDetails', 'N/A')} | {table_row.get('cvDetails', 'N/A')} | {table_row.get('explanation', '')} | **{table_row.get('severityScore', 0)}** |\n" | |
return section + "\n" | |
report = ( | |
f"# CV Analysis Report\n\n" | |
f"**Name:** {REPORT_OBJ.get('personName', 'Unknown')} \n" | |
f"**Job:** {REPORT_OBJ.get('jobTitle', 'N/A')} at {REPORT_OBJ.get('companyName', 'N/A')} \n" | |
f"**Job Description:** {REPORT_OBJ.get('jobDesc', 'No description available.')}\n\n" | |
"---\n\n" | |
"## Key Findings\n\n" | |
) | |
sections = ["experience", "education", "responsibilities", "languages", "tools"] | |
for section in sections: | |
report += add_section(section, REPORT_OBJ.get(section, [])) | |
report += "---\n" | |
return report | |
def CVReviewPage(): | |
"""Source Code for CV Review Page""" | |
SHARED_STATE = st.session_state.shared_materials | |
API_KEY = st.session_state.api_key | |
if not SHARED_STATE["valid_flag"]: | |
st.error("You need to upload a Job Description & CV to use this feature.") | |
else: | |
produce_report_button = st.button("Produce Suitability Report") | |
if produce_report_button: | |
try: | |
results = {} | |
# We will make 3 calls in parallel, to get various bits of information efficiently | |
with concurrent.futures.ThreadPoolExecutor() as executor: | |
futures = { | |
critique_type: executor.submit( | |
produce_report, | |
SHARED_STATE["cv"], | |
SHARED_STATE["job_posting"], | |
critique_type, | |
API_KEY, | |
) | |
for critique_type in ["basic", "general", "specific"] | |
} | |
for critique_type, future in futures.items(): | |
results[critique_type] = future.result() | |
except LocalProtocolError: | |
st.error("You need to enter a Cohere API Key.") | |
except ApiError: | |
st.error("You need a valid Cohere API Key") | |
# merge the from our calls, by extracting the json object from the gpt message | |
resultsDict = {} | |
for jsonText in results.values(): | |
_, output_report_json = extract_json(jsonText) | |
resultsDict.update(output_report_json) | |
# store this as the report object | |
SHARED_STATE["report"] = resultsDict | |
# if the report object exists | |
if SHARED_STATE["report"]: | |
REPORT = SHARED_STATE["report"] | |
# these are used for file naming | |
name = REPORT.get("personName", "MissingPersonName") | |
job_title = REPORT.get("jobTitle", "MissingTitle") | |
company_name = REPORT.get("companyName", "MissingCompany") | |
# render markdown report | |
st.markdown(generate_markdown_report(REPORT)) | |
# Downloadable in json form ! | |
st.download_button( | |
label="Download Report JSON", | |
data=json.dumps(REPORT, indent=4), | |
file_name=f"{name}_{job_title}_{company_name}.json", | |
mime="application/json", | |
use_container_width=True, | |
) | |