using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Strata.ApiLib.Standard.Models; using Strata.RxNorm.Biz.DbContexts; using Strata.RxNorm.Biz.Jazz; using Strata.RxNorm.Biz.Pagination; using Strata.RxNorm.Biz.RxNorm; using Strata.RxNorm.Biz.Utilities; using Strata.SqlTools.Configuration.Common.AsyncFactory; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; namespace Strata.RxNorm.Biz.Ndc { public class NdcDbService( RxNormDbContext rxNormDbContext, IAsyncDbContextFactory dbContextFactory, ILogger logger) : INdcDbService { private readonly RxNormDbContext _rxNormDbContext = rxNormDbContext; protected readonly IAsyncDbContextFactory _dbContextFactory = dbContextFactory; private readonly ILogger _logger = logger; public async Task GetDbContext(CancellationToken cancellationToken) { return await _dbContextFactory.CreateDbContextAsync(cancellationToken); } public async Task> GetNdcsAsync(PagingOptions pagingOptions, IBaseFilterOptions filterOptions, CancellationToken cancellationToken) { var jazzDbContext = await GetDbContext(cancellationToken); try { var query = jazzDbContext.DimNdcs.AsQueryable().AsNoTracking(); var filters = GetNdcFilters(filterOptions); var result = await query.Where(filters).ToPagedAsync(pagingOptions, cancellationToken); return result; } catch (Exception ex) { _logger.LogServiceError(ex, jazzDbContext); throw; } } private static List>> GetNdcFilters(IBaseFilterOptions filterOptions) { return filterOptions.Search.CreateEntityFilters( m => m.NdcCode, m => m.Description, m => m.BrandCode, m => m.BrandDescription, m => m.DoseFormCode, m => m.DoseFormDescription, m => m.BrandedClinicalDrugCode, m => m.BrandedClinicalDrugDescription, m => m.SemanticClinicalDrugCode, m => m.SemanticClinicalDrugDescription ); } internal Dictionary JazzNdcUpdates = new List{ new ("", (ndc, ndcDetail) => ndc.BrandCode != ndcDetail.BrandCode || ndc.BrandDescription != ndcDetail.BrandDescription || ndc.BrandedClinicalDrugCode != ndcDetail.BrandedClinicalDrugCode || ndc.BrandedClinicalDrugDescription != ndcDetail.BrandedClinicalDrugDescription || ndc.SemanticClinicalDrugCode != ndcDetail.SemanticClinicalDrugCode || ndc.SemanticClinicalDrugDescription != ndcDetail.SemanticClinicalDrugDescription || ndc.DoseFormCode != ndcDetail.DoseFormCode || ndc.DoseFormDescription != ndcDetail.DoseFormDescription, (ndc, ndcDetail) => { ndc.BrandCode = ndcDetail.BrandCode; ndc.BrandDescription = ndcDetail.BrandDescription; ndc.BrandedClinicalDrugCode = ndcDetail.BrandedClinicalDrugCode; ndc.BrandedClinicalDrugDescription = ndcDetail.BrandedClinicalDrugDescription; ndc.SemanticClinicalDrugCode = ndcDetail.SemanticClinicalDrugCode; ndc.SemanticClinicalDrugDescription = ndcDetail.SemanticClinicalDrugDescription; ndc.DoseFormCode = ndcDetail.DoseFormCode; ndc.DoseFormDescription = ndcDetail.DoseFormDescription; }), new ("DF", (ndc, ndcDetail) => ndc.DoseFormCode != ndcDetail.DoseFormCode || ndc.DoseFormDescription != ndcDetail.DoseFormDescription, (ndc, ndcDetail) => { ndc.DoseFormCode = ndcDetail.DoseFormCode; ndc.DoseFormDescription = ndcDetail.DoseFormDescription; }), new ("BN", (ndc, ndcDetail) => ndc.BrandCode != ndcDetail.BrandCode || ndc.BrandDescription != ndcDetail.BrandDescription, (ndc, ndcDetail) => { ndc.BrandCode = ndcDetail.BrandCode; ndc.BrandDescription = ndcDetail.BrandDescription; }), new ("SCD", (ndc, ndcDetail) => ndc.SemanticClinicalDrugCode != ndcDetail.SemanticClinicalDrugCode || ndc.SemanticClinicalDrugDescription != ndcDetail.SemanticClinicalDrugDescription, (ndc, ndcDetail) => { ndc.SemanticClinicalDrugCode = ndcDetail.SemanticClinicalDrugCode; ndc.SemanticClinicalDrugDescription = ndcDetail.SemanticClinicalDrugDescription; }), new("SBD", (ndc, ndcDetail) => ndc.BrandedClinicalDrugCode != ndcDetail.BrandedClinicalDrugCode || ndc.BrandedClinicalDrugDescription != ndcDetail.BrandedClinicalDrugDescription, (ndc, ndcDetail) => { ndc.BrandedClinicalDrugCode = ndcDetail.BrandedClinicalDrugCode; ndc.BrandedClinicalDrugDescription = ndcDetail.BrandedClinicalDrugDescription; }) }.ToDictionary(k => k.Tty, v => v); public async Task UpdateJazzNdc(bool withNormalization, CancellationToken cancellationToken) { var hasDetails = await _rxNormDbContext.FactNdcDetails.AnyAsync(cancellationToken); if (!hasDetails) { return; } var jazzDbContext = await GetDbContext(cancellationToken); await jazzDbContext.DropNdcDetailTable(cancellationToken); await jazzDbContext.CreateNdcDetailTable(cancellationToken); await UpdateJazzNdc(JazzNdcUpdates.TryGetValue("", out var result) ? result : null, withNormalization, cancellationToken); await jazzDbContext.UpdateDimNdcTable(cancellationToken); } public async Task UpdateJazzNdcBrands(bool withNormalization, CancellationToken cancellationToken) { var hasDetails = await _rxNormDbContext.FactNdcDetails.AnyAsync(cancellationToken); if (!hasDetails) { return; } await UpdateJazzNdc(JazzNdcUpdates.TryGetValue("BN", out var result) ? result : null, withNormalization, cancellationToken); } public async Task UpdateJazzNdcDoseForms(bool withNormalization, CancellationToken cancellationToken) { var hasDetails = await _rxNormDbContext.FactNdcDetails.AnyAsync(cancellationToken); if (!hasDetails) { return; } await UpdateJazzNdc(JazzNdcUpdates.TryGetValue("DF", out var result) ? result : null, withNormalization, cancellationToken); } public async Task UpdateJazzNdcSemanticClinicalDrugs(bool withNormalization, CancellationToken cancellationToken) { var hasDetails = await _rxNormDbContext.FactNdcDetails.AnyAsync(cancellationToken); if (!hasDetails) { return; } await UpdateJazzNdc(JazzNdcUpdates.TryGetValue("SCD", out var result) ? result : null, withNormalization, cancellationToken); } public async Task UpdateJazzNdcBrandedClinicalDrugs(bool withNormalization, CancellationToken cancellationToken) { var hasDetails = await _rxNormDbContext.FactNdcDetails.AnyAsync(cancellationToken); if (!hasDetails) { return; } await UpdateJazzNdc(JazzNdcUpdates.TryGetValue("SBD", out var result) ? result : null, withNormalization, cancellationToken); } public async Task ClearDimNdcData(CancellationToken cancellationToken) { var jazzDbContext = await GetDbContext(cancellationToken); await jazzDbContext.ClearDimNdc(cancellationToken); } /// /// /// /// /// /// private async Task UpdateJazzNdc(JazzNdcUpdate jazzNdcUpdate, bool withNormalization, CancellationToken cancellationToken) { await UpdateJazzNdc([jazzNdcUpdate], withNormalization, cancellationToken); } /// /// /// /// /// /// private async Task UpdateJazzNdc(IEnumerable jazzNdcUpdates, bool withNormalization, CancellationToken cancellationToken) { var jazzDbContext = await GetDbContext(cancellationToken); foreach (var jazzNdcUpdate in jazzNdcUpdates) { await UpdateJazzNdc(jazzDbContext, jazzNdcUpdate, withNormalization, cancellationToken); } } /// /// /// /// /// /// private async Task UpdateJazzNdc(JazzDbContext jazzDbContext, JazzNdcUpdate jazzNdcUpdate, bool withNormalization, CancellationToken cancellationToken) { var start = Stopwatch.StartNew(); var skip = 0; var take = 10000; var totalUpdates = 0; var query = jazzDbContext.DimNdcs.AsQueryable(); while (true) { var batch = await query.Skip(skip).Take(take).ToListAsync(cancellationToken); if (batch.Count == 0) { break; } var batchNdcs = batch.Select(ndc => withNormalization ? ndc.NdcCode.NormalizeNdc() : ndc.NdcCode).Distinct().ToList(); var ndcDetails = await _rxNormDbContext.FactNdcDetails .Where(r => batchNdcs.Contains(r.NdcCode)).ToListAsync(cancellationToken); totalUpdates += await UpdateDetails(jazzDbContext, jazzNdcUpdate, withNormalization, batch, ndcDetails, cancellationToken); skip += take; } var elapsed = start.Elapsed.TotalSeconds; _logger.LogServiceInfo(jazzDbContext, nameof(UpdateJazzNdc), new Dictionary { { nameof(elapsed), elapsed }, { nameof(totalUpdates), totalUpdates } }); } private async Task UpdateDetails(JazzDbContext jazzDbContext, JazzNdcUpdate jazzNdcUpdate, bool withNormalization, List batch, List ndcDetails, CancellationToken cancellationToken) { if (ndcDetails.Count == 0) { return 0; } var updates = (from ndc in batch join ndcDetail in ndcDetails on withNormalization ? ndc.NdcCode.NormalizeNdc() : ndc.NdcCode equals ndcDetail.NdcCode where jazzNdcUpdate.IsUpdateNdc(ndc, ndcDetail) select new { ndc, ndcDetail }).ToList(); if (updates.Count == 0) { return 0; } if (string.IsNullOrEmpty(jazzNdcUpdate.Tty)) { var ndcDetailInserts = updates.Select(update => new NdcDetail(update.ndc, update.ndcDetail)); await jazzDbContext.NdcDetails.AddRangeAsync(ndcDetailInserts, cancellationToken); return await jazzDbContext.SaveChangesAsync(cancellationToken); } try { updates.ForEach(update => jazzNdcUpdate.UpdateData(update.ndc, update.ndcDetail)); return await jazzDbContext.SaveChangesAsync(cancellationToken); } catch (Exception ex) { _logger.LogServiceError(ex); throw; } } } }