Build a Smart Hiring Assistant with Django, OpenAI, and SendGrid
Time to read: 13 minutes
Build a Smart Hiring Assistant with Django, OpenAI, and SendGrid
In today’s competitive job market, efficiently processing and evaluating resumes can be a challenging task for HR departments and recruiters. Traditional methods are often manual, time-consuming, and prone to human biases and error.
To address these challenges, organizations are increasingly turning to advanced AI technologies. Large Language Models (LLMs), such as GPT-4o, have revolutionized natural language processing with their ability to generate human-like text and understand complex queries. However, while LLMs are powerful, they do have limitations, particularly in terms of accessing and utilizing specific, up-to-date, or contextually relevant information. This is where Retriever-Augmented Generation (RAG) comes into play. It is a state-of-the-art AI model that combines information retrieval with text generation. It excels in understanding and processing complex documents, such as resumes.
In addition to AI-driven resume screening, effective communication with candidates is crucial in the recruitment process. Twilio SendGrid, a trusted email service provider, is known for its reliability and ease of integration. It offers seamless integration with Django, allowing for smooth and efficient email handling without complex configurations. It also handles large volumes of email traffic efficiently, making it suitable for high-volume recruitment processes.
In this tutorial, you will build a smart hiring assistant application using Django as the main framework. By leveraging RAG and OpenAI capabilities, the app can intelligently analyze resumes, extract key information and assess candidates against job requirements. For successful candidates, the application not only notifies them but also forwards their key information to the recruiting team, streamlining the internal review process. It then uses Twilio SendGrid to send automated notifications to candidates, ensuring efficient communication throughout the recruitment process.
Prerequisites
Before you begin, ensure that you have the following:
- Python 3.7 or higher installed globally
- A SendGrid account - Sign up here and you can send up to 100 emails per day at no cost.
- An OpenAI account.
- Basic knowledge of Python and familiarity with Django.
Set up the Project
To start, create your project structure and set up a virtual environment. Open your terminal and run the commands below:
The virtual environment will help you manage your project’s dependencies in isolation, ensuring that they do not conflict with other Python projects you may have.
With your virtual environment activated, install the necessary packages.
These packages power your Django application and enable AI functionalities. The following command installs django
which serves as the framework for web application, openai
for evaluating the resumes, python-dotenv
for environment variable management, sendgrid
for email communication, pdfminer.six
for PDF parsing and finally, python-magic
helps verify file types, enhancing the robustness of file handling in your application.
Next, create a new Django project and app. Run the command below:
This creates your Django project and the main application within it. The Django project serves as the overarching structure for your application, while the app handles specific functionalities. In this case, resume screening.
Open the settings.py file located in the smart_hiring_assistant folder and add Screener to the INSTALLED_APPS
to register your application:
This step is essential for enabling the app's functionalities within the overall project.
It ensures Django recognizes your newly created app.
Also in settings.py, make sure the following lines are added to the top of the code:
These lines ensure Django can find your environment variables and urls.
By completing these steps, you've laid the groundwork for your smart hiring assistant project. The next phase will involve setting up the data models and building out the functionality for resume parsing and analysis. With these foundational components in place, you're well on your way to creating a robust application that leverages AI for efficient recruitment processes.
Set Up the .env File
Create a file named .env in the smart_hiring_assistant directory and add the following lines, replacing the placeholders with your actual API keys and email address. These APIs allow for communication with third party applications. Your application will need an API key to authenticate against OpenAI and sendgrid. The application will read the key configuration from a .env file. Obtain the OpenAI API key and SendGrid credentials on the linked pages if you don’t have them already.
FROM_EMAIL: This is the email address that your application will use to send emails to candidates. It is your verified SendGrid email. This variable is later referenced in the code when setting the sender's email address in the email message.
RECRUITER_EMAIL: This refers to the email that a successful application will be forwarded to. Ensure that the email address provided is a valid one. For the purpose of testing, make sure it's an email address you have access to and can easily check.
Set Up the Data Layer
To manage candidate information and uploaded resumes, define your data model.
Update the code below in the Screener/models.py.
In the Screener app, the model called Resume
captures essential information about candidates and their resumes. This model includes fields for the candidate's name, email, the uploaded resume file, the extracted text from the resume, and a timestamp for when the resume was created.
Run migrations to propagate these changes made to your models into the database schema. Use the command below:
The makemigrations
command generates a migration file that describes the changes to your models, while the migrate
command applies those changes to your database. This process creates the necessary tables and fields for your Resume
model.
Set Up the URL Configuration
In Django, URL configuration is essential for routing the web requests to the appropriate views. Without it, the application won't know where to send users when they try to access certain pages.
In this section, first create a new urls.pyfile in the screener app. This file will route the request to views.py. Add the following code in screener/urls.py :
Next, include this screener URL configuration in the main urls.py file of the project. In the smart_hiring_assistant folder, find the urls.py file that is already there, and, replace the existing code with the following:
This configuration ensures that when you navigate to http://localhost:8000/screener/upload/, the view for uploading resumes will be triggered.
Now that you have successfully set up the URL configuration for routing requests to the appropriate views, the next essential step is to provide a way for users to upload their resumes.
In order for the Django web site to display a custom upload form as in the sample shown below, you will need an html template. For the sake of brevity, the HTML used is omitted from this tutorial. You can find a working template for the upload site here on Github, or create your own upload site. Save this in the templates/Screener folder as demonstrated in the Github.
Create the Upload Form
With your model in place, you need a form to handle resume uploads to allow users to submit files easily. In the Screener folder, create a file forms.py and update it with the code below.
In this code, the ResumeUploadForm
class is derived from forms.ModelForm
, which connects it directly to your Resume
model. This integration allows the form to automatically generate fields corresponding to the model's attributes, specifically for the candidate's name, email, and resume file.
Within the class, you define the model and specify the fields that should be included in the form. To enhance the user interface, the __init__
method customizes the widget attributes for each field.
The form includes validation for the uploaded resume file. The clean_resume_file
method checks whether the uploaded file is indeed a PDF and enforces a size limit of 2MB. If a user attempts to upload a file that does not meet these criteria, a ValidationError is raised, providing clear feedback to the user about the issue.
In the next section, you’ll focus on building the service layer, which will handle the core functionalities of your smart hiring assistant, such as parsing PDFs, analyzing resumes and automating sending of emails.
Build the Service Layer
The service layer will handle the primary functionalities such as parsing PDF files, interacting with OpenAI, utilizing RAG for resume analysis, and sending email notifications via SendGrid for communication.
PDF Parser Service
Extracting text from resumes in PDF format is a key step. To achieve this, create a folder and name it services in the Screener folder. All the services we are about to create will be added to this folder. In the services folder create a file pdf_parser_service.py and update it with the code below.
In this code, the PDFParserService
class contains a static method extract_text_from_pdf
, which takes the path to a PDF file as an argument. The method uses the pdfminer library to extract text from the PDF. If the extraction is successful, it cleans the text by removing excessive whitespace and limits the output to the first 15,000 characters to ensure performance and manageability.
If the extraction fails for any reason, the method catches the exception, prints an error message, and returns a user-friendly message indicating that the text extraction was unsuccessful.
With this PDF Parser Service implemented, your application is equipped to effectively handle resume uploads in PDF format, paving the way for further analysis and processing in the hiring workflow. Next, you will explore how to integrate this service into your application after text extraction.
OpenAI Service
This service will handle interactions with the OpenAI API, analyzing resumes to extract qualifications and match them to job requirements. In the services folder, create a file openai_service.py and update it with the code below.
In this code, the OpenAIService class is designed to handle all interactions with the OpenAI API. The constructor, __init__
method, sets the API key by retrieving it from the environment variables, which ensures that sensitive information is not hardcoded into your application ensuring proper security measures.
The generate_chat_completion
method is the core functionality of this service. It takes a list of messages, which represent the conversation context, and sends a request to the OpenAI API to generate a response. If the request is successful, it returns the API's response. In case of an error such as connectivity issues, the method captures the exception, logs an error message, and raises the exception for further handling.
By implementing the OpenAI Service, your application will be capable of analyzing resumes intelligently and extracting relevant qualifications, making it a powerful tool in the hiring process. In the next section, you will create the RAG (Retriever-Augmented Generation) service, which will leverage both the PDF Parser and OpenAI services to evaluate resumes against job requirements.
RAG Service
The Retriever-Augmented Generation service plays a vital role in your hiring application by assessing resumes against predefined job requirements. This service utilizes the OpenAI service to provide a detailed evaluation, ensuring that candidates are matched accurately to the skills and qualifications necessary for the position.
In the services folder, create a file rag_service.py and update it with the code below.
This RAGService class serves to evaluate resumes against a detailed set of job requirements. The __init__
method initializes the class, storing the job requirements in a formatted string that outlines the qualifications needed for the role.
The process_resume
method orchestrates the evaluation by combining the resume text with the job requirements, generating an assessment via the OpenAI service, and parsing the response to determine whether the candidate meets the qualifications. It ensures that the response contains the required format, handling errors gracefully and providing feedback on the evaluation.
In the _combine_context
method, the job requirements and the applicant's resume text are structured into a context string that will be sent to the OpenAI API for analysis. This structure ensures that the evaluation is thorough and considers various factors and overall candidate strength.
Finally, the _generate_assessment
method prepares the messages for the OpenAI API, guiding it to assess the candidate comprehensively based on the provided context.
With the RAG service in place, your application will be capable of evaluating resumes intelligently, providing a clear decision on whether candidates meet the qualifications for the job. Next, you will create the SendGrid service to handle communication with candidates based on the evaluation results.
SendGrid Service
Finally, the SendGrid service sends automated responses to candidates. To achieve this, create a file sendgrid_service.py in the services folder and update it with the code below.
In this SendGridService
class, the __init__
method initializes the SendGrid client using the API key stored in your environment variables. It also sets the sender's email address and name, which are essential for personalizing the communication with candidates.
The send_rejection_email
method constructs and sends an email to inform candidates that their application will not be moving forward. It creates a plain text message, addressing the recipient by name and thanking them for their interest. If the email fails to send, it catches and logs the error, returning False
.
On the other hand, the forward_successful_applicant
method is designed to notify the hiring team about successful applicants. It builds HTML content for the email, providing detailed information about the applicant and their assessment.
With the SendGrid service in place, your application can now effectively communicate with candidates based on their application outcomes, providing a professional touch to the hiring process. You can now proceed to bind all these components for the entire application workflow.
Implement the View Layer
In this section, you’ll delve into the views.py file where the core functionality of your resume processing application is achieved. The view layer serves as the bridge between the user interface and the underlying logic. The view binds everything together. It handles resume uploads, coordinates with various services, and manages user feedback.
In the file Screener/views.py, update with the code below:
In this section, the views.py
file imports a variety of libraries essential for its operation. Django's modules are included for handling web requests, rendering templates, and managing user messages. Custom services are imported to facilitate interactions with external APIs and manage email communications. The os module is used for file and environment variable management, while datetime handles date and time operations. Together, these libraries enable the application to effectively process resumes, assess qualifications, and communicate with users.
Upload the Resume
The upload_resume
function is the main entry point for handling resume uploads. It responds to HTTP POST requests to process the submitted resume form. In the Screener/views.py, update it with the code below, adding below the code you've already added to the file:
This function begins by checking if the request method is POST, indicating that the form has been submitted. It initializes the ResumeUploadForm
with the submitted data and files. If the form is valid, it saves the uploaded resume and logs the success.
Next, the function initializes the various services required for processing the resume: OpenAIService
, RAGService
, and SendGridService
. If any service fails to initialize, an error message is logged and displayed to the user.
The applicant's data is then prepared, and the screen_resume
function is called to evaluate the resume. Based on the assessment result, success or failure messages are generated and displayed to the user. Finally, it attempts to store the assessment results for future reference.
The upload.html template file is crucial for rendering the form and handling user interactions. Make sure to download or refer to the template file provided in the GitHub repository.
Screen the Resume
The screen_resume
function is responsible for evaluating the uploaded resume using the RAG service. In the Screener/views.py, update it with the code below:
This function begins by extracting the text from the uploaded PDF resume. If the extraction fails, it logs an error and returns a failure response. It then processes the extracted text using the RAG service, which assesses the qualifications against predefined job requirements.
If the assessment is successful and the result is valid, it logs the applicant's qualification status. Depending on whether the applicant meets the requirements, it sends a corresponding email notification through the SendGrid service. This function effectively coordinates resume evaluation and email communication based on the outcome of the assessment.
Extract the Text from PDF
This function is responsible for extracting and cleaning the text from a PDF file. In the Screener/views.py, update it with the code below:
In this function, the PDF text is first extracted and then subjected to a series of cleaning operations. These operations include removing excessive whitespace, filtering out non-ASCII characters, and fixing common OCR errors. The cleaned text is returned, and any issues during the extraction process are logged.
Format the Detailed Assessment
This function formats the detailed assessment results for better readability in email communications. In the Screener/views.py, update it with the code below:
The format_detailed_assessment
function takes the raw assessment text, cleans it up, and wraps it in HTML for better presentation. If an error occurs during formatting, it logs the issue and returns the original assessment text.
As you move forward, test your application to verify everything works as intended.
Test the Application
Having followed all the steps of the tutorial, you should have a complete application.
To test if everything works, save all files and close them. In the terminal of your project, ensure you activate the virtual environment, and run the development server using the following commands:
For Windows users:
For Mac users:
It will give you the following link: http://127.0.0.1:8000. Click the link to open your homepage. You can as well copy the link and paste it in your browser. It should launch a user interface as shown in the screenshot below.
Now fill in the form fields with your respective name, valid email address and upload your resume, then click submit. In this initial submission, use this resume linked to see the response you get. The resumes used here are real case scenarios.
As shown in the screenshot above, this response is received via email for the applicant who does not meet the job qualifications.
Now move on and try with this resume and see what response you get.
In the screenshot above, an email response is sent to the recruiter containing the user's name, email for contacting them, and the assessment. As you can see, the application works as it is intended. Feel free to try with different resumes as well as customizing it with your specific job case.
What's next for Django apps with OpenAI and SendGrid projects?
You’ve created a full-featured, automated resume screening system that:
- Handles file uploads.
- Extracts text from PDFs.
- Analyzes resumes with RAG and OpenAI.
- Communicates with candidates via SendGrid.
With this system, you can streamline and automate your recruitment process, saving time and reducing bias in hiring.
If you're looking to expand or enhance your application further, consider exploring these articles for inspiration:
- How to Send Translated Messages with Google and Django
- Build a Movie Recommendation App with Python, OpenAI, and Twilio SendGrid
- Build an Email Newsletter with Django and Twilio SendGrid
These resources can help you take your smart hiring assistant to the next level!
Jacob Snipes is a passionate software engineer committed to creating intuitive applications and harnessing AI to simplify complex challenges. In his free time, he enjoys mentoring emerging developers and sharing his insights through technical writing.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.