diff --git a/PartSource.Automation/Jobs/POC/BulkUpdateInventory.cs b/PartSource.Automation/Jobs/POC/BulkUpdateInventory.cs index 067d963..5b60373 100644 --- a/PartSource.Automation/Jobs/POC/BulkUpdateInventory.cs +++ b/PartSource.Automation/Jobs/POC/BulkUpdateInventory.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; using PartSource.Automation.Models.Configuration; using PartSource.Automation.Models.Ftp; using PartSource.Automation.Services; @@ -18,36 +19,50 @@ namespace PartSource.Automation.Jobs.POC public class BulkUpdateInventory : IAutomationJob { private readonly FtpService _ftpService; + private readonly ILogger _logger; - public BulkUpdateInventory(IConfiguration configuration) + public BulkUpdateInventory(IConfiguration configuration, ILogger logger) { - FtpConfiguration ftpConfiguration = configuration.GetSection("FtpServers:AzureConfiguration").Get(); + FtpConfiguration ftpConfiguration = configuration.GetSection("FtpServers:AutomationConfiguration").Get(); _ftpService = new FtpService(ftpConfiguration); + + _logger = logger; } public async Task Run(CancellationToken token, params string[] arguments) { FtpFileInfo lastUploadedFile = _ftpService.ListFilesExtended() - .Where(f => f.FileType == FtpFileType.File && f.Filename.IndexOf("Availability") > -1) + .Where(f => f.FileType == FtpFileType.File && f.Filename.IndexOf("Availability Full") > -1) .OrderByDescending(f => f.Modified) - .First(); + .FirstOrDefault(); - string file = _ftpService.Download(lastUploadedFile.Filename, Path.GetTempPath()); + if (lastUploadedFile == null) + { + _logger.LogInformation($"No full inventory file available."); + return; + } + + string file = _ftpService.Download(lastUploadedFile.Filename); DataTable dataTable = GetDataTable(file); - using SqlConnection connection = new SqlConnection("Server=tcp:ps-automation-stage.eastus2.cloudapp.azure.com,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=stageuser;Password=]FXepK^cFYS|[H<;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=True;Connection Timeout=30;"); + using SqlConnection connection = new SqlConnection("Server=tcp:ps-whi.database.windows.net,1433;Initial Catalog=ps-whi-test;Persist Security Info=False;User ID=ps-whi;Password=9-^*N5dw!6:|.5Q;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"); connection.Open(); + using SqlCommand command = new SqlCommand("TRUNCATE TABLE PartAvailability", connection); + await command.ExecuteNonQueryAsync(); + using SqlBulkCopy bulk = new SqlBulkCopy(connection) { - DestinationTableName = $"PartAvailability", + DestinationTableName = "PartAvailability", BulkCopyTimeout = 14400 }; bulk.WriteToServer(dataTable); - return; + _ftpService.Delete(lastUploadedFile.Filename); + + return; } private DataTable GetDataTable(string filename) diff --git a/PartSource.Automation/Jobs/POC/PartialInventoryUpdate.cs b/PartSource.Automation/Jobs/POC/PartialInventoryUpdate.cs index fddc010..ff6286e 100644 --- a/PartSource.Automation/Jobs/POC/PartialInventoryUpdate.cs +++ b/PartSource.Automation/Jobs/POC/PartialInventoryUpdate.cs @@ -32,20 +32,21 @@ namespace PartSource.Automation.Jobs.POC public async Task Run(CancellationToken token, params string[] arguments) { FtpFileInfo lastUploadedFile = _ftpService.ListFilesExtended() - .Where(f => f.FileType == FtpFileType.File && f.Modified >= DateTime.Now.AddHours(-24) && f.Filename.IndexOf("Availability Partial") > -1) + .Where(f => f.FileType == FtpFileType.File && f.Filename.IndexOf("Availability Partial") > -1) .OrderByDescending(f => f.Modified) .FirstOrDefault(); if (lastUploadedFile == null) { - _logger.LogInformation($"No partial inventory file available for the time period {DateTime.Now.AddHours(-24)} - {DateTime.Now}"); + _logger.LogInformation($"No partial inventory file available."); return; } - string file = _ftpService.Download($"{lastUploadedFile.Filename}", "C:\\Users\\Tom\\Desktop"); + _logger.LogInformation("Processing {filename}", lastUploadedFile.Filename); + string file = _ftpService.Download($"{lastUploadedFile.Filename}"); - using SqlConnection connection = new SqlConnection("Server=tcp:ps-automation-stage.eastus2.cloudapp.azure.com,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=stageuser;Password=]FXepK^cFYS|[H<;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=True;Connection Timeout=30;"); + using SqlConnection connection = new SqlConnection("Server=tcp:ps-whi.database.windows.net,1433;Initial Catalog=ps-whi-test;Persist Security Info=False;User ID=ps-whi;Password=9-^*N5dw!6:|.5Q;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"); connection.Open(); using StreamReader reader = new StreamReader(file); @@ -55,17 +56,17 @@ namespace PartSource.Automation.Jobs.POC { line = reader.ReadLine(); - string[] columns = line.Split(","); + string[] columns = line.Split("|"); for (int i = 0; i < columns.Length; i++) { columns[i] = columns[i].Replace("\"", string.Empty); } if (int.TryParse(columns[0], out int store) - && int.TryParse(columns[1], out int quantity) - && int.TryParse(columns[2], out int sku)) + && int.TryParse(columns[1], out int sku) + && int.TryParse(columns[2], out int quantity)) { - using SqlCommand sqlCommand = new SqlCommand("UPDATE Inventory SET QTY = @qty WHERE SKU = @sku AND Store = @store", connection); + using SqlCommand sqlCommand = new SqlCommand("UPDATE PartAvailability SET QTY = @qty WHERE SKU = @sku AND Store = @store", connection); sqlCommand.Parameters.Add(new SqlParameter("qty", quantity)); sqlCommand.Parameters.Add(new SqlParameter("sku", sku)); sqlCommand.Parameters.Add(new SqlParameter("store", store)); @@ -74,7 +75,9 @@ namespace PartSource.Automation.Jobs.POC } } - return; + _ftpService.Delete(lastUploadedFile.Filename); + + return; } } } diff --git a/PartSource.Automation/Program.cs b/PartSource.Automation/Program.cs index 63a5c38..9b3b2a3 100644 --- a/PartSource.Automation/Program.cs +++ b/PartSource.Automation/Program.cs @@ -1,5 +1,4 @@ -using AutoMapper; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -7,12 +6,10 @@ using Microsoft.Extensions.Logging; using PartSource.Automation.Jobs; using PartSource.Automation.Jobs.POC; using PartSource.Automation.Services; -using PartSource.Data; using PartSource.Data.AutoMapper; using PartSource.Data.Contexts; using PartSource.Services; using Ratermania.Automation.DependencyInjection; -using Ratermania.Automation.Logging; using Ratermania.Shopify.DependencyInjection; using System; using System.IO; @@ -20,111 +17,116 @@ using System.Threading.Tasks; namespace PartSource.Automation { - class Program - { - static async Task Main(string[] args){ - try - { - using IHost host = CreateHostBuilder().Build(); + class Program + { + static async Task Main(string[] args) + { + try + { + using IHost host = CreateHostBuilder().Build(); - await host.StartAsync(); - } + await host.StartAsync(); + } - catch (Exception ex) - { - Console.WriteLine(ex.ToString()); - throw; - } - } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + throw; + } + } - private static IHostBuilder CreateHostBuilder() - { - return Host.CreateDefaultBuilder() - .ConfigureAppConfiguration(builder => - { - string environment = Environment.GetEnvironmentVariable("AUTOMATION_ENVIRONMENT"); + private static IHostBuilder CreateHostBuilder() + { + return Host.CreateDefaultBuilder() + .ConfigureAppConfiguration(builder => + { + string environment = Environment.GetEnvironmentVariable("AUTOMATION_ENVIRONMENT"); - builder.SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true); - }) - .ConfigureServices((builder, services) => - { - services.AddDbContext(options => - options.UseSqlServer(builder.Configuration.GetConnectionString("PartSourceDatabase"), opts => opts.EnableRetryOnFailure()) - ) + builder.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true); + }) + .ConfigureServices((builder, services) => + { + services.AddDbContext(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("PartSourceDatabase"), opts => opts.EnableRetryOnFailure()) + ) - .AddDbContext(options => - options.UseSqlServer(builder.Configuration.GetConnectionString("FitmentDatabase"), opts => - { - opts.EnableRetryOnFailure(); - opts.CommandTimeout(600); - }) - ) + .AddDbContext(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("FitmentDatabase"), opts => + { + opts.EnableRetryOnFailure(); + opts.CommandTimeout(600); + }) + ) - .AddShopify(options => - { - options.ApiKey = builder.Configuration["Shopify:ApiKey"]; - options.ApiSecret = builder.Configuration["Shopify:ApiSecret"]; - options.ApiVersion = "2022-10"; - options.ShopDomain = builder.Configuration["Shopify:ShopDomain"]; + .AddShopify(options => + { + options.ApiKey = builder.Configuration["Shopify:ApiKey"]; + options.ApiSecret = builder.Configuration["Shopify:ApiSecret"]; + options.ApiVersion = "2022-10"; + options.ShopDomain = builder.Configuration["Shopify:ShopDomain"]; - //options.ApiKey = "9a533dad460321c6ce8f30bf5b8691ed"; - //options.ApiSecret = "dc9e28365d9858e544d57ac7af43fee7"; - //options.ApiVersion = "2022-10"; - //options.ShopDomain = "dev-partsource.myshopify.com"; - }) + //options.ApiKey = "9a533dad460321c6ce8f30bf5b8691ed"; + //options.ApiSecret = "dc9e28365d9858e544d57ac7af43fee7"; + //options.ApiVersion = "2022-10"; + //options.ShopDomain = "dev-partsource.myshopify.com"; + }) - .AddAutomation(options => - { - options.HasBaseInterval(new TimeSpan(0, 5, 0)) - .HasMaxFailures(1) - //.HasJob(options => options.HasInterval(new TimeSpan(7, 0, 0, 0))); - // - //.HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0))) - // .HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0))); - //.HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0)) - //.HasDependency() - //.HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0))); - //.HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0)) - // .HasDependency() - // .HasDependency() - // .HasDependency() - // .StartsAt(DateTime.Today.AddHours(8)) - //) ; - //.HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0)) - // .StartsAt(DateTime.Parse("2021-04-01 08:00:00")) - //) - .HasJob(options => - options.HasInterval(new TimeSpan(24, 0, 0)) - .StartsAt(DateTime.Today) - ); + .AddAutomation(options => + { + options.HasBaseInterval(new TimeSpan(0, 5, 0)) + .HasMaxFailures(1) + //.HasJob(options => options.HasInterval(new TimeSpan(7, 0, 0, 0))); + // + //.HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0))) + // .HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0))); + //.HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0)) + //.HasDependency() + //.HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0))); + //.HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0)) + // .HasDependency() + // .HasDependency() + // .HasDependency() + // .StartsAt(DateTime.Today.AddHours(8)) + //) ; + //.HasJob(options => options.HasInterval(new TimeSpan(24, 0, 0)) + // .StartsAt(DateTime.Parse("2021-04-01 08:00:00")) + //) + .HasJob(options => + options.HasInterval(new TimeSpan(1, 0, 0)) + .StartsAt(DateTime.Today.AddHours(-27)) + ) + .HasJob(options => + options.HasInterval(new TimeSpan(1, 0, 0)) + .StartsAt(DateTime.Today.AddHours(-27).AddMinutes(30)) + ); - //.HasJob(options => options.HasInterval(new TimeSpan(1, 0, 0)) - //.HasDependency() - // .StartsAt(DateTime.Today) - //); - //); - //.AddApiServer(); - }) + //.HasJob(options => options.HasInterval(new TimeSpan(1, 0, 0)) + //.HasDependency() + // .StartsAt(DateTime.Today) + //); + //); + //.AddApiServer(); + }) - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() - .AddAutoMapper(typeof(PartSourceProfile)); - }) - .ConfigureLogging((builder, logging) => - { - logging.AddEventLog(); - logging.AddConsole(); + .AddAutoMapper(typeof(PartSourceProfile)); + }) + .ConfigureLogging((builder, logging) => + { + logging.AddEventLog(); + logging.AddConsole(); - // logging.AddProvider(new AutomationLoggerProvider()); - }); - } - } + // logging.AddProvider(new AutomationLoggerProvider()); + }); + } + } } diff --git a/PartSource.Automation/Services/FtpService.cs b/PartSource.Automation/Services/FtpService.cs index c2598a3..3609296 100644 --- a/PartSource.Automation/Services/FtpService.cs +++ b/PartSource.Automation/Services/FtpService.cs @@ -85,8 +85,6 @@ namespace PartSource.Automation.Services public string Download(string filename, string destination = null) { - - FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri($"{_ftpConfiguration.Url}/{filename}")); request.Credentials = new NetworkCredential(_ftpConfiguration.Username, _ftpConfiguration.Password); request.Method = WebRequestMethods.Ftp.DownloadFile; @@ -116,5 +114,15 @@ namespace PartSource.Automation.Services return destination; } + + public void Delete(string filename) + { + FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri($"{_ftpConfiguration.Url}/{filename}")); + request.Credentials = new NetworkCredential(_ftpConfiguration.Username, _ftpConfiguration.Password); + request.Method = WebRequestMethods.Ftp.DeleteFile; + + + using FtpWebResponse response = (FtpWebResponse)request.GetResponse(); + } } } diff --git a/PartSource.Automation/appsettings.json b/PartSource.Automation/appsettings.json index 4377f40..2fc2d0a 100644 --- a/PartSource.Automation/appsettings.json +++ b/PartSource.Automation/appsettings.json @@ -1,6 +1,8 @@ { "ConnectionStrings": { - "FitmentDatabase": "Data Source=localhost;Initial Catalog=WhiFitment;Integrated Security=true;TrustServerCertificate=True", + //"FitmentDatabase": "Data Source=localhost;Initial Catalog=WhiFitment;Integrated Security=true;TrustServerCertificate=True", + //"FitmentDatabase": "Data Source=localhost;Initial Catalog=WhiFitment;User ID=stageuser;Password=FXepK^cFYS|[H<;Encrypt=True;TrustServerCertificate=True;Connection Timeout=300", + "FitmentDatabase": "Data Source=localhost;User ID=stageuser;Password=FXepK^cFYS|[H<;Connect Timeout=30;Encrypt=True;Trust Server Certificate=True;Application Intent=ReadWrite;Multi Subnet Failover=False", "PartSourceDatabase": "Server=tcp:ps-whi.database.windows.net,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=ps-whi;Password=9-^*N5dw!6:|.5Q;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" }, "emailConfiguration": {