Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ web_modules/
.env.test.local
.env.production.local
.env.local
.env*

# parcel-bundler cache (https://parceljs.org/)
.cache
Expand Down
4 changes: 3 additions & 1 deletion nextstep-backend/src/controllers/resources_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ const getImageResource = async (req: Request, res: Response) => {

const createResumeResource = async (req: Request, res: Response) => {
try {
const resumeFilename = await uploadResume(req);
const resumeFilename = await uploadResume(req);


return res.status(201).send(resumeFilename);
} catch (error) {
if (error instanceof multer.MulterError || error instanceof TypeError) {
Expand Down
36 changes: 31 additions & 5 deletions nextstep-backend/src/controllers/resume_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ import { Request, Response } from 'express';
import { config } from '../config/config';
import fs from 'fs';
import path from 'path';
import { scoreResume, streamScoreResume, getResumeTemplates, generateImprovedResume, parseResumeFields } from '../services/resume_service';
import { scoreResume, streamScoreResume, getResumeTemplates,
generateImprovedResume, parseResumeFields,
saveParsedResume, getResumeByOwner } from '../services/resume_service';
import multer from 'multer';
import {getResumeBuffer, resumeExists, uploadResume} from '../services/resources_service';
import { CustomRequest } from "types/customRequest";
import { handleError } from "../utils/handle_error";

Expand Down Expand Up @@ -94,17 +97,40 @@ const generateResume = async (req: Request, res: Response) => {
};


const parseResume = async (req: Request, res: Response) => {
const parseResume = async (req: CustomRequest, res: Response) => {
try {
if (!req.file) {
if (!req.body.resumefileName) {
return res.status(400).json({ error: 'No resume file uploaded' });
}
const parsed = await parseResumeFields(req.file.buffer, req.file.originalname);
else if (!resumeExists(req.body.resumefileName)) {
return res.status(400).json({ error: 'No resume file uploaded' });
}

const resumeFilename = req.body.resumefileName;
const parsed = await parseResumeFields(getResumeBuffer(req.body.resumefileName), resumeFilename);
const resumeData = await saveParsedResume(parsed, req.user.id, resumeFilename);

return res.status(200).json(parsed);
} catch (err: any) {
console.error('Error parsing resume:', err);
return handleError(err, res);
}
};

export default { parseResume, getResumeScore, getStreamResumeScore, getTemplates, generateResume };
const getResumeData = async (req: CustomRequest, res: Response) => {
try {
const ownerId = req.user.id;
// Get the optional version parameter from query string
const version = req.query.version ? parseInt(req.query.version as string) : undefined;
const resume = await getResumeByOwner(ownerId, version);

return res.status(200).json(resume);
} catch (error) {
console.error('Error retrieving resume data:', error);
return handleError(error, res);
}
};

export default { parseResume, getResumeScore,
getStreamResumeScore, getTemplates,
generateResume, getResumeData };
36 changes: 36 additions & 0 deletions nextstep-backend/src/models/resume_model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import mongoose, { Schema } from 'mongoose';
import {ResumeData} from "types/resume_types";

const ResumeSchema = new Schema({
owner: { type: Schema.Types.ObjectId, ref: 'User', required: true },
version: { type: Number, required: true },
rawContentLink: { type: String, required: true },
parsedData: {
type: {
aboutMe: { type: String, required: false },
skills: { type: [String], required: false },
roleMatch: { type: String, required: false },
experience: { type: [String], required: false }

},
required: false
},
createdAt: { type: Date, default: Date.now }
}, { versionKey: false });


ResumeSchema.set('toJSON', {
transform: (doc, ret): ResumeData => {
return {
id: ret._id,
owner: ret.owner._id.toString(),
createdAt: ret.createdAt,
updatedAt: ret.updatedAt,
version: ret.version,
rawContentLink: ret.rawContentLink,
parsedData: ret.parsedData
};
}
});

export const ResumeModel = mongoose.model('Resume', ResumeSchema);
7 changes: 6 additions & 1 deletion nextstep-backend/src/routes/resume_routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import express, { Request, Response } from 'express';
import Resume from '../controllers/resume_controller';
import { CustomRequest } from "types/customRequest";
import multer from 'multer';
import * as commentsController from "../controllers/comments_controller";

const upload = multer();

Expand All @@ -15,6 +16,10 @@ router.get('/templates', Resume.getTemplates);

router.post('/generate', Resume.generateResume);

router.post('/parseResume', upload.single('resume'), Resume.parseResume);
router.post('/parseResume', upload.single('resume'), (req: Request, res: Response) => Resume.parseResume(req as CustomRequest, res));

// TODO - Use it in the frontend after the parse and upload resume
router.get('/resumeData/:version', (req: Request, res: Response) => Resume.getResumeData(req as CustomRequest, res))


export default router;
5 changes: 3 additions & 2 deletions nextstep-backend/src/services/posts_service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {PostModel } from '../models/posts_model';
import { IPost, PostData } from 'types/post_types';
import {ClientSession, Document} from 'mongoose';
import {Document} from 'mongoose';
import * as mongoose from 'mongoose';
import * as commentsService from './comments_service';
import * as usersService from './users_service';
Expand All @@ -9,7 +9,8 @@ import likeModel from "../models/like_model";
import {CommentData} from "types/comment_types";
import {UserData} from 'types/user_types';
import * as chatService from './chat_api_service';
import {config} from "../config/config";
import {config} from "../config/config"



const postToPostData = async (post: Document<unknown, {}, IPost> & IPost): Promise<PostData> => {
Expand Down
30 changes: 29 additions & 1 deletion nextstep-backend/src/services/resources_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,32 @@ const uploadResume = (req: MulterRequest): Promise<string> => {
});
};

export { uploadImage, uploadResume };
const getResumePath = (filename: string): string => {
const resumesDirectoryPath = config.resources.resumesDirectoryPath();
const resumePath = path.resolve(resumesDirectoryPath, filename);

if (!fs.existsSync(resumePath)) {
throw new TypeError('Resume not found');
}

return resumePath;
}

const getResumeBuffer = (filename: string): Buffer => {
const resumePath = getResumePath(filename);

try {
return fs.readFileSync(resumePath);
} catch (error: any) {
throw new Error(`Failed to read resume file: ${error.message}`);
}
}

const resumeExists = (filename: string): boolean => {
const resumesDirectoryPath = config.resources.resumesDirectoryPath();
const resumePath = path.resolve(resumesDirectoryPath, filename);
return fs.existsSync(resumePath);
};


export { uploadImage, uploadResume, getResumeBuffer, resumeExists };
Loading
Loading