Files
rxnorm/src/Strata.RxNorm.Biz/Ndc/NdcDbService.cs
T

269 lines
12 KiB
C#

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<JazzDbContext> dbContextFactory,
ILogger<NdcDbService> logger) : INdcDbService
{
private readonly RxNormDbContext _rxNormDbContext = rxNormDbContext;
protected readonly IAsyncDbContextFactory<JazzDbContext> _dbContextFactory = dbContextFactory;
private readonly ILogger<NdcDbService> _logger = logger;
public async Task<JazzDbContext> GetDbContext(CancellationToken cancellationToken)
{
return await _dbContextFactory.CreateDbContextAsync(cancellationToken);
}
public async Task<PagedApiResponse<DimNdc>> 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<Expression<Func<DimNdc, bool>>> GetNdcFilters(IBaseFilterOptions filterOptions)
{
return filterOptions.Search.CreateEntityFilters<DimNdc>(
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<string, JazzNdcUpdate> JazzNdcUpdates = new List<JazzNdcUpdate>{
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);
}
/// <summary>
///
/// </summary>
/// <param name="jazzNdcUpdate"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task UpdateJazzNdc(JazzNdcUpdate jazzNdcUpdate, bool withNormalization, CancellationToken cancellationToken)
{
await UpdateJazzNdc([jazzNdcUpdate], withNormalization, cancellationToken);
}
/// <summary>
///
/// </summary>
/// <param name="jazzNdcUpdates"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task UpdateJazzNdc(IEnumerable<JazzNdcUpdate> jazzNdcUpdates, bool withNormalization, CancellationToken cancellationToken)
{
var jazzDbContext = await GetDbContext(cancellationToken);
foreach (var jazzNdcUpdate in jazzNdcUpdates)
{
await UpdateJazzNdc(jazzDbContext, jazzNdcUpdate, withNormalization, cancellationToken);
}
}
/// <summary>
///
/// </summary>
/// <param name="jazzNdcUpdate"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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<string, object> {
{ nameof(elapsed), elapsed },
{ nameof(totalUpdates), totalUpdates }
});
}
private async Task<int> UpdateDetails(JazzDbContext jazzDbContext, JazzNdcUpdate jazzNdcUpdate,
bool withNormalization,
List<DimNdc> batch, List<FactNdcDetail> 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;
}
}
}
}