Selekt is an AI-powered web application designed to help recruiters manage candidates in a faster, smarter, and more structured way. Upload a CV, and Selekt instantly summarizes the profile, extracts key skills, and organizes everything into a clean, searchable format – no formatting or manual input needed.
The idea for Selekt came from my years working as a web administrator for companies in the recruitment industry. I saw how much valuable candidate information gets lost in inboxes, folders, and outdated spreadsheets. With Selekt, I wanted to build a tool that not only streamlines day-to-day recruitment tasks but also gives recruiters long-term value by helping them build their own candidate pool.
Selekt acts as your personal, always-accessible candidate database – a single place where you can store everything you know about your candidates: CVs, interview notes, test results, links, documents, skills, and more. All profiles are searchable and structured, making it easy to rediscover great candidates for future roles.
This project was created as part of my Full-Stack Software Development diploma at Code Institute and allowed me to bring together my passion for recruitment, psychology, and tech – and turn it into a real, working solution.
💻 View the application live here
You’re welcome to create your own account and test the full flow — or use the test account to explore the features directly:
Login credentials
Username: example-user
Password: EXAMPLEUSERSELEKT1337
This account contains several pre-loaded candidates with test data.
Check out the candidate Isabelle Ström — under "Files" you’ll find a PDF CV.
You can download and re-upload this CV to test the AI-powered PDF upload feature.
Enjoy exploring!
- User Experience (UX)
- Agile Methodology
- Project Overview
- Features
- Tools and Technologies
- Testing
- Deployment
- Credits
This section outlines the user-centered design approach behind Selekt. The project was planned with both recruiter needs and technical feasibility in mind – ensuring a smooth, intuitive experience backed by powerful functionality.
Selekt is built for recruiters who are short on time and often manage multiple roles simultaneously. These users are typically:
- Working in fast-paced environments with high candidate volume
- Using desktop computers as their primary tool (Selekt should be designed desktop-first)
- Needing instant access to structured information without distractions
- Looking for automation, clarity, and speed in their daily workflow
Selekt prioritizes simplicity, responsiveness, and automation – it’s designed to save time, reduce admin tasks, and help recruiters make better decisions, faster.
External users (recruiters) want to:
- Quickly upload and summarize candidate CVs
- Store all candidate information in one place (CVs, notes, test results, links, images)
- Search and filter candidates based on skills, roles, or custom tags
- Assess candidates and related files efficiently
- Reuse previously evaluated candidates in future recruitment
The site owner (admin/developer) wants to:
- Provide a streamlined, user-friendly tool for recruiters
- Store data securely and enable user-based authentication
- Ensure the system is scalable and easy to maintain
- Gain experience with Django, PostgreSQL, and AI APIs in a real-world project
User stories were defined using the MoSCoW prioritization method to ensure a focused and value-driven development.
They are grouped below by priority level and mapped to project epics to support traceability and agile planning.
These features are critical to delivering the main user value and are linked to key epics such as User Authentication, Candidate Management, and AI Upload & Parsing.
- Authentication: Users must be able to sign up, log in, and log out securely.
- Candidate CRUD: Recruiters must be able to add, view, edit, and delete candidate profiles.
- AI CV Parsing: CVs must be uploaded and parsed into structured summaries using AI.
- Data Storage: Extracted data (skills, summary, etc.) must be saved to the database.
- Search & Sort: Recruiters must be able to search and sort candidates effectively.
- Private Access: Each user must only see their own candidate data.
- Account Control: Users must be able to manage and delete their account if needed.
- UI Framework: A clean and styled user interface must support all key workflows.
These features enhance the overall user journey and support better interaction and security. They are included if time permits and are linked to Interface, User Roles, and Performance Feedback.
- Support uploading additional files and notes during candidate creation.
- Allow adding tags or categories to candidates for easier filtering.
- Enable users to mark candidates as favorites for quick reference.
- Paginate long candidate lists and display loading/progress indicators.
- Preview extracted candidate data before saving it.
- Protect URLs and views based on user login status.
These are nice-to-have features that provide additional polish and flexibility. Linked to AI Tools and Candidate Comparison epics.
- Generate ready-to-send email templates for each candidate.
- Compare candidates side by side based on selected criteria.
- Show welcome messages or helpful empty states for better UX.
- Add advanced filtering, search combinations, or bulk actions.
To support the early design process, a few simple wireframes were created using Adobe XD. These focused on illustrating the overall structure of the dashboard, candidate list view, and profile pages. Rather than investing heavily in detailed wireframes, I transitioned early to high-fidelity design in code to better experiment with layout, styling, and interactions in a live environment.
This approach helped me quickly get a sense of how recruiters would navigate and interact with the platform. Since the application is intended for use primarily on desktop by busy recruiters, I adopted a desktop-first design from the start. While the platform is responsive, mobile optimization was not a key focus at this stage.
The initial wireframes served as a basic layout reference and evolved naturally as new features were added and tested. A screenshot of the original wireframes is included below for reference.
The graphic design of Selekt is crafted to communicate clarity, structure, and a sense of trust — all essential traits for a digital product aimed at busy recruiters and HR professionals. Since Selekt is designed as a desktop-first web application, the visual identity is tailored to larger screens and professional use cases, where users need to process and compare information efficiently.
The color palette combines vibrant accents with neutral foundations to create a modern, clean, and professional feel. The overall look aims to be easy on the eyes, while supporting intuitive scanning and usability in high-focus work environments.
-
Primary Colors: Talent Blue
#5453EC, Lime Flash#E1F44B, and Focus Blue#B5CAFFserve as attention-grabbing UI accents. These colors were chosen to energize the interface while giving a unique and recognizable brand identity. Blue represents reliability and digital innovation; yellow conveys energy and potential; and light blue adds a sense of clarity and calm. -
Base Colors: Black
#2C2B2Aand off-white#F3ECE6are used as grounding tones to balance out the bright accents. This ensures strong readability and gives the interface a confident, editorial feel.
Together, these colors give Selekt a confident yet friendly tone — serious enough for recruitment professionals, but dynamic enough to reflect the forward-thinking nature of AI-powered tools.
- Libre Baskerville is used for headings. It’s a serif font that adds a professional and refined tone, making headings stand out and signaling structure.
- Poppins is used for paragraph text. It’s a geometric sans-serif font optimized for screen readability, especially helpful when reading structured content like candidate profiles or tool instructions.
This combination of serif and sans-serif fonts supports both hierarchy and usability, giving the interface a polished and user-focused aesthetic.
The interface uses clean layouts, rounded buttons, and spacious margins to create a calm and approachable environment. Hover states, shadows, and iconography are used sparingly and with intent — keeping attention on the data, not the decorations.
The visual language of Selekt is enriched by a series of custom illustrations that represent the core actions, emotions, and situations recruiters face in their daily work. These illustrations help humanize the interface and guide users through the application in a light-hearted and approachable way — without compromising the professional tone.
A single character — the recruiter persona — appears across all visuals, creating consistency and personality throughout the platform. The character is shown in different contexts: using the tool, celebrating, exploring, and making decisions.
The illustrations were created with ChatGPT and with a soft color palette and rounded shapes to match the UI’s calm and friendly tone. Each image supports a specific scenario or state in the application.
The user interface and overall user experience of Selekt were designed with a clear goal in mind: to create a fast, intuitive, and stress-free tool for busy recruiters. With limited time and a high volume of candidate data, recruiters need a system that feels effortless to navigate and easy to trust.
Selekt was developed with a desktop-first mindset, as the tool is intended to be used primarily in professional settings where large screens and structured layouts are the norm.
The initial focus was on creating UI mockups for the most essential user flows:
- The landing page (public marketing page)
- The login/signup view
- The dashboard with candidate cards and filters
- The candidate profile view
- The edit candidate form
- Various popups (e.g. onboarding, upload options)
- The PDF upload screen
These views were created in Adobe XD. During development, the product evolved beyond the original design scope, and several additional screens were added or refined based on feedback and functionality needs. The visual design followed the same principles throughout to ensure consistency and clarity.
- Clean, distraction-free layout: Users should always understand where they are, what they can do, and what’s next.
- Minimal color usage: Bright accent colors are used sparingly for clarity, drawing focus to key actions like uploading or saving.
- Hierarchy through typography: Headings (Libre Baskerville) create structure, while body text (Poppins) ensures readability.
- Illustrations and feedback: Custom visuals help guide the user through actions, errors, and empty states.
- Accessibility and responsiveness: While optimized for desktop, key elements such as color contrast, tab order, and alt-text have been considered for inclusive use.
Throughout development, several design decisions were adjusted:
- The layout of the candidate cards was optimized for readability when working with large datasets.
- The edit candidate form was simplified and grouped for better overview.
- The upload flow was split into two clear options: AI-based PDF parsing or manual entry.
All changes were guided by the core principle: fast access to relevant candidate information, with minimal effort.
This project is developed using an Agile-inspired workflow with GitHub Projects. See Selekt Github Projekt here.
All functionality is broken down into user stories, written from the user's perspective in the format:
As a user, I want to... so that...
Each user story includes:
- Type: User Story or Epic Story
- Priority: Assigned using the MoSCoW method (Must Have, Should Have, Could Have, Won’t Have)
- Acceptance Criteria: Clear conditions to define when a story is complete
I use GitHub Projects (Kanban board view) to visually organize and track my work:
Backlog: All user stories and epics are added here initiallyReady: Tasks that are prioritized and ready to beginIn Progress: Tasks currently being worked onIn Review: Stories ready for testing or feedbackDone: Completed work
Each issue is tagged with:
- Type (e.g.,
user story,epic story) - Priority (e.g.,
must-have,should-have)
Larger themes are grouped into epic stories. Each epic includes links to related user stories and acts as a checklist of progress.
Example epics: User Authentication, Candidate Management, File Uploads.
The scope of the project evolved over time. For example:
- The original design included only a basic dashboard and candidate profile view.
- During development, new features like a welcome modal, PDF upload flow, and favorites system were added as a result of testing and feedback.
- Several wireframes were revised or expanded to match new user needs.
This reflects an agile mindset — allowing the plan to adapt while keeping the user’s goals in focus.
Story: As a recruiter, I want to upload a CV so that I can automatically generate a structured candidate profile.
Acceptance Criteria:
- The recruiter can select a PDF file
- The file is parsed by the AI and displays extracted data
- The extracted data can be previewed and edited before saving
This section provides a high-level overview of the Selekt application: its purpose, workflows, technical architecture, and structure. Selekt is built using Django and follows an MVC (Model-View-Template) pattern to ensure a scalable and maintainable codebase.
Selekt is a web-based platform designed for recruiters and hiring professionals to streamline candidate management. The goal is to reduce time spent on repetitive admin tasks by using AI to extract and structure CV data, and to make it easier to search, compare, and reuse candidate profiles in future recruitment processes.
The target user is:
- A recruiter or HR manager working primarily on a desktop
- Often under time pressure
- In need of a simple and intuitive system that works “out of the box”
Users interact with the system in the following key flows:
| Step | Action | Purpose |
|---|---|---|
| 1 | Register or log in | Secure access to candidate data |
| 2 | Upload CV or enter candidate manually | Create new structured profiles |
| 3 | Browse candidates via dashboard | View, sort, and filter candidates |
| 4 | View full candidate profile | See skills, experience, notes, and files |
| 5 | Add notes or upload documents | Enrich candidate profile |
| 6 | Favorite candidates | Shortlist the database |
It's important to note that this system is designed to support internal candidate management only. Actions such as contacting candidates, scheduling interviews, or making hiring decisions happen outside of this application. The goal of the platform is to help recruiters collect, organize, and evaluate all available candidate data in one place — including CVs, test results, notes, and other files.
The user journey effectively ends once a candidate has been fully profiled and optionally marked as a favorite for shortlisting purposes. This separation ensures a clear focus on data structuring and decision support, rather than trying to replicate external communication or HR processes.
The core data models in Selekt are designed to represent candidates and their related content in a structured, reusable, and recruiter-friendly way.
| Model | Description | Key Relationships |
|---|---|---|
Candidate |
Main candidate profile with personal info, experience, skills, etc. | ForeignKey to User |
CandidateFile |
Stores uploaded files such as CVs or test results | ForeignKey to Candidate |
Favorite |
Links a user to a candidate they’ve bookmarked | ForeignKey to both User and Candidate (unique together) |
Profile |
Extended user profile, includes profile image and first-login flag | OneToOneField to User |
User |
Django's built-in user model used for authentication | Owns related Candidate, Favorite, and Profile objects |
| Goal | Description |
|---|---|
| Simplify candidate management | Avoid cluttered spreadsheets or disorganized documents |
| Leverage AI to reduce manual tasks | Use OpenAI to extract structured candidate data from uploaded PDFs |
| Speed up recruitment | Use OpenAI to extract skills and experience from CVs instantly |
| Improve reusability | Allow recruiters to save and search through their own talent pool |
| Provide structure | Offer a standardized way to view, edit, and compare candidates |
| Keep it minimal | Focus on core needs — no distractions, no bloated features |
The Selekt project is built using Django and follows the Model-View-Template (MVT) architectural pattern. It is organized into clearly separated folders for candidates-related logic, global project configuration, templates, static files, and media storage. The structure supports modular development, clean routing, and easy feature expansion.
Below is an overview of the core folders and their responsibilities:
| Folder/File | Description |
|---|---|
candidates/ |
The main Django app containing all candidate logic, models, forms, views, and URLs |
├── models.py |
Data models: Candidate, CandidateFile, Favorite, Profile |
├── views.py |
All class-based and function-based views including CRUD, AI upload, dashboard, modals |
├── urls.py |
URL routes for candidate-related features |
├── forms.py |
Form classes for adding/editing candidates |
├── context_processors.py |
Adds profile picture globally to templates |
cleoproject/ |
Root project folder containing settings and global URL config |
├── settings.py |
Project settings including cloud storage, OpenAI integration, login redirects |
├── urls.py |
Top-level URL routes for login, dashboard, and app includes |
templates/ |
HTML templates organized by view (e.g. dashboard, candidate-form, modals) |
static/ |
Static assets (CSS, JavaScript, fonts, images) used in the frontend |
media/ |
Cloudinary-based media handling for uploaded files and profile images |
.env / env.py |
Environment variables for local development (e.g. API keys, debug mode) |
requirements.txt |
Python dependencies |
Procfile |
Used for deployment to Heroku |
manage.py |
Django’s command-line utility |
This setup makes the application modular, extensible, and easy to maintain or scale in the future. The structure follows Django conventions and has been kept clean and consistent throughout development.
The application uses the OpenAI GPT-4o API to automatically extract structured candidate information from uploaded CVs in PDF format.
This feature enhances productivity by transforming unstructured data into prefilled candidate profiles.
The logic is implemented directly in Django views and includes:
- PDF text extraction (via PyMuPDF)
- Prompt engineering and response handling
- JSON prefill into the candidate form via session
This section outlines all core and additional features included in Selekt, along with changes made during development and planned improvements. The application has been built with a focus on usability, speed, and structured candidate management.
The dashboard includes a basic search bar where users can search candidates by name, title, location, or top skills. While functional, the search currently uses simple substring matching. A more advanced implementation using AI logic with fuzzy search or weighted fields could improve result accuracy in future iterations.
| Planned Feature | Description |
|---|---|
| Bulk Upload | Allow multiple PDFs to be parsed at once |
| AI Interview Notes Summarizer | Upload notes and get a clean summary |
| Candidate Comparison Tool | Compare two candidates side-by-side |
| Tags & Smart Filters | Add custom tags and more flexible filtering |
Selekt was developed using a modern full-stack toolchain, combining proven backend technologies with flexible frontend libraries and cloud-based services.
| Language | Usage |
|---|---|
| Python | Core backend logic using Django framework |
| HTML5 | Templates for user interface and structure |
| CSS3 | Styling, responsive layout, and custom branding |
| JavaScript | Frontend interactivity (modals, dynamic forms, tag inputs) |
| Tool | Purpose |
|---|---|
| Django | Main web framework (MVT pattern, authentication, ORM) |
| Bootstrap (via CDN) | Responsive layout and component structure |
| Trumbowyg | WYSIWYG editor for candidate profile fields |
| Tagify | Skill tag input system for comma-separated fields |
| Cloudinary + Cloudinary Storage | File/image upload handling for candidate profile pictures and CVs |
| OpenAI (gpt-4) | AI integration for CV parsing and profile generation |
| Tool | Role |
|---|---|
| Git & GitHub | Version control, branching, issues, and epics |
| GitHub Projects | Agile board for user stories and MoSCoW prioritization |
| Visual Studio Code (VS Code) | Local development, debugging, and extension support |
| SQLite | Default development database for local testing |
| PostgreSQL (Heroku-ready) | Cloud-compatible database for deployment scenarios |
| Tool | Description |
|---|---|
| Heroku | Cloud platform for deployment and production hosting |
| Whitenoise | Static file management for production |
| dj-database-url | Simplifies cloud DB configuration in settings.py |
| env.py | For environment variable handling across local and prod |
| Adobe XD | Used for wireframing, UI design, and ERD sketches |
| Markdown | Used extensively for documenting user stories, README and planning |
This project includes both automated and manual testing to ensure robust functionality, user safety, and a seamless experience across devices and browsers. All key flows such as authentication, CRUD operations, AI processing, and session handling have been tested.
Automated tests were implemented using Django's TestCase framework and run via:
python manage.py test| Test Name | Description |
|---|---|
test_dashboard_loads |
Authenticated users can access the dashboard |
test_dashboard_requires_login |
Anonymous users are redirected to login |
test_create_candidate |
A candidate can be created via the form |
test_update_candidate |
Candidate data can be updated |
test_delete_candidate |
Candidates can be removed |
test_user_cannot_access_others_candidate |
Protects data privacy between users |
test_toggle_favorite |
AJAX request toggles favorite and returns correct JSON |
test_call_openai_returns_expected_data |
Mocks OpenAI, validates structure |
test_candidate_create_form_prefills_from_session |
Session prefill is correctly parsed into form fields |
test_welcome_modal_shown_only_once |
Welcome modal appears only once on first login |
All tests pass successfully
Measured using coverage.py:
coverage run manage.py test
coverage report
coverage html| File | Coverage |
|---|---|
candidates/tests.py |
100% |
views.py |
57% – All critical views tested |
models.py |
76% – Core logic and methods |
| Other files (forms/context processors) | Partial |
| Boilerplate/migrations | Excluded |
💡 Total project coverage: 74%
In addition to automated testing, this project was thoroughly manually tested to ensure robustness, proper validation, and user experience across different usage scenarios. Each test includes a description of the area, the expected result, the actual result, and optionally a screenshot for visual proof.
- Python 3.11
- Django TestCase (unit and view tests)
- unittest.mock (mocking external APIs like OpenAI)
- coverage.py (code coverage reports)
- Bootstrap modals and JavaScript for frontend behavior simulation
The following features were considered "Could Have" and may be added in future iterations:
| Feature | Description |
|---|---|
| Improved formatting of OpenAI responses | Using Markdown or structured HTML tags to enhance readability of generated candidate summaries. |
| Sticky candidate sidebar | Keeping key candidate information visible while scrolling the dashboard. |
| More extensive validation | Such as handling empty form submissions or displaying custom messages for missing candidate data. |
| Upload loading indicator | Add a visual loading animation or progress indicator when uploading files to improve feedback and avoid user confusion. |
To evaluate the overall performance, accessibility, and SEO compliance of the application, I used Lighthouse in Chrome DevTools on the deployed version of the site (outside the login wall).
Since the platform requires authentication, full Lighthouse testing was only possible on public-facing pages.
The performance scores on the index page were excellent:
| Page | Mobile | Desktop |
|---|---|---|
| Index | ![]() |
![]() |
All templates were validated via W3C Validator.
| File | Validator Result | Comments |
|---|---|---|
base.html |
✅ Valid | - |
dashboard.html |
✅ Valid | - |
candidate-form.html |
✅ Valid | - |
login.html |
✅ Valid | - |
404.html |
✅ Valid | - |
add-candidate.html |
✅ Valid | - |
base-auth.html |
✅ Valid | - |
candidate-form.html |
✅ Valid | - |
file-list.html |
✅ Valid | - |
index.html |
✅ Valid | - |
modals.html |
✅ Valid | - |
settings.html |
✅ Valid | - |
signup.html |
✅ Valid | - |
upload-candidate.html |
✅ Valid | - |
Validated using W3C CSS Validator.
| File | Validator Result | Comments |
|---|---|---|
style.css |
✅ Valid | - |
Tested via JSLint. ES6 syntax was enabled via /* jslint esversion: 6 */.
All remaining warnings were reviewed and documented below. No functional issues were found.
| File | Validator Result | Comments |
|---|---|---|
main.js |
✅ Valid | initUploadScript is defined in another file and used globally; warning can be ignored. |
editor.js |
✅ Valid | $ is provided by jQuery; showAddLinkForm, addLinkRow, and removeLink are used in HTML or reserved for flexibility. |
fileUpload.js |
✅ Valid | openDeleteFileModal is defined elsewhere; initUploadScript and reloadFiles are reserved for future use. |
modals.js |
✅ Valid | bootstrap and global functions are defined externally; modal functions are triggered via HTML. |
favorites.js |
✅ Valid | toggleFavorite is used in HTML; warning can be ignored. "?" line break warning is stylistic only. |
sorting.js |
✅ Valid | Line 16 warning is stylistic; code works as intended. |
uploadCandidate.js |
✅ Valid | Line break before "?" is stylistic and does not affect execution; code runs as intended. |
utils.js |
✅ Valid | updateHiddenLinks is defined in another file and used globally; warning can be ignored. |
welcomeModal.js |
✅ Valid | Optional chaining requires ES11; works in all modern browsers. bootstrap is defined via CDN. |
All Python files were validated using flake8 in VS Code.
No critical issues were found.
| File | flake8 Result | Comments |
|---|---|---|
views.py |
✅ Valid | - |
models.py |
✅ Valid | - |
forms.py |
✅ Valid | - |
urls.py |
✅ Valid | (app file) |
urls.py |
✅ Valid | (project file) |
tests.py |
✅ Valid | - |
admin.py |
✅ Valid | - |
Notes
Some lines in settings.py and env.py exceed the standard PEP8 line length (79–119 characters) particularly for API keys.
These have been kept as single lines intentionally for clarity and to follow Django and third-party library documentation more closely.
This deviation is considered acceptable in a production context and does not affect performance, security, or maintainability.
This section outlines key bugs discovered during development and testing, along with how they were resolved. These fixes improved form functionality, file handling, frontend behavior, and data persistence.
Issue:
Uploaded profile images were not saved correctly when creating a new candidate. The form silently failed due to missing file encoding setup.
Cause:
The form lacked the required enctype="multipart/form-data" attribute, preventing proper file transmission.
Fix:
The <form> tag was updated to include enctype="multipart/form-data":
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
...
</form>✅ After the fix, both text and image data were saved as expected to /media/profile_images/.
Issue:
While editing a candidate allowed saving links correctly, creating a new candidate did not.
Cause:
Links were not added to form.instance before saving in the CandidateCreateView.
Fix:
Combined link data (name:::url;;;) is now injected into form.instance.links before saving. Example:
link_names = self.request.POST.getlist('link_names')
link_urls = self.request.POST.getlist('link_urls')
combined_links = ''
for name, url in zip(link_names, link_urls):
if name and url:
combined_links += f'{name}:::{url};;;'
form.instance.links = combined_links| Action | When Links Are Saved |
|---|---|
| Add | Before form_valid() |
| Edit | After form_valid() |
Issue:
The Trumbowyg WYSIWYG editor stopped working. Toolbar was missing and error Uncaught ReferenceError: $ is not defined appeared.
Cause:
jQuery was not fully loaded before the editor script was executed.
Fix:
- Ensured all jQuery-based logic is placed inside
$(document).ready(...) - Adjusted script load order: jQuery → Trumbowyg → Bootstrap → Custom scripts
✅ The editor now loads properly and supports formatting on both create and edit forms.
Issue:
Dynamically adding links caused JS errors:
linksArray is not defined, jsonInput is not defined.
Cause:
The JavaScript function updateHiddenLinks() referenced undefined variables and assumed missing DOM nodes.
Fix:
- Declared all expected variables at the top of the script.
- Added
nullchecks before accessing DOM. - Included missing hidden fields in the HTML:
<input type="hidden" id="linksField" name="linksField">
<input type="hidden" id="hiddenLinksInput" name="linksJson">✅ Links can now be added and removed dynamically without breaking the form.
To prevent JS errors on pages missing upload elements, the initUploadScript() function now includes:
if (!document.querySelector("#uploadBox")) {
console.warn("Upload script not initialized: missing DOM elements.");
return;
}This ensures modular script behavior across templates.
| Issue | Status |
|---|---|
aria-hidden warning on modal focus |
Non-blocking, ignored |
| Back to candidates after editing | Returns to dashboard, not modal – future UX improvement |
| Basic search logic | Works but lacks fuzzy logic or relevancy weighting |
| Page titles | Static <title> tags – should be dynamic for SEO |
This section describes how the Selekt application was deployed for both local development and live production using Heroku.
To run the project locally:
- Clone the repository:
git clone https://github.com/fridalannerstrom/selekt.git
cd selekt- Create a virtual environment:
python -m venv venv
source venv/bin/activate # macOS/Linux
venv\Scripts\activate # Windows- Install dependencies:
pip install -r requirements.txt- Create
.envorenv.pyfile and add your environment variables:
SECRET_KEY=your_secret_key
DEBUG=True
CLOUDINARY_CLOUD_NAME=...
CLOUDINARY_API_KEY=...
CLOUDINARY_API_SECRET=...
OPENAI_API_KEY=...- Run migrations:
python manage.py migrate- Start the server:
python manage.py runserverThen visit http://localhost:8000 to use the application locally.
The project is version controlled using Git and hosted on GitHub:
Best practices followed:
- Commits are small and descriptive
- Feature branches used for development
- GitHub Projects used to track epics, user stories, and bugs
.envand.sqlite3files excluded via.gitignore
The live version is hosted on Heroku, using PostgreSQL and Cloudinary for file storage.
Steps followed:
- Create Heroku app:
heroku create selekt-app-name- Add Heroku PostgreSQL add-on:
heroku addons:create heroku-postgresql:hobby-dev- Set environment variables in Heroku Dashboard:
SECRET_KEYDEBUG = FalseCLOUDINARY_*OPENAI_API_KEY
- Update settings.py:
- Used
dj_database_urlto connect to PostgreSQL - Added
whitenoisefor static file handling - Set
STATIC_ROOTandMEDIA_ROOT
- Configure Cloudinary:
- Cloud-based storage for profile images and uploaded files
- Used
cloudinary_storagefor bothDEFAULT_FILE_STORAGEandSTATICFILES_STORAGE
- Push code to Heroku:
git push heroku main- Run migrations and create superuser:
heroku run python manage.py migrate
heroku run python manage.py createsuperuser- Open the live app:
heroku openThe project uses environment variables to manage sensitive settings like SECRET_KEY and DEBUG.
DEBUGis set using:DEBUG = os.environ.get("DEBUG", "False") == "True".- This ensures
DEBUG=Truelocally for development, andDEBUG=Falsein production (e.g., on Render). - No secrets are committed to the repository.
- All secret keys are stored in environment variables, which are managed through Render's environment settings.
- Static files are served via Whitenoise
- Media files (CVs, profile images) are stored in Cloudinary
- Database is automatically switched between SQLite (local) and PostgreSQL (Heroku) using
dj_database_url - Environment variables are securely managed via
.env(local) and Heroku Config Vars (production) DEBUGis turned off in production, and all secrets are hidden from Git
This section lists tools, resources, and people who contributed directly or indirectly to the creation of Selekt.
| Resource | Usage |
|---|---|
| Unsplash | Candidate profile images (placeholder content) |
| Font Awesome | Icons used throughout the UI |
| Mockup Generator | Device mockups for documentation and README |
| Adobe XD | UI wireframes and design prototypes |
| CloudConvert | Converted image formats (e.g., PNG to WebP) |
| ChatGPT | Helped generate placeholder CVs, summaries, UX content, and illustrations for UI/README |
| Source | Contribution |
|---|---|
| Code Institute | Course structure, guidance, and the Full Stack framework |
| Django | Main backend framework and detailed documentation |
| Bootstrap | Frontend components and responsive layout system |
| W3Schools | Quick references for HTML, CSS, JavaScript |
| Conventional Commits | Helped shape meaningful commit messages and structure version control habits |
A huge thank you to:
- My mentor Rory Patrick – for his support, code reviews, and valuable guidance throughout the project.
- Code Institute – for building the foundation and structure that made this full-stack journey possible.
- The developer community – Stack Overflow, GitHub discussions, and Discord chats were incredibly helpful during tricky debugging moments.
- Friends and testers – who gave early feedback, found UI quirks, and helped test features.
This project was created by Frida Lannerström as part of Portfolio Project 4 in the Full Stack Developer Diploma at Code Institute.














































