diff --git a/Tests/Controller/Elements/ItemsTest.cs b/Tests/Controller/Elements/ItemsTest.cs index eb56ed2..4f757ad 100755 --- a/Tests/Controller/Elements/ItemsTest.cs +++ b/Tests/Controller/Elements/ItemsTest.cs @@ -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); diff --git a/Tests/Formatter/ItemCommentTest.cs b/Tests/Formatter/ItemCommentTest.cs new file mode 100755 index 0000000..1223273 --- /dev/null +++ b/Tests/Formatter/ItemCommentTest.cs @@ -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: µg/l)\""); + i.Value = String.Empty; + Assert.AreEqual(String.Empty, i.BuildComment()); + } + } +} diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 644547a..1119cc5 100755 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -80,6 +80,7 @@ + diff --git a/zaaReloaded2/Controller/Elements/Items.cs b/zaaReloaded2/Controller/Elements/Items.cs index 61f403d..8ebf00d 100755 --- a/zaaReloaded2/Controller/Elements/Items.cs +++ b/zaaReloaded2/Controller/Elements/Items.cs @@ -94,6 +94,15 @@ namespace zaaReloaded2.Controller.Elements #endregion + #region Events + + /// + /// Propagates the FillInComment events of collected items. + /// + public event EventHandler FillInComment; + + #endregion + #region Constructors public Items() : base() { } @@ -123,7 +132,7 @@ namespace zaaReloaded2.Controller.Elements { _items = null; _caption = null; - Regex r = new Regex(@"((?[^:]+):\s*)?((?[^,]+),\s*)*(?[^,]+)"); + Regex r = new Regex(@"((?[^:""]+):\s*)?((?[^,]+),\s*)*(?[^,]+)"); Match m = r.Match(Content); if (m.Success) { @@ -186,9 +195,22 @@ namespace zaaReloaded2.Controller.Elements /// /// Collects items for output by name. /// - /// Item name to look for. + /// Item name to look for. If the item name contains + /// a comment definition (see example in the description of the + /// event), List 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 items = new List(); 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 h = FillInComment; + if (h != null) + { + h(this, args); + } + } #endregion #region Fields string _caption; List _items; - static Regex _wildcard = new Regex(@"(?[^-]+-)?\*"); - + static readonly Regex _wildcard = new Regex(@"(?[^-]+-)?\*"); + #endregion } } diff --git a/zaaReloaded2/Formatter/Formatter.cs b/zaaReloaded2/Formatter/Formatter.cs index 6639198..c5fd796 100755 --- a/zaaReloaded2/Formatter/Formatter.cs +++ b/zaaReloaded2/Formatter/Formatter.cs @@ -34,7 +34,20 @@ namespace zaaReloaded2.Formatter { #region Properties - public Settings Settings { get; set; } + /// + /// Gets or sets the Settings that this Formatter works with. + /// + public Settings Settings + { + get { return _settings; } + set + { + _settings = value; + // Listen to the FillInComment event of + _settings.Elements.OfType().ToList().ForEach( + items => items.FillInComment += items_FillInComment); + } + } /// /// Gets or sets the that shall be @@ -66,9 +79,28 @@ namespace zaaReloaded2.Formatter /// /// 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. /// - 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 + + /// + /// Relays the FillInComment events of any Items elements + /// in the Settings, which in turn relay the FillInComment + /// events of their collected items' ItemComments. + /// + public event EventHandler FillInComment; #endregion @@ -119,7 +151,14 @@ namespace zaaReloaded2.Formatter /// current position of the cursor). 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(); + } } /// @@ -385,6 +431,23 @@ namespace zaaReloaded2.Formatter #endregion + #region Private methods + + /// + /// Relays the FillInComment event of Items elements in the + /// Settings. + /// + void items_FillInComment(object sender, ItemCommentEventArgs e) + { + EventHandler h = FillInComment; + if (h != null) + { + h(this, e); + } + } + + #endregion + #region Protected properties /// @@ -396,6 +459,7 @@ namespace zaaReloaded2.Formatter #region Fields + Settings _settings; TimePointFormatterDictionary _timePointFormatters; Laboratory _laboratory; DocumentWriter _primaryBuffer; diff --git a/zaaReloaded2/Formatter/ItemComment.cs b/zaaReloaded2/Formatter/ItemComment.cs new file mode 100755 index 0000000..157eebf --- /dev/null +++ b/zaaReloaded2/Formatter/ItemComment.cs @@ -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 +{ + /// + /// 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. + /// + /// + /// TAC "(Zielspiegel: <8-10> µg/l)" + /// + /// + /// 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 ("<>"). + /// + public class ItemComment + { + #region Factory + + /// + /// Creates a new ItemComment object from a definition. + /// + /// Definition string. + /// ItemComment object, or null if the + /// string could not be + /// parsed. + 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 + + /// + /// Gets or sets the item name that this comment is for. + /// + public string Item { get; set; } + + /// + /// Prefix of this comment, e.g. "(target trough level: " + /// + public string Prefix { get; set; } + + /// + /// Value of this comment; String.Empty represents a + /// cancelled comment. + /// + public string Value { get; set; } + + /// + /// Suffix of this comment, e.g. " µg/l)" + /// + public string Suffix { get; set; } + + #endregion + + #region Event + + /// + /// Event that is raised when the comment value needs to be + /// filled in by someone or something. + /// + public event EventHandler 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 + + /// + /// 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. + /// + /// Comment string with filled-in value, or String.Empty + /// if the value is an empty string. + public string BuildComment() + { + OnFillInComment(); + + if (String.IsNullOrEmpty(Value)) + return String.Empty; + + return Prefix + Value + Suffix; + } + + #endregion + + #region Private methods + + /// + /// Raises the FillInComment event. + /// + protected virtual void OnFillInComment() + { + EventHandler 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(@"(?[^""]+)""(?[^""]+)"""); + static readonly Regex _commentDefinition = + new Regex(@"(?[^<]+)<(?[^>]*)>(?[^""]+)"); + + #endregion + } +} diff --git a/zaaReloaded2/Formatter/ItemCommentEventArgs.cs b/zaaReloaded2/Formatter/ItemCommentEventArgs.cs new file mode 100755 index 0000000..9787ed9 --- /dev/null +++ b/zaaReloaded2/Formatter/ItemCommentEventArgs.cs @@ -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 +{ + /// + /// Event arguments used in item commenting. + /// + public class ItemCommentEventArgs : EventArgs + { + #region Properties + + /// + /// Gets the comment object for this parameter. + /// + public ItemComment Comment { get; private set; } + + /// + /// Gets or sets whether the commenting was cancelled. + /// + public bool IsCancelled { get; set; } + + #endregion + + #region Constructor + + public ItemCommentEventArgs( + ItemComment comment) + { + Comment = comment; + } + + #endregion + } +} diff --git a/zaaReloaded2/Formatter/ItemFormatter.cs b/zaaReloaded2/Formatter/ItemFormatter.cs index c71baf9..fcd5fd1 100755 --- a/zaaReloaded2/Formatter/ItemFormatter.cs +++ b/zaaReloaded2/Formatter/ItemFormatter.cs @@ -70,6 +70,11 @@ namespace zaaReloaded2.Formatter /// public bool IsBlacklisted { get { return LabItem.IsBlacklisted; } } + /// + /// Gets or sets the item's comment. + /// + 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; } diff --git a/zaaReloaded2/zaaReloaded2.csproj b/zaaReloaded2/zaaReloaded2.csproj index 6d33e56..bfbc184 100755 --- a/zaaReloaded2/zaaReloaded2.csproj +++ b/zaaReloaded2/zaaReloaded2.csproj @@ -195,6 +195,8 @@ + +