2015-06-17 18:20:57 +00:00
|
|
|
|
/* LabItem.cs
|
|
|
|
|
* part of zaaReloaded2
|
|
|
|
|
*
|
2017-02-23 15:44:07 +00:00
|
|
|
|
* Copyright 2015-2017 Daniel Kraus
|
2015-06-17 18:20:57 +00:00
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
using System;
|
2015-06-20 14:26:10 +00:00
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.Text.RegularExpressions;
|
2015-07-06 13:48:43 +00:00
|
|
|
|
using zaaReloaded2.LabModel;
|
2015-06-17 18:20:57 +00:00
|
|
|
|
|
2015-07-06 13:48:43 +00:00
|
|
|
|
namespace zaaReloaded2.Importer.ZaaImporter
|
2015-06-17 18:20:57 +00:00
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Represents a single laboratory item (e.g., sodium or creatinine).
|
|
|
|
|
/// </summary>
|
2015-07-06 13:48:43 +00:00
|
|
|
|
public class LaurisItem : LabItem
|
2015-06-17 18:20:57 +00:00
|
|
|
|
{
|
|
|
|
|
#region Properties
|
|
|
|
|
|
2015-06-20 14:26:10 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The original Lauris string from which this lab item was created.
|
|
|
|
|
/// </summary>
|
2015-07-06 13:48:43 +00:00
|
|
|
|
public string LaurisText { get; private set; }
|
2015-06-20 14:26:10 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
2015-07-06 13:48:43 +00:00
|
|
|
|
/// The original name of this item as known by Lauris
|
2015-06-20 14:26:10 +00:00
|
|
|
|
/// </summary>
|
2015-07-06 13:48:43 +00:00
|
|
|
|
public string OriginalName { get; private set; }
|
2015-06-17 18:20:57 +00:00
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Constructors
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates an empty LabItem object.
|
|
|
|
|
/// </summary>
|
2015-07-06 13:48:43 +00:00
|
|
|
|
public LaurisItem() : base() { }
|
2015-06-17 18:20:57 +00:00
|
|
|
|
|
2015-07-06 13:48:43 +00:00
|
|
|
|
public LaurisItem(string laurisString)
|
|
|
|
|
: this()
|
2015-06-17 18:20:57 +00:00
|
|
|
|
{
|
2015-07-06 13:48:43 +00:00
|
|
|
|
LaurisText = laurisString;
|
2015-06-17 18:20:57 +00:00
|
|
|
|
ParseLauris();
|
2015-06-29 19:50:27 +00:00
|
|
|
|
DetectMaterial();
|
2015-06-17 18:20:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-28 07:44:33 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates a LabItem object from a given Lauris output, using
|
|
|
|
|
/// a <see cref="ParameterDictionary"/> too look up additional
|
|
|
|
|
/// properties (canonical name, material type, whether or not
|
|
|
|
|
/// to always print the reference interval).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="laurisString">Lauris output to parse.</param>
|
2015-06-29 19:50:27 +00:00
|
|
|
|
/// <param name="parameterDictionary">ParameterDictionary that is used
|
2015-06-28 07:44:33 +00:00
|
|
|
|
/// to look up the canonical name, material type, and whether or
|
|
|
|
|
/// not to always print the reference interval</param>
|
2015-07-06 13:48:43 +00:00
|
|
|
|
public LaurisItem(string laurisString,
|
2015-07-14 21:50:39 +00:00
|
|
|
|
Thesaurus.Parameters parameterDictionary,
|
|
|
|
|
Thesaurus.Units unitDictionary)
|
2015-06-28 07:44:33 +00:00
|
|
|
|
: this(laurisString)
|
|
|
|
|
{
|
2015-06-29 19:50:27 +00:00
|
|
|
|
if (parameterDictionary != null)
|
|
|
|
|
{
|
2015-07-06 13:48:43 +00:00
|
|
|
|
Name = parameterDictionary.GetCanonicalName(OriginalName);
|
2015-08-06 05:47:44 +00:00
|
|
|
|
AlwaysPrintLimits = parameterDictionary.GetForceReferenceDisplay(OriginalName);
|
|
|
|
|
IsBlacklisted = parameterDictionary.GetIsBlacklisted(OriginalName);
|
2015-08-10 04:55:57 +00:00
|
|
|
|
Material = parameterDictionary.GetMaterial(OriginalName, Material);
|
2015-08-25 18:25:29 +00:00
|
|
|
|
PreferredPrecision = parameterDictionary.GetPrecision(OriginalName);
|
2015-06-29 19:50:27 +00:00
|
|
|
|
}
|
|
|
|
|
if (unitDictionary != null)
|
|
|
|
|
{
|
|
|
|
|
Unit = unitDictionary.TranslateLaurisUnit(Unit);
|
|
|
|
|
}
|
2015-06-28 07:44:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-17 18:20:57 +00:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Private methods
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Parses the original Lauris string contained in
|
2015-07-06 13:48:43 +00:00
|
|
|
|
/// <see cref="LaurisText"/>.
|
2015-06-17 18:20:57 +00:00
|
|
|
|
/// </summary>
|
2015-06-29 19:50:27 +00:00
|
|
|
|
void ParseLauris()
|
2015-06-17 18:20:57 +00:00
|
|
|
|
{
|
2015-06-20 14:26:10 +00:00
|
|
|
|
// Examples of Lauris output strings:
|
|
|
|
|
// "Natrium: 139 [135 - 145] mmol/l"
|
|
|
|
|
// "HDL - Cholesterin: 45 [>= 35] mg/dl"
|
|
|
|
|
// "GOT (ASAT): 303.0 [<= 50] U/l; "
|
|
|
|
|
// "Niedermol. Heparin (Anti-Xa): 0.99 U/ml;"
|
|
|
|
|
// "HBs-Antigen: neg. ;"
|
|
|
|
|
// "Erythrozyten (U): + [negativ]"
|
|
|
|
|
Match match;
|
2016-10-03 15:12:47 +00:00
|
|
|
|
Logger.Debug("ParseLauris: {0}", LaurisText);
|
2015-08-28 15:21:19 +00:00
|
|
|
|
if (_numericalRegex.IsMatch(LaurisText))
|
2015-06-20 14:26:10 +00:00
|
|
|
|
{
|
2016-09-03 21:18:43 +00:00
|
|
|
|
Logger.Debug("ParseLauris: Numerical match");
|
2015-08-28 15:21:19 +00:00
|
|
|
|
match = _numericalRegex.Match(LaurisText);
|
2015-06-20 14:26:10 +00:00
|
|
|
|
ParseLimits(match);
|
2015-08-28 15:18:42 +00:00
|
|
|
|
Value = match.Groups["value"].Value.Trim().Replace(',', '.');
|
2015-06-20 14:26:10 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-09-03 21:18:43 +00:00
|
|
|
|
Logger.Debug("ParseLauris: Not a numerical match");
|
2015-08-28 15:21:19 +00:00
|
|
|
|
match = _categoricalRegex.Match(LaurisText);
|
2015-06-20 14:26:10 +00:00
|
|
|
|
Normal = match.Groups["normal"].Value.Trim();
|
2015-08-28 15:18:42 +00:00
|
|
|
|
Value = match.Groups["value"].Value.Trim();
|
2015-06-20 14:26:10 +00:00
|
|
|
|
}
|
|
|
|
|
if (match != null)
|
|
|
|
|
{
|
2015-07-06 13:48:43 +00:00
|
|
|
|
OriginalName = match.Groups["name"].Value.Trim();
|
|
|
|
|
Name = OriginalName;
|
2015-06-20 14:26:10 +00:00
|
|
|
|
Unit = match.Groups["unit"].Value.Trim();
|
2016-09-03 21:18:43 +00:00
|
|
|
|
Logger.Debug("ParseLauris: Match: {0}, {1}", Name, Unit);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Logger.Debug("ParseLauris: No match: \"{0}\"", LaurisText);
|
2015-06-20 14:26:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-06-17 18:20:57 +00:00
|
|
|
|
|
2015-06-20 14:26:10 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Parses a string containing value limits. The string must be like
|
|
|
|
|
/// "[3.5 - 5]", "[>= 50]", or "[<= 100]".
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="match">Match object that should contain a group "limits".</param>
|
|
|
|
|
void ParseLimits(Match match)
|
|
|
|
|
{
|
|
|
|
|
if (match.Groups["limits"].Success)
|
|
|
|
|
{
|
2016-10-03 15:12:47 +00:00
|
|
|
|
Logger.Debug("ParseLimits: Has limits: {0}", match.Groups["limits"].Value);
|
2015-08-28 15:21:19 +00:00
|
|
|
|
Match limitMatch = _limitRegex.Match(match.Groups["limits"].Value);
|
2015-06-20 14:26:10 +00:00
|
|
|
|
if (limitMatch.Groups["limit1"].Success && limitMatch.Groups["limit2"].Success)
|
|
|
|
|
{
|
2016-10-03 15:12:47 +00:00
|
|
|
|
Logger.Debug("ParseLimits: Upper and lower limit detected");
|
2015-06-20 14:26:10 +00:00
|
|
|
|
// Use InvariantCulture because Lauris always outputs dots as decimal separator
|
2015-08-28 15:18:42 +00:00
|
|
|
|
// Only in rare cases, a comma sneaks in...
|
|
|
|
|
LowerLimit = Double.Parse(limitMatch.Groups["limit1"].Value.Replace(',', '.'),
|
2015-06-20 14:26:10 +00:00
|
|
|
|
CultureInfo.InvariantCulture);
|
2015-08-28 15:18:42 +00:00
|
|
|
|
UpperLimit = Double.Parse(limitMatch.Groups["limit2"].Value.Replace(',', '.'),
|
2015-06-20 14:26:10 +00:00
|
|
|
|
CultureInfo.InvariantCulture);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-10-03 15:12:47 +00:00
|
|
|
|
Logger.Debug("ParseLimits: Single limit detected");
|
2015-06-20 14:26:10 +00:00
|
|
|
|
switch (limitMatch.Groups["operator"].Value.Trim())
|
|
|
|
|
{
|
|
|
|
|
case "<=":
|
2015-08-28 15:18:42 +00:00
|
|
|
|
UpperLimit = Double.Parse(limitMatch.Groups["limit2"].Value.Replace(',', '.'),
|
2015-06-20 14:26:10 +00:00
|
|
|
|
CultureInfo.InvariantCulture);
|
|
|
|
|
break;
|
|
|
|
|
case ">=":
|
2015-08-28 15:18:42 +00:00
|
|
|
|
LowerLimit = Double.Parse(limitMatch.Groups["limit2"].Value.Replace(',', '.'),
|
2015-06-20 14:26:10 +00:00
|
|
|
|
CultureInfo.InvariantCulture);
|
|
|
|
|
break;
|
2015-09-04 19:12:50 +00:00
|
|
|
|
case "":
|
|
|
|
|
// NOOP for special cases such as "[s. Bem.]" which occurs in NT-proBNP
|
|
|
|
|
// Fixes exception ID 65ca8575.
|
|
|
|
|
break;
|
2015-06-20 14:26:10 +00:00
|
|
|
|
default:
|
2016-10-03 15:12:47 +00:00
|
|
|
|
string unknown = match.Groups["limits"].Value;
|
|
|
|
|
Logger.Fatal("ParseLimits: Unknown operator \"{0}\"", unknown);
|
|
|
|
|
throw new InvalidOperationException(String.Format("Unknown operator in {0}",unknown));
|
2015-06-20 14:26:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-06-17 18:20:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-06-29 19:50:27 +00:00
|
|
|
|
/// <summary>
|
2015-08-10 04:55:57 +00:00
|
|
|
|
/// Parses the original Lauris name for a material abbreviation.
|
|
|
|
|
/// This may be misleading in certain cases, e.g. "Sammelmenge (U)"
|
|
|
|
|
/// appears to be spot urine ("U"), but is collected urine instead
|
|
|
|
|
/// ("SU"). Therefore, in the constructor that takes the thesaurus
|
|
|
|
|
/// parameters, the material is looked up in the Parameters thesaurus.
|
|
|
|
|
/// ("Sammelmenge (U)" is contained in the Parameters thesaurus.)
|
2015-06-29 19:50:27 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <example>
|
|
|
|
|
/// Gesamt-Eiweiss (SU), Albumin (SU)/die, Gesamt-Eiweiss (PU)
|
|
|
|
|
/// </example>
|
|
|
|
|
void DetectMaterial()
|
|
|
|
|
{
|
2015-07-06 13:48:43 +00:00
|
|
|
|
// The material is encoded in the original name of the item
|
|
|
|
|
// that was produced by Lauris (eg. "Natrium (PU)" for spot
|
|
|
|
|
// urine).
|
|
|
|
|
Match m = _materialRegex.Match(OriginalName);
|
2015-06-29 19:50:27 +00:00
|
|
|
|
if (m.Success)
|
|
|
|
|
{
|
|
|
|
|
switch (m.Groups["material"].Value.ToUpper())
|
|
|
|
|
{
|
|
|
|
|
case "SU":
|
2015-07-06 13:48:43 +00:00
|
|
|
|
Material = LabModel.Material.SU;
|
2015-06-29 19:50:27 +00:00
|
|
|
|
break;
|
|
|
|
|
case "PU":
|
2015-07-06 13:48:43 +00:00
|
|
|
|
Material = LabModel.Material.U;
|
2015-06-29 19:50:27 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-17 18:20:57 +00:00
|
|
|
|
#endregion
|
2015-06-20 14:26:10 +00:00
|
|
|
|
|
|
|
|
|
#region Fields
|
|
|
|
|
|
2015-08-28 15:21:19 +00:00
|
|
|
|
static readonly Regex _numericalRegex = new Regex(
|
2015-09-18 15:22:04 +00:00
|
|
|
|
@"(?<name>[^:]+):\s*(?<value>([\<\>]\s)?[\d,.]+)\s*(?<limits>\[[^\]]+])?\s*(?<unit>[^;]+)?");
|
2015-08-28 15:21:19 +00:00
|
|
|
|
static readonly Regex _categoricalRegex = new Regex(
|
|
|
|
|
@"(?<name>[^:]+):\s*(?<value>[^[;]+)\s*(\[(?<normal>[^\]]+)])?");
|
|
|
|
|
static readonly Regex _limitRegex = new Regex(@"\[(?<limit1>[-\d,.]+)?\s*(?<operator>\S+)\s*(?<limit2>[-\d,.]+)?]");
|
2015-06-29 19:50:27 +00:00
|
|
|
|
static readonly Regex _materialRegex = new Regex(@"\((?<material>(SU|PU))\)");
|
2015-06-20 14:26:10 +00:00
|
|
|
|
|
|
|
|
|
#endregion
|
2016-09-03 21:18:43 +00:00
|
|
|
|
|
|
|
|
|
#region Class logger
|
|
|
|
|
|
|
|
|
|
private static NLog.Logger Logger { get { return _logger.Value; } }
|
|
|
|
|
|
|
|
|
|
private static readonly Lazy<NLog.Logger> _logger = new Lazy<NLog.Logger>(() => NLog.LogManager.GetCurrentClassLogger());
|
|
|
|
|
|
|
|
|
|
#endregion
|
2015-06-17 18:20:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|