Spaces:
Configuration error
Configuration error
File size: 10,198 Bytes
cb97851 146e731 10f94c1 cb97851 5d93a4f 91b2483 146e731 cb97851 f9a80bc cb97851 146e731 edc4b6c cb97851 bef6750 cb97851 146e731 bef6750 cb97851 edc4b6c bef6750 cb97851 f8bff10 cb97851 b9464fb cb97851 f9a80bc 146e731 f9a80bc 146e731 b9464fb cb97851 146e731 b9464fb cb97851 b9464fb cb97851 b9464fb cb97851 b9464fb cb97851 146e731 cb97851 146e731 b9464fb cb97851 146e731 b9464fb cb97851 b9464fb cb97851 b9464fb cb97851 b9464fb cb97851 b9464fb 5d93a4f 0b01037 7e01b9e 5d93a4f 7e01b9e cb97851 7e01b9e cb97851 edc4b6c b9464fb 146e731 edc4b6c cb97851 91b2483 f8bff10 bef6750 b9464fb f8bff10 b9464fb f9a80bc f8bff10 b9464fb f8bff10 b9464fb f8bff10 b9464fb cb97851 b9464fb cb97851 bef6750 cb97851 bef6750 cb97851 b9464fb cb97851 b9464fb 5d93a4f 7e01b9e 0b01037 7e01b9e 5d93a4f cb97851 b9464fb cb97851 b9464fb cb97851 146e731 b9464fb cb97851 b9464fb cb97851 b9464fb cb97851 b9464fb cb97851 b9464fb cb97851 b9464fb cb97851 10f94c1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
"""
gradio.py
Functions for handling Gradio UI interactions and processing user inputs.
"""
import logging
import shutil
from pathlib import Path
from functions.linkedin_resume import extract_text_from_linkedin_pdf, check_default_linkedin_pdf
from functions.github import get_github_repositories
from functions.job_call import load_default_job_call, summarize_job_call
from functions.writer_agent import write_resume
from configuration import DEFAULT_GITHUB_PROFILE
# pylint: disable=broad-exception-caught
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def process_with_default_option(
use_default_pdf,
linkedin_pdf,
github_profile,
job_post,
user_instructions
):
"""Process inputs with consideration for default PDF option."""
has_default, default_path = check_default_linkedin_pdf()
# Determine which PDF file to use
pdf_file = None
if use_default_pdf and has_default:
pdf_file = MockFile(default_path)
elif linkedin_pdf is not None:
pdf_file = linkedin_pdf
return process_inputs(pdf_file, github_profile, job_post, user_instructions)
def process_inputs(linkedin_pdf, github_url, job_post_text, user_instructions):
"""
Process the input files and URLs from the Gradio interface.
Args:
linkedin_pdf: Uploaded LinkedIn resume export PDF file or mock file object with path
github_url (str): GitHub profile URL
job_post_text (str): Job post text content
user_instructions (str): Additional instructions from the user
Returns:
str: Formatted output with file and URL information
"""
result = ""
extraction_result = None
logger.info("Processing user inputs from Gradio interface")
# Process LinkedIn PDF file
if linkedin_pdf is not None:
# Handle both file objects and mock file objects with path strings
file_path = linkedin_pdf.name
file_display_name = Path(file_path).name
result += "β
LinkedIn Resume PDF provided\n"
logger.info("Processing LinkedIn PDF: %s", file_display_name)
# Save uploaded file as new default (only if it's not already the default)
project_root = Path(__file__).parent.parent
default_pdf_path = project_root / "data" / "linkedin_profile.pdf"
# Check if this is an uploaded file (not the default file)
if not isinstance(linkedin_pdf, MockFile):
try:
# Create data directory if it doesn't exist
default_pdf_path.parent.mkdir(exist_ok=True)
# Copy uploaded file to default location
shutil.copy2(file_path, default_pdf_path)
result += " β
Saved as new default LinkedIn profile\n"
logger.info("Saved uploaded LinkedIn PDF as new default: %s", default_pdf_path)
except Exception as save_error:
result += f" β οΈ Could not save as default: {str(save_error)}\n"
logger.warning("Failed to save LinkedIn PDF as default: %s", str(save_error))
# Extract and structure text from the PDF
extraction_result = extract_text_from_linkedin_pdf(file_path)
if extraction_result["status"] == "success":
result += " β
Text extraction successful\n\n"
logger.info("LinkedIn PDF text extraction successful")
elif extraction_result["status"] == "warning":
result += f" β οΈ Text extraction: {extraction_result['message']}\n\n"
logger.warning("LinkedIn PDF extraction warning: %s", extraction_result['message'])
else:
result += f" β Text extraction failed: {extraction_result['message']}\n\n"
logger.error("LinkedIn PDF extraction failed: %s", extraction_result['message'])
else:
result += "β No LinkedIn resume PDF file uploaded\n\n"
logger.info("No LinkedIn PDF file provided")
# Process GitHub profile
# Use default GitHub profile if none provided
if github_url and github_url.strip():
github_url_to_use = github_url.strip()
else:
github_url_to_use = DEFAULT_GITHUB_PROFILE
if github_url_to_use:
if github_url and github_url.strip():
result += "β
GitHub Profile URL provided\n"
else:
result += "β
Using default GitHub Profile URL\n"
logger.info("Processing GitHub URL: %s", github_url_to_use)
# Retrieve repositories from GitHub
github_result = get_github_repositories(github_url_to_use)
if github_result["status"] == "success":
result += " β
GitHub list download successful\n\n"
logger.info(
"GitHub repositories retrieved successfully for %s",
github_result['metadata']['username']
)
else:
result += f" β GitHub extraction failed: {github_result['message']}\n\n"
logger.error("GitHub extraction failed: %s", github_result['message'])
else:
result += "β No GitHub profile URL provided\n\n"
logger.info("No GitHub URL provided")
# Process job post text
if job_post_text and job_post_text.strip():
result += "β
Job post text provided\n"
logger.info("Job post text provided (%d characters)", len(job_post_text))
job_text_to_use = job_post_text.strip()
else:
result += "βΉοΈ No job post provided, attempting to use default\n"
logger.info("No job post text provided, trying default")
# Try to load default job call
default_job = load_default_job_call()
if default_job:
job_text_to_use = default_job
else:
result += "βΉοΈ No default job post available, proceeding without job post\n"
logger.info("No default job post available, proceeding without job analysis")
job_text_to_use = None
# Generate job summary (will use default if job_text_to_use is None)
summary = None
if job_text_to_use:
summary = summarize_job_call(job_text_to_use)
if summary:
if job_post_text and job_post_text.strip():
result += " β
Job post summary generated\n"
else:
result += "β
Using default job post\n"
result += " β
Job post summary generated\n"
logger.info("Job post summary generated (%d characters)", len(summary))
else:
result += " β Job post summary generation failed\n"
logger.warning("Job post summary generation failed")
else:
result += "βΉοΈ Proceeding without job post analysis\n"
logger.info("No job post available for analysis")
# Process user instructions
if user_instructions and user_instructions.strip():
result += "β
Additional instructions provided\n"
logger.info("User instructions provided (%d characters)", len(user_instructions))
else:
result += "βΉοΈ No additional instructions provided\n"
logger.info("No additional instructions provided")
logger.info("Input processing completed")
# Generate resume only if we have valid extraction result
if extraction_result and extraction_result.get("status") == "success":
try:
_ = write_resume(extraction_result, user_instructions, summary)
result += "\nβ
Resume generated successfully\n"
logger.info("Resume generation completed successfully")
except Exception as e:
result += f"\nβ Resume generation failed: {str(e)}\n"
logger.error("Resume generation failed: %s", str(e))
else:
result += "\nβ Cannot generate resume: No valid LinkedIn data extracted\n"
result += "Please ensure you upload a valid LinkedIn PDF export file.\n"
logger.warning("Resume generation skipped - no valid LinkedIn data available")
return result
def get_processed_data(linkedin_pdf, github_url, job_post_text, instructions):
"""
Get structured data from all inputs for further processing.
Args:
linkedin_pdf: Uploaded LinkedIn resume export PDF file
github_url (str): GitHub profile URL
job_post_text (str): Job post text content
instructions (str): Additional instructions from the user
Returns:
dict: Structured data containing all processed information
"""
job_post_text = job_post_text.strip() if job_post_text and job_post_text.strip() else None
instructions = instructions.strip() if instructions and instructions.strip() else None
# If no job post text provided, try to get default
if not job_post_text:
default_job = load_default_job_call()
if default_job:
job_post_text = default_job
else:
# No job post provided and no default available
logger.info("No job post provided and no default available")
job_post_text = None
processed_data = {
"linkedin": None,
"github": None,
"job_post": job_post_text,
"user_instructions": instructions,
"errors": []
}
# Process LinkedIn PDF
if linkedin_pdf is not None:
# Handle both file objects and mock file objects with path strings
file_path = linkedin_pdf.name
extraction_result = extract_text_from_linkedin_pdf(file_path)
if extraction_result["status"] == "success":
processed_data["linkedin"] = extraction_result
else:
processed_data["errors"].append(f"LinkedIn: {extraction_result['message']}")
# Process GitHub profile
if github_url and github_url.strip():
github_result = get_github_repositories(github_url)
if github_result["status"] == "success":
processed_data["github"] = github_result
else:
processed_data["errors"].append(f"GitHub: {github_result['message']}")
return processed_data
class MockFile:
"""Mock file object that mimics uploaded file interface with just a file path."""
def __init__(self, path):
self.name = path
|