Spaces:
Sleeping
Sleeping
File size: 8,649 Bytes
9a951d7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | -- =============================================================
-- MODULE: Resume & AI Analysis
-- File: docs/schema-resume.sql
-- Managed by: TypeORM (synchronize: true in development)
-- =============================================================
-- -----------------------------------------------------------
-- ENUMS
-- -----------------------------------------------------------
CREATE TYPE lens_type_enum AS ENUM (
'targeted', -- Lens A: Job-specific analysis (requires job description)
'general' -- Lens B: General resume quality analysis
);
CREATE TYPE confidence_level_enum AS ENUM (
'High',
'Medium',
'Low'
);
-- -----------------------------------------------------------
-- TABLE: resumes
-- Entity: src/modules/resume/entities/resume.entity.ts
-- Description: Stores metadata for every uploaded resume file.
-- The actual file lives in uploads/ (not in DB).
-- Extracted text is cached here after Python service runs.
-- -----------------------------------------------------------
CREATE TABLE resumes (
-- Primary key
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Ownership
user_id UUID NOT NULL
REFERENCES "user"(id) ON DELETE CASCADE,
-- File metadata
original_file_name VARCHAR(255) NOT NULL,
file_path VARCHAR(500) NOT NULL, -- Relative path: uploads/<uuid>-<filename>
file_size_bytes INTEGER NOT NULL,
-- Extracted content (populated by Python service POST /extract)
extracted_text TEXT, -- Full raw text from pdfplumber
extraction_metadata JSONB,
/*
* extraction_metadata structure:
* {
* "pageCount": 2,
* "hasTables": true,
* "hasImages": false,
* "confidence": 0.95
* }
*/
-- State flag
is_extracted BOOLEAN NOT NULL DEFAULT FALSE,
-- Timestamp
uploaded_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- Indexes
CREATE INDEX idx_resumes_user_id ON resumes (user_id);
CREATE INDEX idx_resumes_is_extracted ON resumes (user_id, is_extracted);
-- -----------------------------------------------------------
-- TABLE: resume_analyses
-- Entity: src/modules/resume/entities/resume-analysis.entity.ts
-- Description: AI-generated analysis result for one resume.
-- Each analysis is for a specific lens type.
-- A single resume can have multiple analyses over time.
-- -----------------------------------------------------------
CREATE TABLE resume_analyses (
-- Primary key
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Foreign keys
resume_id UUID NOT NULL
REFERENCES resumes(id) ON DELETE CASCADE,
user_id UUID NOT NULL
REFERENCES "user"(id) ON DELETE CASCADE,
-- Analysis configuration
lens_type lens_type_enum NOT NULL,
-- Lens A (targeted) fields β NULL when lens_type = 'general'
job_description TEXT,
job_title VARCHAR(200),
company VARCHAR(200),
-- Scores (0-100, 2 decimal places)
overall_score NUMERIC(5, 2) NOT NULL,
ats_score NUMERIC(5, 2) NOT NULL, -- ATS keyword match
content_quality_score NUMERIC(5, 2) NOT NULL, -- Writing quality & clarity
experience_score NUMERIC(5, 2) NOT NULL, -- Impact of work history
skills_score NUMERIC(5, 2) NOT NULL, -- Skills section quality
achievement_score NUMERIC(5, 2) NOT NULL, -- Quantified achievements
formatting_score NUMERIC(5, 2) NOT NULL, -- Layout & structure
relevance_score NUMERIC(5, 2), -- Job-match relevance (Lens A only)
-- Qualitative outcome
interpretation_band VARCHAR(100) NOT NULL, -- e.g. "Strong Match", "Needs Work"
confidence_level confidence_level_enum NOT NULL DEFAULT 'High',
-- Detailed feedback (JSONB)
remarks JSONB NOT NULL,
/*
* remarks structure:
* {
* "strengths": ["Strong quantified achievements", "ATS-friendly keywords present"],
* "weaknesses": ["Missing skills section", "No measurable impact in role 2"],
* "actionable": ["Add 3 bullet points with metrics", "Include a skills section"]
* }
*/
-- Debug / audit
raw_llm_response TEXT, -- Full LLM JSON string before parsing
processing_time_ms INTEGER, -- End-to-end latency for this analysis
-- Timestamp
created_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- Indexes
CREATE INDEX idx_resume_analyses_resume_id ON resume_analyses (resume_id);
CREATE INDEX idx_resume_analyses_user_id ON resume_analyses (user_id);
CREATE INDEX idx_resume_analyses_lens ON resume_analyses (user_id, lens_type);
CREATE INDEX idx_resume_analyses_created ON resume_analyses (user_id, created_at DESC);
-- -----------------------------------------------------------
-- VARIABLE REFERENCE β RESUME & AI MODULE
-- -----------------------------------------------------------
--
-- resumes:
-- user_id β Owner; used to scope all resume queries
-- original_file_name β Displayed in UI; original filename from upload
-- file_path β Server disk path; fed to Python extraction service
-- file_size_bytes β Validated at upload (max 5 MB = 5,242,880 bytes)
-- extracted_text β Full PDF text; injected into Groq LLM prompt
-- extraction_metadata β JSONB: pageCount, hasTables, hasImages, confidence
-- is_extracted β Guards analysis endpoint (400 if FALSE)
--
-- resume_analyses:
-- resume_id β Which resume was analysed
-- user_id β Redundant FK for fast per-user queries without JOIN
-- lens_type β 'targeted' (Lens A) or 'general' (Lens B)
-- job_description β Full JD text; used in targeted scoring prompt
-- job_title β Job title for context; shown in results UI
-- company β Company name for context; shown in results UI
-- overall_score β Weighted average of all subscores (0-100)
-- ats_score β Keyword/format ATS compatibility (0-100)
-- content_quality_score β Writing clarity and resume content quality (0-100)
-- experience_score β Depth and impact of work experience (0-100)
-- skills_score β Skills section completeness and relevance (0-100)
-- achievement_score β Quantified and measurable achievements (0-100)
-- formatting_score β Layout, structure, visual readability (0-100)
-- relevance_score β Job description match relevance β Lens A only (0-100)
-- interpretation_band β Human-readable verdict: "Excellent" / "Good" / "Needs Work" / etc.
-- confidence_level β LLM confidence: 'High' | 'Medium' | 'Low'
-- remarks β JSONB: { strengths[], weaknesses[], actionable[] }
-- raw_llm_response β Unprocessed Groq API response; kept for debugging
-- processing_time_ms β Latency metric; useful for performance monitoring
--
-- -----------------------------------------------------------
-- AI MODULE VARIABLES (no database table β stateless service)
-- -----------------------------------------------------------
-- Defined in: src/modules/ai/groq.service.ts
--
-- GROQ_API_KEY β .env β Groq API key (starts with gsk_...)
-- MODEL β Hardcoded: 'llama-3.3-70b-versatile'
-- TEMPERATURE β 0.1 (deterministic scoring output)
-- MAX_TOKENS β 2048 (enough for full JSON analysis response)
-- System prompt β Built by UserContextService.generateUserContext()
-- User prompt β Built by ResumeAnalysisService with resume text + JD
--
-- ATS Checker (ats-checker.util.ts):
-- extractedText β Resume plain text
-- jobDescription β Job description (Lens A only)
-- Returns: { score, matchedKeywords[], missingKeywords[] }
--
-- Score Parser (score-parser.util.ts):
-- rawJson β LLM JSON string
-- Returns typed AnalysisResult with all numeric scores parsed
|