zaaReloaded2/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs

234 lines
9.3 KiB
C#
Raw Normal View History

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,
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);
AlwaysPrintLimits = parameterDictionary.GetForceReferenceDisplay(OriginalName);
IsBlacklisted = parameterDictionary.GetIsBlacklisted(OriginalName);
Material = parameterDictionary.GetMaterial(OriginalName, Material);
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);
if (_numericalRegex.IsMatch(LaurisText))
2015-06-20 14:26:10 +00:00
{
Logger.Debug("ParseLauris: Numerical match");
match = _numericalRegex.Match(LaurisText);
2015-06-20 14:26:10 +00:00
ParseLimits(match);
Value = match.Groups["value"].Value.Trim().Replace(',', '.');
2015-06-20 14:26:10 +00:00
}
else
{
Logger.Debug("ParseLauris: Not a numerical match");
match = _categoricalRegex.Match(LaurisText);
2015-06-20 14:26:10 +00:00
Normal = match.Groups["normal"].Value.Trim();
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();
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 "[&lt;= 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);
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
// 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);
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 "<=":
UpperLimit = Double.Parse(limitMatch.Groups["limit2"].Value.Replace(',', '.'),
2015-06-20 14:26:10 +00:00
CultureInfo.InvariantCulture);
break;
case ">=":
LowerLimit = Double.Parse(limitMatch.Groups["limit2"].Value.Replace(',', '.'),
2015-06-20 14:26:10 +00:00
CultureInfo.InvariantCulture);
break;
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>
/// 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
static readonly Regex _numericalRegex = new Regex(
@"(?<name>[^:]+):\s*(?<value>([\<\>]\s)?[\d,.]+)\s*(?<limits>\[[^\]]+])?\s*(?<unit>[^;]+)?");
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
#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
}
}