public virtual void InsertHistoryEntry(ScheduleTaskHistory historyEntry) { Guard.NotNull(historyEntry, nameof(historyEntry)); applicationDbContext.ScheduleTaskHistories.Add(historyEntry); applicationDbContext.SaveChanges(); }
public virtual void DeleteHistoryEntry(ScheduleTaskHistory historyEntry) { Guard.NotNull(historyEntry, nameof(historyEntry)); Guard.IsTrue(!historyEntry.IsRunning, nameof(historyEntry.IsRunning), "Cannot delete a running schedule task history entry."); _taskHistoryRepository.Delete(historyEntry); }
public virtual void DeleteHistoryEntry(ScheduleTaskHistory historyEntry) { Guard.NotNull(historyEntry, nameof(historyEntry)); Guard.IsTrue(!historyEntry.IsRunning, nameof(historyEntry.IsRunning), "Cannot delete a running schedule task history entry."); applicationDbContext.ScheduleTaskHistories.Remove(historyEntry); applicationDbContext.SaveChanges(); }
/// <summary> /// Creates and prepares a schedule task view model. /// </summary> /// <param name="task">Schedule task.</param> /// <param name="lastEntry">Last task history entry.</param> /// <returns>Schedule task model.</returns> public ScheduleTaskModel CreateScheduleTaskModel(ScheduleTask task, ScheduleTaskHistory lastEntry) { if (task == null) { return(null); } var now = DateTime.UtcNow; var nextRunPretty = string.Empty; var isOverdue = false; TimeSpan?dueIn = task.NextRunUtc.HasValue ? task.NextRunUtc.Value - now : (TimeSpan?)null; if (dueIn.HasValue) { if (dueIn.Value.TotalSeconds > 0) { nextRunPretty = dueIn.Value.Prettify(); } else { nextRunPretty = T("Common.Waiting") + "..."; isOverdue = true; } } var model = new ScheduleTaskModel { Id = task.Id, Name = task.Name, CronExpression = task.CronExpression, CronDescription = CronExpression.GetFriendlyDescription(task.CronExpression), Enabled = task.Enabled, Priority = task.Priority, RunPerMachine = task.RunPerMachine, StopOnError = task.StopOnError, NextRunPretty = nextRunPretty, CancelUrl = _urlHelper.Action("CancelJob", "ScheduleTask", new { id = task.Id }), ExecuteUrl = _urlHelper.Action("RunJob", "ScheduleTask", new { id = task.Id }), EditUrl = _urlHelper.Action("Edit", "ScheduleTask", new { id = task.Id }), IsOverdue = isOverdue }; if (task.NextRunUtc.HasValue) { model.NextRun = _dateTimeHelper.ConvertToUserTime(task.NextRunUtc.Value, DateTimeKind.Utc); } model.LastHistoryEntry = CreateScheduleTaskHistoryModel(lastEntry); return(model); }
public virtual void UpdateHistoryEntry(ScheduleTaskHistory historyEntry) { Guard.NotNull(historyEntry, nameof(historyEntry)); try { _taskHistoryRepository.Update(historyEntry); } catch (Exception ex) { Logger.Error(ex); // Do not throw. } }
public virtual void UpdateHistoryEntry(ScheduleTaskHistory historyEntry) { Guard.NotNull(historyEntry, nameof(historyEntry)); try { applicationDbContext.Entry(historyEntry).State = System.Data.Entity.EntityState.Modified; applicationDbContext.SaveChanges(); } catch (Exception ex) { Logger.Error(ex); // Do not throw. } }
/// <summary> /// Creates and prepares a schedule task history view model. /// </summary> /// <param name="historyEntry">Schedule task history.</param> /// <returns>Schedule task history model.</returns> public ScheduleTaskHistoryModel CreateScheduleTaskHistoryModel(ScheduleTaskHistory historyEntry) { if (historyEntry == null) { return(new ScheduleTaskHistoryModel()); } var model = new ScheduleTaskHistoryModel { Id = historyEntry.Id, ScheduleTaskId = historyEntry.ScheduleTaskId, IsRunning = historyEntry.IsRunning, Error = historyEntry.Error.EmptyNull(), ProgressPercent = historyEntry.ProgressPercent, ProgressMessage = historyEntry.ProgressMessage, StartedOn = _dateTimeHelper.ConvertToUserTime(historyEntry.StartedOnUtc, DateTimeKind.Utc), StartedOnPretty = historyEntry.StartedOnUtc.RelativeFormat(true, "f"), MachineName = historyEntry.MachineName }; model.StartedOnString = model.StartedOn.ToString("g"); if (historyEntry.FinishedOnUtc.HasValue) { model.FinishedOn = _dateTimeHelper.ConvertToUserTime(historyEntry.FinishedOnUtc.Value, DateTimeKind.Utc); model.FinishedOnString = model.FinishedOn.Value.ToString("g"); model.FinishedOnPretty = historyEntry.FinishedOnUtc.Value.RelativeFormat(true, "f"); } if (historyEntry.SucceededOnUtc.HasValue) { model.SucceededOn = _dateTimeHelper.ConvertToUserTime(historyEntry.SucceededOnUtc.Value, DateTimeKind.Utc); model.SucceededOnPretty = historyEntry.SucceededOnUtc.Value.ToNativeString("G"); } var span = model.IsRunning ? DateTime.UtcNow - historyEntry.StartedOnUtc : (historyEntry.FinishedOnUtc ?? historyEntry.StartedOnUtc) - historyEntry.StartedOnUtc; if (span > TimeSpan.Zero) { model.Duration = span.ToString("g"); } return(model); }
public static ScheduleTaskHistoryDTO FromEntity(ScheduleTaskHistory item) { return(new ScheduleTaskHistoryDTO() { Id = item.Id, ScheduleTaskId = item.ScheduleTaskId, IsRunning = item.IsRunning, MachineName = item.MachineName, StartedOnUtc = item.StartedOnUtc, FinishedOnUtc = item.FinishedOnUtc, SucceededOnUtc = item.SucceededOnUtc, Error = item.Error, ProgressPercent = item.ProgressPercent, ProgressMessage = item.ProgressMessage, ScheduleTask = item.ScheduleTask != null?ScheduleTaskDTO.FromEntity(item.ScheduleTask) : null, }); }
/// <summary> /// Creates a transient context, which can be passed to methods requiring a <see cref="TaskExecutionContext"/> instance. /// Such methods are usually executed by the task executor in the background. If a manual invocation of such /// methods is required, this is the way to go. /// </summary> /// <param name="componentContext">The component context</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns>A transient context</returns> public static TaskExecutionContext CreateTransientContext(IComponentContext componentContext, CancellationToken cancellationToken) { var originalHistoryEntry = new ScheduleTaskHistory { ScheduleTask = new ScheduleTask { Name = "Transient", IsHidden = true, Enabled = true } }; var context = new TransientTaskExecutionContext(componentContext, originalHistoryEntry); context.CancellationToken = cancellationToken; context.ScheduleTaskHistory = originalHistoryEntry.Clone(); return(context); }
internal TaskExecutionContext(IComponentContext componentContext, ScheduleTaskHistory originalTaskHistory) { _componentContext = componentContext; _originalTaskHistory = originalTaskHistory; Parameters = new Dictionary <string, string>(); }
public TransientTaskExecutionContext(IComponentContext componentContext, ScheduleTaskHistory originalHistoryEntry) : base(componentContext, originalHistoryEntry) { }
public virtual void InsertHistoryEntry(ScheduleTaskHistory historyEntry) { Guard.NotNull(historyEntry, nameof(historyEntry)); _taskHistoryRepository.Insert(historyEntry); }
private void PrepareProfileModel( ImportProfileModel model, ImportProfile profile, ScheduleTaskHistory lastHistoryEntry, bool forEdit, ColumnMap invalidMap = null) { model.Id = profile.Id; model.Name = profile.Name; model.EntityType = profile.EntityType; model.Enabled = profile.Enabled; model.ImportRelatedData = profile.ImportRelatedData; model.Skip = profile.Skip == 0 ? (int?)null : profile.Skip; model.Take = profile.Take == 0 ? (int?)null : profile.Take; model.UpdateOnly = profile.UpdateOnly; model.KeyFieldNames = profile.KeyFieldNames.SplitSafe(",").Distinct().ToArray(); model.ScheduleTaskId = profile.SchedulingTaskId; model.ScheduleTaskName = profile.ScheduleTask.Name.NaIfEmpty(); model.IsTaskRunning = lastHistoryEntry?.IsRunning ?? false; model.IsTaskEnabled = profile.ScheduleTask.Enabled; model.LogFileExists = System.IO.File.Exists(profile.GetImportLogPath()); model.EntityTypeName = profile.EntityType.GetLocalizedEnum(Services.Localization, Services.WorkContext); model.ExistingFiles = profile.GetImportFiles(); foreach (var file in model.ExistingFiles) { if (file.RelatedType.HasValue) { file.Label = string.Concat(T("Admin.Common.Data"), " ", file.RelatedType.Value.GetLocalizedEnum(Services.Localization, Services.WorkContext)); } } if (profile.ResultInfo.HasValue()) { model.ImportResult = XmlHelper.Deserialize <SerializableImportResult>(profile.ResultInfo); } if (!forEdit) { return; } CsvConfiguration csvConfiguration = null; if (profile.FileType == ImportFileType.CSV) { var csvConverter = new CsvConfigurationConverter(); csvConfiguration = csvConverter.ConvertFrom <CsvConfiguration>(profile.FileTypeConfiguration) ?? CsvConfiguration.ExcelFriendlyConfiguration; model.CsvConfiguration = new CsvConfigurationModel(csvConfiguration); } else { csvConfiguration = CsvConfiguration.ExcelFriendlyConfiguration; } // Common configuration. var extraData = XmlHelper.Deserialize <ImportExtraData>(profile.ExtraData); model.ExtraData.NumberOfPictures = extraData.NumberOfPictures; // Column mapping. model.AvailableSourceColumns = new List <ColumnMappingItemModel>(); model.AvailableEntityProperties = new List <ColumnMappingItemModel>(); model.AvailableKeyFieldNames = new List <SelectListItem>(); model.ColumnMappings = new List <ColumnMappingItemModel>(); model.FolderName = profile.GetImportFolder(absolutePath: false); try { string[] availableKeyFieldNames = null; string[] disabledDefaultFieldNames = GetDisabledDefaultFieldNames(profile); var mapConverter = new ColumnMapConverter(); var storedMap = mapConverter.ConvertFrom <ColumnMap>(profile.ColumnMapping); var map = (invalidMap ?? storedMap) ?? new ColumnMap(); // Property name to localized property name. var allProperties = _importProfileService.GetImportableEntityProperties(profile.EntityType) ?? new Dictionary <string, string>(); switch (profile.EntityType) { case ImportEntityType.Product: availableKeyFieldNames = ProductImporter.SupportedKeyFields; break; case ImportEntityType.Category: availableKeyFieldNames = CategoryImporter.SupportedKeyFields; break; case ImportEntityType.Customer: availableKeyFieldNames = CustomerImporter.SupportedKeyFields; break; case ImportEntityType.NewsLetterSubscription: availableKeyFieldNames = NewsLetterSubscriptionImporter.SupportedKeyFields; break; } model.AvailableEntityProperties = allProperties .Select(x => { var mapping = new ColumnMappingItemModel { Property = x.Key, PropertyDescription = x.Value, IsDefaultDisabled = IsDefaultValueDisabled(x.Key, x.Key, disabledDefaultFieldNames) }; return(mapping); }) .ToList(); model.AvailableKeyFieldNames = availableKeyFieldNames .Select(x => { var item = new SelectListItem { Value = x, Text = x }; if (x == "Id") { item.Text = T("Admin.Common.Entity.Fields.Id"); } else if (allProperties.ContainsKey(x)) { item.Text = allProperties[x]; } return(item); }) .ToList(); model.ColumnMappings = map.Mappings .Select(x => { var mapping = new ColumnMappingItemModel { Column = x.Value.MappedName, Property = x.Key, Default = x.Value.Default }; if (x.Value.IgnoreProperty) { // Explicitly ignore the property. mapping.Column = null; mapping.Default = null; } mapping.PropertyDescription = GetPropertyDescription(allProperties, mapping.Property); mapping.IsDefaultDisabled = IsDefaultValueDisabled(mapping.Column, mapping.Property, disabledDefaultFieldNames); return(mapping); }) .ToList(); var file = model.ExistingFiles.FirstOrDefault(x => !x.RelatedType.HasValue); if (file == null) { return; } using (var stream = new FileStream(file.Path, FileMode.Open, FileAccess.Read, FileShare.Read)) { var dataTable = LightweightDataTable.FromFile(Path.GetFileName(file.Path), stream, stream.Length, csvConfiguration, 0, 1); foreach (var column in dataTable.Columns.Where(x => x.Name.HasValue())) { string columnWithoutIndex, columnIndex; ColumnMap.ParseSourceName(column.Name, out columnWithoutIndex, out columnIndex); model.AvailableSourceColumns.Add(new ColumnMappingItemModel { Index = dataTable.Columns.IndexOf(column), Column = column.Name, ColumnWithoutIndex = columnWithoutIndex, ColumnIndex = columnIndex, PropertyDescription = GetPropertyDescription(allProperties, column.Name) }); // Auto map where field equals property name. if (!model.ColumnMappings.Any(x => x.Column == column.Name)) { var kvp = allProperties.FirstOrDefault(x => x.Key.IsCaseInsensitiveEqual(column.Name)); if (kvp.Key.IsEmpty()) { var alternativeName = LightweightDataTable.GetAlternativeColumnNameFor(column.Name); kvp = allProperties.FirstOrDefault(x => x.Key.IsCaseInsensitiveEqual(alternativeName)); } if (kvp.Key.HasValue() && !model.ColumnMappings.Any(x => x.Property == kvp.Key)) { model.ColumnMappings.Add(new ColumnMappingItemModel { Column = column.Name, Property = kvp.Key, PropertyDescription = kvp.Value, IsDefaultDisabled = IsDefaultValueDisabled(column.Name, kvp.Key, disabledDefaultFieldNames) }); } } } // Sorting. model.AvailableSourceColumns = model.AvailableSourceColumns .OrderBy(x => x.PropertyDescription) .ToList(); model.AvailableEntityProperties = model.AvailableEntityProperties .OrderBy(x => x.PropertyDescription) .ToList(); model.ColumnMappings = model.ColumnMappings .OrderBy(x => x.PropertyDescription) .ToList(); } } catch (Exception ex) { NotifyError(ex, true, false); } }
public void Execute( ScheduleTask task, IDictionary <string, string> taskParameters = null, bool throwOnError = false) { if (AsyncRunner.AppShutdownCancellationToken.IsCancellationRequested) { return; } if (task.LastHistoryEntry == null) { // The task was started manually. task.LastHistoryEntry = _scheduledTaskService.GetLastHistoryEntryByTaskId(task.Id); } if (task?.LastHistoryEntry?.IsRunning == true) { return; } bool faulted = false; bool canceled = false; string lastError = null; ITask instance = null; string stateName = null; Type taskType = null; Exception exception = null; var historyEntry = new ScheduleTaskHistory { ScheduleTaskId = task.Id, IsRunning = true, MachineName = _env.MachineName, StartedOnUtc = DateTime.UtcNow }; try { taskType = Type.GetType(task.Type); if (taskType == null) { Logger.DebugFormat("Invalid scheduled task type: {0}", task.Type.NaIfEmpty()); } if (taskType == null) { return; } if (!PluginManager.IsActivePluginAssembly(taskType.Assembly)) { return; } task.ScheduleTaskHistory.Add(historyEntry); _scheduledTaskService.UpdateTask(task); } catch { return; } try { // Task history entry has been successfully added, now we execute the task. // Create task instance. instance = _taskResolver(taskType); stateName = task.Id.ToString(); // Create & set a composite CancellationTokenSource which also contains the global app shoutdown token. var cts = CancellationTokenSource.CreateLinkedTokenSource(AsyncRunner.AppShutdownCancellationToken, new CancellationTokenSource().Token); _asyncState.SetCancelTokenSource <ScheduleTask>(cts, stateName); var ctx = new TaskExecutionContext(_componentContext, historyEntry) { ScheduleTaskHistory = historyEntry.Clone(), CancellationToken = cts.Token, Parameters = taskParameters ?? new Dictionary <string, string>() }; Logger.DebugFormat("Executing scheduled task: {0}", task.Type); instance.Execute(ctx); } catch (Exception ex) { exception = ex; faulted = true; canceled = ex is OperationCanceledException; lastError = ex.Message.Truncate(995, "..."); if (canceled) { Logger.Warn(ex, T("Admin.System.ScheduleTasks.Cancellation", task.Name)); } else { Logger.Error(ex, string.Concat(T("Admin.System.ScheduleTasks.RunningError", task.Name), ": ", ex.Message)); } } finally { var now = DateTime.UtcNow; var updateTask = false; historyEntry.IsRunning = false; historyEntry.ProgressPercent = null; historyEntry.ProgressMessage = null; historyEntry.Error = lastError; historyEntry.FinishedOnUtc = now; if (faulted) { if ((!canceled && task.StopOnError) || instance == null) { task.Enabled = false; updateTask = true; } } else { historyEntry.SucceededOnUtc = now; } try { Logger.DebugFormat("Executed scheduled task: {0}. Elapsed: {1} ms.", task.Type, (now - historyEntry.StartedOnUtc).TotalMilliseconds); // Remove from AsyncState. if (stateName.HasValue()) { _asyncState.Remove <ScheduleTask>(stateName); } } catch (Exception ex) { Logger.Error(ex); } if (task.Enabled) { task.NextRunUtc = _scheduledTaskService.GetNextSchedule(task); updateTask = true; } _scheduledTaskService.UpdateHistoryEntry(historyEntry); if (updateTask) { _scheduledTaskService.UpdateTask(task); } Throttle.Check("Delete old schedule task history entries", TimeSpan.FromDays(1), () => _scheduledTaskService.DeleteHistoryEntries() > 0); } if (throwOnError && exception != null) { throw exception; } }
public async Task UpdateScheduleTaskHistory(ScheduleTaskHistory scheduleTaskHistory) { await _scheduleTaskHistoryRepository.UpdateAsync(scheduleTaskHistory); }
public async Task CreateScheduleTaskHistory(ScheduleTaskHistory scheduleTaskHistory) { await _scheduleTaskHistoryRepository.AddAsync(scheduleTaskHistory); }