Initial implementation of ItemComments.

This commit is contained in:
Daniel Kraus 2015-08-29 03:16:44 +02:00
parent efbff88b1a
commit a89a8103e5
9 changed files with 442 additions and 12 deletions

View File

@ -181,6 +181,28 @@ namespace Tests.Controller.Elements
Assert.AreEqual(expected, _document.Range().Text);
}
[Test]
public void ItemCommentWithoutHandler()
{
Laboratory lab = new Laboratory();
TimePoint tp = new TimePoint();
tp.TimeStamp = new DateTime(2015, 7, 13, 13, 31, 00);
tp.AddItem(new LabItem("Na", "133", "133"));
tp.AddItem(new LabItem("K", "6", "5"));
lab.AddTimePoint(tp);
_formatter.Laboratory = lab;
_formatter.Settings.Elements.Add(
new zaa.Items("Na \"(Zielbereich: <> mmol/l)\""));
_formatter.Run();
string expected = (
StripMarkup(
TimePointFormatter.DateAndTimeHeader(new DateTime(2015, 07, 13, 13, 31, 00)) +
"Na 133\r\r").Replace(Environment.NewLine, "\r")
);
Assert.AreEqual(expected, _document.Range().Text);
}
static string StripMarkup(string s)
{
return _markupStripper.Replace(s, string.Empty);

View File

@ -0,0 +1,57 @@
/* ItemCommentTest.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 NUnit.Framework;
using zaaReloaded2.Formatter;
namespace Tests.Formatter
{
[TestFixture]
class ItemCommentTest
{
[Test]
public void FactoryWithGoodDefinition()
{
ItemComment i = ItemComment.FromDefinition("TAC \"(Zielbereich: <4-7> µg/l)\"");
Assert.IsNotNull(i);
Assert.AreEqual("(Zielbereich: ", i.Prefix);
Assert.AreEqual("4-7", i.Value);
Assert.AreEqual(" µg/l)", i.Suffix);
Assert.AreEqual("TAC", i.Item);
Assert.AreEqual("(Zielbereich: 4-7 µg/l)", i.BuildComment());
}
[Test]
public void FactoryWithBadDefinition()
{
ItemComment i = ItemComment.FromDefinition("some bogus definition");
Assert.IsNull(i);
}
[Test]
public void EmptyValueProducesEmptyComment()
{
ItemComment i = ItemComment.FromDefinition("TAC \"(Zielbereich: <default> µg/l)\"");
i.Value = String.Empty;
Assert.AreEqual(String.Empty, i.BuildComment());
}
}
}

View File

@ -80,6 +80,7 @@
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="Formatter\ItemCommentTest.cs" />
<Compile Include="SerializationTest.cs" />
<Compile Include="Controller\SettingsRepositoryTest.cs" />
<Compile Include="Controller\SettingsTest.cs" />

View File

@ -94,6 +94,15 @@ namespace zaaReloaded2.Controller.Elements
#endregion
#region Events
/// <summary>
/// Propagates the FillInComment events of collected items.
/// </summary>
public event EventHandler<ItemCommentEventArgs> FillInComment;
#endregion
#region Constructors
public Items() : base() { }
@ -123,7 +132,7 @@ namespace zaaReloaded2.Controller.Elements
{
_items = null;
_caption = null;
Regex r = new Regex(@"((?<caption>[^:]+):\s*)?((?<items>[^,]+),\s*)*(?<items>[^,]+)");
Regex r = new Regex(@"((?<caption>[^:""]+):\s*)?((?<items>[^,]+),\s*)*(?<items>[^,]+)");
Match m = r.Match(Content);
if (m.Success)
{
@ -186,9 +195,22 @@ namespace zaaReloaded2.Controller.Elements
/// <summary>
/// Collects items for output by name.
/// </summary>
/// <param name="name">Item name to look for.</param>
/// <param name="name">Item name to look for. If the item name contains
/// a comment definition (see example in the description of the
/// <see cref="RequestParameterComment"/> event), </param>
List<ItemFormatter> CollectByName(zaaReloaded2.Formatter.Formatter formatter, string name)
{
// First, check if the item name contains an optional comment
// definition
ItemComment comment = ItemComment.FromDefinition(name);
if (comment != null)
{
name = comment.Item;
// Enable propagation of FillInComment events
comment.FillInComment += comment_FillInComment;
}
// Then, see if we have such an item
List<ItemFormatter> items = new List<ItemFormatter>();
TimePointFormatter tpf = formatter.WorkingTimePoints
.FirstOrDefault(tp => tp.Value.ContainsItem(name))
@ -200,19 +222,37 @@ namespace zaaReloaded2.Controller.Elements
ItemFormatter i = tpf.ItemFormatters[name];
i.HasBeenUsed = true;
i.IncludeMaterial = false;
i.Comment = comment;
items.Add(i);
}
return items;
}
void comment_FillInComment(object sender, ItemCommentEventArgs e)
{
OnFillInComment(e);
}
#endregion
#region Protected methods
protected virtual void OnFillInComment(ItemCommentEventArgs args)
{
EventHandler<ItemCommentEventArgs> h = FillInComment;
if (h != null)
{
h(this, args);
}
}
#endregion
#region Fields
string _caption;
List<string> _items;
static Regex _wildcard = new Regex(@"(?<material>[^-]+-)?\*");
static readonly Regex _wildcard = new Regex(@"(?<material>[^-]+-)?\*");
#endregion
}
}

View File

@ -34,7 +34,20 @@ namespace zaaReloaded2.Formatter
{
#region Properties
public Settings Settings { get; set; }
/// <summary>
/// Gets or sets the Settings that this Formatter works with.
/// </summary>
public Settings Settings
{
get { return _settings; }
set
{
_settings = value;
// Listen to the FillInComment event of
_settings.Elements.OfType<Items>().ToList().ForEach(
items => items.FillInComment += items_FillInComment);
}
}
/// <summary>
/// Gets or sets the <see cref="Laboratory"/> that shall be
@ -66,9 +79,28 @@ namespace zaaReloaded2.Formatter
/// <summary>
/// Is true if this Formatter object carries a Laboratory with
/// at least one time point.
/// at least one time point, and if there are Settings to work with.
/// </summary>
public bool CanRun { get { return Laboratory.TimePoints.Count > 0; } }
public bool CanRun
{
get
{
return (Settings != null)
&& (Laboratory != null)
&& (Laboratory.TimePoints.Count > 0);
}
}
#endregion
#region Event
/// <summary>
/// Relays the FillInComment events of any Items elements
/// in the Settings, which in turn relay the FillInComment
/// events of their collected items' ItemComments.
/// </summary>
public event EventHandler<ItemCommentEventArgs> FillInComment;
#endregion
@ -119,7 +151,14 @@ namespace zaaReloaded2.Formatter
/// current position of the cursor).</param>
public void Run()
{
if (!CanRun) throw new NoLaboratoryDataException("No laboratory data to format.");
if (!CanRun)
{
if (Settings == null)
throw new InvalidOperationException("No settings data to work with.");
if ((Laboratory == null) || Laboratory.TimePoints.Count == 0)
throw new NoLaboratoryDataException("No laboratory data to format.");
throw new InvalidOperationException("Cannot run formatter.");
}
int current = 0;
while (current < Settings.Elements.Count)
@ -147,12 +186,19 @@ namespace zaaReloaded2.Formatter
}
// Write everything to the Word document
Globals.ThisAddIn.Application.UndoRecord.StartCustomRecord(
String.Format("Laborformatierung ({0})", Properties.Settings.Default.AddinName)
);
bool hasAddin = Globals.ThisAddIn != null;
if (hasAddin)
{
Globals.ThisAddIn.Application.UndoRecord.StartCustomRecord(
String.Format("Laborformatierung ({0})", Properties.Settings.Default.AddinName)
);
}
CreateStyles();
_secondaryBuffer.Flush();
Globals.ThisAddIn.Application.UndoRecord.EndCustomRecord();
if (hasAddin)
{
Globals.ThisAddIn.Application.UndoRecord.EndCustomRecord();
}
}
/// <summary>
@ -385,6 +431,23 @@ namespace zaaReloaded2.Formatter
#endregion
#region Private methods
/// <summary>
/// Relays the FillInComment event of Items elements in the
/// Settings.
/// </summary>
void items_FillInComment(object sender, ItemCommentEventArgs e)
{
EventHandler<ItemCommentEventArgs> h = FillInComment;
if (h != null)
{
h(this, e);
}
}
#endregion
#region Protected properties
/// <summary>
@ -396,6 +459,7 @@ namespace zaaReloaded2.Formatter
#region Fields
Settings _settings;
TimePointFormatterDictionary _timePointFormatters;
Laboratory _laboratory;
DocumentWriter _primaryBuffer;

View File

@ -0,0 +1,184 @@
/* ParameterComment.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 System.Text.RegularExpressions;
namespace zaaReloaded2.Formatter
{
/// <summary>
/// Represents an optional comment for a laboratory item.
/// The zaaReloaded2.Controller.Elements.Items class can
/// parse these optional comment strings which may be used
/// to prompt users to enter target ranges etc.
/// </summary>
/// <example>
/// TAC "(Zielspiegel: &lt;8-10&gt; µg/l)"
/// </example>
/// <remarks>
/// In the example, the tacrolimus trough level TAC carries a comment about the
/// recommended range. The comment must be enclosed in quotes in order for it
/// to be recognized. The quotes will be stripped. The angle brackets denote
/// a place holder that will be replaced by the comment that is set in the
/// RequestParameterComment's event args. The text between the angle brackets
/// ("8-10") is an optional default value. One could also just use 'empty'
/// brackets ("&lt;&gt;").
/// </remarks>
public class ItemComment
{
#region Factory
/// <summary>
/// Creates a new ItemComment object from a definition.
/// </summary>
/// <param name="definition">Definition string.</param>
/// <returns>ItemComment object, or null if the
/// <paramref name="definition"/> string could not be
/// parsed.</returns>
public static ItemComment FromDefinition(string definition)
{
ItemComment itemComment = null;
// TODO: Maybe turn these to regexes into one.
Match checkHasComment = _itemDefinition.Match(definition);
if (checkHasComment.Success)
{
Match commentComponents =
_commentDefinition.Match(checkHasComment.Groups["comment"].Value);
if (commentComponents.Success)
{
itemComment = new ItemComment(
checkHasComment.Groups["item"].Value.Trim(),
commentComponents.Groups["prefix"].Value,
commentComponents.Groups["value"].Value,
commentComponents.Groups["suffix"].Value
);
}
}
return itemComment;
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the item name that this comment is for.
/// </summary>
public string Item { get; set; }
/// <summary>
/// Prefix of this comment, e.g. "(target trough level: "
/// </summary>
public string Prefix { get; set; }
/// <summary>
/// Value of this comment; String.Empty represents a
/// cancelled comment.
/// </summary>
public string Value { get; set; }
/// <summary>
/// Suffix of this comment, e.g. " µg/l)"
/// </summary>
public string Suffix { get; set; }
#endregion
#region Event
/// <summary>
/// Event that is raised when the comment value needs to be
/// filled in by someone or something.
/// </summary>
public event EventHandler<ItemCommentEventArgs> FillInComment;
#endregion
#region Constructors
public ItemComment()
{
Item = String.Empty;
Prefix = String.Empty;
Value = String.Empty;
Suffix = String.Empty;
}
public ItemComment(string item, string prefix, string value, string suffix)
{
Item = item;
Prefix = prefix;
Value = value;
Suffix = suffix;
}
#endregion
#region Methods
/// <summary>
/// Builds the comment string from the prefix, the value, and
/// the suffix. Raises the FillInComment event to get the value
/// first. If the value is an empty string, the entire comment
/// string will be an empty string.
/// </summary>
/// <returns>Comment string with filled-in value, or String.Empty
/// if the value is an empty string.</returns>
public string BuildComment()
{
OnFillInComment();
if (String.IsNullOrEmpty(Value))
return String.Empty;
return Prefix + Value + Suffix;
}
#endregion
#region Private methods
/// <summary>
/// Raises the FillInComment event.
/// </summary>
protected virtual void OnFillInComment()
{
EventHandler<ItemCommentEventArgs> h = FillInComment;
if (h != null)
{
ItemCommentEventArgs args = new ItemCommentEventArgs(this);
h(this, args);
if (args.IsCancelled)
args.Comment.Value = String.Empty;
}
}
#endregion
#region Fields
static readonly Regex _itemDefinition =
new Regex(@"(?<item>[^""]+)""(?<comment>[^""]+)""");
static readonly Regex _commentDefinition =
new Regex(@"(?<prefix>[^<]+)<(?<value>[^>]*)>(?<suffix>[^""]+)");
#endregion
}
}

View File

@ -0,0 +1,54 @@
/* ParameterCommentEventArgs.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.Formatter
{
/// <summary>
/// Event arguments used in item commenting.
/// </summary>
public class ItemCommentEventArgs : EventArgs
{
#region Properties
/// <summary>
/// Gets the comment object for this parameter.
/// </summary>
public ItemComment Comment { get; private set; }
/// <summary>
/// Gets or sets whether the commenting was cancelled.
/// </summary>
public bool IsCancelled { get; set; }
#endregion
#region Constructor
public ItemCommentEventArgs(
ItemComment comment)
{
Comment = comment;
}
#endregion
}
}

View File

@ -70,6 +70,11 @@ namespace zaaReloaded2.Formatter
/// </summary>
public bool IsBlacklisted { get { return LabItem.IsBlacklisted; } }
/// <summary>
/// Gets or sets the item's comment.
/// </summary>
public ItemComment Comment { get; set; }
#endregion
#region Constructor
@ -184,6 +189,7 @@ namespace zaaReloaded2.Formatter
output = AbnormalStyle.ToMarkup(false) + output + AbnormalStyle.ToMarkup(true);
}
formatter.Write(output);
HasBeenUsed = true;
}

View File

@ -195,6 +195,8 @@
<Compile Include="Controller\Elements\ControlElementBase.cs" />
<Compile Include="Controller\Elements\FormatElementBase.cs" />
<Compile Include="Controller\Elements\NextColumn.cs" />
<Compile Include="Formatter\ItemComment.cs" />
<Compile Include="Formatter\ItemCommentEventArgs.cs" />
<Compile Include="Controller\Elements\SelectEachDay.cs" />
<Compile Include="Controller\Elements\SelectLastDay.cs" />
<Compile Include="Controller\Elements\SelectFirstDay.cs" />