Refactor: Separate concerns.

This commit is contained in:
Daniel Kraus 2015-07-06 15:48:43 +02:00
parent 2bd9c0e696
commit 2cf31914dd
19 changed files with 492 additions and 259 deletions

View File

@ -1,4 +1,4 @@
/* LabItemTest.cs /* LaurisItemTest.cs
* part of zaaReloaded2 * part of zaaReloaded2
* *
* Copyright 2015 Daniel Kraus * Copyright 2015 Daniel Kraus
@ -20,12 +20,13 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using NUnit.Framework; using NUnit.Framework;
using zaaReloaded2.Models; using zaaReloaded2.LabModel;
using zaaReloaded2.Importer.ZaaImporter;
namespace Tests namespace Tests.Importer.ZaaImporter
{ {
[TestFixture] [TestFixture]
class LabItemTest class LaurisItemTest
{ {
[Test] [Test]
[TestCase("Natrium: 139 [135 - 145] mmol/l", "Natrium", 139, "mmol/l", 135, 145, true)] [TestCase("Natrium: 139 [135 - 145] mmol/l", "Natrium", 139, "mmol/l", 135, 145, true)]
@ -34,7 +35,7 @@ namespace Tests
string laurisString, string name, double value, string laurisString, string name, double value,
string unit, double lowerLimit, double upperLimit, bool isNormal) string unit, double lowerLimit, double upperLimit, bool isNormal)
{ {
LabItem i = new LabItem(laurisString); LaurisItem i = new LaurisItem(laurisString);
Assert.AreEqual(name, i.Name, "Name"); Assert.AreEqual(name, i.Name, "Name");
Assert.AreEqual(unit, i.Unit, "Unit"); Assert.AreEqual(unit, i.Unit, "Unit");
Assert.IsTrue(i.IsNumerical, "IsNumerical"); Assert.IsTrue(i.IsNumerical, "IsNumerical");
@ -52,7 +53,7 @@ namespace Tests
string laurisString, string name, double value, string laurisString, string name, double value,
string unit, double lowerLimit, bool isNormal) string unit, double lowerLimit, bool isNormal)
{ {
LabItem i = new LabItem(laurisString); LaurisItem i = new LaurisItem(laurisString);
Assert.AreEqual(name, i.Name, "Name"); Assert.AreEqual(name, i.Name, "Name");
Assert.AreEqual(unit, i.Unit, "Unit"); Assert.AreEqual(unit, i.Unit, "Unit");
Assert.IsTrue(i.IsNumerical, "IsNumerical"); Assert.IsTrue(i.IsNumerical, "IsNumerical");
@ -69,7 +70,7 @@ namespace Tests
string laurisString, string name, double value, string laurisString, string name, double value,
string unit, double upperLimit, bool isNormal) string unit, double upperLimit, bool isNormal)
{ {
LabItem i = new LabItem(laurisString); LaurisItem i = new LaurisItem(laurisString);
Assert.AreEqual(name, i.Name, "Name"); Assert.AreEqual(name, i.Name, "Name");
Assert.AreEqual(unit, i.Unit, "Unit"); Assert.AreEqual(unit, i.Unit, "Unit");
Assert.IsTrue(i.IsNumerical, "IsNumerical"); Assert.IsTrue(i.IsNumerical, "IsNumerical");
@ -87,7 +88,7 @@ namespace Tests
string laurisString, string name, double value, string laurisString, string name, double value,
string unit) string unit)
{ {
LabItem i = new LabItem(laurisString); LaurisItem i = new LaurisItem(laurisString);
Assert.AreEqual(name, i.Name, "Name"); Assert.AreEqual(name, i.Name, "Name");
Assert.AreEqual(unit, i.Unit, "Unit"); Assert.AreEqual(unit, i.Unit, "Unit");
Assert.IsTrue(i.IsNumerical, "IsNumerical"); Assert.IsTrue(i.IsNumerical, "IsNumerical");
@ -101,14 +102,14 @@ namespace Tests
public void ParseLaurisNonNumericNoNormal( public void ParseLaurisNonNumericNoNormal(
string laurisString, string name, string value) string laurisString, string name, string value)
{ {
LabItem i = new LabItem(laurisString); LaurisItem i = new LaurisItem(laurisString);
Assert.AreEqual(name, i.Name, "Name"); Assert.AreEqual(name, i.Name, "Name");
Assert.AreEqual(value, i.Value, "Value"); Assert.AreEqual(value, i.Value, "Value");
Assert.IsTrue(String.IsNullOrEmpty(i.Unit), "Unit should be a null string"); Assert.IsTrue(String.IsNullOrEmpty(i.Unit), "Unit should be a null string");
Assert.IsFalse(i.HasLimits, "HasLimits"); Assert.IsFalse(i.HasLimits, "HasLimits");
Assert.IsFalse(i.HasLowerLimit, "HasLowerLimit"); Assert.IsFalse(i.HasLowerLimit, "HasLowerLimit");
Assert.IsFalse(i.HasUpperLimit, "HasUpperLimit"); Assert.IsFalse(i.HasUpperLimit, "HasUpperLimit");
// TODO: Define the behavior of LabItem.IsNormal if no normal value known // TODO: Define the behavior of LaurisItem.IsNormal if no normal value known
} }
[TestCase("Erythrozyten (U): + [negativ]", "Erythrozyten (U)", "+", "negativ", false)] [TestCase("Erythrozyten (U): + [negativ]", "Erythrozyten (U)", "+", "negativ", false)]
@ -116,7 +117,7 @@ namespace Tests
public void ParseLaurisNonNumericWithNormal( public void ParseLaurisNonNumericWithNormal(
string laurisString, string name, string value, string normal, bool isNormal) string laurisString, string name, string value, string normal, bool isNormal)
{ {
LabItem i = new LabItem(laurisString); LaurisItem i = new LaurisItem(laurisString);
Assert.AreEqual(name, i.Name, "Name"); Assert.AreEqual(name, i.Name, "Name");
Assert.AreEqual(value, i.Value, "Value"); Assert.AreEqual(value, i.Value, "Value");
Assert.AreEqual(normal, i.Normal, "Normal"); Assert.AreEqual(normal, i.Normal, "Normal");
@ -124,7 +125,7 @@ namespace Tests
Assert.IsFalse(i.HasLimits, "HasLimits"); Assert.IsFalse(i.HasLimits, "HasLimits");
Assert.IsFalse(i.HasLowerLimit, "HasLowerLimit"); Assert.IsFalse(i.HasLowerLimit, "HasLowerLimit");
Assert.IsFalse(i.HasUpperLimit, "HasUpperLimit"); Assert.IsFalse(i.HasUpperLimit, "HasUpperLimit");
// TODO: Define the behavior of LabItem.IsNormal if no normal value known // TODO: Define the behavior of LaurisItem.IsNormal if no normal value known
} }
[TestCase("Albumin (SU)/die: 149.9 [<= 30] mg/d; ", Material.SU)] [TestCase("Albumin (SU)/die: 149.9 [<= 30] mg/d; ", Material.SU)]
@ -132,7 +133,7 @@ namespace Tests
[TestCase("Cystatin C (N Latex): 2.37 [0.57 - 0.96] mg/l; ", Material.B)] [TestCase("Cystatin C (N Latex): 2.37 [0.57 - 0.96] mg/l; ", Material.B)]
public void ParseLaurisMaterial(string laurisString, Material expectedMaterial) public void ParseLaurisMaterial(string laurisString, Material expectedMaterial)
{ {
LabItem i = new LabItem(laurisString); LaurisItem i = new LaurisItem(laurisString);
Assert.AreEqual(expectedMaterial, i.Material); Assert.AreEqual(expectedMaterial, i.Material);
} }
} }

View File

@ -16,9 +16,10 @@
* limitations under the License. * limitations under the License.
*/ */
using NUnit.Framework; using NUnit.Framework;
using zaaReloaded2.Models; using zaaReloaded2.LabModel;
using zaaReloaded2.Importer.ZaaImporter;
namespace Tests namespace Tests.Importer.ZaaImporter
{ {
[TestFixture] [TestFixture]
class LaurisParagraphTest class LaurisParagraphTest

View File

@ -1,4 +1,4 @@
/* TimePointTest.cs /* LaurisTimePointTest.cs
* part of zaaReloaded2 * part of zaaReloaded2
* *
* Copyright 2015 Daniel Kraus * Copyright 2015 Daniel Kraus
@ -20,47 +20,48 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using NUnit.Framework; using NUnit.Framework;
using zaaReloaded2.Models; using zaaReloaded2.LabModel;
using zaaReloaded2.Importer.ZaaImporter;
namespace Tests namespace Tests.Importer.ZaaImporter
{ {
[TestFixture] [TestFixture]
class TimePointTest class LaurisTimePointTest
{ {
[Test] [Test]
public void ParseValidTimePoint() public void ParseValidLaurisTimePoint()
{ {
TimePoint tp = new TimePoint( LaurisTimePoint tp = new LaurisTimePoint(
"[22.10.2013 12:30:00]" + Environment.NewLine + "[22.10.2013 12:30:00]" + Environment.NewLine +
"Klinische Chemie: Natrium: 139 [135 - 145] mmol/l;  Kalium: 5.2 [3.5 - 5] mmol/l;"); "Klinische Chemie: Natrium: 139 [135 - 145] mmol/l;  Kalium: 5.2 [3.5 - 5] mmol/l;");
Assert.IsTrue(tp.IsValidTimePoint); Assert.IsTrue(tp.IsValidTimePoint);
} }
[Test] [Test]
public void ParseInvalidTimePoints() public void ParseInvalidLaurisTimePoints()
{ {
TimePoint tp = new TimePoint("Aerobe Kultur: Enterokokken ;  Wachstum: 10 ;"); LaurisTimePoint tp = new LaurisTimePoint("Aerobe Kultur: Enterokokken ;  Wachstum: 10 ;");
Assert.IsFalse(tp.IsValidTimePoint, Assert.IsFalse(tp.IsValidTimePoint,
"Bogus paragraph should be invalid TimePoint"); "Bogus paragraph should be invalid LaurisTimePoint");
tp.LaurisText = "[22.10.2013 12:30:00]"; tp.LaurisText = "[22.10.2013 12:30:00]";
Assert.IsFalse(tp.IsValidTimePoint, Assert.IsFalse(tp.IsValidTimePoint,
"TimePoint should be invalid if it consists of time stamp only."); "LaurisTimePoint should be invalid if it consists of time stamp only.");
} }
[Test] [Test]
public void ParseTimePointWithDuplicateItems() public void ParseLaurisTimePointWithDuplicateItems()
{ {
TimePoint tp = new TimePoint( LaurisTimePoint tp = new LaurisTimePoint(
"[22.10.2013 12:30:00]" + Environment.NewLine + "[22.10.2013 12:30:00]" + Environment.NewLine +
"Klinische Chemie: Natrium: 139 [135 - 145] mmol/l;  Kalium: 5.2 [3.5 - 5] mmol/l;" + Environment.NewLine + "Klinische Chemie: Natrium: 139 [135 - 145] mmol/l;  Kalium: 5.2 [3.5 - 5] mmol/l;" + Environment.NewLine +
"Klinische Chemie: Natrium: 142 [135 - 145] mmol/l;  Kalium: 3.7 [3.5 - 5] mmol/l;" "Klinische Chemie: Natrium: 142 [135 - 145] mmol/l;  Kalium: 3.7 [3.5 - 5] mmol/l;"
); );
Assert.IsTrue(tp.Items.ContainsKey("Kalium"), Assert.IsTrue(tp.Items.ContainsKey("Kalium"),
"TimePoint should contain 'Kalium' item."); "LaurisTimePoint should contain 'Kalium' item.");
Assert.AreEqual(3.7, tp.Items["Kalium"].NumericalValue, Assert.AreEqual(3.7, tp.Items["Kalium"].NumericalValue,
"TimePoint does not use last occurrence of 'Kalium'."); "LaurisTimePoint does not use last occurrence of 'Kalium'.");
} }
} }
} }

View File

@ -34,6 +34,12 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>zaaReloaded2.pfx</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL"> <Reference Include="nunit.framework, Version=2.6.4.14350, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
@ -58,16 +64,17 @@
</Choose> </Choose>
<ItemGroup> <ItemGroup>
<Compile Include="Dictionaries\DictionaryTest.cs" /> <Compile Include="Dictionaries\DictionaryTest.cs" />
<Compile Include="LabItemTest.cs" /> <Compile Include="Importer\ZaaImporter\LaurisItemTest.cs" />
<Compile Include="LaurisParagraphTest.cs" /> <Compile Include="Importer\ZaaImporter\LaurisParagraphTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="LineParserTest.cs" /> <Compile Include="LineParserTest.cs" />
<Compile Include="Dictionaries\TestDictionary.cs" /> <Compile Include="Dictionaries\TestDictionary.cs" />
<Compile Include="TimePointTest.cs" /> <Compile Include="Importer\ZaaImporter\TimePointTest.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="Tests.licenseheader" /> <None Include="Tests.licenseheader" />
<None Include="zaaReloaded2.pfx" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\zaaReloaded2\zaaReloaded2.csproj"> <ProjectReference Include="..\zaaReloaded2\zaaReloaded2.csproj">

BIN
Tests/zaaReloaded2.pfx Executable file

Binary file not shown.

View File

@ -25,7 +25,7 @@ namespace zaaReloaded2.Dictionaries
/// <summary> /// <summary>
/// A dictionary of <see cref="LabItem"/>s. /// A dictionary of <see cref="LabItem"/>s.
/// </summary> /// </summary>
public interface IItemDictionary : IDictionary<string, Models.LabItem> public interface IItemDictionary : IDictionary<string, LabModel.LabItem>
{ {
void Merge(IItemDictionary otherItemDictionary); void Merge(IItemDictionary otherItemDictionary);
} }

View File

@ -19,7 +19,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using zaaReloaded2.Models; using zaaReloaded2.LabModel;
namespace zaaReloaded2.Dictionaries namespace zaaReloaded2.Dictionaries
{ {

View File

@ -21,7 +21,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.IO; using System.IO;
using zaaReloaded2.Models; using zaaReloaded2.LabModel;
namespace zaaReloaded2.Dictionaries namespace zaaReloaded2.Dictionaries
{ {
@ -49,7 +49,7 @@ namespace zaaReloaded2.Dictionaries
/// Looks up the material for a given <paramref name="laurisName"/>. /// Looks up the material for a given <paramref name="laurisName"/>.
/// </summary> /// </summary>
/// <param name="laurisName">Lauris item name to look up.</param> /// <param name="laurisName">Lauris item name to look up.</param>
/// <returns><see cref="zaaReloaded2.Models.Material"/> enum; if no material is /// <returns><see cref="zaaReloaded2.LabModel.Material"/> enum; if no material is
/// found in the dictionary, the default material "S" (serum) is returned.</returns> /// found in the dictionary, the default material "S" (serum) is returned.</returns>
public Material GetMaterial(string laurisName) public Material GetMaterial(string laurisName)
{ {

View File

@ -0,0 +1,39 @@
/* IImporter.cs
* part of zaaReloaded2
*
* Copyright 2015 Daniel Kraus
*
* 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using zaaReloaded2.LabModel;
namespace zaaReloaded2.Importer
{
public interface IImporter
{
/// <summary>
/// Gets the Laboratory resulting from the data import.
/// </summary>
Laboratory Laboratory { get; set; }
/// <summary>
/// Imports laboratory data contained in a string.
/// </summary>
/// <param name="text">String with laboratory data.</param>
void Import(string text);
}
}

View File

@ -18,183 +18,26 @@
using System; using System;
using System.Globalization; using System.Globalization;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using zaaReloaded2.LabModel;
namespace zaaReloaded2.Models namespace zaaReloaded2.Importer.ZaaImporter
{ {
/// <summary> /// <summary>
/// Represents a single laboratory item (e.g., sodium or creatinine). /// Represents a single laboratory item (e.g., sodium or creatinine).
/// </summary> /// </summary>
public class LabItem public class LaurisItem : LabItem
{ {
#region Properties #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; }
/// <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> /// <summary>
/// The original Lauris string from which this lab item was created. /// The original Lauris string from which this lab item was created.
/// </summary> /// </summary>
public string Lauris { get; private set; } public string LaurisText { get; private set; }
/// <summary> /// <summary>
/// The canonical name of this item, or the Lauris <see cref="Name"/> /// The original name of this item as known by Lauris
/// of the item if no canonical name is known.
/// </summary> /// </summary>
public string CanonicalName { get; private set; } public string OriginalName { get; private set; }
/// <summary>
/// Returns the canonical name prefixed with the abbreviation
/// for the material, e.g. "U-Na" for sodium in the spot urine,
/// but only if the material is not blood.
/// </summary>
public string QualifiedCanonicalName
{
get
{
if (Material == Models.Material.B)
{
return CanonicalName;
}
else
{
return String.Format("{0}-{1}", Material.ToString(), CanonicalName);
}
}
}
public bool AlwaysPrintLimits { get; private set; }
public Material Material { get; private set; }
#endregion #endregion
@ -203,11 +46,12 @@ namespace zaaReloaded2.Models
/// <summary> /// <summary>
/// Creates an empty LabItem object. /// Creates an empty LabItem object.
/// </summary> /// </summary>
public LabItem() { } public LaurisItem() : base() { }
public LabItem(string laurisString) public LaurisItem(string laurisString)
: this()
{ {
Lauris = laurisString; LaurisText = laurisString;
ParseLauris(); ParseLauris();
DetectMaterial(); DetectMaterial();
} }
@ -222,20 +66,16 @@ namespace zaaReloaded2.Models
/// <param name="parameterDictionary">ParameterDictionary that is used /// <param name="parameterDictionary">ParameterDictionary that is used
/// to look up the canonical name, material type, and whether or /// to look up the canonical name, material type, and whether or
/// not to always print the reference interval</param> /// not to always print the reference interval</param>
public LabItem(string laurisString, public LaurisItem(string laurisString,
Dictionaries.ParameterDictionary parameterDictionary, Dictionaries.ParameterDictionary parameterDictionary,
Dictionaries.UnitDictionary unitDictionary) Dictionaries.UnitDictionary unitDictionary)
: this(laurisString) : this(laurisString)
{ {
if (parameterDictionary != null) if (parameterDictionary != null)
{ {
CanonicalName = parameterDictionary.GetCanonicalName(Name); Name = parameterDictionary.GetCanonicalName(OriginalName);
AlwaysPrintLimits = parameterDictionary.GetForceReferenceDisplay(Name); AlwaysPrintLimits = parameterDictionary.GetForceReferenceDisplay(Name);
} }
else
{
CanonicalName = Name;
}
if (unitDictionary != null) if (unitDictionary != null)
{ {
Unit = unitDictionary.TranslateLaurisUnit(Unit); Unit = unitDictionary.TranslateLaurisUnit(Unit);
@ -248,7 +88,7 @@ namespace zaaReloaded2.Models
/// <summary> /// <summary>
/// Parses the original Lauris string contained in /// Parses the original Lauris string contained in
/// <see cref="Lauris"/>. /// <see cref="LaurisText"/>.
/// </summary> /// </summary>
void ParseLauris() void ParseLauris()
{ {
@ -264,19 +104,20 @@ namespace zaaReloaded2.Models
@"(?<name>[^:]+):\s*(?<value>[\d.]+)\s*(?<limits>\[[^\]]+])?\s*(?<unit>[^;]+)?"); @"(?<name>[^:]+):\s*(?<value>[\d.]+)\s*(?<limits>\[[^\]]+])?\s*(?<unit>[^;]+)?");
Regex categoricalRegex = new Regex( Regex categoricalRegex = new Regex(
@"(?<name>[^:]+):\s*(?<value>[^[;]+)\s*(\[(?<normal>[^\]]+)])?"); @"(?<name>[^:]+):\s*(?<value>[^[;]+)\s*(\[(?<normal>[^\]]+)])?");
if (numericalRegex.IsMatch(Lauris)) if (numericalRegex.IsMatch(LaurisText))
{ {
match = numericalRegex.Match(Lauris); match = numericalRegex.Match(LaurisText);
ParseLimits(match); ParseLimits(match);
} }
else else
{ {
match = categoricalRegex.Match(Lauris); match = categoricalRegex.Match(LaurisText);
Normal = match.Groups["normal"].Value.Trim(); Normal = match.Groups["normal"].Value.Trim();
} }
if (match != null) if (match != null)
{ {
Name = match.Groups["name"].Value.Trim(); OriginalName = match.Groups["name"].Value.Trim();
Name = OriginalName;
Value = match.Groups["value"].Value.Trim(); Value = match.Groups["value"].Value.Trim();
Unit = match.Groups["unit"].Value.Trim(); Unit = match.Groups["unit"].Value.Trim();
} }
@ -333,16 +174,19 @@ namespace zaaReloaded2.Models
/// </example> /// </example>
void DetectMaterial() void DetectMaterial()
{ {
Match m = _materialRegex.Match(Name); // 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);
if (m.Success) if (m.Success)
{ {
switch (m.Groups["material"].Value.ToUpper()) switch (m.Groups["material"].Value.ToUpper())
{ {
case "SU": case "SU":
Material = Models.Material.SU; Material = LabModel.Material.SU;
break; break;
case "PU": case "PU":
Material = Models.Material.U; Material = LabModel.Material.U;
break; break;
} }
} }
@ -352,9 +196,6 @@ namespace zaaReloaded2.Models
#region Fields #region Fields
double _numericalValue;
double _lowerLimit;
double _upperLimit;
static readonly Regex _materialRegex = new Regex(@"\((?<material>(SU|PU))\)"); static readonly Regex _materialRegex = new Regex(@"\((?<material>(SU|PU))\)");
#endregion #endregion

View File

@ -22,8 +22,9 @@ using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using zaaReloaded2.Dictionaries; using zaaReloaded2.Dictionaries;
using zaaReloaded2.LabModel;
namespace zaaReloaded2.Models namespace zaaReloaded2.Importer.ZaaImporter
{ {
/// <summary> /// <summary>
/// Parses an entire Lauris paragraph (such as "Klinische Chemie: ...") /// Parses an entire Lauris paragraph (such as "Klinische Chemie: ...")
@ -106,8 +107,8 @@ namespace zaaReloaded2.Models
foreach (Capture itemCapture in m.Groups["items"].Captures) foreach (Capture itemCapture in m.Groups["items"].Captures)
{ {
LabItem i = new LabItem(itemCapture.Value, _parameterDictionary, _unitDictionary); LaurisItem i = new LaurisItem(itemCapture.Value, _parameterDictionary, _unitDictionary);
Items.Add(i.QualifiedCanonicalName, i); Items.Add(i.QualifiedName, i);
} }
IsLaurisParagraph = Items.Count > 0; IsLaurisParagraph = Items.Count > 0;
} }

View File

@ -1,4 +1,4 @@
/* TimePoint.cs /* LaurisTimePoint.cs
* part of zaaReloaded2 * part of zaaReloaded2
* *
* Copyright 2015 Daniel Kraus * Copyright 2015 Daniel Kraus
@ -23,16 +23,28 @@ using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using zaaReloaded2.Dictionaries; using zaaReloaded2.Dictionaries;
using zaaReloaded2.LabModel;
namespace zaaReloaded2.Models namespace zaaReloaded2.Importer.ZaaImporter
{ {
/// <summary> /// <summary>
/// Represents all Lauris items for a given time point. /// Holds all laboratory items for a given time point.
/// </summary> /// </summary>
public class TimePoint class LaurisTimePoint : TimePoint
{ {
#region Properties #region Properties
/// <summary>
/// Gets an array of paragraphs in this LaurisText.
/// </summary>
public string[] Paragraphs { get; private set; }
/// <summary>
/// Is true if the LaurisText has time stamp in the first
/// paragraph and <see cref="LabItem"/>s in the others.
/// </summary>
public bool IsValidTimePoint { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the original Lauris text for this timepoint. /// Gets or sets the original Lauris text for this timepoint.
/// </summary> /// </summary>
@ -55,38 +67,13 @@ namespace zaaReloaded2.Models
} }
} }
/// <summary>
/// Gets an array of paragraphs in this LaurisText.
/// </summary>
public string[] Paragraphs { get; private set; }
/// <summary>
/// Is true if the LaurisText has time stamp in the first
/// paragraph and <see cref="LabItem"/>s in the others.
/// </summary>
public bool IsValidTimePoint { get; private set; }
/// <summary>
/// Gets the date and time information for this TimePoint.
/// If IsValidTimePoint is false, the value of TimeStamp
/// is undefined.
/// </summary>
public DateTime TimeStamp { get; private set; }
/// <summary>
/// Returns a sorted dictionary of all <see cref="LabItem"/>s found in
/// the <see cref="LaurisText"/>. If a laboratory parameter occurs more
/// than once, only the last occurrence is saved.
/// </summary>
public IItemDictionary Items { get; private set; }
#endregion #endregion
#region Constructors #region Constructors
public TimePoint() { } public LaurisTimePoint() { }
public TimePoint(string laurisTest) public LaurisTimePoint(string laurisTest)
: this() : this()
{ {
_parameterDictionary = null; _parameterDictionary = null;
@ -94,7 +81,7 @@ namespace zaaReloaded2.Models
LaurisText = laurisTest; LaurisText = laurisTest;
} }
public TimePoint( public LaurisTimePoint(
string laurisTest, string laurisTest,
ParameterDictionary parameterDictionary, ParameterDictionary parameterDictionary,
UnitDictionary unitDictionary) UnitDictionary unitDictionary)

View File

@ -0,0 +1,54 @@
/* ZaaImporter.cs
* part of zaaReloaded2
*
* Copyright 2015 Daniel Kraus
*
* 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using zaaReloaded2.LabModel;
namespace zaaReloaded2.Importer.ZaaImporter
{
/// <summary>
/// Imports laboratory items by parsing the Lauris data from a
/// physician's letter.
/// </summary>
public class ZaaImporter : IImporter
{
#region IImporter implementation
public Laboratory Laboratory
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public void Import(string text)
{
throw new NotImplementedException();
}
#endregion
}
}

204
zaaReloaded2/LabModel/LabItem.cs Executable file
View File

@ -0,0 +1,204 @@
/* LabItem.cs
* part of zaaReloaded2
*
* Copyright 2015 Daniel Kraus
*
* 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;
using System.Globalization;
using System.Text.RegularExpressions;
namespace zaaReloaded2.LabModel
{
/// <summary>
/// Represents a single laboratory item (e.g., sodium or creatinine).
/// </summary>
public class LabItem
{
#region Properties
/// <summary>
/// Gets or sets the original name of the item (as known by Lauris).
/// </summary>
public string Name { get; protected set; }
/// <summary>
/// Gets or sets the unit of the item (as known by Lauris).
/// </summary>
public string Unit { get; protected set; }
/// <summary>
/// Gets or sets the value of the item. This may be a number or a string.
/// </summary>
public string Value { get; protected 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 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; protected set; }
/// <summary>
/// Gets or sets the lower limit of normal.
/// </summary>
public double LowerLimit
{
get
{
return _lowerLimit;
}
protected set
{
_lowerLimit = value;
HasLowerLimit = true;
}
}
/// <summary>
/// Is true if the item has a lower limit of normal.
/// </summary>
public bool HasLowerLimit { get; protected 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; protected 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>
/// Returns the canonical name prefixed with the abbreviation
/// for the material, e.g. "U-Na" for sodium in the spot urine,
/// but only if the material is not blood.
/// </summary>
public string QualifiedName
{
get
{
if (Material == LabModel.Material.B)
{
return Name;
}
else
{
return String.Format("{0}-{1}", Material.ToString(), Name);
}
}
}
public bool AlwaysPrintLimits { get; protected set; }
public Material Material { get; protected set; }
#endregion
#region Constructor
public LabItem() { }
#endregion
#region Fields
double _numericalValue;
double _lowerLimit;
double _upperLimit;
#endregion
}
}

View File

@ -0,0 +1,36 @@
/* Laboratory.cs
* part of zaaReloaded2
*
* Copyright 2015 Daniel Kraus
*
* 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace zaaReloaded2.LabModel
{
/// <summary>
/// Holds laboratory items grouped by time points.
/// </summary>
public class Laboratory
{
#region Properties
public SortedDictionary<DateTime, TimePoint> TimePoints { get; set; }
#endregion
}
}

View File

@ -21,7 +21,7 @@ using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
namespace zaaReloaded2.Models namespace zaaReloaded2.LabModel
{ {
/// <summary> /// <summary>
/// Enumerates the known materials. /// Enumerates the known materials.

View File

@ -0,0 +1,52 @@
/* TimePoint.cs
* part of zaaReloaded2
*
* Copyright 2015 Daniel Kraus
*
* 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;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using zaaReloaded2.Dictionaries;
namespace zaaReloaded2.LabModel
{
/// <summary>
/// Holds laboratory items for a given time point.
/// </summary>
public class TimePoint
{
#region Properties
/// <summary>
/// Gets the date and time information for this TimePoint.
/// If IsValidTimePoint is false, the value of TimeStamp
/// is undefined.
/// </summary>
public DateTime TimeStamp { get; set; }
/// <summary>
/// Returns a sorted dictionary of all <see cref="LabItem"/>s found in
/// the <see cref="LaurisText"/>. If a laboratory parameter occurs more
/// than once, only the last occurrence is saved.
/// </summary>
public IItemDictionary Items { get; set; }
#endregion
}
}

View File

@ -14,6 +14,7 @@ using System.Security;
[assembly: AssemblyCopyright("Copyright © Microsoft 2015")] [assembly: AssemblyCopyright("Copyright © Microsoft 2015")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
[assembly: InternalsVisibleTo("Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010061ecc0718277dee13e7dae2dad33787a981c9883ba80a659bebbdbec76563e201a7a3a6a5852e01bb5eb328d24d5889244b4626da9af9f93db663565441a3120e3985789e6f2a39289f4eed063725b84152cbef472d9dd2f7495f51dad6c91f4dc6fb7c72cb6bd9381335ac9878ec0a6369e880f35b8eca3063e16468d7704eb")]
// Setting ComVisible to false makes the types in this assembly not visible // Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from // to COM components. If you need to access a type in this assembly from

View File

@ -166,10 +166,15 @@
<Compile Include="Dictionaries\LineParser.cs" /> <Compile Include="Dictionaries\LineParser.cs" />
<Compile Include="Dictionaries\ParameterDictionary.cs" /> <Compile Include="Dictionaries\ParameterDictionary.cs" />
<Compile Include="Dictionaries\UnitDictionary.cs" /> <Compile Include="Dictionaries\UnitDictionary.cs" />
<Compile Include="Models\LabItem.cs" /> <Compile Include="Importer\IImporter.cs" />
<Compile Include="Models\LaurisParagraph.cs" /> <Compile Include="Importer\ZaaImporter\LaurisItem.cs" />
<Compile Include="Models\Material.cs" /> <Compile Include="Importer\ZaaImporter\LaurisTimePoint.cs" />
<Compile Include="Models\TimePoint.cs" /> <Compile Include="Importer\ZaaImporter\ZaaImporter.cs" />
<Compile Include="LabModel\LabItem.cs" />
<Compile Include="LabModel\Laboratory.cs" />
<Compile Include="Importer\ZaaImporter\LaurisParagraph.cs" />
<Compile Include="LabModel\Material.cs" />
<Compile Include="LabModel\TimePoint.cs" />
<Compile Include="Properties\AssemblyInfo.cs"> <Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
@ -207,6 +212,9 @@
<Content Include="Defaults\parameters.txt" /> <Content Include="Defaults\parameters.txt" />
<Content Include="Defaults\units.txt" /> <Content Include="Defaults\units.txt" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Formatter\" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion> <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>