Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
eae3a46
fix(frontend): debug additions and some visual changes
PrivatePerson10 Oct 26, 2025
ffb9b76
fix(ai): remove unneeded logging
galagyy Oct 26, 2025
a667e59
feat(backend): add more careers
galagyy Oct 26, 2025
d26e6ac
feat(backend): update career endpoints
galagyy Oct 26, 2025
c8cfa22
feat(frontend): use new endpoints
galagyy Oct 26, 2025
c6ae994
add(frontend): add dashboard image
galagyy Oct 26, 2025
6400b4b
feat(backend): update blogs endpoint
galagyy Oct 26, 2025
135aa19
fix(frontend): update API calls
galagyy Oct 26, 2025
6476277
fix(backend): blog images
galagyy Oct 26, 2025
e3255bc
feat(frontend): add questionnaire wait
galagyy Oct 26, 2025
10bcde2
feat(frontend): add AI & update blogs
galagyy Oct 26, 2025
766dc73
fix(frontend): improper card
galagyy Oct 27, 2025
de6e077
fix(frontend): adjust topbar & cards
galagyy Oct 27, 2025
97b5794
add: basic blogs data
PrivatePerson10 Oct 28, 2025
94904e6
fix(frontend): main page changes
PrivatePerson10 Oct 28, 2025
9a93448
feat(frontend): add light theme
galagyy Oct 28, 2025
664e796
merge: add light theme
galagyy Oct 28, 2025
24c6c9a
feat(frontend): use groq AI
galagyy Oct 28, 2025
f1177ed
fix(frontend): dashboard preview image
Om-Kasar Oct 28, 2025
bcda776
feat(frontend): center topbar
galagyy Oct 28, 2025
f7426a9
fix(frontend): blog image loading
galagyy Oct 28, 2025
e3f6584
fix(frontend): remove unneeded code
galagyy Oct 28, 2025
6f4e8e6
fix(frontend): autocomplete & career salary
galagyy Oct 28, 2025
bb7d37d
feat(frontend): add better colors
galagyy Oct 28, 2025
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
21 changes: 21 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Environment variables
.env
.env.local
.env*.local

# Node modules
node_modules/

# Build outputs
dist/
build/
.next/
out/

# IDE
.vscode/
.idea/

# OS files
.DS_Store
Thumbs.db
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Aspira offers a variety of features:

## Build
> [!CAUTION]
> **In order to build and/or edit this project, you will need Docker installed!**
> **In order to build and/or edit this project, you will need Docker installed!** You will also need to create a `.env` file with environment variables at the top level.

This project makes us of **Docker containers**, each microservice is housed in its own container to make it easier to run. Docker also may require WSL (Windows Subsystem for Linux) installed on your machine if you are running a Windows device.

Expand Down
28 changes: 19 additions & 9 deletions ai/careers/app/routes/career_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,24 +223,34 @@ async def generate_recommendation(

result = model.predict(user_input, top_k=5)

# match the dto ....................
important_factors_map = {}
if result.get('important_factors'):
for factor in result['important_factors']:
feature_name = factor.get('feature', '')
importance_value = factor.get('importance', 0.0)
if feature_name:
important_factors_map[feature_name] = float(importance_value)

response = {
"success": True,
"top_career": result['top_career'],
"confidence": result['confidence'],
"confidence_percent": result['confidence_percent'],
"top_predictions": result['top_predictions'],
"important_factors": result['important_factors'],
"model_agreement": result['model_agreement'],
"prediction_timestamp": result['prediction_timestamp'],
"model_version": model.config.get('model_version', '2.0.0')
"topCareer": result['top_career'],
"confidence": float(result['confidence']),
"confidencePercent": result['confidence_percent'],
"topPredictions": result['top_predictions'],
"importantFactors": important_factors_map,
"modelAgreement": float(result['model_agreement']),
"predictionTimestamp": result['prediction_timestamp'],
"modelVersion": model.config.get('model_version', '4.0.0')
}

logger.info(
f"Prediction successful: {result['top_career']} "
f"({result['confidence_percent']})"
)

return response


except HTTPException:
raise
Expand Down
137 changes: 46 additions & 91 deletions backend/src/main/java/com/sck/aspira/controller/BlogController.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package com.sck.aspira.controller;

import com.sck.aspira.model.Blog;
import com.sck.aspira.model.BlogSection;
import com.sck.aspira.model.SectionType;
import com.sck.aspira.repository.BlogRepository;
import com.sck.aspira.dto.BlogDto;
import com.sck.aspira.dto.BlogSectionDto;
import com.sck.aspira.service.BlogService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
Expand All @@ -21,125 +19,114 @@
/**
* Represents the {@code BlogController} class that handles blog-related operations.
* This class provides endpoints for creating, retrieving, updating, and deleting blogs.
* All blog responses include IDs for proper identification.
*
* @author Saumil Sharma (Galagyy)
* @version 1.0
* @version 2.0
* @since 10-24-2025
*/
@RestController
@RequestMapping("/api/blogs")
public class BlogController {
private final BlogRepository blogRepository;
private final BlogService blogService;

@Autowired
public BlogController(BlogRepository blogRepository) {
public BlogController(BlogRepository blogRepository, BlogService blogService) {
this.blogRepository = blogRepository;
this.blogService = blogService;
}

/**
* Get all blogs with optional filtering by author or title.
* Returns BlogDto objects that include the blog ID.
*
* @param author Optional author name to filter by
* @param title Optional title text to search for
* @return List of matching blogs
* @return List of matching blogs as BlogDto with IDs
*/
@GetMapping
@Operation(summary = "Get all blogs", description = "Retrieve all blogs with optional filtering by author or title")
public List<Blog> getAllBlogs(
public List<BlogDto> getAllBlogs(
@RequestParam(required = false) String author,
@RequestParam(required = false) String title) {

List<Blog> blogs;
if (author != null) {
return blogRepository.findByAuthor(author);
blogs = blogRepository.findByAuthor(author);
} else if (title != null) {
return blogRepository.findByTitleContainingIgnoreCase(title);
blogs = blogRepository.findByTitleContainingIgnoreCase(title);
} else {
return blogRepository.findAll();
blogs = blogRepository.findAll();
}

return blogService.convertToDtoList(blogs);
}

/**
* Get recent blogs in descending order of creation date.
* Returns BlogDto objects that include the blog ID.
*
* @return List of blogs ordered by recency
* @return List of blogs ordered by recency as BlogDto with IDs
*/
@GetMapping("/recent")
@Operation(summary = "Get recent blogs", description = "Retrieve blogs ordered by recency")
public List<Blog> getRecentBlogs() {
return blogRepository.findRecentBlogs();
public List<BlogDto> getRecentBlogs() {
List<Blog> blogs = blogRepository.findRecentBlogs();

return blogService.convertToDtoList(blogs);
}

/**
* Get a specific blog by its ID.
* Returns BlogDto object that includes the blog ID.
*
* @param id Blog ID
* @return The blog with the specified ID
* @return The blog with the specified ID as BlogDto
*/
@GetMapping("/{id}")
@Operation(summary = "Get blog by ID", description = "Retrieve a specific blog by its ID")
public Blog getBlogById(@PathVariable Long id) {
return blogRepository.findById(id)
public BlogDto getBlogById(@PathVariable Long id) {
return blogService.getBlogDtoById(id)
.orElseThrow(() -> new ResponseStatusException(
HttpStatus.NOT_FOUND, "Blog not found with id: " + id));
}

/**
* Create a new blog.
* Returns the created blog as BlogDto with the generated ID.
*
* @param blogDto Data for creating the blog
* @return The created blog
* @return The created blog as BlogDto with ID
*/
@PostMapping
@Operation(summary = "Create new blog", description = "Create a new blog with sections")
@ResponseStatus(HttpStatus.CREATED)
public Blog createBlog(@RequestBody BlogDto blogDto) {
Blog blog = new Blog();
blog.setTitle(blogDto.getTitle());
blog.setAuthor(blogDto.getAuthor());
blog.setSummary(blogDto.getSummary());

if (blogDto.getSections() != null) {
int position = 0;

for (BlogSectionDto sectionDto : blogDto.getSections()) {
BlogSection section = createSectionFromDto(sectionDto, position++);
blog.addSection(section);
}
}
public BlogDto createBlog(@RequestBody BlogDto blogDto) {
Blog createdBlog = blogService.createBlog(blogDto);

return blogRepository.save(blog);
return blogService.convertToDto(createdBlog);
}

/**
* Update an existing blog.
* Returns the updated blog as BlogDto with ID.
*
* @param id Blog ID
* @param blogDto Updated blog data
* @return The updated blog
* @return The updated blog as BlogDto with ID
*/
@PutMapping("/{id}")
@Operation(summary = "Update blog", description = "Update an existing blog by its ID")
public Blog updateBlog(@PathVariable Long id, @RequestBody BlogDto blogDto) {
Blog existingBlog = blogRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(
HttpStatus.NOT_FOUND, "Blog not found with id: " + id));

existingBlog.setTitle(blogDto.getTitle());
existingBlog.setAuthor(blogDto.getAuthor());
existingBlog.setSummary(blogDto.getSummary());

existingBlog.getSections().clear();

if (blogDto.getSections() != null) {
int position = 0;

for (BlogSectionDto sectionDto : blogDto.getSections()) {
BlogSection section = createSectionFromDto(sectionDto, position++);
existingBlog.addSection(section);
}
public BlogDto updateBlog(@PathVariable Long id, @RequestBody BlogDto blogDto) {
if (!blogService.existsById(id)) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Blog not found with id: " + id);
}

return blogRepository.save(existingBlog);
Blog updatedBlog = blogService.updateBlog(id, blogDto);

return blogService.convertToDto(updatedBlog);
}

/**
Expand All @@ -151,64 +138,32 @@ public Blog updateBlog(@PathVariable Long id, @RequestBody BlogDto blogDto) {
@DeleteMapping("/{id}")
@Operation(summary = "Delete blog", description = "Delete a blog by its ID")
public ResponseEntity<Void> deleteBlog(@PathVariable Long id) {
if (!blogRepository.existsById(id)) {
if (!blogService.existsById(id)) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Blog not found with id: " + id);
}

blogRepository.deleteById(id);
blogService.deleteBlog(id);

return ResponseEntity.noContent().build();
}

/**
* Find blogs created between two dates.
* Returns BlogDto objects that include the blog IDs.
*
* @param startDate Start date for the search range
* @param endDate End date for the search range
* @return List of blogs created within the specified date range
* @return List of blogs created within the specified date range as BlogDto with IDs
*/
@GetMapping("/search/byDate")
@Operation(summary = "Find blogs by date range", description = "Retrieve blogs created between two dates")
public List<Blog> findBlogsByDateRange(
public List<BlogDto> findBlogsByDateRange(
@RequestParam LocalDateTime startDate,
@RequestParam LocalDateTime endDate) {

return blogRepository.findBlogsBetweenDates(startDate, endDate);
}

/**
* Helper method to create a BlogSection from a DTO.
*
* @param dto The DTO containing section data
* @param position The position to assign to the section
* @return The created BlogSection
*/
private BlogSection createSectionFromDto(BlogSectionDto dto, int position) {
switch (dto.getType()) {
case TEXT:
return BlogSection.createTextSection(dto.getTextContent(), position);
case IMAGE:
return BlogSection.createImageSection(dto.getImageUrl(), dto.getImageCaption(), position);
case CODE:
return BlogSection.createCodeSection(dto.getCodeContent(), dto.getLanguage(), position);
case VIDEO:
BlogSection videoSection = new BlogSection();
videoSection.setType(SectionType.VIDEO);
videoSection.setVideoUrl(dto.getVideoUrl());
videoSection.setPosition(position);

return videoSection;
case QUOTE:
BlogSection quoteSection = new BlogSection();
quoteSection.setType(SectionType.QUOTE);
quoteSection.setTextContent(dto.getTextContent());
quoteSection.setPosition(position);

return quoteSection;
default:
throw new IllegalArgumentException("Unsupported section type: " + dto.getType());
}
List<Blog> blogs = blogRepository.findBlogsBetweenDates(startDate, endDate);
return blogService.convertToDtoList(blogs);
}
}

Loading