HourlyInventory #1

Merged
tommy merged 5 commits from HourlyInventory into master 2025-04-04 00:35:03 +00:00
5 changed files with 149 additions and 119 deletions
Showing only changes of commit 857f94c59a - Show all commits

View File

@@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Data.SqlClient; using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using PartSource.Automation.Models.Configuration; using PartSource.Automation.Models.Configuration;
using PartSource.Automation.Models.Ftp; using PartSource.Automation.Models.Ftp;
using PartSource.Automation.Services; using PartSource.Automation.Services;
@@ -18,36 +19,50 @@ namespace PartSource.Automation.Jobs.POC
public class BulkUpdateInventory : IAutomationJob public class BulkUpdateInventory : IAutomationJob
{ {
private readonly FtpService _ftpService; private readonly FtpService _ftpService;
private readonly ILogger<BulkUpdateInventory> _logger;
public BulkUpdateInventory(IConfiguration configuration) public BulkUpdateInventory(IConfiguration configuration, ILogger<BulkUpdateInventory> logger)
{ {
FtpConfiguration ftpConfiguration = configuration.GetSection("FtpServers:AzureConfiguration").Get<FtpConfiguration>(); FtpConfiguration ftpConfiguration = configuration.GetSection("FtpServers:AutomationConfiguration").Get<FtpConfiguration>();
_ftpService = new FtpService(ftpConfiguration); _ftpService = new FtpService(ftpConfiguration);
_logger = logger;
} }
public async Task Run(CancellationToken token, params string[] arguments) public async Task Run(CancellationToken token, params string[] arguments)
{ {
FtpFileInfo lastUploadedFile = _ftpService.ListFilesExtended() 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) .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); 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(); connection.Open();
using SqlCommand command = new SqlCommand("TRUNCATE TABLE PartAvailability", connection);
await command.ExecuteNonQueryAsync();
using SqlBulkCopy bulk = new SqlBulkCopy(connection) using SqlBulkCopy bulk = new SqlBulkCopy(connection)
{ {
DestinationTableName = $"PartAvailability", DestinationTableName = "PartAvailability",
BulkCopyTimeout = 14400 BulkCopyTimeout = 14400
}; };
bulk.WriteToServer(dataTable); bulk.WriteToServer(dataTable);
return; _ftpService.Delete(lastUploadedFile.Filename);
return;
} }
private DataTable GetDataTable(string filename) private DataTable GetDataTable(string filename)

View File

@@ -32,20 +32,21 @@ namespace PartSource.Automation.Jobs.POC
public async Task Run(CancellationToken token, params string[] arguments) public async Task Run(CancellationToken token, params string[] arguments)
{ {
FtpFileInfo lastUploadedFile = _ftpService.ListFilesExtended() 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) .OrderByDescending(f => f.Modified)
.FirstOrDefault(); .FirstOrDefault();
if (lastUploadedFile == null) 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; 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(); connection.Open();
using StreamReader reader = new StreamReader(file); using StreamReader reader = new StreamReader(file);
@@ -55,17 +56,17 @@ namespace PartSource.Automation.Jobs.POC
{ {
line = reader.ReadLine(); line = reader.ReadLine();
string[] columns = line.Split(","); string[] columns = line.Split("|");
for (int i = 0; i < columns.Length; i++) for (int i = 0; i < columns.Length; i++)
{ {
columns[i] = columns[i].Replace("\"", string.Empty); columns[i] = columns[i].Replace("\"", string.Empty);
} }
if (int.TryParse(columns[0], out int store) if (int.TryParse(columns[0], out int store)
&& int.TryParse(columns[1], out int quantity) && int.TryParse(columns[1], out int sku)
&& int.TryParse(columns[2], 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("qty", quantity));
sqlCommand.Parameters.Add(new SqlParameter("sku", sku)); sqlCommand.Parameters.Add(new SqlParameter("sku", sku));
sqlCommand.Parameters.Add(new SqlParameter("store", store)); sqlCommand.Parameters.Add(new SqlParameter("store", store));
@@ -74,7 +75,9 @@ namespace PartSource.Automation.Jobs.POC
} }
} }
return; _ftpService.Delete(lastUploadedFile.Filename);
return;
} }
} }
} }

View File

@@ -1,5 +1,4 @@
using AutoMapper; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
@@ -7,12 +6,10 @@ using Microsoft.Extensions.Logging;
using PartSource.Automation.Jobs; using PartSource.Automation.Jobs;
using PartSource.Automation.Jobs.POC; using PartSource.Automation.Jobs.POC;
using PartSource.Automation.Services; using PartSource.Automation.Services;
using PartSource.Data;
using PartSource.Data.AutoMapper; using PartSource.Data.AutoMapper;
using PartSource.Data.Contexts; using PartSource.Data.Contexts;
using PartSource.Services; using PartSource.Services;
using Ratermania.Automation.DependencyInjection; using Ratermania.Automation.DependencyInjection;
using Ratermania.Automation.Logging;
using Ratermania.Shopify.DependencyInjection; using Ratermania.Shopify.DependencyInjection;
using System; using System;
using System.IO; using System.IO;
@@ -20,111 +17,116 @@ using System.Threading.Tasks;
namespace PartSource.Automation namespace PartSource.Automation
{ {
class Program class Program
{ {
static async Task Main(string[] args){ static async Task Main(string[] args)
try {
{ try
using IHost host = CreateHostBuilder().Build(); {
using IHost host = CreateHostBuilder().Build();
await host.StartAsync(); await host.StartAsync();
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine(ex.ToString()); Console.WriteLine(ex.ToString());
throw; throw;
} }
} }
private static IHostBuilder CreateHostBuilder() private static IHostBuilder CreateHostBuilder()
{ {
return Host.CreateDefaultBuilder() return Host.CreateDefaultBuilder()
.ConfigureAppConfiguration(builder => .ConfigureAppConfiguration(builder =>
{ {
string environment = Environment.GetEnvironmentVariable("AUTOMATION_ENVIRONMENT"); string environment = Environment.GetEnvironmentVariable("AUTOMATION_ENVIRONMENT");
builder.SetBasePath(Directory.GetCurrentDirectory()) builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true); .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true);
}) })
.ConfigureServices((builder, services) => .ConfigureServices((builder, services) =>
{ {
services.AddDbContext<PartSourceContext>(options => services.AddDbContext<PartSourceContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("PartSourceDatabase"), opts => opts.EnableRetryOnFailure()) options.UseSqlServer(builder.Configuration.GetConnectionString("PartSourceDatabase"), opts => opts.EnableRetryOnFailure())
) )
.AddDbContext<FitmentContext>(options => .AddDbContext<FitmentContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("FitmentDatabase"), opts => options.UseSqlServer(builder.Configuration.GetConnectionString("FitmentDatabase"), opts =>
{ {
opts.EnableRetryOnFailure(); opts.EnableRetryOnFailure();
opts.CommandTimeout(600); opts.CommandTimeout(600);
}) })
) )
.AddShopify(options => .AddShopify(options =>
{ {
options.ApiKey = builder.Configuration["Shopify:ApiKey"]; options.ApiKey = builder.Configuration["Shopify:ApiKey"];
options.ApiSecret = builder.Configuration["Shopify:ApiSecret"]; options.ApiSecret = builder.Configuration["Shopify:ApiSecret"];
options.ApiVersion = "2022-10"; options.ApiVersion = "2022-10";
options.ShopDomain = builder.Configuration["Shopify:ShopDomain"]; options.ShopDomain = builder.Configuration["Shopify:ShopDomain"];
//options.ApiKey = "9a533dad460321c6ce8f30bf5b8691ed"; //options.ApiKey = "9a533dad460321c6ce8f30bf5b8691ed";
//options.ApiSecret = "dc9e28365d9858e544d57ac7af43fee7"; //options.ApiSecret = "dc9e28365d9858e544d57ac7af43fee7";
//options.ApiVersion = "2022-10"; //options.ApiVersion = "2022-10";
//options.ShopDomain = "dev-partsource.myshopify.com"; //options.ShopDomain = "dev-partsource.myshopify.com";
}) })
.AddAutomation(options => .AddAutomation(options =>
{ {
options.HasBaseInterval(new TimeSpan(0, 5, 0)) options.HasBaseInterval(new TimeSpan(0, 5, 0))
.HasMaxFailures(1) .HasMaxFailures(1)
//.HasJob<TestJob>(options => options.HasInterval(new TimeSpan(7, 0, 0, 0))); //.HasJob<TestJob>(options => options.HasInterval(new TimeSpan(7, 0, 0, 0)));
// //
//.HasJob<SyncronizeProducts>(options => options.HasInterval(new TimeSpan(24, 0, 0))) //.HasJob<SyncronizeProducts>(options => options.HasInterval(new TimeSpan(24, 0, 0)))
// .HasJob<ProcessWhiFitment>(options => options.HasInterval(new TimeSpan(24, 0, 0))); // .HasJob<ProcessWhiFitment>(options => options.HasInterval(new TimeSpan(24, 0, 0)));
//.HasJob<ProcessWhiVehicles>(options => options.HasInterval(new TimeSpan(24, 0, 0)) //.HasJob<ProcessWhiVehicles>(options => options.HasInterval(new TimeSpan(24, 0, 0))
//.HasDependency<SyncronizeProducts>() //.HasDependency<SyncronizeProducts>()
//.HasJob<UpdateFitment>(options => options.HasInterval(new TimeSpan(24, 0, 0))); //.HasJob<UpdateFitment>(options => options.HasInterval(new TimeSpan(24, 0, 0)));
//.HasJob<UpdatePositioning>(options => options.HasInterval(new TimeSpan(24, 0, 0)) //.HasJob<UpdatePositioning>(options => options.HasInterval(new TimeSpan(24, 0, 0))
// .HasDependency<UpdateFitment>() // .HasDependency<UpdateFitment>()
// .HasDependency<ProcessWhiFitment>() // .HasDependency<ProcessWhiFitment>()
// .HasDependency<SyncronizeProducts>() // .HasDependency<SyncronizeProducts>()
// .StartsAt(DateTime.Today.AddHours(8)) // .StartsAt(DateTime.Today.AddHours(8))
//) ; //) ;
//.HasJob<StatusCheck>(options => options.HasInterval(new TimeSpan(24, 0, 0)) //.HasJob<StatusCheck>(options => options.HasInterval(new TimeSpan(24, 0, 0))
// .StartsAt(DateTime.Parse("2021-04-01 08:00:00")) // .StartsAt(DateTime.Parse("2021-04-01 08:00:00"))
//) //)
.HasJob<ProcessWhiVehicles>(options => .HasJob<BulkUpdateInventory>(options =>
options.HasInterval(new TimeSpan(24, 0, 0)) options.HasInterval(new TimeSpan(1, 0, 0))
.StartsAt(DateTime.Today) .StartsAt(DateTime.Today.AddHours(-27))
); )
.HasJob<PartialInventoryUpdate>(options =>
options.HasInterval(new TimeSpan(1, 0, 0))
.StartsAt(DateTime.Today.AddHours(-27).AddMinutes(30))
);
//.HasJob<PartialInventoryUpdate>(options => options.HasInterval(new TimeSpan(1, 0, 0)) //.HasJob<PartialInventoryUpdate>(options => options.HasInterval(new TimeSpan(1, 0, 0))
//.HasDependency<ExecuteSsisPackages>() //.HasDependency<ExecuteSsisPackages>()
// .StartsAt(DateTime.Today) // .StartsAt(DateTime.Today)
//); //);
//); //);
//.AddApiServer(); //.AddApiServer();
}) })
.AddSingleton<EmailService>() .AddSingleton<EmailService>()
.AddSingleton<SsisService>() .AddSingleton<SsisService>()
.AddSingleton<WhiSeoService>() .AddSingleton<WhiSeoService>()
.AddSingleton<VehicleService>() .AddSingleton<VehicleService>()
.AddSingleton<VehicleFitmentService>() .AddSingleton<VehicleFitmentService>()
.AddSingleton<NexpartService>() .AddSingleton<NexpartService>()
.AddSingleton<PartService>() .AddSingleton<PartService>()
.AddAutoMapper(typeof(PartSourceProfile)); .AddAutoMapper(typeof(PartSourceProfile));
}) })
.ConfigureLogging((builder, logging) => .ConfigureLogging((builder, logging) =>
{ {
logging.AddEventLog(); logging.AddEventLog();
logging.AddConsole(); logging.AddConsole();
// logging.AddProvider(new AutomationLoggerProvider()); // logging.AddProvider(new AutomationLoggerProvider());
}); });
} }
} }
} }

View File

@@ -85,8 +85,6 @@ namespace PartSource.Automation.Services
public string Download(string filename, string destination = null) public string Download(string filename, string destination = null)
{ {
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri($"{_ftpConfiguration.Url}/{filename}")); FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri($"{_ftpConfiguration.Url}/{filename}"));
request.Credentials = new NetworkCredential(_ftpConfiguration.Username, _ftpConfiguration.Password); request.Credentials = new NetworkCredential(_ftpConfiguration.Username, _ftpConfiguration.Password);
request.Method = WebRequestMethods.Ftp.DownloadFile; request.Method = WebRequestMethods.Ftp.DownloadFile;
@@ -116,5 +114,15 @@ namespace PartSource.Automation.Services
return destination; 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();
}
} }
} }

View File

@@ -1,6 +1,8 @@
{ {
"ConnectionStrings": { "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;" "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": { "emailConfiguration": {