Compare commits
No commits in common. "4f4187eba49e83481386a2cadaf3eea0e08e7f36" and "1531a05de00b90351c067f0c378fe2c342b1f5a7" have entirely different histories.
4f4187eba4
...
1531a05de0
@ -1,19 +1,27 @@
|
||||
import React, { useState, useRef, JSX } from 'react';
|
||||
import React, { useState, useEffect, useRef, JSX } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Typography,
|
||||
Paper,
|
||||
TextField,
|
||||
Grid,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogActions,
|
||||
IconButton,
|
||||
useTheme,
|
||||
useMediaQuery,
|
||||
Chip,
|
||||
Divider,
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
LinearProgress,
|
||||
Stack,
|
||||
Paper,
|
||||
Alert
|
||||
} from '@mui/material';
|
||||
import {
|
||||
SyncAlt,
|
||||
@ -28,22 +36,21 @@ import {
|
||||
CloudUpload,
|
||||
Description,
|
||||
Business,
|
||||
LocationOn,
|
||||
Work,
|
||||
CheckCircle,
|
||||
Star
|
||||
} from '@mui/icons-material';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import DescriptionIcon from '@mui/icons-material/Description';
|
||||
import FileUploadIcon from '@mui/icons-material/FileUpload';
|
||||
|
||||
import { useAuth } from 'hooks/AuthContext';
|
||||
import { useAppState, useSelectedJob } from 'hooks/GlobalContext';
|
||||
import { useAppState, useSelectedCandidate, useSelectedJob } from 'hooks/GlobalContext';
|
||||
import { BackstoryElementProps } from './BackstoryTab';
|
||||
import { LoginRequired } from 'components/ui/LoginRequired';
|
||||
|
||||
import * as Types from 'types/types';
|
||||
import { StyledMarkdown } from './StyledMarkdown';
|
||||
import { JobInfo } from './ui/JobInfo';
|
||||
import { Scrollable } from './Scrollable';
|
||||
|
||||
const VisuallyHiddenInput = styled('input')({
|
||||
clip: 'rect(0 0 0 0)',
|
||||
@ -107,27 +114,35 @@ const getIcon = (type: Types.ApiActivityType) => {
|
||||
}
|
||||
};
|
||||
|
||||
interface JobCreatorProps extends BackstoryElementProps {
|
||||
interface JobCreator extends BackstoryElementProps {
|
||||
onSave?: (job: Types.Job) => void;
|
||||
}
|
||||
const JobCreator = (props: JobCreatorProps) => {
|
||||
const JobCreator = (props: JobCreator) => {
|
||||
const { user, apiClient } = useAuth();
|
||||
const { onSave } = props;
|
||||
const { selectedCandidate } = useSelectedCandidate();
|
||||
const { selectedJob, setSelectedJob } = useSelectedJob();
|
||||
const { setSnack } = useAppState();
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
const [openUploadDialog, setOpenUploadDialog] = useState<boolean>(false);
|
||||
const [jobDescription, setJobDescription] = useState<string>('');
|
||||
const [jobRequirements, setJobRequirements] = useState<Types.JobRequirements | null>(null);
|
||||
const [jobTitle, setJobTitle] = useState<string>('');
|
||||
const [company, setCompany] = useState<string>('');
|
||||
const [summary, setSummary] = useState<string>('');
|
||||
const [job, setJob] = useState<Types.Job | null>(null);
|
||||
const [jobLocation, setJobLocation] = useState<string>('');
|
||||
const [jobId, setJobId] = useState<string>('');
|
||||
const [jobStatus, setJobStatus] = useState<string>('');
|
||||
const [jobStatusIcon, setJobStatusIcon] = useState<JSX.Element>(<></>);
|
||||
const [isProcessing, setIsProcessing] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
}, [jobTitle, jobDescription, company]);
|
||||
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
|
||||
@ -143,10 +158,8 @@ const JobCreator = (props: JobCreatorProps) => {
|
||||
setJobStatusIcon(getIcon(status.activity));
|
||||
setJobStatus(status.content);
|
||||
},
|
||||
onMessage: (jobMessage: Types.JobRequirementsMessage) => {
|
||||
const job: Types.Job = jobMessage.job
|
||||
onMessage: (job: Types.Job) => {
|
||||
console.log('onMessage - job', job);
|
||||
setJob(job);
|
||||
setCompany(job.company || '');
|
||||
setJobDescription(job.description);
|
||||
setSummary(job.summary || '');
|
||||
@ -320,9 +333,8 @@ const JobCreator = (props: JobCreatorProps) => {
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
setIsProcessing(true);
|
||||
const jobMessage = await apiClient.createJob(newJob);
|
||||
const job = await apiClient.createJob(newJob);
|
||||
setIsProcessing(false);
|
||||
const job: Types.Job = jobMessage.job;
|
||||
onSave ? onSave(job) : setSelectedJob(job);
|
||||
};
|
||||
|
||||
@ -345,6 +357,10 @@ const JobCreator = (props: JobCreatorProps) => {
|
||||
};
|
||||
|
||||
const renderJobCreation = () => {
|
||||
if (!user) {
|
||||
return <Box>You must be logged in</Box>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
mx: 'auto', p: { xs: 2, sm: 3 },
|
||||
@ -484,6 +500,21 @@ const JobCreator = (props: JobCreatorProps) => {
|
||||
}}
|
||||
/>
|
||||
</Grid> */}
|
||||
|
||||
<Grid size={{ xs: 12, md: 6 }}>
|
||||
<Box sx={{ display: 'flex', gap: 2, alignItems: 'flex-end', height: '100%' }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleSave}
|
||||
disabled={!jobTitle || !company || !jobDescription || isProcessing}
|
||||
fullWidth={isMobile}
|
||||
size="large"
|
||||
startIcon={<CheckCircle />}
|
||||
>
|
||||
Save Job
|
||||
</Button>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@ -517,27 +548,7 @@ const JobCreator = (props: JobCreatorProps) => {
|
||||
width: "100%",
|
||||
display: "flex", flexDirection: "column"
|
||||
}}>
|
||||
{job === null && renderJobCreation()}
|
||||
{job &&
|
||||
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
||||
<Box sx={{ display: 'flex', gap: 2, alignItems: 'flex-end', height: '100%' }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handleSave}
|
||||
disabled={!jobTitle || !company || !jobDescription || isProcessing}
|
||||
fullWidth={isMobile}
|
||||
size="large"
|
||||
startIcon={<CheckCircle />}
|
||||
>
|
||||
Save Job
|
||||
</Button>
|
||||
</Box>
|
||||
<Box sx={{ display: "flex", flexDirection: "row", flexShrink: 1, gap: 1 }}>
|
||||
<Paper elevation={1} sx={{ p: 1, m: 1, flexGrow: 1 }}><Scrollable><JobInfo job={job} /></Scrollable></Paper>
|
||||
<Paper elevation={1} sx={{ p: 1, m: 1, flexGrow: 1 }}><Scrollable><StyledMarkdown content={job.description} /></Scrollable></Paper>
|
||||
</Box>
|
||||
</Box>
|
||||
}
|
||||
{selectedJob === null && renderJobCreation()}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
} from '@mui/material';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
import { useMediaQuery } from '@mui/material';
|
||||
import { Job, JobFull } from 'types/types';
|
||||
import { JobFull } from 'types/types';
|
||||
import { CopyBubble } from "components/CopyBubble";
|
||||
import { rest } from 'lodash';
|
||||
import { AIBanner } from 'components/ui/AIBanner';
|
||||
@ -17,7 +17,7 @@ import { DeleteConfirmation } from '../DeleteConfirmation';
|
||||
import { Build, CheckCircle, Description, Psychology, Star, Work } from '@mui/icons-material';
|
||||
|
||||
interface JobInfoProps {
|
||||
job: Job | JobFull;
|
||||
job: JobFull;
|
||||
sx?: SxProps;
|
||||
action?: string;
|
||||
elevation?: number;
|
||||
@ -153,7 +153,7 @@ const JobInfo: React.FC<JobInfoProps> = (props: JobInfoProps) => {
|
||||
>
|
||||
<CardContent sx={{ display: "flex", flexGrow: 1, p: 3, height: '100%', flexDirection: 'column', alignItems: 'stretch', position: "relative" }}>
|
||||
{variant !== "small" && <>
|
||||
{'location' in job &&
|
||||
{job.location &&
|
||||
<Typography variant="body2" sx={{ mb: 1 }}>
|
||||
<strong>Location:</strong> {job.location.city}, {job.location.state || job.location.country}
|
||||
</Typography>
|
||||
|
@ -83,7 +83,7 @@ const LoginPage: React.FC<BackstoryPageProps> = (props: BackstoryPageProps) => {
|
||||
Session ID: {guest.sessionId}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Created: {guest.createdAt?.toLocaleString()}
|
||||
Created: {guest.createdAt.toLocaleString()}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
@ -39,7 +39,6 @@ const TOKEN_STORAGE = {
|
||||
PENDING_VERIFICATION_EMAIL: 'pendingVerificationEmail'
|
||||
} as const;
|
||||
|
||||
|
||||
// ============================
|
||||
// Streaming Types and Interfaces
|
||||
// ============================
|
||||
@ -648,12 +647,12 @@ class ApiClient {
|
||||
// Job Methods with Date Conversion
|
||||
// ============================
|
||||
|
||||
createJobFromDescription(job_description: string, streamingOptions?: StreamingOptions<Types.JobRequirementsMessage>): StreamingResponse<Types.JobRequirementsMessage> {
|
||||
createJobFromDescription(job_description: string, streamingOptions?: StreamingOptions<Types.Job>): StreamingResponse<Types.Job> {
|
||||
const body = JSON.stringify(job_description);
|
||||
return this.streamify<Types.JobRequirementsMessage>('/jobs/from-content', body, streamingOptions, "JobRequirementsMessage");
|
||||
return this.streamify<Types.Job>('/jobs/from-content', body, streamingOptions);
|
||||
}
|
||||
|
||||
async createJob(job: Omit<Types.Job, 'id' | 'datePosted' | 'views' | 'applicationCount'>): Promise<Types.JobRequirementsMessage> {
|
||||
async createJob(job: Omit<Types.Job, 'id' | 'datePosted' | 'views' | 'applicationCount'>): Promise<Types.Job> {
|
||||
const body = JSON.stringify(formatApiRequest(job));
|
||||
const response = await fetch(`${this.baseUrl}/jobs`, {
|
||||
method: 'POST',
|
||||
@ -661,7 +660,7 @@ class ApiClient {
|
||||
body: body
|
||||
});
|
||||
|
||||
return this.handleApiResponseWithConversion<Types.JobRequirementsMessage>(response, 'JobRequirementsMessage');
|
||||
return this.handleApiResponseWithConversion<Types.Job>(response, 'Job');
|
||||
}
|
||||
|
||||
async getJob(id: string): Promise<Types.Job> {
|
||||
@ -885,10 +884,10 @@ class ApiClient {
|
||||
'Authorization': this.defaultHeaders['Authorization']
|
||||
}
|
||||
};
|
||||
return this.streamify<Types.DocumentMessage>('/candidates/documents/upload', formData, streamingOptions, "DocumentMessage");
|
||||
return this.streamify<Types.DocumentMessage>('/candidates/documents/upload', formData, streamingOptions);
|
||||
}
|
||||
|
||||
createJobFromFile(file: File, streamingOptions?: StreamingOptions<Types.JobRequirementsMessage>): StreamingResponse<Types.JobRequirementsMessage> {
|
||||
createJobFromFile(file: File, streamingOptions?: StreamingOptions<Types.Job>): StreamingResponse<Types.Job> {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file);
|
||||
formData.append('filename', file.name);
|
||||
@ -899,7 +898,7 @@ class ApiClient {
|
||||
'Authorization': this.defaultHeaders['Authorization']
|
||||
}
|
||||
};
|
||||
return this.streamify<Types.JobRequirementsMessage>('/jobs/upload', formData, streamingOptions, "JobRequirementsMessage");
|
||||
return this.streamify<Types.Job>('/jobs/upload', formData, streamingOptions);
|
||||
}
|
||||
|
||||
getJobRequirements(jobId: string, streamingOptions?: StreamingOptions<Types.DocumentMessage>): StreamingResponse<Types.DocumentMessage> {
|
||||
@ -907,7 +906,7 @@ class ApiClient {
|
||||
...streamingOptions,
|
||||
headers: this.defaultHeaders,
|
||||
};
|
||||
return this.streamify<Types.DocumentMessage>(`/jobs/requirements/${jobId}`, null, streamingOptions, "DocumentMessage");
|
||||
return this.streamify<Types.DocumentMessage>(`/jobs/requirements/${jobId}`, null, streamingOptions);
|
||||
}
|
||||
|
||||
generateResume(candidateId: string, skills: Types.SkillAssessment[], streamingOptions?: StreamingOptions<Types.ChatMessageResume>): StreamingResponse<Types.ChatMessageResume> {
|
||||
@ -916,7 +915,7 @@ class ApiClient {
|
||||
...streamingOptions,
|
||||
headers: this.defaultHeaders,
|
||||
};
|
||||
return this.streamify<Types.ChatMessageResume>(`/candidates/${candidateId}/generate-resume`, body, streamingOptions, "ChatMessageResume");
|
||||
return this.streamify<Types.ChatMessageResume>(`/candidates/${candidateId}/generate-resume`, body, streamingOptions);
|
||||
}
|
||||
candidateMatchForRequirement(candidate_id: string, requirement: string,
|
||||
streamingOptions?: StreamingOptions<Types.ChatMessageSkillAssessment>)
|
||||
@ -926,7 +925,7 @@ class ApiClient {
|
||||
...streamingOptions,
|
||||
headers: this.defaultHeaders,
|
||||
};
|
||||
return this.streamify<Types.ChatMessageSkillAssessment>(`/candidates/${candidate_id}/skill-match`, body, streamingOptions, "ChatMessageSkillAssessment");
|
||||
return this.streamify<Types.ChatMessageSkillAssessment>(`/candidates/${candidate_id}/skill-match`, body, streamingOptions);
|
||||
}
|
||||
|
||||
async updateCandidateDocument(document: Types.Document) : Promise<Types.Document> {
|
||||
@ -1227,7 +1226,7 @@ class ApiClient {
|
||||
* @param options callbacks, headers, and method
|
||||
* @returns
|
||||
*/
|
||||
streamify<T = Types.ChatMessage[]>(api: string, data: BodyInit | null, options: StreamingOptions<T> = {}, modelType?: string) : StreamingResponse<T> {
|
||||
streamify<T = Types.ChatMessage[]>(api: string, data: BodyInit | null, options: StreamingOptions<T> = {}) : StreamingResponse<T> {
|
||||
const abortController = new AbortController();
|
||||
const signal = options.signal || abortController.signal;
|
||||
const headers = options.headers || null;
|
||||
@ -1309,8 +1308,8 @@ class ApiClient {
|
||||
break;
|
||||
|
||||
case 'done':
|
||||
const message = (modelType ? convertFromApi<T>(incoming, modelType) : incoming) as T;
|
||||
finalMessage = message;
|
||||
const message = Types.convertApiMessageFromApi(incoming) as T;
|
||||
finalMessage = message as any;
|
||||
try {
|
||||
options.onMessage?.(message);
|
||||
} catch (error) {
|
||||
@ -1362,7 +1361,7 @@ class ApiClient {
|
||||
options: StreamingOptions = {}
|
||||
): StreamingResponse {
|
||||
const body = JSON.stringify(formatApiRequest(chatMessage));
|
||||
return this.streamify(`/chat/sessions/${chatMessage.sessionId}/messages/stream`, body, options, "ChatMessage")
|
||||
return this.streamify(`/chat/sessions/${chatMessage.sessionId}/messages/stream`, body, options)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1471,6 +1470,7 @@ class ApiClient {
|
||||
// ============================
|
||||
// Error Handling Helper
|
||||
// ============================
|
||||
|
||||
async handleRequest<T>(requestFn: () => Promise<Response>, modelType?: string): Promise<T> {
|
||||
try {
|
||||
const response = await requestFn();
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Generated TypeScript types from Pydantic models
|
||||
// Source: src/backend/models.py
|
||||
// Generated on: 2025-06-10T02:48:12.087485
|
||||
// Generated on: 2025-06-09T20:36:06.432367
|
||||
// DO NOT EDIT MANUALLY - This file is auto-generated
|
||||
|
||||
// ============================
|
||||
@ -161,8 +161,8 @@ export interface BaseUser {
|
||||
fullName: string;
|
||||
phone?: string;
|
||||
location?: Location;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
lastLogin?: Date;
|
||||
profileImage?: string;
|
||||
status: "active" | "inactive" | "pending" | "banned";
|
||||
@ -185,8 +185,8 @@ export interface Candidate {
|
||||
fullName: string;
|
||||
phone?: string;
|
||||
location?: Location;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
lastLogin?: Date;
|
||||
profileImage?: string;
|
||||
status: "active" | "inactive" | "pending" | "banned";
|
||||
@ -219,8 +219,8 @@ export interface CandidateAI {
|
||||
fullName: string;
|
||||
phone?: string;
|
||||
location?: Location;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
lastLogin?: Date;
|
||||
profileImage?: string;
|
||||
status: "active" | "inactive" | "pending" | "banned";
|
||||
@ -353,8 +353,6 @@ export interface ChatMessageResume {
|
||||
tunables?: Tunables;
|
||||
metadata: ChatMessageMetaData;
|
||||
resume: string;
|
||||
systemPrompt?: string;
|
||||
prompt?: string;
|
||||
}
|
||||
|
||||
export interface ChatMessageSkillAssessment {
|
||||
@ -569,8 +567,8 @@ export interface Employer {
|
||||
fullName: string;
|
||||
phone?: string;
|
||||
location?: Location;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
lastLogin?: Date;
|
||||
profileImage?: string;
|
||||
status: "active" | "inactive" | "pending" | "banned";
|
||||
@ -622,8 +620,8 @@ export interface Guest {
|
||||
fullName: string;
|
||||
phone?: string;
|
||||
location?: Location;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
lastLogin?: Date;
|
||||
profileImage?: string;
|
||||
status: "active" | "inactive" | "pending" | "banned";
|
||||
@ -684,8 +682,8 @@ export interface InterviewFeedback {
|
||||
weaknesses: Array<string>;
|
||||
recommendation: "strong_hire" | "hire" | "no_hire" | "strong_no_hire";
|
||||
comments: string;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
isVisible: boolean;
|
||||
skillAssessments?: Array<SkillAssessment>;
|
||||
}
|
||||
@ -714,8 +712,8 @@ export interface Job {
|
||||
company?: string;
|
||||
description: string;
|
||||
requirements?: JobRequirements;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export interface JobApplication {
|
||||
@ -744,12 +742,12 @@ export interface JobFull {
|
||||
company?: string;
|
||||
description: string;
|
||||
requirements?: JobRequirements;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
location: Location;
|
||||
salaryRange?: SalaryRange;
|
||||
employmentType: "full-time" | "part-time" | "contract" | "internship" | "freelance";
|
||||
datePosted?: Date;
|
||||
datePosted: Date;
|
||||
applicationDeadline?: Date;
|
||||
isActive: boolean;
|
||||
applicants?: Array<JobApplication>;
|
||||
@ -786,7 +784,11 @@ export interface JobRequirementsMessage {
|
||||
status: "streaming" | "status" | "done" | "error";
|
||||
type: "binary" | "text" | "json";
|
||||
timestamp?: Date;
|
||||
job: Job;
|
||||
title?: string;
|
||||
summary?: string;
|
||||
company?: string;
|
||||
description: string;
|
||||
requirements?: JobRequirements;
|
||||
}
|
||||
|
||||
export interface JobResponse {
|
||||
@ -899,8 +901,8 @@ export interface RAGConfiguration {
|
||||
embeddingModel: string;
|
||||
vectorStoreType: "chroma";
|
||||
retrievalParameters: RetrievalParameters;
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
version: number;
|
||||
isActive: boolean;
|
||||
}
|
||||
@ -1076,14 +1078,14 @@ export interface WorkExperience {
|
||||
}
|
||||
|
||||
// ============================
|
||||
// Date and Nested Model Conversion Functions
|
||||
// Date Conversion Functions
|
||||
// ============================
|
||||
|
||||
// These functions convert API responses to properly typed objects
|
||||
// with Date objects instead of ISO date strings and nested model conversions
|
||||
// with Date objects instead of ISO date strings
|
||||
|
||||
/**
|
||||
* Convert Analytics from API response
|
||||
* Convert Analytics from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
*/
|
||||
export function convertAnalyticsFromApi(data: any): Analytics {
|
||||
@ -1096,7 +1098,7 @@ export function convertAnalyticsFromApi(data: any): Analytics {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert ApiMessage from API response
|
||||
* Convert ApiMessage from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
*/
|
||||
export function convertApiMessageFromApi(data: any): ApiMessage {
|
||||
@ -1109,7 +1111,7 @@ export function convertApiMessageFromApi(data: any): ApiMessage {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert ApplicationDecision from API response
|
||||
* Convert ApplicationDecision from API response, parsing date fields
|
||||
* Date fields: date
|
||||
*/
|
||||
export function convertApplicationDecisionFromApi(data: any): ApplicationDecision {
|
||||
@ -1122,7 +1124,7 @@ export function convertApplicationDecisionFromApi(data: any): ApplicationDecisio
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert Attachment from API response
|
||||
* Convert Attachment from API response, parsing date fields
|
||||
* Date fields: uploadedAt
|
||||
*/
|
||||
export function convertAttachmentFromApi(data: any): Attachment {
|
||||
@ -1135,22 +1137,8 @@ export function convertAttachmentFromApi(data: any): Attachment {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert AuthResponse from API response
|
||||
* Nested models: user (Candidate)
|
||||
*/
|
||||
export function convertAuthResponseFromApi(data: any): AuthResponse {
|
||||
if (!data) return data;
|
||||
|
||||
return {
|
||||
...data,
|
||||
// Convert nested Candidate model
|
||||
user: convertCandidateFromApi(data.user),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert Authentication from API response
|
||||
* Convert Authentication from API response, parsing date fields
|
||||
* Date fields: resetPasswordExpiry, lastPasswordChange, lockedUntil
|
||||
* Nested models: refreshTokens (RefreshToken)
|
||||
*/
|
||||
export function convertAuthenticationFromApi(data: any): Authentication {
|
||||
if (!data) return data;
|
||||
@ -1163,12 +1151,10 @@ export function convertAuthenticationFromApi(data: any): Authentication {
|
||||
lastPasswordChange: new Date(data.lastPasswordChange),
|
||||
// Convert lockedUntil from ISO string to Date
|
||||
lockedUntil: data.lockedUntil ? new Date(data.lockedUntil) : undefined,
|
||||
// Convert nested RefreshToken model
|
||||
refreshTokens: data.refreshTokens.map((item: any) => convertRefreshTokenFromApi(item)),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert BaseUser from API response
|
||||
* Convert BaseUser from API response, parsing date fields
|
||||
* Date fields: lastActivity, createdAt, updatedAt, lastLogin
|
||||
*/
|
||||
export function convertBaseUserFromApi(data: any): BaseUser {
|
||||
@ -1179,15 +1165,15 @@ export function convertBaseUserFromApi(data: any): BaseUser {
|
||||
// Convert lastActivity from ISO string to Date
|
||||
lastActivity: data.lastActivity ? new Date(data.lastActivity) : undefined,
|
||||
// Convert createdAt from ISO string to Date
|
||||
createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
|
||||
createdAt: new Date(data.createdAt),
|
||||
// Convert updatedAt from ISO string to Date
|
||||
updatedAt: data.updatedAt ? new Date(data.updatedAt) : undefined,
|
||||
updatedAt: new Date(data.updatedAt),
|
||||
// Convert lastLogin from ISO string to Date
|
||||
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert BaseUserWithType from API response
|
||||
* Convert BaseUserWithType from API response, parsing date fields
|
||||
* Date fields: lastActivity
|
||||
*/
|
||||
export function convertBaseUserWithTypeFromApi(data: any): BaseUserWithType {
|
||||
@ -1200,9 +1186,8 @@ export function convertBaseUserWithTypeFromApi(data: any): BaseUserWithType {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert Candidate from API response
|
||||
* Convert Candidate from API response, parsing date fields
|
||||
* Date fields: lastActivity, createdAt, updatedAt, lastLogin, availabilityDate
|
||||
* Nested models: experience (WorkExperience), education (Education), certifications (Certification), jobApplications (JobApplication)
|
||||
*/
|
||||
export function convertCandidateFromApi(data: any): Candidate {
|
||||
if (!data) return data;
|
||||
@ -1212,27 +1197,18 @@ export function convertCandidateFromApi(data: any): Candidate {
|
||||
// Convert lastActivity from ISO string to Date
|
||||
lastActivity: data.lastActivity ? new Date(data.lastActivity) : undefined,
|
||||
// Convert createdAt from ISO string to Date
|
||||
createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
|
||||
createdAt: new Date(data.createdAt),
|
||||
// Convert updatedAt from ISO string to Date
|
||||
updatedAt: data.updatedAt ? new Date(data.updatedAt) : undefined,
|
||||
updatedAt: new Date(data.updatedAt),
|
||||
// Convert lastLogin from ISO string to Date
|
||||
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
||||
// Convert availabilityDate from ISO string to Date
|
||||
availabilityDate: data.availabilityDate ? new Date(data.availabilityDate) : undefined,
|
||||
// Convert nested WorkExperience model
|
||||
experience: data.experience ? convertWorkExperienceFromApi(data.experience) : undefined,
|
||||
// Convert nested Education model
|
||||
education: data.education ? convertEducationFromApi(data.education) : undefined,
|
||||
// Convert nested Certification model
|
||||
certifications: data.certifications ? convertCertificationFromApi(data.certifications) : undefined,
|
||||
// Convert nested JobApplication model
|
||||
jobApplications: data.jobApplications ? convertJobApplicationFromApi(data.jobApplications) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert CandidateAI from API response
|
||||
* Convert CandidateAI from API response, parsing date fields
|
||||
* Date fields: lastActivity, createdAt, updatedAt, lastLogin, availabilityDate
|
||||
* Nested models: experience (WorkExperience), education (Education), certifications (Certification)
|
||||
*/
|
||||
export function convertCandidateAIFromApi(data: any): CandidateAI {
|
||||
if (!data) return data;
|
||||
@ -1242,49 +1218,17 @@ export function convertCandidateAIFromApi(data: any): CandidateAI {
|
||||
// Convert lastActivity from ISO string to Date
|
||||
lastActivity: data.lastActivity ? new Date(data.lastActivity) : undefined,
|
||||
// Convert createdAt from ISO string to Date
|
||||
createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
|
||||
createdAt: new Date(data.createdAt),
|
||||
// Convert updatedAt from ISO string to Date
|
||||
updatedAt: data.updatedAt ? new Date(data.updatedAt) : undefined,
|
||||
updatedAt: new Date(data.updatedAt),
|
||||
// Convert lastLogin from ISO string to Date
|
||||
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
||||
// Convert availabilityDate from ISO string to Date
|
||||
availabilityDate: data.availabilityDate ? new Date(data.availabilityDate) : undefined,
|
||||
// Convert nested WorkExperience model
|
||||
experience: data.experience ? convertWorkExperienceFromApi(data.experience) : undefined,
|
||||
// Convert nested Education model
|
||||
education: data.education ? convertEducationFromApi(data.education) : undefined,
|
||||
// Convert nested Certification model
|
||||
certifications: data.certifications ? convertCertificationFromApi(data.certifications) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert CandidateListResponse from API response
|
||||
* Nested models: data (Candidate)
|
||||
*/
|
||||
export function convertCandidateListResponseFromApi(data: any): CandidateListResponse {
|
||||
if (!data) return data;
|
||||
|
||||
return {
|
||||
...data,
|
||||
// Convert nested Candidate model
|
||||
data: data.data ? convertCandidateFromApi(data.data) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert CandidateResponse from API response
|
||||
* Nested models: data (Candidate)
|
||||
*/
|
||||
export function convertCandidateResponseFromApi(data: any): CandidateResponse {
|
||||
if (!data) return data;
|
||||
|
||||
return {
|
||||
...data,
|
||||
// Convert nested Candidate model
|
||||
data: data.data ? convertCandidateFromApi(data.data) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert Certification from API response
|
||||
* Convert Certification from API response, parsing date fields
|
||||
* Date fields: issueDate, expirationDate
|
||||
*/
|
||||
export function convertCertificationFromApi(data: any): Certification {
|
||||
@ -1299,7 +1243,7 @@ export function convertCertificationFromApi(data: any): Certification {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert ChatMessage from API response
|
||||
* Convert ChatMessage from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
*/
|
||||
export function convertChatMessageFromApi(data: any): ChatMessage {
|
||||
@ -1312,7 +1256,7 @@ export function convertChatMessageFromApi(data: any): ChatMessage {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert ChatMessageError from API response
|
||||
* Convert ChatMessageError from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
*/
|
||||
export function convertChatMessageErrorFromApi(data: any): ChatMessageError {
|
||||
@ -1325,7 +1269,7 @@ export function convertChatMessageErrorFromApi(data: any): ChatMessageError {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert ChatMessageRagSearch from API response
|
||||
* Convert ChatMessageRagSearch from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
*/
|
||||
export function convertChatMessageRagSearchFromApi(data: any): ChatMessageRagSearch {
|
||||
@ -1338,7 +1282,7 @@ export function convertChatMessageRagSearchFromApi(data: any): ChatMessageRagSea
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert ChatMessageResume from API response
|
||||
* Convert ChatMessageResume from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
*/
|
||||
export function convertChatMessageResumeFromApi(data: any): ChatMessageResume {
|
||||
@ -1351,9 +1295,8 @@ export function convertChatMessageResumeFromApi(data: any): ChatMessageResume {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert ChatMessageSkillAssessment from API response
|
||||
* Convert ChatMessageSkillAssessment from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
* Nested models: skillAssessment (SkillAssessment)
|
||||
*/
|
||||
export function convertChatMessageSkillAssessmentFromApi(data: any): ChatMessageSkillAssessment {
|
||||
if (!data) return data;
|
||||
@ -1362,12 +1305,10 @@ export function convertChatMessageSkillAssessmentFromApi(data: any): ChatMessage
|
||||
...data,
|
||||
// Convert timestamp from ISO string to Date
|
||||
timestamp: data.timestamp ? new Date(data.timestamp) : undefined,
|
||||
// Convert nested SkillAssessment model
|
||||
skillAssessment: convertSkillAssessmentFromApi(data.skillAssessment),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert ChatMessageStatus from API response
|
||||
* Convert ChatMessageStatus from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
*/
|
||||
export function convertChatMessageStatusFromApi(data: any): ChatMessageStatus {
|
||||
@ -1380,7 +1321,7 @@ export function convertChatMessageStatusFromApi(data: any): ChatMessageStatus {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert ChatMessageStreaming from API response
|
||||
* Convert ChatMessageStreaming from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
*/
|
||||
export function convertChatMessageStreamingFromApi(data: any): ChatMessageStreaming {
|
||||
@ -1393,7 +1334,7 @@ export function convertChatMessageStreamingFromApi(data: any): ChatMessageStream
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert ChatMessageUser from API response
|
||||
* Convert ChatMessageUser from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
*/
|
||||
export function convertChatMessageUserFromApi(data: any): ChatMessageUser {
|
||||
@ -1406,7 +1347,7 @@ export function convertChatMessageUserFromApi(data: any): ChatMessageUser {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert ChatSession from API response
|
||||
* Convert ChatSession from API response, parsing date fields
|
||||
* Date fields: createdAt, lastActivity
|
||||
*/
|
||||
export function convertChatSessionFromApi(data: any): ChatSession {
|
||||
@ -1421,7 +1362,7 @@ export function convertChatSessionFromApi(data: any): ChatSession {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert DataSourceConfiguration from API response
|
||||
* Convert DataSourceConfiguration from API response, parsing date fields
|
||||
* Date fields: lastRefreshed
|
||||
*/
|
||||
export function convertDataSourceConfigurationFromApi(data: any): DataSourceConfiguration {
|
||||
@ -1434,7 +1375,7 @@ export function convertDataSourceConfigurationFromApi(data: any): DataSourceConf
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert Document from API response
|
||||
* Convert Document from API response, parsing date fields
|
||||
* Date fields: uploadDate
|
||||
*/
|
||||
export function convertDocumentFromApi(data: any): Document {
|
||||
@ -1447,22 +1388,8 @@ export function convertDocumentFromApi(data: any): Document {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert DocumentListResponse from API response
|
||||
* Nested models: documents (Document)
|
||||
*/
|
||||
export function convertDocumentListResponseFromApi(data: any): DocumentListResponse {
|
||||
if (!data) return data;
|
||||
|
||||
return {
|
||||
...data,
|
||||
// Convert nested Document model
|
||||
documents: data.documents.map((item: any) => convertDocumentFromApi(item)),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert DocumentMessage from API response
|
||||
* Convert DocumentMessage from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
* Nested models: document (Document)
|
||||
*/
|
||||
export function convertDocumentMessageFromApi(data: any): DocumentMessage {
|
||||
if (!data) return data;
|
||||
@ -1471,12 +1398,10 @@ export function convertDocumentMessageFromApi(data: any): DocumentMessage {
|
||||
...data,
|
||||
// Convert timestamp from ISO string to Date
|
||||
timestamp: data.timestamp ? new Date(data.timestamp) : undefined,
|
||||
// Convert nested Document model
|
||||
document: convertDocumentFromApi(data.document),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert EditHistory from API response
|
||||
* Convert EditHistory from API response, parsing date fields
|
||||
* Date fields: editedAt
|
||||
*/
|
||||
export function convertEditHistoryFromApi(data: any): EditHistory {
|
||||
@ -1489,7 +1414,7 @@ export function convertEditHistoryFromApi(data: any): EditHistory {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert Education from API response
|
||||
* Convert Education from API response, parsing date fields
|
||||
* Date fields: startDate, endDate
|
||||
*/
|
||||
export function convertEducationFromApi(data: any): Education {
|
||||
@ -1504,9 +1429,8 @@ export function convertEducationFromApi(data: any): Education {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert Employer from API response
|
||||
* Convert Employer from API response, parsing date fields
|
||||
* Date fields: lastActivity, createdAt, updatedAt, lastLogin
|
||||
* Nested models: jobs (Job)
|
||||
*/
|
||||
export function convertEmployerFromApi(data: any): Employer {
|
||||
if (!data) return data;
|
||||
@ -1516,30 +1440,15 @@ export function convertEmployerFromApi(data: any): Employer {
|
||||
// Convert lastActivity from ISO string to Date
|
||||
lastActivity: data.lastActivity ? new Date(data.lastActivity) : undefined,
|
||||
// Convert createdAt from ISO string to Date
|
||||
createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
|
||||
createdAt: new Date(data.createdAt),
|
||||
// Convert updatedAt from ISO string to Date
|
||||
updatedAt: data.updatedAt ? new Date(data.updatedAt) : undefined,
|
||||
updatedAt: new Date(data.updatedAt),
|
||||
// Convert lastLogin from ISO string to Date
|
||||
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
||||
// Convert nested Job model
|
||||
jobs: data.jobs ? convertJobFromApi(data.jobs) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert EmployerResponse from API response
|
||||
* Nested models: data (Employer)
|
||||
*/
|
||||
export function convertEmployerResponseFromApi(data: any): EmployerResponse {
|
||||
if (!data) return data;
|
||||
|
||||
return {
|
||||
...data,
|
||||
// Convert nested Employer model
|
||||
data: data.data ? convertEmployerFromApi(data.data) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert Guest from API response
|
||||
* Convert Guest from API response, parsing date fields
|
||||
* Date fields: lastActivity, createdAt, updatedAt, lastLogin
|
||||
*/
|
||||
export function convertGuestFromApi(data: any): Guest {
|
||||
@ -1550,30 +1459,16 @@ export function convertGuestFromApi(data: any): Guest {
|
||||
// Convert lastActivity from ISO string to Date
|
||||
lastActivity: data.lastActivity ? new Date(data.lastActivity) : undefined,
|
||||
// Convert createdAt from ISO string to Date
|
||||
createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
|
||||
createdAt: new Date(data.createdAt),
|
||||
// Convert updatedAt from ISO string to Date
|
||||
updatedAt: data.updatedAt ? new Date(data.updatedAt) : undefined,
|
||||
updatedAt: new Date(data.updatedAt),
|
||||
// Convert lastLogin from ISO string to Date
|
||||
lastLogin: data.lastLogin ? new Date(data.lastLogin) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert GuestSessionResponse from API response
|
||||
* Nested models: user (Guest)
|
||||
*/
|
||||
export function convertGuestSessionResponseFromApi(data: any): GuestSessionResponse {
|
||||
if (!data) return data;
|
||||
|
||||
return {
|
||||
...data,
|
||||
// Convert nested Guest model
|
||||
user: convertGuestFromApi(data.user),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert InterviewFeedback from API response
|
||||
* Convert InterviewFeedback from API response, parsing date fields
|
||||
* Date fields: createdAt, updatedAt
|
||||
* Nested models: skillAssessments (SkillAssessment)
|
||||
*/
|
||||
export function convertInterviewFeedbackFromApi(data: any): InterviewFeedback {
|
||||
if (!data) return data;
|
||||
@ -1581,17 +1476,14 @@ export function convertInterviewFeedbackFromApi(data: any): InterviewFeedback {
|
||||
return {
|
||||
...data,
|
||||
// Convert createdAt from ISO string to Date
|
||||
createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
|
||||
createdAt: new Date(data.createdAt),
|
||||
// Convert updatedAt from ISO string to Date
|
||||
updatedAt: data.updatedAt ? new Date(data.updatedAt) : undefined,
|
||||
// Convert nested SkillAssessment model
|
||||
skillAssessments: data.skillAssessments ? convertSkillAssessmentFromApi(data.skillAssessments) : undefined,
|
||||
updatedAt: new Date(data.updatedAt),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert InterviewSchedule from API response
|
||||
* Convert InterviewSchedule from API response, parsing date fields
|
||||
* Date fields: scheduledDate, endDate
|
||||
* Nested models: feedback (InterviewFeedback)
|
||||
*/
|
||||
export function convertInterviewScheduleFromApi(data: any): InterviewSchedule {
|
||||
if (!data) return data;
|
||||
@ -1602,14 +1494,11 @@ export function convertInterviewScheduleFromApi(data: any): InterviewSchedule {
|
||||
scheduledDate: new Date(data.scheduledDate),
|
||||
// Convert endDate from ISO string to Date
|
||||
endDate: new Date(data.endDate),
|
||||
// Convert nested InterviewFeedback model
|
||||
feedback: data.feedback ? convertInterviewFeedbackFromApi(data.feedback) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert Job from API response
|
||||
* Convert Job from API response, parsing date fields
|
||||
* Date fields: createdAt, updatedAt
|
||||
* Nested models: owner (BaseUser)
|
||||
*/
|
||||
export function convertJobFromApi(data: any): Job {
|
||||
if (!data) return data;
|
||||
@ -1617,17 +1506,14 @@ export function convertJobFromApi(data: any): Job {
|
||||
return {
|
||||
...data,
|
||||
// Convert createdAt from ISO string to Date
|
||||
createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
|
||||
createdAt: new Date(data.createdAt),
|
||||
// Convert updatedAt from ISO string to Date
|
||||
updatedAt: data.updatedAt ? new Date(data.updatedAt) : undefined,
|
||||
// Convert nested BaseUser model
|
||||
owner: data.owner ? convertBaseUserFromApi(data.owner) : undefined,
|
||||
updatedAt: new Date(data.updatedAt),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert JobApplication from API response
|
||||
* Convert JobApplication from API response, parsing date fields
|
||||
* Date fields: appliedDate, updatedDate
|
||||
* Nested models: interviewSchedules (InterviewSchedule), decision (ApplicationDecision)
|
||||
*/
|
||||
export function convertJobApplicationFromApi(data: any): JobApplication {
|
||||
if (!data) return data;
|
||||
@ -1638,16 +1524,11 @@ export function convertJobApplicationFromApi(data: any): JobApplication {
|
||||
appliedDate: new Date(data.appliedDate),
|
||||
// Convert updatedDate from ISO string to Date
|
||||
updatedDate: new Date(data.updatedDate),
|
||||
// Convert nested InterviewSchedule model
|
||||
interviewSchedules: data.interviewSchedules ? convertInterviewScheduleFromApi(data.interviewSchedules) : undefined,
|
||||
// Convert nested ApplicationDecision model
|
||||
decision: data.decision ? convertApplicationDecisionFromApi(data.decision) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert JobFull from API response
|
||||
* Convert JobFull from API response, parsing date fields
|
||||
* Date fields: createdAt, updatedAt, datePosted, applicationDeadline, featuredUntil
|
||||
* Nested models: owner (BaseUser), applicants (JobApplication)
|
||||
*/
|
||||
export function convertJobFullFromApi(data: any): JobFull {
|
||||
if (!data) return data;
|
||||
@ -1655,38 +1536,20 @@ export function convertJobFullFromApi(data: any): JobFull {
|
||||
return {
|
||||
...data,
|
||||
// Convert createdAt from ISO string to Date
|
||||
createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
|
||||
createdAt: new Date(data.createdAt),
|
||||
// Convert updatedAt from ISO string to Date
|
||||
updatedAt: data.updatedAt ? new Date(data.updatedAt) : undefined,
|
||||
updatedAt: new Date(data.updatedAt),
|
||||
// Convert datePosted from ISO string to Date
|
||||
datePosted: data.datePosted ? new Date(data.datePosted) : undefined,
|
||||
datePosted: new Date(data.datePosted),
|
||||
// Convert applicationDeadline from ISO string to Date
|
||||
applicationDeadline: data.applicationDeadline ? new Date(data.applicationDeadline) : undefined,
|
||||
// Convert featuredUntil from ISO string to Date
|
||||
featuredUntil: data.featuredUntil ? new Date(data.featuredUntil) : undefined,
|
||||
// Convert nested BaseUser model
|
||||
owner: data.owner ? convertBaseUserFromApi(data.owner) : undefined,
|
||||
// Convert nested JobApplication model
|
||||
applicants: data.applicants ? convertJobApplicationFromApi(data.applicants) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert JobListResponse from API response
|
||||
* Nested models: data (Job)
|
||||
*/
|
||||
export function convertJobListResponseFromApi(data: any): JobListResponse {
|
||||
if (!data) return data;
|
||||
|
||||
return {
|
||||
...data,
|
||||
// Convert nested Job model
|
||||
data: data.data ? convertJobFromApi(data.data) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert JobRequirementsMessage from API response
|
||||
* Convert JobRequirementsMessage from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
* Nested models: job (Job)
|
||||
*/
|
||||
export function convertJobRequirementsMessageFromApi(data: any): JobRequirementsMessage {
|
||||
if (!data) return data;
|
||||
@ -1695,25 +1558,10 @@ export function convertJobRequirementsMessageFromApi(data: any): JobRequirements
|
||||
...data,
|
||||
// Convert timestamp from ISO string to Date
|
||||
timestamp: data.timestamp ? new Date(data.timestamp) : undefined,
|
||||
// Convert nested Job model
|
||||
job: convertJobFromApi(data.job),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert JobResponse from API response
|
||||
* Nested models: data (Job)
|
||||
*/
|
||||
export function convertJobResponseFromApi(data: any): JobResponse {
|
||||
if (!data) return data;
|
||||
|
||||
return {
|
||||
...data,
|
||||
// Convert nested Job model
|
||||
data: data.data ? convertJobFromApi(data.data) : undefined,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert MessageReaction from API response
|
||||
* Convert MessageReaction from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
*/
|
||||
export function convertMessageReactionFromApi(data: any): MessageReaction {
|
||||
@ -1726,9 +1574,8 @@ export function convertMessageReactionFromApi(data: any): MessageReaction {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert RAGConfiguration from API response
|
||||
* Convert RAGConfiguration from API response, parsing date fields
|
||||
* Date fields: createdAt, updatedAt
|
||||
* Nested models: dataSourceConfigurations (DataSourceConfiguration)
|
||||
*/
|
||||
export function convertRAGConfigurationFromApi(data: any): RAGConfiguration {
|
||||
if (!data) return data;
|
||||
@ -1736,15 +1583,13 @@ export function convertRAGConfigurationFromApi(data: any): RAGConfiguration {
|
||||
return {
|
||||
...data,
|
||||
// Convert createdAt from ISO string to Date
|
||||
createdAt: data.createdAt ? new Date(data.createdAt) : undefined,
|
||||
createdAt: new Date(data.createdAt),
|
||||
// Convert updatedAt from ISO string to Date
|
||||
updatedAt: data.updatedAt ? new Date(data.updatedAt) : undefined,
|
||||
// Convert nested DataSourceConfiguration model
|
||||
dataSourceConfigurations: data.dataSourceConfigurations.map((item: any) => convertDataSourceConfigurationFromApi(item)),
|
||||
updatedAt: new Date(data.updatedAt),
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert RateLimitResult from API response
|
||||
* Convert RateLimitResult from API response, parsing date fields
|
||||
* Date fields: resetTimes
|
||||
*/
|
||||
export function convertRateLimitResultFromApi(data: any): RateLimitResult {
|
||||
@ -1757,7 +1602,7 @@ export function convertRateLimitResultFromApi(data: any): RateLimitResult {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert RateLimitStatus from API response
|
||||
* Convert RateLimitStatus from API response, parsing date fields
|
||||
* Date fields: resetTimes
|
||||
*/
|
||||
export function convertRateLimitStatusFromApi(data: any): RateLimitStatus {
|
||||
@ -1770,7 +1615,7 @@ export function convertRateLimitStatusFromApi(data: any): RateLimitStatus {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert RefreshToken from API response
|
||||
* Convert RefreshToken from API response, parsing date fields
|
||||
* Date fields: expiresAt
|
||||
*/
|
||||
export function convertRefreshTokenFromApi(data: any): RefreshToken {
|
||||
@ -1783,7 +1628,7 @@ export function convertRefreshTokenFromApi(data: any): RefreshToken {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert SkillAssessment from API response
|
||||
* Convert SkillAssessment from API response, parsing date fields
|
||||
* Date fields: createdAt, updatedAt
|
||||
*/
|
||||
export function convertSkillAssessmentFromApi(data: any): SkillAssessment {
|
||||
@ -1798,7 +1643,7 @@ export function convertSkillAssessmentFromApi(data: any): SkillAssessment {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert UserActivity from API response
|
||||
* Convert UserActivity from API response, parsing date fields
|
||||
* Date fields: timestamp
|
||||
*/
|
||||
export function convertUserActivityFromApi(data: any): UserActivity {
|
||||
@ -1811,7 +1656,7 @@ export function convertUserActivityFromApi(data: any): UserActivity {
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Convert WorkExperience from API response
|
||||
* Convert WorkExperience from API response, parsing date fields
|
||||
* Date fields: startDate, endDate
|
||||
*/
|
||||
export function convertWorkExperienceFromApi(data: any): WorkExperience {
|
||||
@ -1842,8 +1687,6 @@ export function convertFromApi<T>(data: any, modelType: string): T {
|
||||
return convertApplicationDecisionFromApi(data) as T;
|
||||
case 'Attachment':
|
||||
return convertAttachmentFromApi(data) as T;
|
||||
case 'AuthResponse':
|
||||
return convertAuthResponseFromApi(data) as T;
|
||||
case 'Authentication':
|
||||
return convertAuthenticationFromApi(data) as T;
|
||||
case 'BaseUser':
|
||||
@ -1854,10 +1697,6 @@ export function convertFromApi<T>(data: any, modelType: string): T {
|
||||
return convertCandidateFromApi(data) as T;
|
||||
case 'CandidateAI':
|
||||
return convertCandidateAIFromApi(data) as T;
|
||||
case 'CandidateListResponse':
|
||||
return convertCandidateListResponseFromApi(data) as T;
|
||||
case 'CandidateResponse':
|
||||
return convertCandidateResponseFromApi(data) as T;
|
||||
case 'Certification':
|
||||
return convertCertificationFromApi(data) as T;
|
||||
case 'ChatMessage':
|
||||
@ -1882,8 +1721,6 @@ export function convertFromApi<T>(data: any, modelType: string): T {
|
||||
return convertDataSourceConfigurationFromApi(data) as T;
|
||||
case 'Document':
|
||||
return convertDocumentFromApi(data) as T;
|
||||
case 'DocumentListResponse':
|
||||
return convertDocumentListResponseFromApi(data) as T;
|
||||
case 'DocumentMessage':
|
||||
return convertDocumentMessageFromApi(data) as T;
|
||||
case 'EditHistory':
|
||||
@ -1892,12 +1729,8 @@ export function convertFromApi<T>(data: any, modelType: string): T {
|
||||
return convertEducationFromApi(data) as T;
|
||||
case 'Employer':
|
||||
return convertEmployerFromApi(data) as T;
|
||||
case 'EmployerResponse':
|
||||
return convertEmployerResponseFromApi(data) as T;
|
||||
case 'Guest':
|
||||
return convertGuestFromApi(data) as T;
|
||||
case 'GuestSessionResponse':
|
||||
return convertGuestSessionResponseFromApi(data) as T;
|
||||
case 'InterviewFeedback':
|
||||
return convertInterviewFeedbackFromApi(data) as T;
|
||||
case 'InterviewSchedule':
|
||||
@ -1908,12 +1741,8 @@ export function convertFromApi<T>(data: any, modelType: string): T {
|
||||
return convertJobApplicationFromApi(data) as T;
|
||||
case 'JobFull':
|
||||
return convertJobFullFromApi(data) as T;
|
||||
case 'JobListResponse':
|
||||
return convertJobListResponseFromApi(data) as T;
|
||||
case 'JobRequirementsMessage':
|
||||
return convertJobRequirementsMessageFromApi(data) as T;
|
||||
case 'JobResponse':
|
||||
return convertJobResponseFromApi(data) as T;
|
||||
case 'MessageReaction':
|
||||
return convertMessageReactionFromApi(data) as T;
|
||||
case 'RAGConfiguration':
|
||||
|
@ -823,16 +823,5 @@ Content: {content}
|
||||
|
||||
raise ValueError("No JSON found in the response")
|
||||
|
||||
def extract_markdown_from_text(self, text: str) -> str:
|
||||
"""Extract Markdown string from text that may contain other content."""
|
||||
markdown_pattern = r"```(md|markdown)\s*([\s\S]*?)\s*```"
|
||||
match = re.search(markdown_pattern, text)
|
||||
if match:
|
||||
return match.group(2).strip()
|
||||
|
||||
raise ValueError("No Markdown found in the response")
|
||||
|
||||
|
||||
|
||||
# Register the base agent
|
||||
agent_registry.register(Agent._agent_type, Agent)
|
||||
|
@ -511,5 +511,14 @@ Make sure at least one of the candidate's job descriptions take into account the
|
||||
|
||||
raise ValueError("No JSON found in the response")
|
||||
|
||||
def extract_markdown_from_text(self, text: str) -> str:
|
||||
"""Extract Markdown string from text that may contain other content."""
|
||||
markdown_pattern = r"```(md|markdown)\s*([\s\S]*?)\s*```"
|
||||
match = re.search(markdown_pattern, text)
|
||||
if match:
|
||||
return match.group(2).strip()
|
||||
|
||||
raise ValueError("No Markdown found in the response")
|
||||
|
||||
# Register the base agent
|
||||
agent_registry.register(GeneratePersona._agent_type, GeneratePersona)
|
||||
|
@ -19,7 +19,7 @@ import asyncio
|
||||
import numpy as np # type: ignore
|
||||
|
||||
from .base import Agent, agent_registry, LLMMessage
|
||||
from models import ApiActivityType, Candidate, ChatMessage, ChatMessageError, ChatMessageMetaData, ApiMessageType, ChatMessageStatus, ChatMessageUser, ChatOptions, ChatSenderType, ApiStatusType, Job, JobRequirements, JobRequirementsMessage, Tunables
|
||||
from models import ApiActivityType, Candidate, ChatMessage, ChatMessageError, ChatMessageMetaData, ApiMessageType, ChatMessageStatus, ChatMessageUser, ChatOptions, ChatSenderType, ApiStatusType, JobRequirements, JobRequirementsMessage, Tunables
|
||||
import model_cast
|
||||
from logger import logger
|
||||
import defines
|
||||
@ -107,15 +107,6 @@ class JobRequirementsAgent(Agent):
|
||||
async def generate(
|
||||
self, llm: Any, model: str, session_id: str, prompt: str, tunables: Optional[Tunables] = None, temperature=0.7
|
||||
) -> AsyncGenerator[ChatMessage, None]:
|
||||
if not self.user:
|
||||
error_message = ChatMessageError(
|
||||
session_id=session_id,
|
||||
content="User is not set for this agent."
|
||||
)
|
||||
logger.error(f"⚠️ {error_message.content}")
|
||||
yield error_message
|
||||
return
|
||||
|
||||
# Stage 1A: Analyze job requirements
|
||||
status_message = ChatMessageStatus(
|
||||
session_id=session_id,
|
||||
@ -175,20 +166,14 @@ class JobRequirementsAgent(Agent):
|
||||
logger.error(f"⚠️ {status_message.content}")
|
||||
yield status_message
|
||||
return
|
||||
job = Job(
|
||||
owner_id=self.user.id,
|
||||
owner_type=self.user.user_type,
|
||||
company=company,
|
||||
title=title,
|
||||
summary=summary,
|
||||
requirements=requirements,
|
||||
session_id=session_id,
|
||||
description=prompt,
|
||||
)
|
||||
job_requirements_message = JobRequirementsMessage(
|
||||
session_id=session_id,
|
||||
status=ApiStatusType.DONE,
|
||||
job=job,
|
||||
requirements=requirements,
|
||||
company=company,
|
||||
title=title,
|
||||
summary=summary,
|
||||
description=prompt,
|
||||
)
|
||||
yield job_requirements_message
|
||||
logger.info(f"✅ Job requirements analysis completed successfully.")
|
||||
|
@ -2,15 +2,14 @@
|
||||
"""
|
||||
Enhanced Type Generator - Generate TypeScript types from Pydantic models
|
||||
Now with command line parameters, pre-test validation, TypeScript compilation,
|
||||
automatic date field conversion functions, proper enum default handling,
|
||||
and NESTED MODEL CONVERSION SUPPORT
|
||||
automatic date field conversion functions, and proper enum default handling
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
import subprocess
|
||||
from typing import Any, Dict, List, Optional, Union, get_origin, get_args, Set
|
||||
from typing import Any, Dict, List, Optional, Union, get_origin, get_args
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
@ -65,7 +64,7 @@ current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, current_dir)
|
||||
|
||||
try:
|
||||
from pydantic import BaseModel # type: ignore
|
||||
from pydantic import BaseModel
|
||||
except ImportError as e:
|
||||
print(f"Error importing pydantic: {e}")
|
||||
print("Make sure pydantic is installed: pip install pydantic")
|
||||
@ -139,61 +138,6 @@ def is_date_type(python_type: Any) -> bool:
|
||||
|
||||
return False
|
||||
|
||||
def is_pydantic_model_type(python_type: Any) -> bool:
|
||||
"""Check if a Python type is a Pydantic model"""
|
||||
# Unwrap any annotations first
|
||||
python_type = unwrap_annotated_type(python_type)
|
||||
|
||||
# Handle Union types (like Optional[SomeModel])
|
||||
origin = get_origin(python_type)
|
||||
if origin is Union:
|
||||
args = get_args(python_type)
|
||||
# Check if any of the union args is a Pydantic model (excluding None)
|
||||
return any(is_pydantic_model_type(arg) for arg in args if arg is not type(None))
|
||||
|
||||
# Handle List types (like List[SomeModel])
|
||||
if origin is list or origin is List:
|
||||
args = get_args(python_type)
|
||||
if args:
|
||||
return is_pydantic_model_type(args[0])
|
||||
|
||||
# Check if it's a Pydantic model
|
||||
try:
|
||||
if isinstance(python_type, type) and issubclass(python_type, BaseModel):
|
||||
return python_type != BaseModel
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def get_model_name_from_type(python_type: Any) -> Optional[str]:
|
||||
"""Extract the model name from a type"""
|
||||
# Unwrap any annotations first
|
||||
python_type = unwrap_annotated_type(python_type)
|
||||
|
||||
# Handle Union types
|
||||
origin = get_origin(python_type)
|
||||
if origin is Union:
|
||||
args = get_args(python_type)
|
||||
# Find the first Pydantic model in the union
|
||||
for arg in args:
|
||||
if arg is not type(None):
|
||||
model_name = get_model_name_from_type(arg)
|
||||
if model_name:
|
||||
return model_name
|
||||
|
||||
# Handle List types
|
||||
if origin is list or origin is List:
|
||||
args = get_args(python_type)
|
||||
if args:
|
||||
return get_model_name_from_type(args[0])
|
||||
|
||||
# Direct model check
|
||||
if isinstance(python_type, type) and issubclass(python_type, BaseModel):
|
||||
return python_type.__name__
|
||||
|
||||
return None
|
||||
|
||||
def get_default_enum_value(field_info: Any, debug: bool = False) -> Optional[Any]:
|
||||
"""Extract the specific enum value from a field's default, if it exists"""
|
||||
if not hasattr(field_info, 'default'):
|
||||
@ -472,12 +416,11 @@ def is_field_optional(field_info: Any, field_type: Any, debug: bool = False) ->
|
||||
print(f" └─ RESULT: Required (fallback - no Optional type, no default)")
|
||||
return False
|
||||
|
||||
def process_pydantic_model(model_class, all_models: Set[str], debug: bool = False) -> Dict[str, Any]:
|
||||
def process_pydantic_model(model_class, debug: bool = False) -> Dict[str, Any]:
|
||||
"""Process a Pydantic model and return TypeScript interface definition"""
|
||||
interface_name = model_class.__name__
|
||||
properties = []
|
||||
date_fields = [] # Track date fields for conversion functions
|
||||
model_fields = [] # Track fields that are Pydantic models
|
||||
|
||||
if debug:
|
||||
print(f" 🔍 Processing model: {interface_name}")
|
||||
@ -519,21 +462,6 @@ def process_pydantic_model(model_class, all_models: Set[str], debug: bool = Fals
|
||||
elif debug and ('date' in str(field_type).lower() or 'time' in str(field_type).lower()):
|
||||
print(f" ⚠️ Field {ts_name} contains 'date'/'time' but not detected as date type: {field_type}")
|
||||
|
||||
# Check if this is a Pydantic model field
|
||||
if is_pydantic_model_type(field_type):
|
||||
model_name = get_model_name_from_type(field_type)
|
||||
if model_name and model_name in all_models:
|
||||
is_optional = is_field_optional(field_info, field_type, debug)
|
||||
is_list = get_origin(unwrap_annotated_type(field_type)) in (list, List)
|
||||
model_fields.append({
|
||||
'name': ts_name,
|
||||
'model': model_name,
|
||||
'optional': is_optional,
|
||||
'is_list': is_list
|
||||
})
|
||||
if debug:
|
||||
print(f" 🔗 Model field detected: {ts_name} -> {model_name} (optional: {is_optional}, list: {is_list})")
|
||||
|
||||
# Pass field_info to the type converter for default enum handling
|
||||
ts_type = python_type_to_typescript(field_type, field_info, debug)
|
||||
|
||||
@ -586,21 +514,6 @@ def process_pydantic_model(model_class, all_models: Set[str], debug: bool = Fals
|
||||
elif debug and ('date' in str(field_type).lower() or 'time' in str(field_type).lower()):
|
||||
print(f" ⚠️ Field {ts_name} contains 'date'/'time' but not detected as date type: {field_type}")
|
||||
|
||||
# Check if this is a Pydantic model field
|
||||
if is_pydantic_model_type(field_type):
|
||||
model_name = get_model_name_from_type(field_type)
|
||||
if model_name and model_name in all_models:
|
||||
is_optional = is_field_optional(field_info, field_type)
|
||||
is_list = get_origin(unwrap_annotated_type(field_type)) in (list, List)
|
||||
model_fields.append({
|
||||
'name': ts_name,
|
||||
'model': model_name,
|
||||
'optional': is_optional,
|
||||
'is_list': is_list
|
||||
})
|
||||
if debug:
|
||||
print(f" 🔗 Model field detected: {ts_name} -> {model_name} (optional: {is_optional}, list: {is_list})")
|
||||
|
||||
# Pass field_info to the type converter for default enum handling
|
||||
ts_type = python_type_to_typescript(field_type, field_info, debug)
|
||||
|
||||
@ -622,8 +535,7 @@ def process_pydantic_model(model_class, all_models: Set[str], debug: bool = Fals
|
||||
return {
|
||||
'name': interface_name,
|
||||
'properties': properties,
|
||||
'date_fields': date_fields,
|
||||
'model_fields': model_fields
|
||||
'date_fields': date_fields
|
||||
}
|
||||
|
||||
def process_enum(enum_class) -> Dict[str, Any]:
|
||||
@ -637,87 +549,38 @@ def process_enum(enum_class) -> Dict[str, Any]:
|
||||
'values': " | ".join(values)
|
||||
}
|
||||
|
||||
def build_conversion_dependency_graph(interfaces: List[Dict[str, Any]]) -> Dict[str, Set[str]]:
|
||||
"""Build a graph of which models depend on which other models for conversion"""
|
||||
dependencies = {}
|
||||
|
||||
# First pass: identify which models have conversions
|
||||
models_with_conversions = set()
|
||||
for interface in interfaces:
|
||||
if interface.get('date_fields') or interface.get('model_fields'):
|
||||
models_with_conversions.add(interface['name'])
|
||||
|
||||
# Second pass: build dependency graph
|
||||
for interface in interfaces:
|
||||
interface_name = interface['name']
|
||||
deps = set()
|
||||
|
||||
# Add dependencies for nested models
|
||||
for model_field in interface.get('model_fields', []):
|
||||
model_name = model_field['model']
|
||||
if model_name in models_with_conversions:
|
||||
deps.add(model_name)
|
||||
|
||||
if deps or interface.get('date_fields'):
|
||||
dependencies[interface_name] = deps
|
||||
|
||||
return dependencies
|
||||
|
||||
def generate_conversion_functions(interfaces: List[Dict[str, Any]]) -> str:
|
||||
"""Generate TypeScript conversion functions for models with date fields or nested models"""
|
||||
# Build dependency graph
|
||||
dependencies = build_conversion_dependency_graph(interfaces)
|
||||
|
||||
if not dependencies:
|
||||
return ""
|
||||
|
||||
"""Generate TypeScript conversion functions for models with date fields"""
|
||||
conversion_functions = []
|
||||
|
||||
for interface in interfaces:
|
||||
interface_name = interface['name']
|
||||
date_fields = interface.get('date_fields', [])
|
||||
model_fields = interface.get('model_fields', [])
|
||||
|
||||
# Skip if no conversion needed
|
||||
if not date_fields and not model_fields:
|
||||
continue
|
||||
|
||||
# Check if any model fields need conversion
|
||||
model_fields_needing_conversion = [
|
||||
mf for mf in model_fields
|
||||
if mf['model'] in dependencies
|
||||
]
|
||||
|
||||
if not date_fields and not model_fields_needing_conversion:
|
||||
continue
|
||||
if not date_fields:
|
||||
continue # Skip interfaces without date fields
|
||||
|
||||
function_name = f"convert{interface_name}FromApi"
|
||||
|
||||
# Generate function
|
||||
func_lines = [
|
||||
f"/**",
|
||||
f" * Convert {interface_name} from API response",
|
||||
]
|
||||
|
||||
if date_fields:
|
||||
func_lines.append(f" * Date fields: {', '.join([f['name'] for f in date_fields])}")
|
||||
if model_fields_needing_conversion:
|
||||
func_lines.append(f" * Nested models: {', '.join([f'{mf['name']} ({mf['model']})' for mf in model_fields_needing_conversion])}")
|
||||
|
||||
func_lines.extend([
|
||||
f" * Convert {interface_name} from API response, parsing date fields",
|
||||
f" * Date fields: {', '.join([f['name'] for f in date_fields])}",
|
||||
f" */",
|
||||
f"export function {function_name}(data: any): {interface_name} {{",
|
||||
f" if (!data) return data;",
|
||||
f" ",
|
||||
f" return {{",
|
||||
f" ...data,"
|
||||
])
|
||||
]
|
||||
|
||||
# Add date field conversions
|
||||
# Add date field conversions with validation
|
||||
for date_field in date_fields:
|
||||
field_name = date_field['name']
|
||||
is_optional = date_field['optional']
|
||||
|
||||
# Add a comment for clarity
|
||||
func_lines.append(f" // Convert {field_name} from ISO string to Date")
|
||||
|
||||
if is_optional:
|
||||
@ -725,26 +588,6 @@ def generate_conversion_functions(interfaces: List[Dict[str, Any]]) -> str:
|
||||
else:
|
||||
func_lines.append(f" {field_name}: new Date(data.{field_name}),")
|
||||
|
||||
# Add nested model conversions
|
||||
for model_field in model_fields_needing_conversion:
|
||||
field_name = model_field['name']
|
||||
model_name = model_field['model']
|
||||
is_optional = model_field['optional']
|
||||
is_list = model_field['is_list']
|
||||
|
||||
func_lines.append(f" // Convert nested {model_name} model")
|
||||
|
||||
if is_list:
|
||||
if is_optional:
|
||||
func_lines.append(f" {field_name}: data.{field_name} ? data.{field_name}.map((item: any) => convert{model_name}FromApi(item)) : undefined,")
|
||||
else:
|
||||
func_lines.append(f" {field_name}: data.{field_name}.map((item: any) => convert{model_name}FromApi(item)),")
|
||||
else:
|
||||
if is_optional:
|
||||
func_lines.append(f" {field_name}: data.{field_name} ? convert{model_name}FromApi(data.{field_name}) : undefined,")
|
||||
else:
|
||||
func_lines.append(f" {field_name}: convert{model_name}FromApi(data.{field_name}),")
|
||||
|
||||
func_lines.extend([
|
||||
f" }};",
|
||||
f"}}"
|
||||
@ -758,11 +601,11 @@ def generate_conversion_functions(interfaces: List[Dict[str, Any]]) -> str:
|
||||
# Generate the conversion functions section
|
||||
result = [
|
||||
"// ============================",
|
||||
"// Date and Nested Model Conversion Functions",
|
||||
"// Date Conversion Functions",
|
||||
"// ============================",
|
||||
"",
|
||||
"// These functions convert API responses to properly typed objects",
|
||||
"// with Date objects instead of ISO date strings and nested model conversions",
|
||||
"// with Date objects instead of ISO date strings",
|
||||
"",
|
||||
]
|
||||
|
||||
@ -770,9 +613,9 @@ def generate_conversion_functions(interfaces: List[Dict[str, Any]]) -> str:
|
||||
result.append("")
|
||||
|
||||
# Generate a generic converter function
|
||||
models_with_conversions = list(dependencies.keys())
|
||||
models_with_dates = [interface['name'] for interface in interfaces if interface.get('date_fields')]
|
||||
|
||||
if models_with_conversions:
|
||||
if models_with_dates:
|
||||
result.extend([
|
||||
"/**",
|
||||
" * Generic converter that automatically selects the right conversion function",
|
||||
@ -784,7 +627,7 @@ def generate_conversion_functions(interfaces: List[Dict[str, Any]]) -> str:
|
||||
" switch (modelType) {"
|
||||
])
|
||||
|
||||
for model_name in sorted(models_with_conversions):
|
||||
for model_name in models_with_dates:
|
||||
result.append(f" case '{model_name}':")
|
||||
result.append(f" return convert{model_name}FromApi(data) as T;")
|
||||
|
||||
@ -828,17 +671,8 @@ def generate_typescript_interfaces(source_file: str, debug: bool = False):
|
||||
|
||||
interfaces = []
|
||||
enums = []
|
||||
all_models = set()
|
||||
|
||||
# First pass: collect all model names
|
||||
for name in dir(models_module):
|
||||
obj = getattr(models_module, name)
|
||||
if (isinstance(obj, type) and
|
||||
issubclass(obj, BaseModel) and
|
||||
obj != BaseModel):
|
||||
all_models.add(name)
|
||||
|
||||
# Second pass: process models with knowledge of all models
|
||||
# Scan the models module
|
||||
for name in dir(models_module):
|
||||
obj = getattr(models_module, name)
|
||||
|
||||
@ -852,17 +686,10 @@ def generate_typescript_interfaces(source_file: str, debug: bool = False):
|
||||
issubclass(obj, BaseModel) and
|
||||
obj != BaseModel):
|
||||
|
||||
interface = process_pydantic_model(obj, all_models, debug)
|
||||
interface = process_pydantic_model(obj, debug)
|
||||
interfaces.append(interface)
|
||||
date_count = len(interface.get('date_fields', []))
|
||||
nested_count = len(interface.get('model_fields', []))
|
||||
status_parts = []
|
||||
if date_count > 0:
|
||||
status_parts.append(f"{date_count} date fields")
|
||||
if nested_count > 0:
|
||||
status_parts.append(f"{nested_count} nested models")
|
||||
status = f" ({', '.join(status_parts)})" if status_parts else ""
|
||||
print(f" ✅ Found Pydantic model: {name}{status}")
|
||||
print(f" ✅ Found Pydantic model: {name}" + (f" ({date_count} date fields)" if date_count > 0 else ""))
|
||||
|
||||
# Check if it's an Enum
|
||||
elif (isinstance(obj, type) and
|
||||
@ -880,10 +707,8 @@ def generate_typescript_interfaces(source_file: str, debug: bool = False):
|
||||
continue
|
||||
|
||||
total_date_fields = sum(len(interface.get('date_fields', [])) for interface in interfaces)
|
||||
total_nested_models = sum(len(interface.get('model_fields', [])) for interface in interfaces)
|
||||
print(f"\n📊 Found {len(interfaces)} interfaces and {len(enums)} enums")
|
||||
print(f"🗓️ Found {total_date_fields} date fields across all models")
|
||||
print(f"🔗 Found {total_nested_models} nested model fields requiring conversion")
|
||||
|
||||
# Generate TypeScript content
|
||||
ts_content = f"""// Generated TypeScript types from Pydantic models
|
||||
@ -956,7 +781,7 @@ def compile_typescript(ts_file: str) -> bool:
|
||||
def main():
|
||||
"""Main function with command line argument parsing"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate TypeScript types from Pydantic models with nested model conversion support',
|
||||
description='Generate TypeScript types from Pydantic models with date conversion functions and proper enum handling',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
@ -967,19 +792,12 @@ Examples:
|
||||
python generate_types.py --debug # Enable debug output
|
||||
python generate_types.py --source models.py --output types.ts --skip-test --skip-compile --debug
|
||||
|
||||
Generated conversion functions now support nested models:
|
||||
// If JobRequirementsMessage has a 'job' field of type Job
|
||||
const message = convertJobRequirementsMessageFromApi(apiResponse);
|
||||
// The nested job field will also be converted automatically
|
||||
Generated conversion functions can be used like:
|
||||
const candidate = convertCandidateFromApi(apiResponse);
|
||||
const jobs = convertArrayFromApi<Job>(apiResponse, 'Job');
|
||||
|
||||
// Arrays of models are also supported
|
||||
const messages = convertArrayFromApi<JobRequirementsMessage>(apiResponse, 'JobRequirementsMessage');
|
||||
|
||||
The conversion functions handle:
|
||||
- Date field conversion (ISO strings to Date objects)
|
||||
- Nested model conversion (recursive conversion of Pydantic model fields)
|
||||
- Arrays of models
|
||||
- Optional fields
|
||||
Enum types are now properly handled:
|
||||
status: ApiStatusType = ApiStatusType.DONE -> status: ApiStatusType (not locked to "done")
|
||||
"""
|
||||
)
|
||||
|
||||
@ -1016,12 +834,12 @@ The conversion functions handle:
|
||||
parser.add_argument(
|
||||
'--version', '-v',
|
||||
action='version',
|
||||
version='TypeScript Generator 4.0 (With Nested Model Conversion Support)'
|
||||
version='TypeScript Generator 3.2 (Fixed Enum Default Handling)'
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
print("🚀 Enhanced TypeScript Type Generator with Nested Model Conversion")
|
||||
print("🚀 Enhanced TypeScript Type Generator with Fixed Enum Handling")
|
||||
print("=" * 60)
|
||||
print(f"📁 Source file: {args.source}")
|
||||
print(f"📁 Output file: {args.output}")
|
||||
@ -1067,24 +885,29 @@ The conversion functions handle:
|
||||
# Count conversion functions and provide detailed feedback
|
||||
conversion_count = ts_content.count('export function convert') - ts_content.count('convertFromApi') - ts_content.count('convertArrayFromApi')
|
||||
enum_type_count = ts_content.count('export type')
|
||||
nested_conversion_count = ts_content.count('// Convert nested')
|
||||
|
||||
if conversion_count > 0:
|
||||
print(f"🗓️ Generated {conversion_count} conversion functions")
|
||||
if nested_conversion_count > 0:
|
||||
print(f"🔗 Including {nested_conversion_count} nested model conversions")
|
||||
print(f"🗓️ Generated {conversion_count} date conversion functions")
|
||||
if enum_type_count > 0:
|
||||
print(f"🎯 Generated {enum_type_count} enum types")
|
||||
print(f"🎯 Generated {enum_type_count} enum types (properly allowing all values)")
|
||||
|
||||
if args.debug:
|
||||
# Show which models have conversions
|
||||
models_with_conversions = []
|
||||
# Show which models have date conversion
|
||||
models_with_dates = []
|
||||
for line in ts_content.split('\n'):
|
||||
if line.startswith('export function convert') and 'FromApi' in line and 'convertFromApi' not in line:
|
||||
model_name = line.split('convert')[1].split('FromApi')[0]
|
||||
models_with_conversions.append(model_name)
|
||||
if models_with_conversions:
|
||||
print(f" Models with conversions: {', '.join(models_with_conversions)}")
|
||||
models_with_dates.append(model_name)
|
||||
if models_with_dates:
|
||||
print(f" Models with date conversion: {', '.join(models_with_dates)}")
|
||||
|
||||
# Provide troubleshooting info if debug mode
|
||||
if args.debug:
|
||||
print(f"\n🐛 Debug mode was enabled. If you see incorrect type conversions:")
|
||||
print(f" 1. Check the debug output above for enum default handling")
|
||||
print(f" 2. Look for '📅 Date type check' lines for date handling")
|
||||
print(f" 3. Look for '⚠️' warnings about fallback types")
|
||||
print(f" 4. Verify your Pydantic model field types and defaults are correct")
|
||||
|
||||
# Step 5: Compile TypeScript (unless skipped)
|
||||
if not args.skip_compile:
|
||||
@ -1101,24 +924,19 @@ The conversion functions handle:
|
||||
print(f"✅ Generated {args.output} from {args.source}")
|
||||
print(f"✅ File size: {file_size} characters")
|
||||
if conversion_count > 0:
|
||||
print(f"✅ Conversion functions: {conversion_count}")
|
||||
if nested_conversion_count > 0:
|
||||
print(f"✅ Nested model conversions: {nested_conversion_count}")
|
||||
print(f"✅ Date conversion functions: {conversion_count}")
|
||||
if enum_type_count > 0:
|
||||
print(f"✅ Enum types: {enum_type_count}")
|
||||
print(f"✅ Enum types (with full value range): {enum_type_count}")
|
||||
if not args.skip_test:
|
||||
print("✅ Model validation passed")
|
||||
if not args.skip_compile:
|
||||
print("✅ TypeScript syntax validated")
|
||||
|
||||
print(f"\n💡 Usage in your TypeScript project:")
|
||||
print(f" import {{ Candidate, Job, convertJobRequirementsMessageFromApi }} from './{Path(args.output).stem}';")
|
||||
print(f" import {{ Candidate, Employer, Job, convertCandidateFromApi }} from './{Path(args.output).stem}';")
|
||||
if conversion_count > 0:
|
||||
print(f"\n // Example with nested model conversion:")
|
||||
print(f" const message = convertJobRequirementsMessageFromApi(apiResponse);")
|
||||
print(f" // The nested 'job' field is automatically converted too!")
|
||||
print(f"\n // For arrays:")
|
||||
print(f" const messages = convertArrayFromApi<JobRequirementsMessage>(apiResponse, 'JobRequirementsMessage');")
|
||||
print(f" const candidate = convertCandidateFromApi(apiResponse);")
|
||||
print(f" const jobs = convertArrayFromApi<Job>(apiResponse, 'Job');")
|
||||
|
||||
return True
|
||||
|
||||
|
@ -2357,6 +2357,7 @@ async def create_job_from_content(database: RedisDatabase, current_user: Candida
|
||||
activity=ApiActivityType.SEARCHING
|
||||
)
|
||||
yield status_message
|
||||
await asyncio.sleep(0)
|
||||
|
||||
async for message in chat_agent.generate(
|
||||
llm=llm_manager.get_llm(),
|
||||
@ -2365,52 +2366,16 @@ async def create_job_from_content(database: RedisDatabase, current_user: Candida
|
||||
prompt=content
|
||||
):
|
||||
pass
|
||||
|
||||
if not message or not isinstance(message, JobRequirementsMessage):
|
||||
error_message = ChatMessageError(
|
||||
sessionId=MOCK_UUID, # No session ID for document uploads
|
||||
content="Job extraction did not convert successfully"
|
||||
content="Failed to process job description file"
|
||||
)
|
||||
yield error_message
|
||||
return
|
||||
|
||||
status_message = ChatMessageStatus(
|
||||
sessionId=MOCK_UUID, # No session ID for document uploads
|
||||
content=f"Reformatting job description as markdown...",
|
||||
activity=ApiActivityType.CONVERTING
|
||||
)
|
||||
yield status_message
|
||||
|
||||
job_requirements : JobRequirementsMessage = message
|
||||
async for message in chat_agent.llm_one_shot(
|
||||
llm=llm_manager.get_llm(),
|
||||
model=defines.model,
|
||||
session_id=MOCK_UUID,
|
||||
prompt=content,
|
||||
system_prompt="""
|
||||
You are a document editor. Take the provided job description and reformat as legible markdown.
|
||||
Return only the markdown content, no other text. Make sure all content is included.
|
||||
"""
|
||||
):
|
||||
pass
|
||||
|
||||
if not message or not isinstance(message, ChatMessage):
|
||||
logger.error("❌ Failed to reformat job description to markdown")
|
||||
error_message = ChatMessageError(
|
||||
sessionId=MOCK_UUID, # No session ID for document uploads
|
||||
content="Failed to reformat job description"
|
||||
)
|
||||
yield error_message
|
||||
return
|
||||
chat_message : ChatMessage = message
|
||||
markdown = chat_message.content
|
||||
try:
|
||||
markdown = chat_agent.extract_markdown_from_text(chat_message.content)
|
||||
except Exception as e:
|
||||
pass
|
||||
job_requirements.job.description = markdown
|
||||
logger.info(f"✅ Successfully saved job requirements job {job_requirements.id}")
|
||||
yield job_requirements
|
||||
logger.info(f"✅ Successfully saved job requirements job {message.id}")
|
||||
yield message
|
||||
return
|
||||
|
||||
@api_router.post("/candidates/profile/upload")
|
||||
@ -3307,7 +3272,6 @@ async def create_job_from_description(
|
||||
logger.info(f"📁 Received file content: size='{len(content)} bytes'")
|
||||
|
||||
async for message in create_job_from_content(database=database, current_user=current_user, content=content):
|
||||
logger.info(f"📄 Yielding job creation message status: {message.status}")
|
||||
yield message
|
||||
return
|
||||
|
||||
@ -3439,7 +3403,6 @@ async def create_job_from_file(
|
||||
yield error_message
|
||||
logger.error(f"❌ Error converting {file.filename} to Markdown: {e}")
|
||||
return
|
||||
|
||||
async for message in create_job_from_content(database=database, current_user=current_user, content=file_content):
|
||||
yield message
|
||||
return
|
||||
|
@ -480,8 +480,8 @@ class BaseUser(BaseUserWithType):
|
||||
full_name: str = Field(..., alias="fullName")
|
||||
phone: Optional[str] = None
|
||||
location: Optional[Location] = None
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC), alias="createdAt")
|
||||
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC), alias="updatedAt")
|
||||
created_at: datetime = Field(..., alias="createdAt")
|
||||
updated_at: datetime = Field(..., alias="updatedAt")
|
||||
last_login: Optional[datetime] = Field(None, alias="lastLogin")
|
||||
profile_image: Optional[str] = Field(None, alias="profileImage")
|
||||
status: UserStatus
|
||||
@ -613,7 +613,7 @@ class Guest(BaseUser):
|
||||
username: str # Add username for consistency with other user types
|
||||
converted_to_user_id: Optional[str] = Field(None, alias="convertedToUserId")
|
||||
ip_address: Optional[str] = Field(None, alias="ipAddress")
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC), alias="createdAt")
|
||||
created_at: datetime = Field(..., alias="createdAt")
|
||||
user_agent: Optional[str] = Field(None, alias="userAgent")
|
||||
rag_content_size: int = 0
|
||||
model_config = {
|
||||
@ -690,8 +690,8 @@ class Job(BaseModel):
|
||||
company: Optional[str]
|
||||
description: str
|
||||
requirements: Optional[JobRequirements]
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC), alias="createdAt")
|
||||
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC), alias="updatedAt")
|
||||
created_at: datetime = Field(..., alias="createdAt")
|
||||
updated_at: datetime = Field(..., alias="updatedAt")
|
||||
model_config = {
|
||||
"populate_by_name": True # Allow both field names and aliases
|
||||
}
|
||||
@ -700,7 +700,7 @@ class JobFull(Job):
|
||||
location: Location
|
||||
salary_range: Optional[SalaryRange] = Field(None, alias="salaryRange")
|
||||
employment_type: EmploymentType = Field(..., alias="employmentType")
|
||||
date_posted: datetime = Field(default_factory=lambda: datetime.now(UTC), alias="datePosted")
|
||||
date_posted: datetime = Field(..., alias="datePosted")
|
||||
application_deadline: Optional[datetime] = Field(None, alias="applicationDeadline")
|
||||
is_active: bool = Field(..., alias="isActive")
|
||||
applicants: Optional[List["JobApplication"]] = None
|
||||
@ -723,8 +723,8 @@ class InterviewFeedback(BaseModel):
|
||||
weaknesses: List[str]
|
||||
recommendation: InterviewRecommendation
|
||||
comments: str
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC), alias="createdAt")
|
||||
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC), alias="updatedAt")
|
||||
created_at: datetime = Field(..., alias="createdAt")
|
||||
updated_at: datetime = Field(..., alias="updatedAt")
|
||||
is_visible: bool = Field(..., alias="isVisible")
|
||||
skill_assessments: Optional[List[SkillAssessment]] = Field(None, alias="skillAssessments")
|
||||
model_config = {
|
||||
@ -954,7 +954,11 @@ class ChatMessageRagSearch(ApiMessage):
|
||||
|
||||
class JobRequirementsMessage(ApiMessage):
|
||||
type: ApiMessageType = ApiMessageType.JSON
|
||||
job: Job = Field(..., alias="job")
|
||||
title: Optional[str]
|
||||
summary: Optional[str]
|
||||
company: Optional[str]
|
||||
description: str
|
||||
requirements: Optional[JobRequirements]
|
||||
|
||||
class DocumentMessage(ApiMessage):
|
||||
type: ApiMessageType = ApiMessageType.JSON
|
||||
@ -1075,8 +1079,8 @@ class RAGConfiguration(BaseModel):
|
||||
embedding_model: str = Field(..., alias="embeddingModel")
|
||||
vector_store_type: VectorStoreType = Field(..., alias="vectorStoreType")
|
||||
retrieval_parameters: RetrievalParameters = Field(..., alias="retrievalParameters")
|
||||
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC), alias="createdAt")
|
||||
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC), alias="updatedAt")
|
||||
created_at: datetime = Field(..., alias="createdAt")
|
||||
updated_at: datetime = Field(..., alias="updatedAt")
|
||||
version: int
|
||||
is_active: bool = Field(..., alias="isActive")
|
||||
model_config = {
|
||||
@ -1247,7 +1251,7 @@ class EmployerResponse(BaseModel):
|
||||
|
||||
class JobResponse(BaseModel):
|
||||
success: bool
|
||||
data: Optional[Job] = None
|
||||
data: Optional["Job"] = None
|
||||
error: Optional[ErrorDetail] = None
|
||||
meta: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user