private IEnumerable <SlotDto> GetSlotsForAvailability(OfficeHoursTeacherAvailabilityDto availabilityDto, DateTime dateStart, DateTime dateEnd, int lmsUserId, OfficeHoursTeacherAvailability availability) { var slots = new List <SlotDto>(); var availabilityStart = availabilityDto.PeriodStart; var checkStart = availabilityStart > dateStart ? availabilityStart : dateStart; var periodEnd = availabilityDto.PeriodEnd.Date.AddDays(1); var checkEnd = periodEnd < dateEnd ? periodEnd : dateEnd; var dayCheckStart = checkStart.Date; var dayCheckEnd = checkEnd.Date.AddDays(1); var dbSlots = availability?.Slots; while (dayCheckStart < dayCheckEnd) { if (availabilityDto.DaysOfWeek.Contains((int)dayCheckStart.DayOfWeek)) { //dayly cycles for intervals foreach (var interval in availabilityDto.Intervals) { var intervalCheckStart = dayCheckStart.AddMinutes(interval.Start) > checkStart ? dayCheckStart.AddMinutes(interval.Start) : new DateTime(checkStart.Year, checkStart.Month, checkStart.Day, checkStart.Hour, 0, 0, DateTimeKind.Utc) .AddMinutes((checkStart.Minute / availabilityDto.Duration + (checkStart.Minute % availabilityDto.Duration == 0 ? 0 : 1)) * availabilityDto.Duration); if (intervalCheckStart < checkStart || intervalCheckStart >= checkEnd || intervalCheckStart >= availabilityDto.PeriodEnd) //check if interval start is inside of period that we need to check { continue; } var intervalCheckEnd = interval.End > interval.Start ? dayCheckStart.AddMinutes(interval.End) : dayCheckStart.AddDays(1).AddMinutes(interval.End); while (intervalCheckStart < intervalCheckEnd) { var dbSlot = dbSlots?.FirstOrDefault(x => x.Start == intervalCheckStart); // && end slots.Add(dbSlot == null ? new SlotDto { Start = intervalCheckStart, End = intervalCheckStart.AddMinutes(availabilityDto.Duration) } : ConvertToDto(dbSlot, lmsUserId)); intervalCheckStart = intervalCheckStart.AddMinutes(availabilityDto.Duration); } } } dayCheckStart = dayCheckStart.AddDays(1); } return(slots); }
public async Task <IEnumerable <SlotDto> > ValidateSlotsRange(OfficeHours oh, int lmsUserId, OfficeHoursTeacherAvailabilityDto availabilityDto) { var currentSlots = await GetSlots(oh.Id, lmsUserId, DateTime.UtcNow, DateTime.UtcNow.AddYears(1)); var slotsToCheck = GetSlotsForAvailability(availabilityDto, DateTime.UtcNow, DateTime.UtcNow.AddYears(1), lmsUserId, null); var overlappingSlots = currentSlots.Where(cs => slotsToCheck.Any( x => (cs.Start <= x.Start && cs.End > x.Start) || (cs.Start < x.End && cs.End >= x.End))); //slotsToCheck.Any(x => currentSlots.Any(cs => (checkEmptySlots || cs.Status != 0) && ((cs.Start<= x.Start && cs.End > x.Start) || (cs.Start < x.End && cs.End >= x.End))))) //return false; return(overlappingSlots); }
private OfficeHoursTeacherAvailabilityDto BuildAvailabilityBySlots(IEnumerable <SlotDto> slots, long timeshift, int duration) { var orderedSlots = slots.OrderBy(x => x.Start); var availabilityStart = orderedSlots.First().Start; var availabilityEnd = orderedSlots.Last().End; var availability = new OfficeHoursTeacherAvailabilityDto { Duration = duration, PeriodEnd = availabilityEnd.AddMilliseconds(timeshift), PeriodStart = availabilityStart.AddMilliseconds(timeshift), Intervals = BuildIntervalsBySlots(orderedSlots, timeshift, duration) }; availability.DaysOfWeek = new[] { (int)availability.PeriodStart.DayOfWeek }; //assuming that we move only one day return(availability); }
public async Task <OperationResultWithData <OfficeHoursTeacherAvailabilityDto> > AddAvailabitily([FromBody] OfficeHoursTeacherAvailabilityDto dto) { LmsCourseMeeting meeting = _lmsCourseMeetingModel.GetOneByCourseAndId(LmsCompany.Id, CourseId, dto.MeetingId); if (meeting?.OfficeHours == null) { return(OperationResultWithData <OfficeHoursTeacherAvailabilityDto> .Error("Meeting not found")); } var result = await _officeHoursService.AddAvailability(meeting.OfficeHours, Session.LmsUser, dto); return(result); }
private static OfficeHoursTeacherAvailability ConvertFromDto(OfficeHours oh, LmsUser lmsUser, OfficeHoursTeacherAvailabilityDto availabilityDto) { var entity = new OfficeHoursTeacherAvailability { User = lmsUser, Duration = availabilityDto.Duration, Intervals = string.Join(",", availabilityDto.Intervals.Select(x => $"{x.Start}-{x.End}")), DaysOfWeek = string.Join(",", availabilityDto.DaysOfWeek), PeriodStart = availabilityDto.PeriodStart, PeriodEnd = availabilityDto.PeriodEnd, Meeting = oh }; return(entity); }
public async Task <OperationResultWithData <OfficeHoursTeacherAvailabilityDto> > AddAvailability(OfficeHours oh, LmsUser lmsUser, OfficeHoursTeacherAvailabilityDto availabilityDto) { var overlappingSlots = await ValidateSlotsRange(oh, lmsUser.Id, availabilityDto); if (overlappingSlots.Any()) { return(OperationResultWithData <OfficeHoursTeacherAvailabilityDto> .Error( "The range of dates overlaps another date range. Please choose another date range.")); } var entity = ConvertFromDto(oh, lmsUser, availabilityDto); _availabilityModel.RegisterSave(entity, true); return(ConvertToDto(entity).ToSuccessResult()); }
public async Task <OperationResultWithData <SlotDto> > RescheduleSlot(int slotId, LmsUser lmsUser, RescheduleSlotDto dto) { if (dto.Start < DateTime.UtcNow) { return(OperationResultWithData <SlotDto> .Error("Upcoming slots couldn't be moved for elapsed time")); } var slot = _slotModel.GetOneById(slotId).Value; //var slot = await _context.OhSlots.Include(x => x.Availability).ThenInclude(x => x.Meeting).FirstOrDefaultAsync(x => x.Id == slotId); if (slot == null) { return(OperationResultWithData <SlotDto> .Error("Slot not found")); } var currentSlots = await GetSlots(slot.Availability.Meeting.Id, lmsUser.Id, DateTime.UtcNow, DateTime.UtcNow.AddYears(1)); var busySlot = currentSlots.FirstOrDefault(x => x.Start == dto.Start); if (busySlot != null) { if (busySlot.Status == (int)OfficeHoursSlotStatus.Free) //free { if ((busySlot.End - busySlot.Start).Minutes != slot.Availability.Duration) { return(OperationResultWithData <SlotDto> .Error( "Another slot with different duration is available at the same time.")); } var slotResult = await AddSlots(slot.Availability.Meeting.Id, slot.User, new[] { new CreateSlotDto { Start = dto.Start, End = dto.Start.AddMinutes(slot.Availability.Duration), Questions = slot.Questions, Subject = slot.Subject } }); if (slotResult.IsSuccess) { var oldSlotResult = await DeleteSlot(slotId, lmsUser); return(slotResult.Data.First().ToSuccessResult()); } //todo: send email return(OperationResultWithData <SlotDto> .Error(slotResult.Message)); } else { return(OperationResultWithData <SlotDto> .Error( "Another slot has already been booked at this time or time is not available for booking.")); } } if (currentSlots.Any(cs => (cs.Start <= dto.Start && cs.End > dto.Start) || (cs.Start < dto.Start.AddMinutes(slot.Availability.Duration) && cs.End >= dto.Start.AddMinutes(slot.Availability.Duration)))) { return(OperationResultWithData <SlotDto> .Error( "Another slot with different duration is available at the same time.")); } var availabilityDto = new OfficeHoursTeacherAvailabilityDto { Intervals = new List <AvailabilityInterval> { new AvailabilityInterval { Start = dto.Start.Hour * 60 + dto.Start.Minute, End = dto.Start.Hour * 60 + dto.Start.Minute + slot.Availability.Duration } }, PeriodStart = dto.Start, PeriodEnd = dto.Start.AddMinutes(slot.Availability.Duration), Duration = slot.Availability.Duration, DaysOfWeek = new[] { (int)dto.Start.DayOfWeek } }; var availabilityResult = await AddAvailability(slot.Availability.Meeting, lmsUser, availabilityDto); if (!availabilityResult.IsSuccess) { return(OperationResultWithData <SlotDto> .Error($"Error during slot time move. {availabilityResult.Message}")); } var newSlotResult = await AddSlots(slot.Availability.Meeting.Id, slot.User, new[] { new CreateSlotDto { Start = dto.Start, End = dto.Start.AddMinutes(slot.Availability.Duration), Questions = slot.Questions, Subject = slot.Subject } }); if (newSlotResult.IsSuccess && !string.IsNullOrEmpty(dto.Message)) { //var meeting = slot.Availability.Meeting; //var details = await _meetingService.GetMeetingApiDetails(meeting); //await _notificationService.SendOHRescheduleEmail(slot.Start, newSlotResult.Data.First(), details.Topic, dto.Message); } var oldSlotResultAdd = await DeleteSlot(slotId, lmsUser); //todo: send email return(newSlotResult.IsSuccess ? newSlotResult.Data.First().ToSuccessResult() : OperationResultWithData <SlotDto> .Error(newSlotResult.Message)); }