Implement Lauris parsing in LabItem.
This commit is contained in:
parent
130d0eabdb
commit
aa2ad96774
@ -20,11 +20,111 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using zaaReloaded2.Models;
|
||||
|
||||
namespace Tests
|
||||
{
|
||||
[TestFixture]
|
||||
class LabItemTest
|
||||
{
|
||||
[Test]
|
||||
[TestCase("Natrium: 139 [135 - 145] mmol/l", "Natrium", 139, "mmol/l", 135, 145, true)]
|
||||
[TestCase("Kalium: 5.2 [3.5 - 5] mmol/l", "Kalium", 5.2, "mmol/l", 3.5, 5, false)]
|
||||
public void ParseLaurisWithBothLimits(
|
||||
string laurisString, string name, double value,
|
||||
string unit, double lowerLimit, double upperLimit, bool isNormal)
|
||||
{
|
||||
LabItem i = new LabItem(laurisString);
|
||||
Assert.AreEqual(name, i.Name, "Name");
|
||||
Assert.AreEqual(unit, i.Unit, "Unit");
|
||||
Assert.IsTrue(i.IsNumerical, "IsNumerical");
|
||||
Assert.AreEqual(value, i.NumericalValue, "NumericalValue");
|
||||
Assert.AreEqual(lowerLimit, i.LowerLimit, "Lower limit");
|
||||
Assert.AreEqual(upperLimit, i.UpperLimit, "Upper limit");
|
||||
Assert.AreEqual(isNormal, i.IsNormal, "IsNormal");
|
||||
Assert.IsTrue(i.HasLimits, "HasLimits");
|
||||
Assert.IsTrue(i.HasLowerLimit, "HasLowerLimit");
|
||||
Assert.IsTrue(i.HasUpperLimit, "HasUpperLimit");
|
||||
}
|
||||
|
||||
[TestCase("HDL - Cholesterin: 45 [>= 35] mg/dl", "HDL - Cholesterin", 45, "mg/dl", 35, true)]
|
||||
public void ParseLaurisWithLowerLimit(
|
||||
string laurisString, string name, double value,
|
||||
string unit, double lowerLimit, bool isNormal)
|
||||
{
|
||||
LabItem i = new LabItem(laurisString);
|
||||
Assert.AreEqual(name, i.Name, "Name");
|
||||
Assert.AreEqual(unit, i.Unit, "Unit");
|
||||
Assert.IsTrue(i.IsNumerical, "IsNumerical");
|
||||
Assert.AreEqual(value, i.NumericalValue, "NumericalValue");
|
||||
Assert.AreEqual(lowerLimit, i.LowerLimit, "Lower limit");
|
||||
Assert.AreEqual(isNormal, i.IsNormal, "IsNormal");
|
||||
Assert.IsTrue(i.HasLimits, "HasLimits");
|
||||
Assert.IsTrue(i.HasLowerLimit, "HasLowerLimit");
|
||||
Assert.IsFalse(i.HasUpperLimit, "HasUpperLimit");
|
||||
}
|
||||
|
||||
[TestCase("GOT (ASAT): 303.0 [<= 50] U/l; ", "GOT (ASAT)", 303, "U/l", 50, false)]
|
||||
public void ParseLaurisWithUpperLimit(
|
||||
string laurisString, string name, double value,
|
||||
string unit, double upperLimit, bool isNormal)
|
||||
{
|
||||
LabItem i = new LabItem(laurisString);
|
||||
Assert.AreEqual(name, i.Name, "Name");
|
||||
Assert.AreEqual(unit, i.Unit, "Unit");
|
||||
Assert.IsTrue(i.IsNumerical, "IsNumerical");
|
||||
Assert.AreEqual(value, i.NumericalValue, "NumericalValue");
|
||||
Assert.AreEqual(upperLimit, i.UpperLimit, "Upper limit");
|
||||
Assert.AreEqual(isNormal, i.IsNormal, "IsNormal");
|
||||
Assert.IsTrue(i.HasLimits, "HasLimits");
|
||||
Assert.IsFalse(i.HasLowerLimit, "HasLowerLimit");
|
||||
Assert.IsTrue(i.HasUpperLimit, "HasUpperLimit");
|
||||
}
|
||||
|
||||
[TestCase("Niedermol. Heparin (Anti-Xa): 0.99 U/ml;", "Niedermol. Heparin (Anti-Xa)", 0.99, "U/ml")]
|
||||
[TestCase("glomerul. Filtrationsr. CKD-EP: 42 ml/min /1,73qm;", "glomerul. Filtrationsr. CKD-EP", 42, "ml/min /1,73qm")]
|
||||
public void ParseLaurisWithoutLimits(
|
||||
string laurisString, string name, double value,
|
||||
string unit)
|
||||
{
|
||||
LabItem i = new LabItem(laurisString);
|
||||
Assert.AreEqual(name, i.Name, "Name");
|
||||
Assert.AreEqual(unit, i.Unit, "Unit");
|
||||
Assert.IsTrue(i.IsNumerical, "IsNumerical");
|
||||
Assert.AreEqual(value, i.NumericalValue, "NumericalValue");
|
||||
Assert.IsFalse(i.HasLimits, "HasLimits");
|
||||
Assert.IsFalse(i.HasLowerLimit, "HasLowerLimit");
|
||||
Assert.IsFalse(i.HasUpperLimit, "HasUpperLimit");
|
||||
}
|
||||
|
||||
[TestCase("HBs-Antigen: neg. ;", "HBs-Antigen", "neg.")]
|
||||
public void ParseLaurisNonNumericNoNormal(
|
||||
string laurisString, string name, string value)
|
||||
{
|
||||
LabItem i = new LabItem(laurisString);
|
||||
Assert.AreEqual(name, i.Name, "Name");
|
||||
Assert.AreEqual(value, i.Value, "Value");
|
||||
Assert.IsTrue(String.IsNullOrEmpty(i.Unit), "Unit should be a null string");
|
||||
Assert.IsFalse(i.HasLimits, "HasLimits");
|
||||
Assert.IsFalse(i.HasLowerLimit, "HasLowerLimit");
|
||||
Assert.IsFalse(i.HasUpperLimit, "HasUpperLimit");
|
||||
// TODO: Define the behavior of LabItem.IsNormal if no normal value known
|
||||
}
|
||||
|
||||
[TestCase("Erythrozyten (U): + [negativ]", "Erythrozyten (U)", "+", "negativ", false)]
|
||||
[TestCase("Bilirubin (U): negativ [negativ]", "Bilirubin (U)", "negativ", "negativ", true)]
|
||||
public void ParseLaurisNonNumericWithNormal(
|
||||
string laurisString, string name, string value, string normal, bool isNormal)
|
||||
{
|
||||
LabItem i = new LabItem(laurisString);
|
||||
Assert.AreEqual(name, i.Name, "Name");
|
||||
Assert.AreEqual(value, i.Value, "Value");
|
||||
Assert.AreEqual(normal, i.Normal, "Normal");
|
||||
Assert.AreEqual(isNormal, i.IsNormal, "IsNormal");
|
||||
Assert.IsFalse(i.HasLimits, "HasLimits");
|
||||
Assert.IsFalse(i.HasLowerLimit, "HasLowerLimit");
|
||||
Assert.IsFalse(i.HasUpperLimit, "HasUpperLimit");
|
||||
// TODO: Define the behavior of LabItem.IsNormal if no normal value known
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,12 @@
|
||||
<None Include="packages.config" />
|
||||
<None Include="Tests.licenseheader" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\zaaReloaded2\zaaReloaded2.csproj">
|
||||
<Project>{0478f1b0-17f2-4151-8f93-1cb6eb9732c5}</Project>
|
||||
<Name>zaaReloaded2</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Choose>
|
||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
|
||||
<ItemGroup>
|
||||
|
@ -16,9 +16,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace zaaReloaded2.Models
|
||||
{
|
||||
@ -29,12 +28,162 @@ namespace zaaReloaded2.Models
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the original name of the item (as known by Lauris).
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the unit of the item (as known by Lauris).
|
||||
/// </summary>
|
||||
public string Unit { get; set; }
|
||||
public double Value { get; set; }
|
||||
public double LowerLimit { get; set; }
|
||||
public double UpperLimit { get; set; }
|
||||
public string Lauris { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of the item. This may be a number or a string.
|
||||
/// </summary>
|
||||
public string Value { get; set; }
|
||||
|
||||
public double NumericalValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsNumerical)
|
||||
{
|
||||
return _numericalValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
String.Format("Value '{0}' is not numerical.", Value));
|
||||
}
|
||||
}
|
||||
private set
|
||||
{
|
||||
_numericalValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsNumerical
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Double.TryParse(Value,
|
||||
NumberStyles.Any,
|
||||
CultureInfo.InvariantCulture,
|
||||
out _numericalValue));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the normal value of the item. Need not be set. This is
|
||||
/// used for items with nominal values (as opposed to numbers).
|
||||
/// </summary>
|
||||
public string Normal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the lower limit of normal.
|
||||
/// </summary>
|
||||
public double LowerLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
return _lowerLimit;
|
||||
}
|
||||
set
|
||||
{
|
||||
_lowerLimit = value;
|
||||
HasLowerLimit = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is true if the item has a lower limit of normal.
|
||||
/// </summary>
|
||||
public bool HasLowerLimit { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the upper limit of normal.
|
||||
/// </summary>
|
||||
public double UpperLimit
|
||||
{
|
||||
get
|
||||
{
|
||||
return _upperLimit;
|
||||
}
|
||||
set
|
||||
{
|
||||
_upperLimit = value;
|
||||
HasUpperLimit = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is true if the item has an upper limit of normal.
|
||||
/// </summary>
|
||||
public bool HasUpperLimit { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is true if <see cref="Value"/> is normal.
|
||||
/// </summary>
|
||||
public bool IsNormal
|
||||
{
|
||||
get
|
||||
{
|
||||
if (HasLimits)
|
||||
{
|
||||
if (HasLowerLimit && HasUpperLimit)
|
||||
{
|
||||
return (LowerLimit <= NumericalValue &&
|
||||
NumericalValue <= UpperLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HasLowerLimit)
|
||||
{
|
||||
return (LowerLimit <= NumericalValue);
|
||||
}
|
||||
return (NumericalValue <= UpperLimit);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return (Value == Normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is true if the item has lower and/or upper limits. Is false if the
|
||||
/// item does not have limits but a <see cref="Normal"/> value.
|
||||
/// </summary>
|
||||
public bool HasLimits
|
||||
{
|
||||
get { return (HasLowerLimit || HasUpperLimit); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The original Lauris string from which this lab item was created.
|
||||
/// </summary>
|
||||
public string Lauris { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The canonical name of this item, or the Lauris <see cref="Name"/>
|
||||
/// of the item if no canonical name is known.
|
||||
/// </summary>
|
||||
public string CanonicalName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_canonicalName))
|
||||
{
|
||||
return _canonicalName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@ -66,8 +215,84 @@ namespace zaaReloaded2.Models
|
||||
/// </summary>
|
||||
private void ParseLauris()
|
||||
{
|
||||
|
||||
// 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;
|
||||
Regex numericalRegex = new Regex(
|
||||
@"(?<name>[^:]+):\s*(?<value>[\d.]+)\s*(?<limits>\[[^\]]+])?\s*(?<unit>[^;]+)?");
|
||||
Regex categoricalRegex = new Regex(
|
||||
@"(?<name>[^:]+):\s*(?<value>[^[;]+)\s*(\[(?<normal>[^\]]+)])?");
|
||||
if (numericalRegex.IsMatch(Lauris))
|
||||
{
|
||||
match = numericalRegex.Match(Lauris);
|
||||
ParseLimits(match);
|
||||
}
|
||||
else
|
||||
{
|
||||
match = categoricalRegex.Match(Lauris);
|
||||
Normal = match.Groups["normal"].Value.Trim();
|
||||
}
|
||||
if (match != null)
|
||||
{
|
||||
Name = match.Groups["name"].Value.Trim();
|
||||
Value = match.Groups["value"].Value.Trim();
|
||||
Unit = match.Groups["unit"].Value.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
Regex limitRegex = new Regex(@"\[(?<limit1>[\d.]+)?\s*(?<operator>\S+)\s*(?<limit2>[\d.]+)?]");
|
||||
Match limitMatch = limitRegex.Match(match.Groups["limits"].Value);
|
||||
if (limitMatch.Groups["limit1"].Success && limitMatch.Groups["limit2"].Success)
|
||||
{
|
||||
// Use InvariantCulture because Lauris always outputs dots as decimal separator
|
||||
LowerLimit = Double.Parse(limitMatch.Groups["limit1"].Value,
|
||||
CultureInfo.InvariantCulture);
|
||||
UpperLimit = Double.Parse(limitMatch.Groups["limit2"].Value,
|
||||
CultureInfo.InvariantCulture);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (limitMatch.Groups["operator"].Value.Trim())
|
||||
{
|
||||
case "<=":
|
||||
UpperLimit = Double.Parse(limitMatch.Groups["limit2"].Value,
|
||||
CultureInfo.InvariantCulture);
|
||||
break;
|
||||
case ">=":
|
||||
LowerLimit = Double.Parse(limitMatch.Groups["limit2"].Value,
|
||||
CultureInfo.InvariantCulture);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException(
|
||||
String.Format("Unknown operator in {0}",
|
||||
match.Groups["limits"].Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fields
|
||||
|
||||
string _canonicalName;
|
||||
double _numericalValue;
|
||||
double _lowerLimit;
|
||||
double _upperLimit;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
@ -192,11 +192,21 @@
|
||||
</Compile>
|
||||
<AppDesigner Include="Properties\" />
|
||||
<None Include="zaaReloaded2.licenseheader" />
|
||||
<None Include="zaaReloaded2_TemporaryKey.pfx" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignManifests>true</SignManifests>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ManifestKeyFile>zaaReloaded2_TemporaryKey.pfx</ManifestKeyFile>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ManifestCertificateThumbprint>0EDB0CD8E3605AC48387A527A80943403E568BD9</ManifestCertificateThumbprint>
|
||||
</PropertyGroup>
|
||||
<!-- Include the build rules for a C# project. -->
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- Include additional build rules for an Office application add-in. -->
|
||||
|
Loading…
Reference in New Issue
Block a user