diff --git a/Tests/LabItemTest.cs b/Tests/LabItemTest.cs
index 0012bcb..a270dd6 100755
--- a/Tests/LabItemTest.cs
+++ b/Tests/LabItemTest.cs
@@ -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
+ }
}
}
diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj
index 3a13cc7..83fb960 100755
--- a/Tests/Tests.csproj
+++ b/Tests/Tests.csproj
@@ -64,6 +64,12 @@
+
+
+ {0478f1b0-17f2-4151-8f93-1cb6eb9732c5}
+ zaaReloaded2
+
+
diff --git a/zaaReloaded2/Models/LabItem.cs b/zaaReloaded2/Models/LabItem.cs
index 1f8ccde..f7bc238 100755
--- a/zaaReloaded2/Models/LabItem.cs
+++ b/zaaReloaded2/Models/LabItem.cs
@@ -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
+ ///
+ /// Gets or sets the original name of the item (as known by Lauris).
+ ///
public string Name { get; set; }
+
+ ///
+ /// Gets or sets the unit of the item (as known by Lauris).
+ ///
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; }
+
+ ///
+ /// Gets or sets the value of the item. This may be a number or a string.
+ ///
+ 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));
+ }
+ }
+
+ ///
+ /// 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).
+ ///
+ public string Normal { get; set; }
+
+ ///
+ /// Gets or sets the lower limit of normal.
+ ///
+ public double LowerLimit
+ {
+ get
+ {
+ return _lowerLimit;
+ }
+ set
+ {
+ _lowerLimit = value;
+ HasLowerLimit = true;
+ }
+ }
+
+ ///
+ /// Is true if the item has a lower limit of normal.
+ ///
+ public bool HasLowerLimit { get; private set; }
+
+ ///
+ /// Gets or sets the upper limit of normal.
+ ///
+ public double UpperLimit
+ {
+ get
+ {
+ return _upperLimit;
+ }
+ set
+ {
+ _upperLimit = value;
+ HasUpperLimit = true;
+ }
+ }
+
+ ///
+ /// Is true if the item has an upper limit of normal.
+ ///
+ public bool HasUpperLimit { get; private set; }
+
+ ///
+ /// Is true if is normal.
+ ///
+ 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);
+ }
+ }
+ }
+
+ ///
+ /// Is true if the item has lower and/or upper limits. Is false if the
+ /// item does not have limits but a value.
+ ///
+ public bool HasLimits
+ {
+ get { return (HasLowerLimit || HasUpperLimit); }
+ }
+
+ ///
+ /// The original Lauris string from which this lab item was created.
+ ///
+ public string Lauris { get; private set; }
+
+ ///
+ /// The canonical name of this item, or the Lauris
+ /// of the item if no canonical name is known.
+ ///
+ public string CanonicalName
+ {
+ get
+ {
+ if (!string.IsNullOrEmpty(_canonicalName))
+ {
+ return _canonicalName;
+ }
+ else
+ {
+ return Name;
+ }
+ }
+ }
#endregion
@@ -66,9 +215,85 @@ namespace zaaReloaded2.Models
///
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(
+ @"(?[^:]+):\s*(?[\d.]+)\s*(?\[[^\]]+])?\s*(?[^;]+)?");
+ Regex categoricalRegex = new Regex(
+ @"(?[^:]+):\s*(?[^[;]+)\s*(\[(?[^\]]+)])?");
+ 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();
+ }
}
+ ///
+ /// Parses a string containing value limits. The string must be like
+ /// "[3.5 - 5]", "[>= 50]", or "[<= 100]".
+ ///
+ /// Match object that should contain a group "limits".
+ void ParseLimits(Match match)
+ {
+ if (match.Groups["limits"].Success)
+ {
+ Regex limitRegex = new Regex(@"\[(?[\d.]+)?\s*(?\S+)\s*(?[\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
}
}
diff --git a/zaaReloaded2/zaaReloaded2.csproj b/zaaReloaded2/zaaReloaded2.csproj
index 036132a..7989fa7 100755
--- a/zaaReloaded2/zaaReloaded2.csproj
+++ b/zaaReloaded2/zaaReloaded2.csproj
@@ -192,11 +192,21 @@
+
10.0
$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
+
+ true
+
+
+ zaaReloaded2_TemporaryKey.pfx
+
+
+ 0EDB0CD8E3605AC48387A527A80943403E568BD9
+