269 lines
12 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
} |