CV_Reviewer / CVReview.py
Jonah Ramponi
cleanup 2
0c3cc21
"""
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,
)