Do not use cached skills if content updated
Add candidate/job route to job-analysis
This commit is contained in:
parent
a0cb23df28
commit
4a7a72812f
@ -199,6 +199,9 @@ RUN pip install openapi-python-client
|
||||
# QR code generator
|
||||
RUN pip install pyqrcode pypng
|
||||
|
||||
# Anthropic and other backends
|
||||
RUN pip install anthropic pydantic_ai
|
||||
|
||||
# Automatic type conversion pydantic -> typescript
|
||||
RUN pip install pydantic typing-inspect jinja2
|
||||
RUN apt-get update \
|
||||
|
@ -52,7 +52,7 @@ export const navigationConfig: NavigationConfig = {
|
||||
{
|
||||
id: 'job-analysis',
|
||||
label: 'Job Analysis',
|
||||
path: '/job-analysis',
|
||||
path: '/job-analysis/:candidateId?/:jobId?/:stepId?',
|
||||
variant: 'fullWidth',
|
||||
icon: <WorkIcon />,
|
||||
component: <JobAnalysisPage />,
|
||||
|
@ -29,6 +29,7 @@ import { JobCreator } from 'components/JobCreator';
|
||||
import { LoginRestricted } from 'components/ui/LoginRestricted';
|
||||
import { ResumeGenerator } from 'components/ResumeGenerator';
|
||||
import { JobsView } from 'components/ui/JobsView';
|
||||
import { Navigate, useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
function WorkAddIcon(): JSX.Element {
|
||||
return (
|
||||
@ -78,7 +79,13 @@ const capitalize = (str: string): string => {
|
||||
// Main component
|
||||
const JobAnalysisPage: React.FC<BackstoryPageProps> = () => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const { user, guest, apiClient } = useAuth();
|
||||
const { candidateId, jobId, stepId } = useParams<{
|
||||
candidateId?: string;
|
||||
jobId?: string;
|
||||
stepId?: string;
|
||||
}>();
|
||||
const { selectedCandidate, setSelectedCandidate } = useSelectedCandidate();
|
||||
const { selectedJob, setSelectedJob } = useSelectedJob();
|
||||
|
||||
@ -95,6 +102,61 @@ const JobAnalysisPage: React.FC<BackstoryPageProps> = () => {
|
||||
const [activeStep, setActiveStep] = useState<number>(user === null ? 0 : 1);
|
||||
const maxStep = 4;
|
||||
|
||||
useEffect(() => {
|
||||
if (jobId && candidateId && stepId && activeStep !== parseFloat(stepId)) {
|
||||
setActiveStep(parseFloat(stepId));
|
||||
}
|
||||
}, [jobId, candidateId, activeStep, stepId]);
|
||||
|
||||
useEffect(() => {
|
||||
let routeCandidateId = candidateId || '';
|
||||
let routeJobId = jobId || '';
|
||||
const routeStepId = stepId ? `/${stepId}` : '';
|
||||
|
||||
if (routeCandidateId && routeCandidateId !== selectedCandidate?.id) {
|
||||
apiClient
|
||||
.getCandidate(routeCandidateId)
|
||||
.then((candidate: Candidate | null) => {
|
||||
if (candidate) {
|
||||
setSelectedCandidate(candidate);
|
||||
routeCandidateId = candidate.id || '';
|
||||
} else {
|
||||
setError('Candidate not found.');
|
||||
}
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
console.error('Error fetching candidate:', err);
|
||||
setError('Failed to fetch candidate information.');
|
||||
});
|
||||
} else {
|
||||
routeCandidateId = selectedCandidate?.id || '';
|
||||
}
|
||||
|
||||
if (routeJobId && routeJobId !== selectedJob?.id) {
|
||||
apiClient
|
||||
.getJob(routeJobId)
|
||||
.then((job: Job | null) => {
|
||||
if (job) {
|
||||
setSelectedJob(job);
|
||||
routeJobId = job.id || '';
|
||||
} else {
|
||||
setError('Job not found.');
|
||||
}
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
console.error('Error fetching job:', err);
|
||||
setError('Failed to fetch job information.');
|
||||
});
|
||||
} else {
|
||||
routeJobId = selectedJob?.id || '';
|
||||
}
|
||||
if (routeCandidateId && routeJobId) {
|
||||
navigate(`/job-analysis/${routeCandidateId}/${routeJobId}${routeStepId}`, {
|
||||
replace: true,
|
||||
});
|
||||
}
|
||||
}, [candidateId, jobId, setSelectedCandidate, setSelectedJob]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedCandidate === null && user !== null) {
|
||||
apiClient
|
||||
@ -209,6 +271,12 @@ const JobAnalysisPage: React.FC<BackstoryPageProps> = () => {
|
||||
return;
|
||||
}
|
||||
setActiveStep(step);
|
||||
if (selectedCandidate?.id && selectedJob?.id) {
|
||||
const routeStep = step ? `/${step.toString()}` : '';
|
||||
navigate(`/job-analysis/${selectedCandidate.id}/${selectedJob.id}${routeStep}`, {
|
||||
replace: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onCandidateSelect = (candidate: Candidate): void => {
|
||||
|
@ -1482,6 +1482,24 @@ async def post_job_analysis(
|
||||
continue
|
||||
cache_key = get_skill_cache_key(candidate.id, skill)
|
||||
assessment: SkillAssessment | None = await database.get_cached_skill_match(cache_key)
|
||||
# Determine if we need to regenerate the assessment
|
||||
if assessment:
|
||||
# Get the latest RAG data update time for the current user
|
||||
user_rag_update_time = await database.get_user_rag_update_time(candidate.id)
|
||||
|
||||
updated = assessment.updated_at
|
||||
# Check if cached result is still valid
|
||||
# Regenerate if user's RAG data was updated after cache date
|
||||
if user_rag_update_time and user_rag_update_time >= updated:
|
||||
logger.info(f"🔄 Out-of-date cached entry for {candidate.username} skill {assessment.skill}")
|
||||
assessment = None
|
||||
else:
|
||||
logger.info(
|
||||
f"✅ Using cached skill match for {candidate.username} skill {assessment.skill}: {cache_key}"
|
||||
)
|
||||
else:
|
||||
logger.info(f"💾 No cached skill match data: {cache_key}, {candidate.id}, {skill}")
|
||||
|
||||
if not assessment:
|
||||
logger.info(f"💾 No cached skill match data: {cache_key}, {candidate.id}, {skill}")
|
||||
continue
|
||||
|
Loading…
x
Reference in New Issue
Block a user