using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using PartSource.Automation.Jobs.Interfaces; using PartSource.Automation.Models; using PartSource.Data; using PartSource.Data.Models; using PartSource.Services; using PartSource.Services.Integrations; using Ratermania.Shopify.Entities; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace PartSource.Automation.Jobs { public class UpdatePositioning : IAutomationJob { private readonly ShopifyClient _shopifyClient; private readonly PartSourceContext _partSourceContext; private readonly VehicleService _vehicleService; public UpdatePositioning(PartSourceContext partSourceContext, ShopifyClient shopifyClient, VehicleService vehicleService) { _partSourceContext = partSourceContext; _shopifyClient = shopifyClient; _vehicleService = vehicleService; } public async Task Run() { IDictionary parameters = new Dictionary { { "limit", 250 } }; IEnumerable products = await _shopifyClient.Products.Get(parameters); while (products != null && products.Any()) { foreach (Product product in products) { try { ImportData importData = _partSourceContext.ImportData.FirstOrDefault(i => i.VariantSku == product.Variants[0].Sku); if (importData?.LineCode == "SVG") // Headlights go in front, DUH { continue; } IList fitments = GetPositionOrderedFitments(importData?.PartNumber, importData?.LineCode); IList vehicles = _vehicleService.GetVehiclesForPart(importData?.PartNumber, importData?.LineCode); if (fitments.Count == 0 || vehicles.Count == 0) { continue; } await DeletePositionMetafields(product.Id); string currentPosition = fitments[0].Position; List vehicleIds = new List(); foreach (Fitment fitment in fitments) { if (fitment.Position != currentPosition) { await SavePositionMetafield(product, vehicleIds, currentPosition); currentPosition = fitment.Position; vehicleIds = new List(); } // We don't need to DCF map because these are both sourced from WHI IList fitmentVehicleIds = vehicles.Where(v => v.BaseVehicleId == fitment.BaseVehicleId && v.EngineConfigId == fitment.EngineConfigId) .Select(v => v.VehicleToEngineConfigId) .Distinct() .ToList(); vehicleIds.AddRange(fitmentVehicleIds); } await SavePositionMetafield(product, vehicleIds, currentPosition); importData.UpdatedAt = DateTime.Now; importData.UpdateType = "Positioning"; } catch (Exception ex) { Console.WriteLine($"{product.Id}: {ex.Message}"); } } try { Console.Write('.'); await _partSourceContext.SaveChangesAsync(); products = await _shopifyClient.Products.GetNext(); } catch (Exception ex) { Console.WriteLine($"Retrying: {ex.Message}"); products = await _shopifyClient.Products.GetPrevious(); } } return new AutomationJobResult { Message = "Positioning updated successfully", IsSuccess = true }; } private IList GetPositionOrderedFitments(string partNumber, string lineCode) { partNumber = Regex.Replace(partNumber, "[^a-zA-Z0-9]", string.Empty); IQueryable whiCodes = _partSourceContext.DcfMappings .Where(d => d.LineCode == lineCode) .Select(d => d.WhiCode); return _partSourceContext.Fitments .Where(f => f.PartNumber == partNumber && whiCodes.Contains(f.LineCode) && !string.IsNullOrEmpty(f.Position)) .OrderBy(f => f.Position) .ToList(); } [SuppressMessage("Globalization", "CA1308:Normalize strings to uppercase", Justification = "It's a Shopify metafield key")] private async Task SavePositionMetafield(Product product, IList vehicleIds, string position) { if (vehicleIds.Count == 0) { return; } string json = JsonConvert.SerializeObject(vehicleIds); if (json.Length >= 100000) { // TODO: Logging return; } string key = position.ToLowerInvariant().Replace(" ", "_"); if (key.Length > 20) { key = key.Substring(0, 20); } Metafield vehicleMetafield = new Metafield { Namespace = "position", Key = key, Value = json, ValueType = "json_string", OwnerResource = "product", OwnerId = product.Id }; System.Diagnostics.Debug.WriteLine(json); await _shopifyClient.Metafields.Add(vehicleMetafield); } private async Task DeletePositionMetafields(long shopifyId) { IDictionary parameters = new Dictionary { { "metafield[owner_id]", shopifyId}, { "metafield[owner_resource]", "product" }, { "namespace", "position" }, }; IEnumerable metafields = await _shopifyClient.Metafields.Get(parameters); foreach (Metafield metafield in metafields) { await _shopifyClient.Metafields.Delete(metafield); } } } }