/// <summary> /// Resumes a <see cref="Altask.Data.Model.Occurrence"/>. /// </summary> /// <param name="occurrenceId"></param> /// <param name="userId"></param> /// <returns>Returns a <see cref="Altask.Data.EntityResult"/> indicating success or failure.</returns> public virtual async Task <ActionResult> Resume(long occurrenceId, int userId) { ThrowIfDisposed(); var userEntity = await Context.Users.FindAsync(userId); if (userEntity == null) { return(BadRequest(ErrorDescriber.DoesNotExist("User"))); } var occurrenceEntity = await Context.Occurrences.FindAsync(occurrenceId); if (occurrenceEntity == null) { return(BadRequest(ErrorDescriber.DoesNotExist("Occurrence"))); } if (!occurrenceEntity.Started.GetValueOrDefault(false)) { return(BadRequest(ErrorDescriber.DefaultError("Occurrence has not been started."))); } if (occurrenceEntity.Completed.GetValueOrDefault(false)) { return(BadRequest(ErrorDescriber.DefaultError("Occurrence has already been completed."))); } if (!occurrenceEntity.StoppedOn.HasValue) { return(BadRequest(ErrorDescriber.DefaultError("Occurrence cannot be resumed. The occurrence was not stopped."))); } occurrenceEntity.ResumedBy = userEntity.UserName; occurrenceEntity.ResumedOn = DateTime.Now; occurrenceEntity.Logs.Add(new Data.Model.OccurrenceLog() { Type = "Resumed" }); Context.Entry(occurrenceEntity).State = EntityState.Modified; var result = await Context.SaveChangesAsync(); if (result.Succeeded) { await AfterUpdateAsync(occurrenceEntity); var taskEntity = await Context.Tasks.FindAsync(occurrenceEntity.TaskId); var scheduleEntity = await Context.Schedules.FindAsync(occurrenceEntity.ScheduleId); var instance = TaskInstance.FromSchedule(taskEntity, occurrenceEntity.Date, scheduleEntity).MergeOccurrence(occurrenceEntity); SignalRHub.NotifyOccurrenceCreate(null, instance, occurrenceEntity.ToDto()); return(Ok(new { instance = instance })); } else { return(BadRequest(result)); } }
private async Task <EntityResult <TaskInstance> > AddSingleOccurrence(Data.Model.Task task, Data.Model.Schedule scheduleEntity, int?assetId, int?userId, DateTime date) { date = date.LessSeconds(); Context.Entry(task).Reference(t => t.Form).Load(); var newOccurrence = new Altask.Data.Model.Occurrence() { AsEarlyAsDate = date, Date = date, FormModel = task.Form.PublishedModel, TaskId = task.Id, TimeSpent = 0, ScheduleId = scheduleEntity.Id, }; var assetEntity = await Context.Assets.FindAsync(assetId); if (assetEntity != null) { newOccurrence.AssetId = assetEntity.Id; } var userEntity = await Context.Users.FindAsync(userId); if (userEntity != null) { newOccurrence.UserId = userEntity.Id; } if (assetEntity == null && userEntity == null) { return(EntityResult <TaskInstance> .Failed(ErrorDescriber.DefaultError("An occurrence must have either and asset or user associated to it."))); } newOccurrence.Logs.Add(new Data.Model.OccurrenceLog() { Type = "Created" }); Context.Occurrences.Add(newOccurrence); BeforeCreate(newOccurrence, newOccurrence.ToDto()); var result = await Context.SaveChangesAsync(); if (result.Succeeded) { await AfterCreateAsync(newOccurrence, true); var instance = TaskInstance.FromSchedule(task, newOccurrence.Date, scheduleEntity).MergeOccurrence(newOccurrence); SignalRHub.NotifyOccurrenceCreate(null, instance, newOccurrence.ToDto()); return(EntityResult <TaskInstance> .Succeded(instance)); } else { return(EntityResult <TaskInstance> .Failed(result.Errors.ToArray())); } }
internal override async Task <EntityResult> AfterUpdateAsync(Data.Model.Occurrence entity, bool notifyAll = false) { await Context.Entry(entity).Reference(e => e.Asset).LoadAsync(); await Context.Entry(entity).Reference(e => e.User).LoadAsync(); await Context.Entry(entity).Reference(e => e.Task).LoadAsync(); await Context.Entry(entity).Reference(e => e.Schedule).LoadAsync(); var instance = TaskInstance.FromSchedule(entity.Task, entity.Date, entity.Schedule).MergeOccurrence(entity); SignalRHub.NotifyOccurrenceUpdate(ClientId, instance, entity.ToDto()); return(EntityResult.Succeded(0)); }
/// <summary> /// Completes a <see cref="Altask.Data.Model.Occurrence"/>. /// </summary> /// <param name="occurrenceId"></param> /// <param name="userId"></param> /// <param name="formModel"></param> /// <returns>Returns a <see cref="Altask.Data.EntityResult"/> indicating success or failure.</returns> public virtual async Task <ActionResult> Complete(long occurrenceId, int userId, string formModel) { ThrowIfDisposed(); if (formModel == null) { return(BadRequest(ErrorDescriber.DefaultError("A valid FormModel representing the occurrences results must be specified."))); } var userEntity = await Context.Users.FindAsync(userId); if (userEntity == null) { return(BadRequest(ErrorDescriber.DoesNotExist("User"))); } var occurrenceEntity = await Context.Occurrences.FindAsync(occurrenceId); if (occurrenceEntity == null) { return(BadRequest(ErrorDescriber.DoesNotExist("Occurrence"))); } if (!occurrenceEntity.Started.GetValueOrDefault(false)) { return(BadRequest(ErrorDescriber.DefaultError("Occurrence cannot be completed. The occurrence has not been started."))); } if (occurrenceEntity.Completed.GetValueOrDefault(false)) { return(BadRequest(ErrorDescriber.DefaultError("Occurrence already completed"))); } if (occurrenceEntity.StoppedOn.HasValue && !occurrenceEntity.ResumedOn.HasValue) { return(BadRequest(ErrorDescriber.DefaultError("Occurrence cannot be completed. The occurrence was stopped and has not yet been resumed."))); } occurrenceEntity.Completed = true; occurrenceEntity.CompletedBy = userEntity.UserName; occurrenceEntity.CompletedOn = DateTime.Now; occurrenceEntity.FormModel = formModel; occurrenceEntity.TimeSpent += (int)DateTime.Now.Subtract(occurrenceEntity.ResumedOn.GetValueOrDefault(occurrenceEntity.StartedOn.Value)).TotalSeconds; occurrenceEntity.StoppedBy = null; occurrenceEntity.StoppedOn = null; occurrenceEntity.ResumedBy = null; occurrenceEntity.ResumedOn = null; occurrenceEntity.Logs.Add(new Data.Model.OccurrenceLog() { Type = "Completed" }); Context.Entry(occurrenceEntity).State = EntityState.Modified; var result = await Context.SaveChangesAsync(); if (result.Succeeded) { await AfterUpdateAsync(occurrenceEntity); var taskEntity = await Context.Tasks.FindAsync(occurrenceEntity.TaskId); var scheduleEntity = await Context.Schedules.FindAsync(occurrenceEntity.ScheduleId); var instance = TaskInstance.FromSchedule(taskEntity, occurrenceEntity.Date, scheduleEntity).MergeOccurrence(occurrenceEntity); SignalRHub.NotifyOccurrenceCreate(null, instance, occurrenceEntity.ToDto()); return(Ok(new { instance = instance })); } else { return(BadRequest(result)); } }
/// <summary> /// Adds an <see cref="Altask.Data.Model.Occurrence"/> based on the <see cref="Altask.Data.Model.Schedule"/>. /// </summary> /// <param name="schedule"></param> /// <returns>Returns a <see cref="Altask.Data.EntityResult"/> indicating success or failure.</returns> public virtual async Task <ActionResult> AddAndStart(long scheduleId, int?assetId, int?userId, DateTime date, int startedByUserId) { date = date.LessSeconds(); ThrowIfDisposed(); if (!ModelState.IsValid) { return(BadRequest(ModelState)); } var scheduleEntity = await Context.Schedules.FindAsync(scheduleId); if (scheduleEntity == null) { return(BadRequest(ErrorDescriber.DoesNotExist("Schedule"))); } if (scheduleEntity.EndsOn.HasValue && scheduleEntity.EndsOn.Value < DateTime.Now) { return(BadRequest(ErrorDescriber.DefaultError("No more occurrences can be created for the associated Schedule. The Schedule has ended."))); } if (scheduleEntity.EndsAfter.HasValue) { var pastOccurrences = await Context.Occurrences.Where(o => o.ScheduleId == scheduleEntity.Id && o.Date != date).ToListAsync(); if (pastOccurrences.Count == scheduleEntity.EndsAfter.Value) { return(BadRequest(ErrorDescriber.DefaultError("Not more occurrences can be created for the associated Schedule. The maximum number of occurrences have been created."))); } } var taskEntity = await Context.Tasks.FindAsync(scheduleEntity.TaskId); await Context.Entry(taskEntity).Reference(t => t.Form).LoadAsync(); if (taskEntity == null) { return(BadRequest(ErrorDescriber.DoesNotExistFor("Task", "Schedule"))); } var startedByEntity = await Context.Users.FindAsync(startedByUserId); if (startedByEntity == null) { return(BadRequest(ErrorDescriber.DoesNotExist("User"))); } date = date.Date.Add(scheduleEntity.StartsOn.TimeOfDay).LessSeconds(); var occurrenceEntity = await Context.Occurrences.FirstOrDefaultAsync(o => o.ScheduleId == scheduleEntity.Id && o.Date == date && (assetId.HasValue ? (userId.HasValue ? o.AssetId == assetId && o.UserId == userId : o.AssetId == assetId && o.UserId == null) : (userId.HasValue ? o.UserId == userId && o.AssetId == null : o.AssetId == null && o.UserId == null))); if (occurrenceEntity != null) { if (!occurrenceEntity.Started.GetValueOrDefault(false)) { occurrenceEntity.Started = true; occurrenceEntity.StartedBy = startedByEntity.UserName; occurrenceEntity.StartedOn = DateTime.Now; occurrenceEntity.TimeSpent = 0; occurrenceEntity.Logs.Add(new Data.Model.OccurrenceLog() { Type = "Started" }); Context.Entry(occurrenceEntity).State = EntityState.Modified; var result = await Context.SaveChangesAsync(); if (result.Succeeded) { await AfterUpdateAsync(occurrenceEntity); var instance = TaskInstance.FromSchedule(taskEntity, occurrenceEntity.Date, scheduleEntity).MergeOccurrence(occurrenceEntity); SignalRHub.NotifyOccurrenceCreate(null, instance, occurrenceEntity.ToDto()); return(Ok(new { instance = instance })); } else { return(BadRequest(result)); } } var existing = TaskInstance.FromSchedule(taskEntity, occurrenceEntity.Date, scheduleEntity).MergeOccurrence(occurrenceEntity); SignalRHub.NotifyOccurrenceCreate(null, existing, occurrenceEntity.ToDto()); return(Ok(new { instance = existing })); } else { var newOccurrence = new Altask.Data.Model.Occurrence() { Date = date, FormModel = taskEntity.Form.PublishedModel, Started = true, StartedBy = startedByEntity.UserName, StartedOn = DateTime.Now, TaskId = taskEntity.Id, TimeSpent = 0, ScheduleId = scheduleEntity.Id }; var assetEntity = await Context.Assets.FindAsync(assetId); if (assetEntity != null) { newOccurrence.AssetId = assetEntity.Id; } var userEntity = await Context.Users.FindAsync(userId); if (userEntity != null) { newOccurrence.UserId = userEntity.Id; } if (assetEntity == null && userEntity == null) { return(BadRequest(ErrorDescriber.DefaultError("An occurrence must have either and asset or user associated to it."))); } newOccurrence.Logs.Add(new Data.Model.OccurrenceLog() { Type = "Created" }); newOccurrence.Logs.Add(new Data.Model.OccurrenceLog() { Type = "Started" }); Context.Occurrences.Add(newOccurrence); BeforeCreate(newOccurrence, newOccurrence.ToDto()); var result = await Context.SaveChangesAsync(); if (result.Succeeded) { await AfterCreateAsync(newOccurrence); var instance = TaskInstance.FromSchedule(taskEntity, newOccurrence.Date, scheduleEntity).MergeOccurrence(newOccurrence); SignalRHub.NotifyOccurrenceCreate(null, instance, newOccurrence.ToDto()); return(Ok(new { instance = instance })); } else { return(BadRequest(result)); } } }
public void Run() { while (!Terminated) { _lastRun = DateTime.Now; if (!Settings.TaskAlertServiceActive) { Thread.Sleep(5000); continue; } try { using (_context = new ApplicationDbContext("TaskAlertService")) { var settings = _context.Settings.Where(s => s.Area == "System" && s.Classification == "Email").ToList(); _emailService = new EmailService(settings.Single(s => s.Name == "SmtpAddress").Value, int.Parse(settings.Single(s => s.Name == "SmtpPort").Value), settings.Single(s => s.Name == "UserName").Value, settings.Single(s => s.Name == "Password").Value); var count = 0; using (var command = new SqlCommand("[dbo].[GetTaskAlertCount]", _context.Database.Connection as SqlConnection)) { bool closeConnection = false; if (command.Connection.State != ConnectionState.Open) { command.Connection.Open(); closeConnection = true; } command.CommandTimeout = 300; command.CommandType = System.Data.CommandType.StoredProcedure; command.Parameters.Add(new SqlParameter("@LastDate", _lastDate)); command.Parameters.Add(new SqlParameter("@OffsetPast", Settings.TaskAlertServiceOffsetPast)); command.Parameters.Add(new SqlParameter("@OffsetFuture", Settings.TaskAlertServiceOffsetFuture)); count = Convert.ToInt32(command.ExecuteScalar()); if (closeConnection) { command.Connection.Close(); } } if (count != _lastCount) { _lastCount = count; _lastDate = DateTime.Now; _instances.Clear(); _tasks = _context.Tasks.AsNoTracking().Where(t => t.Alerts.Any(ta => ta.Active)) .Include(e => e.Alerts) .Include(e => e.Schedules) .ToList(); _assets = _context.Assets.AsNoTracking().ToList(); _users = _context.Users.AsNoTracking().ToList(); foreach (var task in _tasks) { foreach (var schedule in task.Schedules) { var dtoSchedule = schedule.ToDto(); var dates = dtoSchedule.GetRunDates(DateTime.Now.AddDays(Settings.TaskAlertServiceOffsetPast), DateTime.Now.AddDays(Settings.TaskAlertServiceOffsetFuture)); foreach (var date in dates) { if (dtoSchedule.Assets.Count > 0) { foreach (var asset in dtoSchedule.Assets) { if (dtoSchedule.Users.Count > 0) { foreach (var user in dtoSchedule.Users) { var instance = TaskInstance.FromSchedule(task, date, schedule); var occurrence = _context.Occurrences.AsNoTracking().SingleOrDefault(o => o.ScheduleId == schedule.Id && o.AssetId == asset.AssetId && o.UserId == user.Id && o.Date == date); if (occurrence != null) { instance.MergeOccurrence(occurrence); } else { instance.AssetId = asset.AssetId; instance.Asset = asset.Asset; instance.UserId = user.UserId; instance.User = user.User; } _instances.Add(instance); } } else { var instance = TaskInstance.FromSchedule(task, date, schedule); var occurrence = _context.Occurrences.AsNoTracking().SingleOrDefault(o => o.ScheduleId == schedule.Id && o.AssetId == asset.AssetId && o.Date == date); if (occurrence != null) { instance.MergeOccurrence(occurrence); } else { instance.AssetId = asset.AssetId; instance.Asset = asset.Asset; } _instances.Add(instance); } } } else { foreach (var user in dtoSchedule.Users) { var instance = TaskInstance.FromSchedule(task, date, schedule); var occurrence = _context.Occurrences.AsNoTracking().SingleOrDefault(o => o.ScheduleId == schedule.Id && o.UserId == user.UserId && o.Date == date); if (occurrence != null) { instance.MergeOccurrence(occurrence); } else { instance.UserId = user.UserId; instance.User = user.User; } _instances.Add(instance); } } } } } } foreach (var instance in _instances) { var task = _tasks.FirstOrDefault(t => t.Id == instance.TaskId); SendAlerts(instance, task.Alerts.ToArray()); } } if (Settings.TaskAlertServiceThrottle > 0) { Thread.Sleep(1000 * Settings.TaskAlertServiceThrottle); } } catch (ThreadAbortException) { Run(); } catch (Exception ex) { EventLog.WriteEntry(Settings.EventLogSource, string.Format("The Task Alert Service encountered the following error: {0}\n\n{1}", ex.Message, ex.StackTrace), EventLogEntryType.Error); _errorCount++; if (_errorCount == 100) { EventLog.WriteEntry(Settings.EventLogSource, "The Task Alert Service has encountered too many errors in succession. It will be automatically shut down.", EventLogEntryType.Warning); Terminated = true; _errorCount = 0; } } } }
/// <summary> /// Returns a collection of <see cref="Altask.Data.Model.Task"/> objects matching the specified filter. /// </summary> /// <param name="filter">A <see cref="Altask.www.Models.TaskListOptions"/> object on which to filter.</param> /// <returns>Returns a <see cref="Altask.Data.EntityResult"/> indicating success or failure.</returns> public virtual async Task <ActionResult> ProjectTo(ProjectToOptions filter) { ThrowIfDisposed(); if (filter == null) { filter = new ProjectToOptions(); } var fromDate = filter.FromDate; var toDate = filter.ToDate; var instances = new List <TaskInstance>(); if (fromDate <= DateTime.Now) { filter.ToDate = DateTime.Now; var pastOccurrences = await Context.Occurrences.AsNoTracking().Where(filter.GetOccurrencePredicate()) .Include(e => e.Task) .Include(e => e.Schedule) .ToListAsync(); foreach (var occurrence in pastOccurrences) { var instance = TaskInstance.FromSchedule(occurrence.Task, occurrence.Date, occurrence.Schedule); instance.MergeOccurrence(occurrence); instances.Add(instance); } filter.FromDate = DateTime.Now; } filter.ToDate = toDate; var tasks = await Context.Tasks.AsNoTracking().Where(filter.GetTaskPredicate()) .Include(e => e.Form) .Include(e => e.Schedules) .ToListAsync(); var occurrences = await Context.Occurrences.AsNoTracking().Where(filter.GetOccurrencePredicate()).ToListAsync(); var userIds = filter.GetUserIds(); var assetIds = filter.GetAssetIds(); foreach (var task in tasks) { foreach (var schedule in task.Schedules) { var dtoSchedule = schedule.ToDto(); var dates = dtoSchedule.GetRunDates(filter.FromDate, filter.ToDate); foreach (var date in dates) { if (dtoSchedule.Assets.Count > 0) { foreach (var asset in dtoSchedule.Assets) { if (assetIds.Count > 0 && !assetIds.Contains(asset.AssetId)) { continue; } if (dtoSchedule.Users.Count > 0) { foreach (var user in dtoSchedule.Users) { if (userIds.Count > 0 && !userIds.Contains(user.UserId)) { continue; } var instance = TaskInstance.FromSchedule(task, date, schedule); var occurrence = occurrences.SingleOrDefault(o => o.ScheduleId == schedule.Id && o.AssetId == asset.AssetId && o.UserId == user.Id && o.Date == date); if (occurrence != null) { instance.MergeOccurrence(occurrence); } else { instance.AssetId = asset.AssetId; instance.Asset = asset.Asset; instance.UserId = user.UserId; instance.User = user.User; } instances.Add(instance); } } else { var instance = TaskInstance.FromSchedule(task, date, schedule); var occurrence = occurrences.SingleOrDefault(o => o.ScheduleId == schedule.Id && o.AssetId == asset.AssetId && o.Date == date); if (occurrence != null) { instance.MergeOccurrence(occurrence); } else { instance.AssetId = asset.AssetId; instance.Asset = asset.Asset; } instances.Add(instance); } } } else { foreach (var user in dtoSchedule.Users) { if (userIds.Count > 0 && !userIds.Contains(user.UserId)) { continue; } var instance = TaskInstance.FromSchedule(task, date, schedule); var occurrence = occurrences.SingleOrDefault(o => o.ScheduleId == schedule.Id && o.UserId == user.UserId && o.Date == date); if (occurrence != null) { instance.MergeOccurrence(occurrence); } else { instance.UserId = user.UserId; instance.User = user.User; } instances.Add(instance); } } } } } return(Ok(new { tasks = instances }, JsonRequestBehavior.AllowGet)); }
public void Run() { while (!Terminated) { _lastRun = DateTime.Now; if (!Settings.OccurrenceServiceActive) { Thread.Sleep(5000); continue; } try { var instances = new List <TaskInstance>(); using (_context = new ApplicationDbContext("OccurrenceService")) { // Joe - Removing "count" check since it's not a very accurate way to determine // if work needs to be done. //using (var command = new SqlCommand("[dbo].[GetTaskOccurrenceCount]", _context.Database.Connection as SqlConnection)) { // bool closeConnection = false; // if (command.Connection.State != ConnectionState.Open) { // command.Connection.Open(); // closeConnection = true; // } // command.CommandTimeout = 300; // command.CommandType = System.Data.CommandType.StoredProcedure; // command.Parameters.Add(new SqlParameter("@LastDate", _lastDate)); // var count = Convert.ToInt32(command.ExecuteScalar()); // if (closeConnection) { // command.Connection.Close(); // } //} // Removing _tasks cache since it held a reference to (and mght have used) a dbcontext which was disposed. var tasks = _context.Tasks.AsNoTracking().Where(t => t.Schedules.Any(s => s.Active)) .Include(e => e.Alerts) .Include(e => e.Schedules) .ToList(); foreach (var task in tasks) { if (task.Id == 20097) { var s = ""; Debug.Print(s); } if (!task.Active) { continue; } foreach (var schedule in task.Schedules) { if (!schedule.Active) { continue; } var dtoSchedule = schedule.ToDto(); var dates = dtoSchedule.GetRunDates(DateTime.Now.AddDays(Settings.OccurrenceServiceOffset), DateTime.Now); foreach (var date in dates) { if (dtoSchedule.Assets.Count > 0) { foreach (var asset in dtoSchedule.Assets) { if (dtoSchedule.Users.Count > 0) { foreach (var user in dtoSchedule.Users) { var instance = TaskInstance.FromSchedule(task, date, schedule); if (!DoesOccurrenceExist(schedule.Id, asset.AssetId, user.UserId, date)) { instance.AssetId = asset.AssetId; instance.Asset = asset.Asset; instance.UserId = user.UserId; instance.User = user.User; instances.Add(instance); } } } else { var instance = TaskInstance.FromSchedule(task, date, schedule); if (!DoesOccurrenceExist(schedule.Id, asset.AssetId, null, date)) { instance.AssetId = asset.AssetId; instance.Asset = asset.Asset; instances.Add(instance); } } } } else { foreach (var user in dtoSchedule.Users) { var instance = TaskInstance.FromSchedule(task, date, schedule); if (!DoesOccurrenceExist(schedule.Id, null, user.UserId, date)) { instance.UserId = user.UserId; instance.User = user.User; instances.Add(instance); } } } } } } foreach (var instance in instances) { CreateOccurrence(instance); } _errorCount = 0; if (Settings.OccurrenceServiceThrottle > 0) { Thread.Sleep(1000 * Settings.OccurrenceServiceThrottle); } } } catch (ThreadAbortException) { Run(); } catch (ObjectDisposedException) { _errorCount++; if (_errorCount == 100) { throw; } else { Run(); } } catch (Exception ex) { EventLog.WriteEntry(Settings.EventLogSource, string.Format("The Occurrence Service encountered the following error: {0}\n\n{1}", ex.Message, ex.StackTrace), EventLogEntryType.Error); _errorCount++; if (_errorCount == 100) { EventLog.WriteEntry(Settings.EventLogSource, "The Occurrence Service has encountered too many errors in succession. It will be automatically shut down.", EventLogEntryType.Warning); Terminated = true; _errorCount = 0; } } } }