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
25 changes: 25 additions & 0 deletions HealthCareABApi/BackgroundJobs/CleanupSlotsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,31 @@ public class CleanupSlotsService(IServiceProvider services) : BackgroundService

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Clean on startup
using (var initialScope = _services.CreateScope())
{
try
{
// Kör rensning direkt vid uppstart
var initialDbContext = initialScope.ServiceProvider.GetRequiredService<HealthCareDbContext>();
var currentTime = DateTime.Now;

var expiredSlots = initialDbContext.Availability.Where(a => a.EndTime < currentTime).ToList();

if (expiredSlots.Any())
{
initialDbContext.RemoveRange(expiredSlots);
await initialDbContext.SaveChangesAsync(stoppingToken);

Console.WriteLine($"Deleted {expiredSlots.Count} expired slots on startup - {DateTime.Now:yyyy-MM-dd}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error during initial cleanup: {ex.Message}");
}
}

while (!stoppingToken.IsCancellationRequested)
{
var currentTime = DateTime.Now;
Expand Down
24 changes: 21 additions & 3 deletions HealthCareABApi/Controllers/AppointmentController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@ public async Task<IActionResult> UpdateAppointment([FromBody] UpdateAppointmentD
}
}

[HttpGet("getappointmentsbypatientid/{patientId}")]
public async Task<IActionResult> GetByUserId(int patientId)
[HttpGet("getcompletedappointmentsbyuserid/{userId}")]
public async Task<IActionResult> GetByUserId(int userId)
{
try
{
var appointments = await _appointmentService.GetByUserIdAsync(patientId);
var appointments = await _appointmentService.GetCompletedByUserIdAsync(userId);
return Ok(appointments);
}
catch (KeyNotFoundException)
Expand All @@ -133,5 +133,23 @@ public async Task<IActionResult> GetByUserId(int patientId)
}
}

[Authorize(Roles = Roles.User)]
[HttpGet("getscheduledappointments/{userId}")]
public async Task<IActionResult> GetScheduledAppointments(int userId)
{
try
{
var scheduledAppointments = await _appointmentService.GetScheduledAppointmentsAsync(userId);
return Ok(scheduledAppointments);
}
catch (KeyNotFoundException)
{
return BadRequest();
}
catch (Exception)
{
return StatusCode(500, "Error processing GET method at api/getscheduledappointments");
}
}
}
}
2 changes: 1 addition & 1 deletion HealthCareABApi/Controllers/HistoryController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public async Task<IActionResult> GetAppointmentsForHistory()
return BadRequest("User Id is not valid");
}

var appointments = await _AppointmentRepository.GetByUserIdAsync(userId);
var appointments = await _AppointmentRepository.GetCompletedByUserIdAsync(userId);

if (appointments == null)
{
Expand Down
10 changes: 10 additions & 0 deletions HealthCareABApi/DTO/ScheduledAppointmentsDTO.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace HealthCareABApi.DTO
{
public class ScheduledAppointmentsDTO
{
public int Id { get; set; }
public DateTime AppointmentTime { get; set; }
public string CaregiverName { get; set; }
public string PatientName { get; set; }
}
}
1 change: 1 addition & 0 deletions HealthCareABApi/DTO/UniqueSlotsDTO.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{
public class UniqueSlotsDTO
{
public int Id { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public List<CaregiverDTO> Caregivers { get; set; }
Expand Down
249 changes: 144 additions & 105 deletions HealthCareABApi/Repositories/Implementations/AppointmentRepository.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
using System;
using HealthCareABApi.Models;
using HealthCareABApi.Repositories.Data;
using Microsoft.EntityFrameworkCore;

namespace HealthCareABApi.Repositories.Implementations
{
public class AppointmentRepository : IAppointmentRepository
{
private readonly HealthCareDbContext _Dbcontext;

public AppointmentRepository(HealthCareDbContext context)
{
_Dbcontext = context;
}

public async Task<IEnumerable<Appointment>> GetAllAsync()
{
return await _Dbcontext.Appointment
.Include(a => a.Patient)
.Include(a => a.Caregiver)
.ToListAsync();
using System;
using HealthCareABApi.Models;
using HealthCareABApi.Repositories.Data;
using Microsoft.EntityFrameworkCore;
namespace HealthCareABApi.Repositories.Implementations
{
public class AppointmentRepository : IAppointmentRepository
{
private readonly HealthCareDbContext _Dbcontext;
public AppointmentRepository(HealthCareDbContext context)
{
_Dbcontext = context;
}
public async Task<IEnumerable<Appointment>> GetAllAsync()
{
return await _Dbcontext.Appointment
.Include(a => a.Patient)
.Include(a => a.Caregiver)
.ToListAsync();
}

public async Task<Appointment> GetByIdAsync(int id)
Expand All @@ -39,87 +39,126 @@ public async Task<Appointment> GetByPatientAndTimeAsync(int patientId, DateTime
a.PatientId == patientId &&
EF.Functions.DateDiffSecond(a.DateTime, localTime) == 0 && // Ignorerar millisekunder i databasen och kollar tiden på sekundnivå
a.Status == AppointmentStatus.Scheduled);
}

public async Task<IEnumerable<Appointment>> GetByUserIdAsync(int patientId)
{
return await _Dbcontext.Appointment
.Include(x => x.Caregiver)
.Include(x => x.Patient)
.Where(x => x.PatientId == patientId && x.Status == AppointmentStatus.Completed)
.ToListAsync();
}

public async Task CreateAsync(Appointment appointment)
{
if (appointment == null)
{
throw new ArgumentNullException(nameof(appointment), "Appointment is null and will blow up the system in 3........2........1.........");
}

try
{
var availability = await _Dbcontext.Availability
.FirstOrDefaultAsync(a =>
a.Caregiver.Id == appointment.CaregiverId &&
a.StartTime <= appointment.DateTime &&
a.EndTime > appointment.DateTime &&
!a.IsBooked);

if (availability == null)
{
throw new InvalidOperationException("No available slot for this time.");
}

availability.IsBooked = true;
availability.Appointment = appointment;

await _Dbcontext.Appointment.AddAsync(appointment);

await _Dbcontext.SaveChangesAsync();
//await _Dbcontext.Appointment.AddAsync(appointment);
//await _Dbcontext.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
throw new InvalidOperationException("Database error while creating appointment", ex);
}
catch (Exception ex)
{
throw new InvalidOperationException("Error creating new appointment", ex);
}
}

public async Task<bool> UpdateAsync(int id, Appointment appointment)
{
var exist = await _Dbcontext.Appointment.Where(a => a.Id == id).FirstOrDefaultAsync();

if (exist == null)
{
return false;
}

_Dbcontext.Appointment.Entry(exist).CurrentValues.SetValues(appointment);
await _Dbcontext.SaveChangesAsync();
return true;
}

public async Task DeleteAsync(int id)
{
try
{
await _Dbcontext.Appointment.Where(a => a.Id == id).ExecuteDeleteAsync();
await _Dbcontext.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
throw new InvalidOperationException("Database error while deleting appointment", ex);
}
catch (Exception ex)
{
throw new InvalidOperationException("Error deleting appointment", ex);
}
}
}
}


public async Task<IEnumerable<Appointment>> GetCompletedByUserIdAsync(int userId)
{
return await _Dbcontext.Appointment
.Include(x => x.Caregiver)
.Include(x => x.Patient)
.Where(x => x.PatientId == userId && x.Status == AppointmentStatus.Completed)
.ToListAsync();
}

public async Task CreateAsync(Appointment appointment)
{
if (appointment == null)
{
throw new ArgumentNullException(nameof(appointment), "Appointment is null and will blow up the system in 3........2........1.........");
}

try
{
var availability = await _Dbcontext.Availability
.FirstOrDefaultAsync(a =>
a.Caregiver.Id == appointment.CaregiverId &&
a.StartTime <= appointment.DateTime &&
a.EndTime > appointment.DateTime &&
!a.IsBooked);

if (availability == null)
{
throw new InvalidOperationException("No available slot for this time.");
}

availability.IsBooked = true;
availability.Appointment = appointment;

await _Dbcontext.Appointment.AddAsync(appointment);

await _Dbcontext.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
throw new InvalidOperationException("Database error while creating appointment", ex);
}
catch (Exception ex)
{
throw new InvalidOperationException("Error creating new appointment", ex);
}
}

public async Task<bool> UpdateAsync(int id, Appointment appointment)
{
var exist = await _Dbcontext.Appointment.Where(a => a.Id == id).FirstOrDefaultAsync();

if (exist == null)
{
return false;
}

_Dbcontext.Appointment.Entry(exist).CurrentValues.SetValues(appointment);
await _Dbcontext.SaveChangesAsync();
return true;
}

public async Task DeleteAsync(int id)
{
using (var transaction = await _Dbcontext.Database.BeginTransactionAsync())
{

try
{
var relatedAvailability = await _Dbcontext.Availability
.Where(a => a.AppointmentId == id)
.FirstOrDefaultAsync();

if (relatedAvailability == null)
{
throw new ArgumentNullException("No availability is related to this appointment.");
}

relatedAvailability.AppointmentId = null;
relatedAvailability.IsBooked = false;
await _Dbcontext.SaveChangesAsync(); // Need to update and save appointmentId in selected slot before deleting the appointment cuz of FK constraint

await _Dbcontext.Appointment.Where(a => a.Id == id).ExecuteDeleteAsync();
await _Dbcontext.SaveChangesAsync();

await transaction.CommitAsync();
}
catch (DbUpdateException ex)
{
await transaction.RollbackAsync();
throw new InvalidOperationException("Database error while deleting appointment", ex);
}
catch (Exception ex)
{
await transaction.RollbackAsync();
throw new InvalidOperationException("Error deleting appointment", ex);
}
}
}

public async Task<IEnumerable<Appointment>> GetScheduledAppointmentsAsync(int userId)
{
try
{
return await _Dbcontext.Appointment
.Include(a => a.Caregiver)
.Include(a => a.Patient)
.Where(a => a.PatientId == userId && a.Status == AppointmentStatus.Scheduled && a.DateTime > DateTime.Now)
.ToListAsync();
}
catch (DbUpdateException ex)
{
throw new InvalidOperationException("Database error while fetching scheduled appointments");
}
catch (Exception ex)
{
throw new InvalidOperationException("Error fetching scheduled appointments.", ex);
}
}
}
}

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using HealthCareABApi.DTO;
using HealthCareABApi.Models;

namespace HealthCareABApi.Repositories
Expand All @@ -10,8 +11,9 @@ public interface IAppointmentRepository
Task CreateAsync(Appointment appointment);
Task<bool> UpdateAsync(int id, Appointment appointment);
Task DeleteAsync(int id);
Task<IEnumerable<Appointment>> GetByUserIdAsync(int patientId);
Task<IEnumerable<Appointment>> GetCompletedByUserIdAsync(int userId);
Task<Appointment> GetByPatientAndTimeAsync(int patientId, DateTime appointmentTime);
Task<IEnumerable<Appointment>> GetScheduledAppointmentsAsync(int userId);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ public interface IAppointmentService
Task<Appointment> GetByIdAsync(int id);
Task UpdateAsync(UpdateAppointmentDTO dto);
Task DeleteAsync(int id);
Task<IEnumerable<DetailedResponseDTO>> GetByUserIdAsync(int patientId);
Task<IEnumerable<DetailedResponseDTO>> GetCompletedByUserIdAsync(int userId);
Task<IEnumerable<GetAllAppointmentsDTO>> GetAllAsync();
Task<IEnumerable<ScheduledAppointmentsDTO>> GetScheduledAppointmentsAsync(int userId);
}
}
Loading