Skip to content

Commit

Permalink
lint fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Jflick58 committed Feb 3, 2025
1 parent 50db969 commit 7a48bbe
Show file tree
Hide file tree
Showing 14 changed files with 1,236 additions and 113 deletions.
1 change: 0 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,3 @@ jobs:
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
shell: bash
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ lint_diff: PYTHON_FILES=$(shell git diff --name-only --diff-filter=d master | gr

lint lint_diff:
poetry run mypy $(PYTHON_FILES)
poetry run black $(PYTHON_FILES) --check
poetry run ruff format
poetry run ruff .

TEST_FILE ?= tests/
Expand Down
1,122 changes: 1,119 additions & 3 deletions poetry.lock

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,19 @@ pytest = "^8.3.4"
pytest-asyncio = "^0.25.0"
pytest-cov = "^6.0.0"
mypy = "^1.14.0"
poetry-types = "^0.6.0"

[tool.poetry.group.lint.dependencies]
black = "^23.7.0"
mypy = "^1.5.1"
ruff = "^0.0.286"
types-chardet = "^5.0.4.6"
poetry-types = "^0.6.0"


[tool.poetry.group.types.dependencies]
types-beautifulsoup4 = "^4.12.0.20241020"


[build-system]
requires = ["poetry-core"]
Expand Down
1 change: 0 additions & 1 deletion tests/test_db.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import pytest
import sqlite3
import json
from yaart.db import JobDatabase
from yaart.models import JobDescription, JobRequirements

Expand Down
11 changes: 6 additions & 5 deletions tests/test_llm.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import pytest
from unittest.mock import Mock, patch, MagicMock
from yaart.llm import ResumeAssistant
from yaart.models import JobDescription, JobRequirements, TailoredResume
from langchain.schema import HumanMessage, AIMessage
from langchain_core.outputs import LLMResult
from langchain.schema.runnable import RunnableParallel
from yaart.models import JobDescription, JobRequirements
from langchain.output_parsers import PydanticOutputParser
import json

Expand Down Expand Up @@ -139,7 +136,11 @@ def test_parse_jd_invalid_response(mock_llm):

@patch('yaart.llm.PydanticOutputParser')
@patch('yaart.llm.PromptTemplate')
def test_tailor_resume_success(mock_prompt, mock_parser, mock_llm, sample_job_description, sample_tailored_resume_dict):
def test_tailor_resume_success(mock_prompt,
mock_parser,
mock_llm,
sample_job_description,
sample_tailored_resume_dict):
mock_tailored_resume = MagicMock()
mock_tailored_resume.to_markdown.return_value = "# Tailored Resume"
mock_tailored_resume.model_dump.return_value = sample_tailored_resume_dict
Expand Down
1 change: 0 additions & 1 deletion tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import pytest
from yaart.models import (
JobRequirements,
JobDescription,
Expand Down
22 changes: 14 additions & 8 deletions tests/test_resume_optimizer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import pytest
from pathlib import Path
import json
from unittest.mock import Mock, patch, AsyncMock, MagicMock
from unittest.mock import patch, AsyncMock, MagicMock
from yaart.optimizer import ResumeOptimizer
from yaart.models import JobDescription, JobRequirements
from yaart.scraper import JobScraper
Expand Down Expand Up @@ -30,7 +29,7 @@ def mock_optimizer():
with patch('yaart.optimizer.ResumeAssistant') as mock_assistant, \
patch('yaart.optimizer.JobScraper') as mock_scraper, \
patch('yaart.optimizer.JobDatabase') as mock_db, \
patch('yaart.optimizer.md2pdf') as mock_md2pdf:
patch('yaart.optimizer.md2pdf'):

optimizer = ResumeOptimizer(api_key="test-key")
optimizer.assistant = mock_assistant
Expand Down Expand Up @@ -132,7 +131,8 @@ def test_generate_documents_pdf_error(mock_optimizer, tmp_path):
markdown_dir.mkdir()
pdf_dir.mkdir()

with patch('yaart.optimizer.md2pdf', side_effect=Exception("PDF generation failed")):
with patch('yaart.optimizer.md2pdf',
side_effect=Exception("PDF generation failed")):
with pytest.raises(ValueError) as exc_info:
mock_optimizer.generate_documents(
"# Test Resume",
Expand All @@ -143,7 +143,9 @@ def test_generate_documents_pdf_error(mock_optimizer, tmp_path):
assert "Failed to generate PDF" in str(exc_info.value)

@pytest.mark.asyncio
async def test_optimize_resume_tailor_error(mock_optimizer, mock_job_description, tmp_path):
async def test_optimize_resume_tailor_error(mock_optimizer,
mock_job_description,
tmp_path):
base_resume = tmp_path / "base_resume.md"
base_resume.write_text("# Test Resume")

Expand All @@ -167,7 +169,8 @@ async def test_optimize_resume_tailor_error(mock_optimizer, mock_job_description
@pytest.mark.asyncio
async def test_get_job_description_from_scraper(mock_optimizer, mock_job_description):
mock_optimizer.db.get_job_description.return_value = None
mock_optimizer.scraper.scrape_job_description = AsyncMock(return_value=mock_job_description)
mock_optimizer.scraper.scrape_job_description = AsyncMock(
return_value=mock_job_description)

result = await mock_optimizer.get_job_description("https://example.com/job")

Expand All @@ -193,7 +196,9 @@ def test_generate_documents(mock_optimizer, tmp_path):
assert markdown_path.read_text() == "# Test Resume"

@pytest.mark.asyncio
async def test_optimize_resume_with_new_jd(mock_optimizer, mock_job_description, tmp_path):
async def test_optimize_resume_with_new_jd(mock_optimizer,
mock_job_description,
tmp_path):
base_resume = tmp_path / "base_resume.md"
base_resume.write_text("# Test Resume")

Expand All @@ -203,7 +208,8 @@ async def test_optimize_resume_with_new_jd(mock_optimizer, mock_job_description,
(output_dir / "PDF").mkdir()

mock_optimizer.db.get_job_description.side_effect = [None, mock_job_description]
mock_optimizer.scraper.scrape_job_description = AsyncMock(return_value=mock_job_description)
mock_optimizer.scraper.scrape_job_description = AsyncMock(
return_value=mock_job_description)
mock_optimizer.assistant.tailor_resume.return_value = "# Tailored Resume"

result = await mock_optimizer.optimize_resume(
Expand Down
3 changes: 2 additions & 1 deletion tests/test_scraper.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ async def test_scrape_job_description_html_parsing(mock_assistant):
async def test_scrape_job_description_http_error(mock_assistant):
# Setup mock client to raise HTTPError
mock_client = AsyncMock()
mock_client.__aenter__.return_value.get.side_effect = httpx.HTTPError("Failed to fetch")
mock_client.__aenter__.return_value.get.side_effect = httpx.HTTPError(
"Failed to fetch")

# Create scraper instance
scraper = JobScraper(assistant=mock_assistant)
Expand Down
17 changes: 12 additions & 5 deletions yaart/llm.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
from typing import Dict, Optional
from typing import Optional
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain.base_language import BaseLanguageModel
from pydantic import SecretStr
from yaart.models import JobDescription, TailoredResume
from yaart.prompts import PARSE_JD_PROMPT, TAILOR_RESUME_PROMPT
import json

class ResumeAssistant:
def __init__(self, llm: Optional[BaseLanguageModel] = None, api_key: Optional[str] = None):
def __init__(self, llm: Optional[BaseLanguageModel] = None,
api_key: Optional[SecretStr] = None):
if llm:
self.llm = llm
elif api_key:
Expand All @@ -23,7 +25,9 @@ def parse_jd(self, text: str, url: str) -> JobDescription:
prompt = PromptTemplate(
template=PARSE_JD_PROMPT,
input_variables=["text"],
partial_variables={"format_instructions": self.jd_parser.get_format_instructions()}
partial_variables={
"format_instructions": self.jd_parser.get_format_instructions()
}
)

try:
Expand All @@ -38,12 +42,15 @@ def parse_jd(self, text: str, url: str) -> JobDescription:
except Exception as e:
raise ValueError(f"Failed to parse job description: {str(e)}")

def tailor_resume(self, resume_content: str, job_description: JobDescription) -> str:
def tailor_resume(self, resume_content: str,
job_description: JobDescription) -> str:
"""Tailor resume content to match job description"""
prompt = PromptTemplate(
template=TAILOR_RESUME_PROMPT,
input_variables=["resume", "job_description"],
partial_variables={"format_instructions": self.resume_parser.get_format_instructions()}
partial_variables={
"format_instructions": self.resume_parser.get_format_instructions()
}
)

try:
Expand Down
Loading

0 comments on commit 7a48bbe

Please sign in to comment.