From 8d6ed32a84c6c269f0151e586dc1e31edc7e2d37 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Tue, 4 Aug 2015 06:21:48 +0200 Subject: [PATCH 01/49] Fix LastSelected and LastSelectedElement properties. --- .../ViewModels/SettingsRepositoryViewModel.cs | 42 ++++++++++++------- zaaReloaded2/ViewModels/SettingsViewModel.cs | 28 +++++++++---- 2 files changed, 48 insertions(+), 22 deletions(-) diff --git a/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs b/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs index f04fbba..5a3fc75 100755 --- a/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs @@ -33,7 +33,21 @@ namespace zaaReloaded2.ViewModels public ObservableCollection SettingsList { get; private set; } - public SettingsViewModel Selected { get; private set; } + /// + /// Gets the SettingsViewModel that was most recently selected. Whether + /// this view model is still selected can be found out be getting the + /// view model's IsSelected property. + /// + /// + /// Due to the way the WPF ListBox (for example) is implemented, selecting + /// a list item will trigger an PropertyChanged event twice: Once for the + /// item being selected, and once for the item being deselected. Thus we + /// can only capture the last item that actually was selected. We cannot + /// howeve capture if an item was deselected without a new selection, + /// because we cannot logicaly connect two occurrences of the same event + /// from different objects. + /// + public SettingsViewModel LastSelected { get; private set; } #endregion @@ -195,23 +209,23 @@ namespace zaaReloaded2.ViewModels { if (CanEditSettings()) { - EditSettingsMessage.Send(new ViewModelMessageContent(Selected)); + EditSettingsMessage.Send(new ViewModelMessageContent(LastSelected)); } } bool CanEditSettings() { - return Selected != null && !IsDefaultSettings(); + return LastSelected != null && LastSelected.IsSelected && !IsDefaultSettings(); } void DoUseSettings() { - UseSettingsMessage.Send(new ViewModelMessageContent(Selected)); + UseSettingsMessage.Send(new ViewModelMessageContent(LastSelected)); } bool CanUseSettings() { - return Selected != null; + return LastSelected != null && LastSelected.IsSelected; } void DoAddSettings() @@ -225,7 +239,7 @@ namespace zaaReloaded2.ViewModels bool CanDeleteSettings() { - return Selected != null && !IsDefaultSettings(); + return LastSelected != null && LastSelected.IsSelected && !IsDefaultSettings(); } void DoDeleteSettings() @@ -233,7 +247,7 @@ namespace zaaReloaded2.ViewModels if (CanDeleteSettings()) { ConfirmDeleteSettingsMessage.Send( - new ViewModelMessageContent(Selected), + new ViewModelMessageContent(LastSelected), param => ConfirmDeleteSettings(param)); } } @@ -265,12 +279,12 @@ namespace zaaReloaded2.ViewModels void DoCopySettings() { - if (Selected != null) + if (LastSelected != null) { - SettingsViewModel copy = Selected.Clone() as SettingsViewModel; + SettingsViewModel copy = LastSelected.Clone() as SettingsViewModel; _repository.SettingsList.Add(copy.RevealModelObject() as Settings); AddSettingsViewModel(copy); - Selected.IsSelected = false; + LastSelected.IsSelected = false; copy.IsSelected = true; } } @@ -280,7 +294,7 @@ namespace zaaReloaded2.ViewModels SettingsViewModel vm = sender as SettingsViewModel; if (vm != null && e.PropertyName == "IsSelected") { - Selected = vm.IsSelected ? vm : null; + if (vm.IsSelected) LastSelected = vm; } } @@ -290,11 +304,11 @@ namespace zaaReloaded2.ViewModels /// bool IsDefaultSettings() { - if (Selected != null) + if (LastSelected != null) { return - (Selected.Name == zaaReloaded2.Properties.Settings.Default.SettingsNameClinic - || Selected.Name == zaaReloaded2.Properties.Settings.Default.SettingsNameWard); + (LastSelected.Name == zaaReloaded2.Properties.Settings.Default.SettingsNameClinic + || LastSelected.Name == zaaReloaded2.Properties.Settings.Default.SettingsNameWard); } else { diff --git a/zaaReloaded2/ViewModels/SettingsViewModel.cs b/zaaReloaded2/ViewModels/SettingsViewModel.cs index b16f5d9..1a5cfd1 100755 --- a/zaaReloaded2/ViewModels/SettingsViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsViewModel.cs @@ -76,7 +76,16 @@ namespace zaaReloaded2.ViewModels /// /// Gets or sets the currently selected element. /// - public ElementViewModel SelectedElement + /// + /// Due to the way the WPF ListBox (for example) is implemented, selecting + /// a list item will trigger an PropertyChanged event twice: Once for the + /// item being selected, and once for the item being deselected. Thus we + /// can only capture the last item that actually was selected. We cannot + /// howeve capture if an item was deselected without a new selection, + /// because we cannot logicaly connect two occurrences of the same event + /// from different objects. + /// + public ElementViewModel LastSelectedElement { get { return _selectedElement; } set @@ -279,7 +288,7 @@ namespace zaaReloaded2.ViewModels picker.ElementChosenMessage.Sent += (sender, args) => { FormatElementViewModel newVM = args.Content.ViewModel as FormatElementViewModel; - AddChildElementViewModel(SelectedElement as ControlElementViewModel, newVM); + AddChildElementViewModel(LastSelectedElement as ControlElementViewModel, newVM); }; AddChildElementMessage.Send(new ViewModelMessageContent(picker)); } @@ -287,27 +296,30 @@ namespace zaaReloaded2.ViewModels bool CanAddChildElement() { - return SelectedElement is ControlElementViewModel; + return LastSelectedElement is ControlElementViewModel; } void DoDeleteElement() { } - bool CanDeleteElement() { return _selectedElement != null; } + bool CanDeleteElement() { return LastSelectedElement != null && LastSelectedElement.IsSelected; } void DoCopyElement() { } - bool CanCopyElement() { return _selectedElement != null; } + bool CanCopyElement() { return LastSelectedElement != null && LastSelectedElement.IsSelected; } /// - /// Sets or unsets the SelectedElement property whenever the IsSelected - /// property of an ElementViewModel changes. + /// Sets LastSelectedElement property whenever the IsSelected + /// property of an ElementViewModel changes /// + /// + /// Please see the remarks on the LastSelectedElement property. + /// void ElementViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { ElementViewModel vm = sender as ElementViewModel; if (vm != null && e.PropertyName == "IsSelected") { - SelectedElement = vm.IsSelected ? vm : null; + if (vm.IsSelected) LastSelectedElement = vm; } } From 33d453cf7380ba9aa36342f31d54566a0dd31805 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Tue, 4 Aug 2015 06:25:13 +0200 Subject: [PATCH 02/49] Use ViewModelListBox dynamic resource. --- zaaReloaded2/Views/SettingsRepositoryView.xaml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/zaaReloaded2/Views/SettingsRepositoryView.xaml b/zaaReloaded2/Views/SettingsRepositoryView.xaml index a8e4309..f47b9cc 100755 --- a/zaaReloaded2/Views/SettingsRepositoryView.xaml +++ b/zaaReloaded2/Views/SettingsRepositoryView.xaml @@ -26,16 +26,7 @@ Title="Stil auswählen" > - - - - - - - - + @@ -51,7 +42,7 @@ From ae9cf7fa2ed0a7bca3ba4e0d8251199551787d6a Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Tue, 4 Aug 2015 20:17:36 +0200 Subject: [PATCH 03/49] Improve demo document. --- zaaReloaded2/Demo/Demo.docx | Bin 22087 -> 27125 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/zaaReloaded2/Demo/Demo.docx b/zaaReloaded2/Demo/Demo.docx index 6164d7b28e301a657f6d0d35c2e30b64c0c85766..ac0714c8bfbbf7a344906a2ad0bbb476bfb4f05a 100755 GIT binary patch delta 22015 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ΠY! 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 delta 16864 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%0Dmu5ri(4{zu)-CRE2k1y<{y z_LRA$GTK2=VB6P9UneO1avLoEzlQnP-#}y|@J6p)URQGY0z6nDJ0F^$@_l&_6W0NS z+u@<7wXv)~KtGZauoIT%{CEMTX`crS>$}jG=P&o^%w1n687gW3H(2+Ncdh?dt8nrs zf1%@-{kDHh<30cHroYZ8hA4lH4#yn=YJ7cse0Ov#MYQmj#WV4khuvuq7EgirJhVgLVr~7<5AuWSCURx ztU(KM-2Pl^%bw^u^nw^;BP*{%ZtgCt_kvj(h*Uy3+vOiTP)$ZAMa~TG>TiV3NKLnQN<6B|qoYM9Ae^^Jh48i*AWcNY%*cCTyG`q_4N8OlzGY*W<;2d%^MAS8hg zh>Nksquj@)_K;Xbc~oWQ;N^v{I^910W4QO{d(TNHIvU7~o!c$D-U?9O;3qCFWheMj zC@fKUas07S*qSTWRxDKp>f%zBJK$#xNB!B|k4Z@+cw=R~;!`t>K*6mCu^jE^J()}*p7195W^g>YS9Qeg^^zlr|xKiXK)qzKv9)rN3X zjLA;p&}-2DNbaPfgo`pCVI` zlqLvL=3FY)8_YUCn_`FulL1bQ<7o|2!`$RNJq6Wtx^F9sMwW)b#&|^>AI3s9h3JH% z79pAg;LdEL_FsOi6wJb1R`D(vCB^~G#nRIjQwiLH!6Ijcd4|@Vd)z*$pm-}- zH_ASYh(G+~wT~J-4#0eptRYl>xa4eQ4%FvYQIpxjkG3+|;kh9sw-y(o)4?P_ZeV@_ zK(mmHb;FU|1KC+#**}ik8=HWK6r2(*S0GyfCd-&$f*?n(;hssWGRR-}&UN)Y2$%_n$ITVO=s`+V&`WiLHo!CO!mxa8T|a zW%i&?t@68HY5xK$ix_rT9^qWLe$Ea8r!Cgu|EI4T0Glioz>=AO(<0bF0xOjNajT8h z3KPth11M!hPVk>}J_rcL*HH4GjgAKxVMRso?|iZV2ngPPj((Nad4XeA3(5Ki^XL&n6=ZM71U-_}>>N|EcgQ4pg!xCHU|0?7xSz|5R9&2gX@b5d8ZZ2L}N` V`Ond>(uE>$%9;#{M)jY){~tss!6E Date: Tue, 4 Aug 2015 23:21:38 +0200 Subject: [PATCH 04/49] Refactor and harden ZaaImporter. --- Tests/Importer/ZaaImporter/ZaaImporterTest.cs | 10 ++ zaaReloaded2/Importer/IImporter.cs | 5 + .../Importer/ZaaImporter/LaurisParagraph.cs | 15 ++- .../Importer/ZaaImporter/LaurisTimePoint.cs | 91 ++++++++++--------- .../Importer/ZaaImporter/ZaaImporter.cs | 51 +++++------ 5 files changed, 99 insertions(+), 73 deletions(-) diff --git a/Tests/Importer/ZaaImporter/ZaaImporterTest.cs b/Tests/Importer/ZaaImporter/ZaaImporterTest.cs index 44b8e23..58433cc 100755 --- a/Tests/Importer/ZaaImporter/ZaaImporterTest.cs +++ b/Tests/Importer/ZaaImporter/ZaaImporterTest.cs @@ -41,5 +41,15 @@ namespace Tests.Importer.ZaaImporter // Only 6 distinct time points (see method documentation above). Assert.AreEqual(6, importer.Laboratory.TimePoints.Count); } + + [Test] + public void ParseInvalidInput() + { + zaa.ZaaImporter importer = new zaa.ZaaImporter(); + importer.Import("some arbitrary text\r\nthat does not represent\r\na valid lab"); + Assert.IsFalse(importer.Success); + importer.Import("(03.03.1930 13:30:00)\r\nKlinische Chemie: Natrium 135 [135 - 145] mmol/l;"); + Assert.IsTrue(importer.Success); + } } } diff --git a/zaaReloaded2/Importer/IImporter.cs b/zaaReloaded2/Importer/IImporter.cs index 7da8fe3..d556d05 100755 --- a/zaaReloaded2/Importer/IImporter.cs +++ b/zaaReloaded2/Importer/IImporter.cs @@ -30,6 +30,11 @@ namespace zaaReloaded2.Importer /// Laboratory Laboratory { get; set; } + /// + /// Indicates whether the import was successful. + /// + bool Success { get; } + /// /// Imports laboratory data contained in a string. /// diff --git a/zaaReloaded2/Importer/ZaaImporter/LaurisParagraph.cs b/zaaReloaded2/Importer/ZaaImporter/LaurisParagraph.cs index c230a32..c81fb5d 100755 --- a/zaaReloaded2/Importer/ZaaImporter/LaurisParagraph.cs +++ b/zaaReloaded2/Importer/ZaaImporter/LaurisParagraph.cs @@ -32,6 +32,19 @@ namespace zaaReloaded2.Importer.ZaaImporter /// public class LaurisParagraph { + #region Static methods + + /// + /// Investigates a paragraph and determines whether it looks + /// like a Lauris laboratory items paragraph. + /// + public static bool ResemblesLaurisParagraph(string paragraph) + { + return _expectedFormat.IsMatch(paragraph); + } + + #endregion + #region Public properties /// @@ -122,7 +135,7 @@ namespace zaaReloaded2.Importer.ZaaImporter #region Fields - static readonly Regex _expectedFormat = new Regex(@"(?[^:]+:\s*)?(?[^:]+:\s*[^;]+;)*"); + static readonly Regex _expectedFormat = new Regex(@"(?[^:]+:\s*)?(?[^:]+:\s*[^;]+;)+"); Thesaurus.Parameters _parameterDictionary; Thesaurus.Units _unitDictionary; diff --git a/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs b/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs index dd6abfa..080527c 100755 --- a/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs +++ b/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs @@ -65,11 +65,15 @@ namespace zaaReloaded2.Importer.ZaaImporter /// /// Gets an array of paragraphs in this LaurisText. /// - public string[] Paragraphs + public IList Paragraphs { [DebuggerStepThrough] get { + if (_paragraphs == null) + { + _paragraphs = new List(); + } return _paragraphs; } set @@ -101,7 +105,7 @@ namespace zaaReloaded2.Importer.ZaaImporter { Paragraphs = value.Split( new string[] { Environment.NewLine }, - StringSplitOptions.None); + StringSplitOptions.None).ToList(); } } } @@ -128,7 +132,7 @@ namespace zaaReloaded2.Importer.ZaaImporter { } public LaurisTimePoint( - string[] paragraphs, + IList paragraphs, Parameters parameterDictionary, Units unitDictionary) : this() @@ -138,72 +142,71 @@ namespace zaaReloaded2.Importer.ZaaImporter Paragraphs = paragraphs; } - public LaurisTimePoint(string[] paragraphs) + public LaurisTimePoint(IList paragraphs) : this(paragraphs, null, null) { } #endregion + #region Public methods + + /// + /// Adds a new paragraph to this time point by parsing + /// the paragraph for laboratory items. + /// + /// Paragraph to add. + public void AddParagraph(string paragraph) + { + Paragraphs.Add(paragraph); + ParseParagraph(paragraph); + } + + #endregion + #region Private methods /// /// Analyzes each Lauris paragraph in this time point, sets the date /// and time, and collects LabItem data. /// - /// True if the LaurisText has time stamp in the first paragraphs - /// and contains s in the others. - bool ParseParagraphs() + void ParseParagraphs() { - if (Paragraphs.Length > 0) + if (Paragraphs != null) { - if (!ParseTimeStamp()) return false; - LaurisParagraph lp; - if (IsValidTimePoint) + foreach (string paragraph in Paragraphs) { - for (int i = 1; i < Paragraphs.Length; i++) - { - lp = new LaurisParagraph( - Paragraphs[i], - _parameterDictionary, - _unitDictionary); - if (lp.IsLaurisParagraph) - { - Items.Merge(lp.Items); - } - } + ParseParagraph(paragraph); } - IsValidTimePoint = Items.Count > 0; } - return true; } - /// - /// Analyzes the date and time information that is expected to be - /// in the first paragraph. - /// - /// True if the LaurisText contains a time stamp in the - /// first paragraph. - bool ParseTimeStamp() + void ParseParagraph(string paragraph) { - if (Paragraphs.Length == 0) - throw new InvalidOperationException("The time point has no paragraphs."); - - Match m = _timeStampRegex.Match(Paragraphs[0]); - bool success = false; - if (m.Success) + if (_timeStampRegex.IsMatch(paragraph)) { DateTime dt; - success = DateTime.TryParseExact( - m.Groups["datetime"].Value, + if (DateTime.TryParseExact( + _timeStampRegex.Match(paragraph).Groups["datetime"].Value, "dd.MM.yyyy HH:mm", CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, - out dt); - TimeStamp = dt; + out dt)) + { + TimeStamp = dt; + } + } + else + { + LaurisParagraph lp = new LaurisParagraph( + paragraph, + _parameterDictionary, + _unitDictionary); + if (lp.IsLaurisParagraph) + { + Items.Merge(lp.Items); + } } - IsValidTimePoint = success; - return success; } void AddItems(IItemDictionary items) @@ -221,7 +224,7 @@ namespace zaaReloaded2.Importer.ZaaImporter /// static readonly Regex _timeStampRegex = new Regex( @"^\s*\(?\s*(?\d\d\.\d\d\.\d\d\d\d\s+\d\d:\d\d)"); - string[] _paragraphs; + IList _paragraphs; Parameters _parameterDictionary; Units _unitDictionary; diff --git a/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs b/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs index f2cd2e3..9583d7b 100755 --- a/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs +++ b/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs @@ -52,6 +52,14 @@ namespace zaaReloaded2.Importer.ZaaImporter } } + public bool Success + { + get + { + return Laboratory.TimePoints.Count > 0; + } + } + /// /// Splits the into individual time points /// and creates objects from them. @@ -62,42 +70,29 @@ namespace zaaReloaded2.Importer.ZaaImporter string[] paragraphs = text.Split( new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); - int i = 0; - int start = 0; - int numParagraphs = paragraphs.Length; + LaurisTimePoint timePoint = null; - while (i < numParagraphs) + foreach (string paragraph in paragraphs) { - // Search for the next occurrence of a time stamp line - while (i < numParagraphs - && !LaurisTimePoint.IsTimeStampLine(paragraphs[i])) + // If the current paragraph looks like a Lauris time stamp, + // create a new time point. + if (LaurisTimePoint.IsTimeStampLine(paragraph)) { - i++; + timePoint = new LaurisTimePoint(paragraph); + Laboratory.AddTimePoint(timePoint); } - - // TODO: Find an alternative to returning in the middle of the method. - if (i >= numParagraphs) return; - - if (LaurisTimePoint.IsTimeStampLine(paragraphs[i])) + // If the current paragraph looks like a paragraph with + // laboratory items, add it to the current time point; + // if no time point exists yet, create one. + else if (LaurisParagraph.ResemblesLaurisParagraph(paragraph)) { - // Remember the time stamp line's index - start = i; - - // Seek the next time stamp line - while (i + 1 < numParagraphs - && !LaurisTimePoint.IsTimeStampLine(paragraphs[i + 1])) + if (timePoint == null) { - i++; + timePoint = new LaurisTimePoint(); + Laboratory.AddTimePoint(timePoint); } + timePoint.AddParagraph(paragraph); } - - Laboratory.AddTimePoint( - new LaurisTimePoint( - paragraphs.Slice(start, i - start + 1), - _parameters, - _units - ) - ); } } From 285c6a25553725e5a7379521020bb7bd45914bf4 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Tue, 4 Aug 2015 23:26:21 +0200 Subject: [PATCH 05/49] Add CanRun property to Formatter. --- zaaReloaded2/Formatter/Formatter.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/zaaReloaded2/Formatter/Formatter.cs b/zaaReloaded2/Formatter/Formatter.cs index 86fae29..cb2724f 100755 --- a/zaaReloaded2/Formatter/Formatter.cs +++ b/zaaReloaded2/Formatter/Formatter.cs @@ -64,6 +64,12 @@ namespace zaaReloaded2.Formatter /// public Dictionary WorkingTimePoints { get; private set; } + /// + /// Is true if this Formatter object carries a Laboratory with + /// at least one time point. + /// + public bool CanRun { get { return Laboratory.TimePoints.Count > 0; } } + #endregion #region Constructors @@ -113,6 +119,8 @@ namespace zaaReloaded2.Formatter /// current position of the cursor). public void Run() { + if (!CanRun) throw new InvalidOperationException("No laboratory data to format."); + int current = 0; while (current < Settings.Elements.Count) { From 06a74992ef054bdd6cc3b7250da2c083f001dd6f Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 5 Aug 2015 06:15:07 +0200 Subject: [PATCH 06/49] Make tests pass with refactored ZaaImporter. --- Tests/Formatter/FormatterTest.cs | 1 + .../Importer/ZaaImporter/LaurisTimePoint.cs | 13 +++++++--- .../Importer/ZaaImporter/ZaaImporter.cs | 16 ++++++++++--- zaaReloaded2/LabModel/Laboratory.cs | 24 +++++++++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/Tests/Formatter/FormatterTest.cs b/Tests/Formatter/FormatterTest.cs index 7437d6b..10b05b0 100755 --- a/Tests/Formatter/FormatterTest.cs +++ b/Tests/Formatter/FormatterTest.cs @@ -55,6 +55,7 @@ namespace Tests.Formatter { _formatter.Settings.Elements.Add(new Items("Klinische Chemie: Na, K, Cl")); _formatter.Run(); + Console.WriteLine(_document.Range().Text); Assert.AreEqual( GetResourceText("Tests.Formatter.FormatterTest-all.txt"), _document.Range().Text); diff --git a/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs b/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs index 080527c..c293938 100755 --- a/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs +++ b/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs @@ -135,10 +135,8 @@ namespace zaaReloaded2.Importer.ZaaImporter IList paragraphs, Parameters parameterDictionary, Units unitDictionary) - : this() + : this(parameterDictionary, unitDictionary) { - _parameterDictionary = parameterDictionary; - _unitDictionary = unitDictionary; Paragraphs = paragraphs; } @@ -147,6 +145,15 @@ namespace zaaReloaded2.Importer.ZaaImporter { } + public LaurisTimePoint( + Parameters parameterDictionary, + Units unitDictionary) + : this() + { + _parameterDictionary = parameterDictionary; + _unitDictionary = unitDictionary; + } + #endregion #region Public methods diff --git a/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs b/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs index 9583d7b..223aadf 100755 --- a/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs +++ b/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs @@ -78,8 +78,18 @@ namespace zaaReloaded2.Importer.ZaaImporter // create a new time point. if (LaurisTimePoint.IsTimeStampLine(paragraph)) { - timePoint = new LaurisTimePoint(paragraph); - Laboratory.AddTimePoint(timePoint); + timePoint = new LaurisTimePoint(paragraph, _parameters, _units); + // Add the time point to the laboratory only if none + // with the same time stamp exists yet. + TimePoint existing = null; + if (Laboratory.TryGetTimePoint(timePoint.TimeStamp, ref existing)) + { + timePoint = existing as LaurisTimePoint; + } + else + { + Laboratory.AddTimePoint(timePoint); + } } // If the current paragraph looks like a paragraph with // laboratory items, add it to the current time point; @@ -88,7 +98,7 @@ namespace zaaReloaded2.Importer.ZaaImporter { if (timePoint == null) { - timePoint = new LaurisTimePoint(); + timePoint = new LaurisTimePoint(_parameters, _units); Laboratory.AddTimePoint(timePoint); } timePoint.AddParagraph(paragraph); diff --git a/zaaReloaded2/LabModel/Laboratory.cs b/zaaReloaded2/LabModel/Laboratory.cs index 8964ae9..99dae9a 100755 --- a/zaaReloaded2/LabModel/Laboratory.cs +++ b/zaaReloaded2/LabModel/Laboratory.cs @@ -66,6 +66,30 @@ namespace zaaReloaded2.LabModel } } + /// + /// Checks if the Laboratory contains a TimePoint with an identical + /// time stamp to the one being queried. + /// + /// TimePoint whose time stamp to look for. + /// True if a TimePoint with identical time stamp exists. + public bool HasTimePoint(TimePoint timePoint) + { + return TimePoints.ContainsKey(timePoint.TimeStamp); + } + + /// + /// Looks for a TimePoint with a given timeStamp and returns + /// it as a reference parameter. + /// + /// Time stamp to look for. + /// Resulting TimePoint (if any). + /// True if TimePoints contains a TimePoint with + /// the requested timeStamp. + public bool TryGetTimePoint(DateTime timeStamp, ref TimePoint timePoint) + { + return TimePoints.TryGetValue(timeStamp, out timePoint); + } + #endregion } } From 880e8b0bdcd716f3db645cddf3c3511bb79f9a55 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 5 Aug 2015 06:46:52 +0200 Subject: [PATCH 07/49] Fix importing of actual Word text. - FIX: Datenimport funktioniert jetzt auch mit Word-Text und nicht nur in den Tests. --- zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs b/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs index 223aadf..aea8b8a 100755 --- a/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs +++ b/zaaReloaded2/Importer/ZaaImporter/ZaaImporter.cs @@ -67,8 +67,11 @@ namespace zaaReloaded2.Importer.ZaaImporter /// ZAA-formatted Lauris output to import. public void Import(string text) { + // Split the text into parargraphs. This implementation relies on the fact + // that the order or splitting strings in C#'s String.Split() method is + // important; see http://stackoverflow.com/a/8664639/270712 string[] paragraphs = text.Split( - new string[] { Environment.NewLine }, + new string[] { "\r\n", "\n\r", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); LaurisTimePoint timePoint = null; From b7fdf87ea5077670d3d5931260a2c837608dc767 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 5 Aug 2015 06:47:30 +0200 Subject: [PATCH 08/49] Close view after sending UseSettingsMessage. --- zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs b/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs index 5a3fc75..3822f8e 100755 --- a/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs @@ -221,6 +221,7 @@ namespace zaaReloaded2.ViewModels void DoUseSettings() { UseSettingsMessage.Send(new ViewModelMessageContent(LastSelected)); + CloseViewCommand.Execute(null); } bool CanUseSettings() From 742a588083e8de11097cd5c780267bf478741126 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 5 Aug 2015 20:02:02 +0200 Subject: [PATCH 09/49] Make tests pass again (except not implemented ones). --- Tests/Controller/Elements/ItemsTest.cs | 10 +++++----- Tests/Controller/SettingsRepositoryTest.cs | 6 ++++-- zaaReloaded2/Controller/Settings.cs | 6 +++++- zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs | 8 +++++++- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/Tests/Controller/Elements/ItemsTest.cs b/Tests/Controller/Elements/ItemsTest.cs index 7fa49d3..46c02b6 100755 --- a/Tests/Controller/Elements/ItemsTest.cs +++ b/Tests/Controller/Elements/ItemsTest.cs @@ -61,7 +61,7 @@ namespace Tests.Controller.Elements _formatter.Laboratory = lab; _formatter.Settings.Elements.Add(new zaa.Items("Na, K, Cl")); _formatter.Run(); - Assert.AreEqual("Na 133, K 6 (5)\r", _document.Range().Text); + Assert.AreEqual("\r13.07.2015 13:31:00:\rNa 133, K 6 (5)\r\r", _document.Range().Text); } [Test] @@ -78,7 +78,7 @@ namespace Tests.Controller.Elements _formatter.Laboratory = lab; _formatter.Settings.Elements.Add(new zaa.Items("Klinische Chemie: Na, K, Cl")); _formatter.Run(); - Assert.AreEqual("Klinische Chemie: Na 133, K 6 (5)\r", _document.Range().Text); + Assert.AreEqual("\r13.07.2015 13:31:00:\rKlinische Chemie: Na 133, K 6 (5)\r\r", _document.Range().Text); } [Test] @@ -113,7 +113,7 @@ namespace Tests.Controller.Elements _formatter.Laboratory = lab; _formatter.Settings.Elements.Add(new zaa.Items("Klinische Chemie: Na, *")); _formatter.Run(); - Assert.AreEqual("Klinische Chemie: Na 133, Cl 110, K 6\r", + Assert.AreEqual("\r13.07.2015 13:31:00:\rKlinische Chemie: Na 133, Cl 110, K 6\r\r", _document.Range().Text); } @@ -133,7 +133,7 @@ namespace Tests.Controller.Elements _formatter.Laboratory = lab; _formatter.Settings.Elements.Add(new zaa.Items("Klinische Chemie: Na, SU-*")); _formatter.Run(); - Assert.AreEqual("Klinische Chemie: Na 133, SU-Protein 2,8\r", + Assert.AreEqual("\r13.07.2015 13:31:00:\rKlinische Chemie: Na 133, SU-Protein 2,8\r\r", _document.Range().Text); } @@ -153,7 +153,7 @@ namespace Tests.Controller.Elements _formatter.Laboratory = lab; _formatter.Settings.Elements.Add(new zaa.Items("Klinische Chemie: Na, SU-*, *")); _formatter.Run(); - Assert.AreEqual("Klinische Chemie: Na 133, SU-Protein 2,8, Cl 110, U-Na 99\r", + Assert.AreEqual("\r13.07.2015 13:31:00:\rKlinische Chemie: Na 133, SU-Protein 2,8, Cl 110, U-Na 99\r\r", _document.Range().Text); } } diff --git a/Tests/Controller/SettingsRepositoryTest.cs b/Tests/Controller/SettingsRepositoryTest.cs index e6b789a..39f04d1 100755 --- a/Tests/Controller/SettingsRepositoryTest.cs +++ b/Tests/Controller/SettingsRepositoryTest.cs @@ -50,11 +50,13 @@ namespace Tests.Controller string testName = "test"; s.Name = testName; sr.SettingsList.Add(s); + int expected = sr.SettingsList.Count; + int index = sr.SettingsList.IndexOf(s); sr.Store(); sr = null; sr = SettingsRepository.Load(); - Assert.AreEqual(1, sr.SettingsList.Count); - Assert.AreEqual(testName, sr.SettingsList[0].Name); + Assert.AreEqual(expected, sr.SettingsList.Count); + Assert.AreEqual(testName, sr.SettingsList[index].Name); } } } diff --git a/zaaReloaded2/Controller/Settings.cs b/zaaReloaded2/Controller/Settings.cs index 5691988..cb81027 100755 --- a/zaaReloaded2/Controller/Settings.cs +++ b/zaaReloaded2/Controller/Settings.cs @@ -58,7 +58,7 @@ namespace zaaReloaded2.Controller #region Constructors public Settings() - : this(string.Empty, new List()) + : this(string.Empty, null) { } public Settings(string name) @@ -87,6 +87,10 @@ namespace zaaReloaded2.Controller Uid = Guid.NewGuid(); Name = name; Elements = initialElements; + if (Elements == null) + { + Elements = new List(); + } } #endregion diff --git a/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs b/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs index c293938..18c751d 100755 --- a/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs +++ b/zaaReloaded2/Importer/ZaaImporter/LaurisTimePoint.cs @@ -87,7 +87,13 @@ namespace zaaReloaded2.Importer.ZaaImporter /// Is true if the LaurisText has time stamp in the first /// paragraph and s in the others. /// - public bool IsValidTimePoint { get; private set; } + public bool IsValidTimePoint + { + get + { + return Items.Count > 0; + } + } /// /// Gets or sets the original Lauris text for this timepoint. From 60296602a96fa7a44b184dd9cbb36f145cd31012 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 5 Aug 2015 20:28:09 +0200 Subject: [PATCH 10/49] Implement DeleteElementCommand. --- Tests/ViewModels/SettingsViewModelTest.cs | 20 ++++++++++- .../ViewModels/ControlElementViewModel.cs | 14 ++++++++ .../ViewModels/FormatElementViewModel.cs | 6 ++++ zaaReloaded2/ViewModels/SettingsViewModel.cs | 33 ++++++++++++++++++- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/Tests/ViewModels/SettingsViewModelTest.cs b/Tests/ViewModels/SettingsViewModelTest.cs index 695e62c..637bb48 100755 --- a/Tests/ViewModels/SettingsViewModelTest.cs +++ b/Tests/ViewModels/SettingsViewModelTest.cs @@ -106,7 +106,25 @@ namespace Tests.ViewModels [Test] public void DeleteElement() { - throw new NotImplementedException(); + Settings settings = new Settings(); + settings.AddElement(new SelectFirstDay()); + settings.AddElement(new SelectEachDay()); + settings.AddElement(new SelectLastDay()); + SettingsViewModel settingsVM = new SettingsViewModel(settings); + int oldCount = settingsVM.Elements.Count; + ElementViewModel elementVM = settingsVM.Elements.First(); + ElementBase element = elementVM.RevealModelObject() as ElementBase; + Assert.IsTrue(settings.Elements.Contains(element), + "Settings object does not contain the model that the VM revealed?!"); + elementVM.IsSelected = true; + Assert.IsTrue(settingsVM.DeleteElementCommand.CanExecute(null), + "DeleteElementCommand should be enabled."); + settingsVM.DeleteElementCommand.Execute(null); + Assert.AreEqual(oldCount - 1, settingsVM.Elements.Count); + Assert.IsFalse(settingsVM.Elements.Contains(elementVM), + "Elements collection still contains the deleted element view model."); + Assert.IsFalse(settings.Elements.Contains(element), + "Settings' Element collection still contains the element model."); } [Test] diff --git a/zaaReloaded2/ViewModels/ControlElementViewModel.cs b/zaaReloaded2/ViewModels/ControlElementViewModel.cs index 58b0421..ada4589 100755 --- a/zaaReloaded2/ViewModels/ControlElementViewModel.cs +++ b/zaaReloaded2/ViewModels/ControlElementViewModel.cs @@ -62,10 +62,24 @@ namespace zaaReloaded2.ViewModels public void AddChildElement(FormatElementViewModel viewModel) { Elements.Add(viewModel); + viewModel.Parent = this; ControlElementBase e = Element as ControlElementBase; e.FormatElements.Add(viewModel.RevealModelObject() as FormatElementBase); } + public void RemoveChildElement(FormatElementViewModel viewModel) + { + if (!Elements.Contains(viewModel)) + { + throw new InvalidOperationException( + "Cannot remove child view model that is not in the collection."); + } + Elements.Remove(viewModel); + ((ControlElementBase)Element).FormatElements + .Remove(viewModel.RevealModelObject() as FormatElementBase); + viewModel.Parent = null; + } + #endregion } } diff --git a/zaaReloaded2/ViewModels/FormatElementViewModel.cs b/zaaReloaded2/ViewModels/FormatElementViewModel.cs index 5dbe16a..27e491e 100755 --- a/zaaReloaded2/ViewModels/FormatElementViewModel.cs +++ b/zaaReloaded2/ViewModels/FormatElementViewModel.cs @@ -39,6 +39,12 @@ namespace zaaReloaded2.ViewModels } } + /// + /// Gets or sets the parent ControlElementViewModel, if + /// there is any. + /// + public ControlElementViewModel Parent { get; set; } + #endregion #region Constructors diff --git a/zaaReloaded2/ViewModels/SettingsViewModel.cs b/zaaReloaded2/ViewModels/SettingsViewModel.cs index 1a5cfd1..9f4670b 100755 --- a/zaaReloaded2/ViewModels/SettingsViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsViewModel.cs @@ -299,7 +299,38 @@ namespace zaaReloaded2.ViewModels return LastSelectedElement is ControlElementViewModel; } - void DoDeleteElement() { } + /// + /// Deletes the selected element. + /// + /// + /// The following algorithm ist used to find out whether the selected + /// element is at the first or the second level of the hierarchy: + /// If the Element is a ControlElement, it must be at the first level. + /// If the Element is a FormatElement, its Parent property will be + /// Null if the Element is at the first level. + /// + void DoDeleteElement() + { + if (CanDeleteElement()) + { + if (LastSelectedElement is ControlElementViewModel || + ((FormatElementViewModel)LastSelectedElement).Parent == null) + { + // First level of the hierarchy + Elements.Remove(LastSelectedElement); + ElementBase element = LastSelectedElement.RevealModelObject() as ElementBase; + _settings.Elements.Remove(element); + } + else + { + // Second level of the hierarchy + FormatElementViewModel formatVM = LastSelectedElement as FormatElementViewModel; + ControlElementViewModel parent = formatVM.Parent; + parent.RemoveChildElement(formatVM); + } + LastSelectedElement = null; + } + } bool CanDeleteElement() { return LastSelectedElement != null && LastSelectedElement.IsSelected; } From 1205958eb19c456d43a6a2c6253114b3b8268fc4 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 5 Aug 2015 20:36:31 +0200 Subject: [PATCH 11/49] Implement test to delete child element and make it pass. --- Tests/ViewModels/SettingsViewModelTest.cs | 26 ++++++++++++++++++++ zaaReloaded2/ViewModels/SettingsViewModel.cs | 1 + 2 files changed, 27 insertions(+) diff --git a/Tests/ViewModels/SettingsViewModelTest.cs b/Tests/ViewModels/SettingsViewModelTest.cs index 637bb48..0fbd160 100755 --- a/Tests/ViewModels/SettingsViewModelTest.cs +++ b/Tests/ViewModels/SettingsViewModelTest.cs @@ -127,6 +127,32 @@ namespace Tests.ViewModels "Settings' Element collection still contains the element model."); } + [Test] + public void DeleteChildElement() + { + Settings settings = new Settings(); + settings.AddElement(new SelectFirstDay(new Items())); + settings.AddElement(new SelectEachDay()); + settings.AddElement(new SelectLastDay()); + SettingsViewModel settingsVM = new SettingsViewModel(settings); + ControlElementViewModel parent = ((ControlElementViewModel)settingsVM.Elements.First()); + int oldCount = parent.Elements.Count; + ElementViewModel elementVM = ((ControlElementViewModel)settingsVM.Elements.First()) + .Elements.First(); + ElementBase element = elementVM.RevealModelObject() as ElementBase; + Assert.IsTrue(((ControlElementBase)settings.Elements[0]).FormatElements.Contains(element), + "Settings object does not contain the model that the VM revealed?!"); + elementVM.IsSelected = true; + Assert.IsTrue(settingsVM.DeleteElementCommand.CanExecute(null), + "DeleteElementCommand should be enabled."); + settingsVM.DeleteElementCommand.Execute(null); + Assert.AreEqual(oldCount - 1, parent.Elements.Count); + Assert.IsFalse(parent.Elements.Contains(elementVM), + "Elements collection still contains the deleted element view model."); + Assert.IsFalse(((ControlElementBase)settings.Elements[0]).FormatElements.Contains(element), + "Settings' collection of FormatElement children still contains the element model."); + } + [Test] public void CopyElement() { diff --git a/zaaReloaded2/ViewModels/SettingsViewModel.cs b/zaaReloaded2/ViewModels/SettingsViewModel.cs index 9f4670b..927d04d 100755 --- a/zaaReloaded2/ViewModels/SettingsViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsViewModel.cs @@ -135,6 +135,7 @@ namespace zaaReloaded2.ViewModels vm = new ControlElementViewModel(element as ControlElementBase); foreach (FormatElementViewModel childVM in ((ControlElementViewModel)vm).Elements) { + childVM.Parent = vm as ControlElementViewModel; childVM.PropertyChanged += ElementViewModel_PropertyChanged; } } From ed434971793770d6140059704781a238c2b6d6ad Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 5 Aug 2015 21:22:03 +0200 Subject: [PATCH 12/49] Implement copying of elements. --- Tests/ViewModels/SettingsViewModelTest.cs | 28 +++++++++++++++++- .../ViewModels/ControlElementViewModel.cs | 13 +++++++++ zaaReloaded2/ViewModels/ElementViewModel.cs | 8 ++++- .../ViewModels/FormatElementViewModel.cs | 8 +++++ zaaReloaded2/ViewModels/SettingsViewModel.cs | 29 +++++++++++++++++-- 5 files changed, 81 insertions(+), 5 deletions(-) diff --git a/Tests/ViewModels/SettingsViewModelTest.cs b/Tests/ViewModels/SettingsViewModelTest.cs index 0fbd160..0ea9ad9 100755 --- a/Tests/ViewModels/SettingsViewModelTest.cs +++ b/Tests/ViewModels/SettingsViewModelTest.cs @@ -156,7 +156,33 @@ namespace Tests.ViewModels [Test] public void CopyElement() { - throw new NotImplementedException(); + string testString = "this text should be duplicated"; + _settingsVM.AddElementViewModel( + new ControlElementViewModel(new SelectEachDay(new Items(testString)))); + int oldCount = _settingsVM.Elements.Count; + _settingsVM.Elements.First().IsSelected = true; + Assert.IsTrue(_settingsVM.CopyElementCommand.CanExecute(null), + "CopyElementCommand should be enabled."); + _settingsVM.CopyElementCommand.Execute(null); + Assert.AreEqual(oldCount + 1, _settingsVM.Elements.Count); + + // Access the first element + ControlElementViewModel controlVM = _settingsVM.Elements.First() as ControlElementViewModel; + FormatElementViewModel formatVM = controlVM.Elements.First() as FormatElementViewModel; + Items items = formatVM.RevealModelObject() as Items; + Assert.IsNotNull(items, + "Child ViewModel of first ControlElementViewModel does not wrap Items object."); + Assert.AreEqual(testString, items.Content, + "First control element's child element has incorrect Content string."); + + // Access the second element + controlVM = _settingsVM.Elements.Last() as ControlElementViewModel; + formatVM = controlVM.Elements.First() as FormatElementViewModel; + items = formatVM.RevealModelObject() as Items; + Assert.IsNotNull(items, + "Child ViewModel of last ControlElementViewModel does not wrap Items object."); + Assert.AreEqual(testString, items.Content, + "First control element's child element has incorrect Content string."); } } } diff --git a/zaaReloaded2/ViewModels/ControlElementViewModel.cs b/zaaReloaded2/ViewModels/ControlElementViewModel.cs index ada4589..88b3969 100755 --- a/zaaReloaded2/ViewModels/ControlElementViewModel.cs +++ b/zaaReloaded2/ViewModels/ControlElementViewModel.cs @@ -81,5 +81,18 @@ namespace zaaReloaded2.ViewModels } #endregion + + public override object Clone() + { + ControlElementViewModel clone = new ControlElementViewModel(); + clone.Element = Element; + clone.Elements = new ObservableCollection( + Elements.Select(evm => evm.Clone() as ElementViewModel)); + foreach (FormatElementViewModel evm in clone.Elements) + { + evm.Parent = clone; + } + return clone; + } } } diff --git a/zaaReloaded2/ViewModels/ElementViewModel.cs b/zaaReloaded2/ViewModels/ElementViewModel.cs index a14a865..add2138 100755 --- a/zaaReloaded2/ViewModels/ElementViewModel.cs +++ b/zaaReloaded2/ViewModels/ElementViewModel.cs @@ -26,7 +26,7 @@ using System.Collections.ObjectModel; namespace zaaReloaded2.ViewModels { - public abstract class ElementViewModel : ViewModelBase + public abstract class ElementViewModel : ViewModelBase, ICloneable { #region Properties @@ -69,5 +69,11 @@ namespace zaaReloaded2.ViewModels } #endregion + + #region Implementation of ICloneable + + public abstract object Clone(); + + #endregion } } diff --git a/zaaReloaded2/ViewModels/FormatElementViewModel.cs b/zaaReloaded2/ViewModels/FormatElementViewModel.cs index 27e491e..21483f2 100755 --- a/zaaReloaded2/ViewModels/FormatElementViewModel.cs +++ b/zaaReloaded2/ViewModels/FormatElementViewModel.cs @@ -58,5 +58,13 @@ namespace zaaReloaded2.ViewModels } #endregion + + public override object Clone() + { + FormatElementViewModel clone = new FormatElementViewModel(); + clone.Parent = Parent; + clone.Element = Element; + return clone; + } } } diff --git a/zaaReloaded2/ViewModels/SettingsViewModel.cs b/zaaReloaded2/ViewModels/SettingsViewModel.cs index 927d04d..8eb178c 100755 --- a/zaaReloaded2/ViewModels/SettingsViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsViewModel.cs @@ -314,8 +314,7 @@ namespace zaaReloaded2.ViewModels { if (CanDeleteElement()) { - if (LastSelectedElement is ControlElementViewModel || - ((FormatElementViewModel)LastSelectedElement).Parent == null) + if (IsTopLevelElement()) { // First level of the hierarchy Elements.Remove(LastSelectedElement); @@ -335,7 +334,21 @@ namespace zaaReloaded2.ViewModels bool CanDeleteElement() { return LastSelectedElement != null && LastSelectedElement.IsSelected; } - void DoCopyElement() { } + void DoCopyElement() + { + if (CanCopyElement()) + { + if (IsTopLevelElement()) + { + AddElementViewModel(LastSelectedElement.Clone() as ElementViewModel); + } + else + { + FormatElementViewModel formatVM = LastSelectedElement as FormatElementViewModel; + formatVM.Parent.AddChildElement(formatVM.Clone() as FormatElementViewModel); + } + } + } bool CanCopyElement() { return LastSelectedElement != null && LastSelectedElement.IsSelected; } @@ -355,6 +368,16 @@ namespace zaaReloaded2.ViewModels } } + /// + /// Returns true if the selected ElementViewModel is at the top + /// level of the hierarchy. + /// + bool IsTopLevelElement() + { + return (LastSelectedElement is ControlElementViewModel || + ((FormatElementViewModel)LastSelectedElement).Parent == null); + } + #endregion #region Implementation of ViewModelBase From d0bd7dd2a5ce0fb9ef19e0ab96361ac2b42135ab Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 5 Aug 2015 21:43:08 +0200 Subject: [PATCH 13/49] Make sure to remember last used setting. --- zaaReloaded2/Ribbon.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/zaaReloaded2/Ribbon.cs b/zaaReloaded2/Ribbon.cs index f5428d8..37305bf 100755 --- a/zaaReloaded2/Ribbon.cs +++ b/zaaReloaded2/Ribbon.cs @@ -200,6 +200,7 @@ namespace zaaReloaded2 Settings settings = settingsVM.RevealModelObject() as Settings; DoFormat(settings); Properties.Settings.Default.LastSettings = settings.Uid; + Properties.Settings.Default.Save(); }; vm.RequestCloseView += (sender, args) => { From 2cefede3c0870f0a3239206cab24b78bb0a75f7b Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 5 Aug 2015 21:52:06 +0200 Subject: [PATCH 14/49] Output formatting. - NEU: Formatierung der Ausgabe mit Absatzvorlage. --- zaaReloaded2/Formatter/DocumentWriter.cs | 14 ++++++++- zaaReloaded2/Formatter/Formatter.cs | 30 ++++++++++++++++++++ zaaReloaded2/Properties/Settings.Designer.cs | 9 ++++++ zaaReloaded2/Properties/Settings.settings | 3 ++ zaaReloaded2/app.config | 3 ++ 5 files changed, 58 insertions(+), 1 deletion(-) diff --git a/zaaReloaded2/Formatter/DocumentWriter.cs b/zaaReloaded2/Formatter/DocumentWriter.cs index 6953f92..51dad02 100755 --- a/zaaReloaded2/Formatter/DocumentWriter.cs +++ b/zaaReloaded2/Formatter/DocumentWriter.cs @@ -53,6 +53,12 @@ namespace zaaReloaded2.Formatter /// public bool HasBufferedText { get { return _buffer.Length > 0; } } + /// + /// Gets or sets the desired paragraph style when flushing into + /// a Document. + /// + public string ParagraphStyle { get; set; } + #endregion #region Constructors @@ -113,7 +119,13 @@ namespace zaaReloaded2.Formatter if (Document != null) { - Document.Range().Text = _buffer.ToString(); + Document.ActiveWindow.Selection.ClearCharacterDirectFormatting(); + Document.ActiveWindow.Selection.ClearParagraphDirectFormatting(); + if (!string.IsNullOrEmpty(ParagraphStyle)) + { + Document.ActiveWindow.Selection.set_Style(ParagraphStyle); + } + Document.ActiveWindow.Selection.Range.Text = _buffer.ToString(); } if (Parent != null) { diff --git a/zaaReloaded2/Formatter/Formatter.cs b/zaaReloaded2/Formatter/Formatter.cs index cb2724f..b7bc89c 100755 --- a/zaaReloaded2/Formatter/Formatter.cs +++ b/zaaReloaded2/Formatter/Formatter.cs @@ -145,6 +145,8 @@ namespace zaaReloaded2.Formatter current++; } } + CreateParagraphStyle(); + _secondaryBuffer.ParagraphStyle = zaaReloaded2.Properties.Settings.Default.ParagraphStyleName; _secondaryBuffer.Flush(); } @@ -206,6 +208,34 @@ namespace zaaReloaded2.Formatter ProcessAllTimePoints(controlElement.FormatElements); } + /// + /// Creates a zaaReloaded2 paragraph style in the document. + /// + public void CreateParagraphStyle() + { + if (Document != null) + { + Style style; + // Don't see a better way to check for the existence of a particular + // paragraph style than by using a try...catch construction. + try + { + style = Document.Styles[Properties.Settings.Default.ParagraphStyleName]; + } + catch + { + style = Document.Styles.Add(Properties.Settings.Default.ParagraphStyleName); + style.Font.Size = 10; // pt + style.Font.Bold = 0; + style.Font.Italic = 0; + style.Font.Underline = 0; + style.ParagraphFormat.LeftIndent = 36; // pt + style.ParagraphFormat.FirstLineIndent = -36; // pt + style.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphJustify; + } + } + } + #endregion #region Protected methods diff --git a/zaaReloaded2/Properties/Settings.Designer.cs b/zaaReloaded2/Properties/Settings.Designer.cs index 902836a..1a3bccc 100755 --- a/zaaReloaded2/Properties/Settings.Designer.cs +++ b/zaaReloaded2/Properties/Settings.Designer.cs @@ -245,5 +245,14 @@ namespace zaaReloaded2.Properties { this["LastSettings"] = value; } } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("zaaReloaded2-Laborwerte")] + public string ParagraphStyleName { + get { + return ((string)(this["ParagraphStyleName"])); + } + } } } diff --git a/zaaReloaded2/Properties/Settings.settings b/zaaReloaded2/Properties/Settings.settings index 0fab6e4..d8eee1d 100755 --- a/zaaReloaded2/Properties/Settings.settings +++ b/zaaReloaded2/Properties/Settings.settings @@ -74,5 +74,8 @@ 00000000-0000-0000-0000-000000000000 + + zaaReloaded2-Laborwerte + \ No newline at end of file diff --git a/zaaReloaded2/app.config b/zaaReloaded2/app.config index a0c5181..abffe24 100755 --- a/zaaReloaded2/app.config +++ b/zaaReloaded2/app.config @@ -92,6 +92,9 @@ Medikamente: TAC, CSA, SIR, Vancomycin, Gentamicin, Tobramicin + + zaaReloaded2-Laborwerte + From 476f2d69be15daaa13f4d35df5718acbc37b09d5 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 5 Aug 2015 22:04:07 +0200 Subject: [PATCH 15/49] Improve date and time header (some tests failing now). - NEU: Datumsangaben werden in eine Zeile "Laborwerte vom ..." eingebettet. --- Tests/Controller/Elements/ItemsTest.cs | 20 ++++++--- zaaReloaded2/Formatter/TimePointFormatter.cs | 44 +++++++++++++------- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/Tests/Controller/Elements/ItemsTest.cs b/Tests/Controller/Elements/ItemsTest.cs index 46c02b6..e722af2 100755 --- a/Tests/Controller/Elements/ItemsTest.cs +++ b/Tests/Controller/Elements/ItemsTest.cs @@ -61,7 +61,9 @@ namespace Tests.Controller.Elements _formatter.Laboratory = lab; _formatter.Settings.Elements.Add(new zaa.Items("Na, K, Cl")); _formatter.Run(); - Assert.AreEqual("\r13.07.2015 13:31:00:\rNa 133, K 6 (5)\r\r", _document.Range().Text); + Assert.AreEqual("\r" + + TimePointFormatter.DateAndTimeHeader(new DateTime(2015, 07, 13, 13, 31, 00)) + + "\rNa 133, K 6 (5)\r\r", _document.Range().Text); } [Test] @@ -78,7 +80,9 @@ namespace Tests.Controller.Elements _formatter.Laboratory = lab; _formatter.Settings.Elements.Add(new zaa.Items("Klinische Chemie: Na, K, Cl")); _formatter.Run(); - Assert.AreEqual("\r13.07.2015 13:31:00:\rKlinische Chemie: Na 133, K 6 (5)\r\r", _document.Range().Text); + Assert.AreEqual("\r" + + TimePointFormatter.DateAndTimeHeader(new DateTime(2015, 07, 13, 13, 31, 00)) + + "\rKlinische Chemie: Na 133, K 6 (5)\r\r", _document.Range().Text); } [Test] @@ -113,7 +117,9 @@ namespace Tests.Controller.Elements _formatter.Laboratory = lab; _formatter.Settings.Elements.Add(new zaa.Items("Klinische Chemie: Na, *")); _formatter.Run(); - Assert.AreEqual("\r13.07.2015 13:31:00:\rKlinische Chemie: Na 133, Cl 110, K 6\r\r", + Assert.AreEqual("\r" + + TimePointFormatter.DateAndTimeHeader(new DateTime(2015, 07, 13, 13, 31, 00)) + + "\rKlinische Chemie: Na 133, Cl 110, K 6\r\r", _document.Range().Text); } @@ -133,7 +139,9 @@ namespace Tests.Controller.Elements _formatter.Laboratory = lab; _formatter.Settings.Elements.Add(new zaa.Items("Klinische Chemie: Na, SU-*")); _formatter.Run(); - Assert.AreEqual("\r13.07.2015 13:31:00:\rKlinische Chemie: Na 133, SU-Protein 2,8\r\r", + Assert.AreEqual("\r" + + TimePointFormatter.DateAndTimeHeader(new DateTime(2015, 07, 13, 13, 31, 00)) + + "\rKlinische Chemie: Na 133, SU-Protein 2,8\r\r", _document.Range().Text); } @@ -153,7 +161,9 @@ namespace Tests.Controller.Elements _formatter.Laboratory = lab; _formatter.Settings.Elements.Add(new zaa.Items("Klinische Chemie: Na, SU-*, *")); _formatter.Run(); - Assert.AreEqual("\r13.07.2015 13:31:00:\rKlinische Chemie: Na 133, SU-Protein 2,8, Cl 110, U-Na 99\r\r", + Assert.AreEqual("\r" + + TimePointFormatter.DateAndTimeHeader(new DateTime(2015, 07, 13, 13, 31, 00)) + + "\rKlinische Chemie: Na 133, SU-Protein 2,8, Cl 110, U-Na 99\r\r", _document.Range().Text); } } diff --git a/zaaReloaded2/Formatter/TimePointFormatter.cs b/zaaReloaded2/Formatter/TimePointFormatter.cs index 6248775..ab5ba9b 100755 --- a/zaaReloaded2/Formatter/TimePointFormatter.cs +++ b/zaaReloaded2/Formatter/TimePointFormatter.cs @@ -28,6 +28,33 @@ namespace zaaReloaded2.Formatter /// public class TimePointFormatter { + #region Static methods + + public static string DateHeader(DateTime date) + { + return FormatHeader(date.ToShortDateString()); + } + + public static string DateAndTimeHeader(DateTime dateTime) + { + return FormatHeader(dateTime.ToString()); + } + + #endregion + + #region Private static methods + + static string FormatHeader(string text) + { + return String.Format("{0}Laborwerte vom {1}:{2}", + Environment.NewLine, + text, + Environment.NewLine + ); + } + + #endregion + #region Properties /// @@ -77,7 +104,7 @@ namespace zaaReloaded2.Formatter /// public string GetDateHeader() { - return FormatHeader(TimeStamp.ToShortDateString()); + return DateHeader(TimeStamp); } @@ -88,20 +115,7 @@ namespace zaaReloaded2.Formatter /// public string GetDateAndTimeHeader() { - return FormatHeader(TimeStamp.ToString()); - } - - #endregion - - #region Private methods - - string FormatHeader(string text) - { - return String.Format("{0}{1}:{2}", - Environment.NewLine, - text, - Environment.NewLine - ); + return DateAndTimeHeader(TimeStamp); } #endregion From b6c33971bdd3cc6be9a94dfa586455217976d97c Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Thu, 6 Aug 2015 06:42:08 +0200 Subject: [PATCH 16/49] Fix thesaurus. - FIX: Mehrere Thesaurus-Fehler behoben. --- .../Thesaurus/Defaults/parameters.txt | 126 +++++++++--------- zaaReloaded2/Thesaurus/Defaults/units.txt | 3 +- zaaReloaded2/Thesaurus/ThesaurusBase.cs | 2 +- 3 files changed, 66 insertions(+), 65 deletions(-) diff --git a/zaaReloaded2/Thesaurus/Defaults/parameters.txt b/zaaReloaded2/Thesaurus/Defaults/parameters.txt index 20cf176..6b5c777 100755 --- a/zaaReloaded2/Thesaurus/Defaults/parameters.txt +++ b/zaaReloaded2/Thesaurus/Defaults/parameters.txt @@ -11,104 +11,104 @@ Albumin Alb S Albumin/Creatinin (PU)" ACR U "Alk. Phosphatase" AP S Amylase Amylase S -anorg. Phosphat" Phosphat S -Bakterien (U)" Bakt U +"anorg. Phosphat" Phosphat S +"Bakterien (U)" Bakt U Basenabweichung BE BGA Basophile Baso E -Bilirubin (U)" Bilirubin U -C-reaktives Protein" CRP S -Calcium (SU)" Ca SU -Calcium (SU)/die" Ca SU +"Bilirubin (U)" Bilirubin U +"C-reaktives Protein" CRP S +"Calcium (SU)" Ca SU +"Calcium (SU)/die" Ca SU Calcium Ca S Calcium-Phosphat-Produkt CaxP S Cholesterin Chol S CK gesamt" CK S -CK MB" CK-MB S -Creatinin (PU)" Krea U -Creatinin (SU)" Krea SU +"CK MB" CK-MB S +"Creatinin (PU)" Krea U +"Creatinin (SU)" Krea SU Creatinin Krea S -Creatinin-Clearance (SU)/min" CrCl SU -Cyclosporin-A vor Gabe" "CsA (C0)" S X -Cystatin C (N Latex)" "Cystatin C" S X +"Creatinin-Clearance (SU)/min" CrCl SU +"Cyclosporin-A vor Gabe" "CsA (C0)" S X +"Cystatin C (N Latex)" "Cystatin C" S X Eisen Fe S Eosinophile Eos E -Erythrozyten (U)" Ery U +"Erythrozyten (U)" Ery U Erythrozyten Ery E Ferritin Ferr S Gesamt-Bilirubin Bilirubin S -Gesamt-Eiweiss (PU)" Protein U -Gesamt-Eiweiss (SU)" Protein SU -Gesamt-Eiweiss (SU)/die" Proteinurie SU +"Gesamt-Eiweiss (PU)" Protein U +"Gesamt-Eiweiss (SU)" Protein SU +"Gesamt-Eiweiss (SU)/die" Proteinurie SU Gesamt-Eiweiss Protein S -Gesamt-Eiweiss/Creatinin (PU)" TPCR U +"Gesamt-Eiweiss/Creatinin (PU)" TPCR U GGT GGT S -glomeruläre Filtrationsrate" GFR SU -glomerul. Filtrationsr. (MDRD)" "eGFR (MDRD)" S -glomerul. Filtrationsr. CKD-EP" "eGFR (CKD-EPI)" S -Glucose (U)" Glukose U +"glomeruläre Filtrationsrate" GFR SU +"glomerul. Filtrationsr. (MDRD)" "eGFR (MDRD)" S +"glomerul. Filtrationsr. CKD-EP" "eGFR (CKD-EPI)" S +"Glucose (U)" Glukose U Glucose Glukose S -GOT (ASAT)" GOT S -GPT (ALAT)" GPT S +"GOT (ASAT)" GOT S +"GPT (ALAT)" GPT S Hämatokrit Hkt E Hämoglobin Hb E Haptoglobin Haptoglobin S X Harnsäure Harnsäure S -Harnstoff (SU)" Hst SU -Harnstoff (SU)/die" Hst/Tag SU +"Harnstoff (SU)" Hst SU +"Harnstoff (SU)/die" Hst/Tag SU Harnstoff" Hst S -Harnstoff-Clearance (SU)/min" HstCl SU -HbA1c (NGSP)" HbA1c E -HDL - Cholesterin" HDL S -hyaline Zylinder (U)" "hyal. Zyl." U -Kalium (SU)" K U -Kalium (SU)/die" K SU +"Harnstoff-Clearance (SU)/min" HstCl SU +"HbA1c (NGSP)" HbA1c E +"HDL - Cholesterin" HDL S +"hyaline Zylinder (U)" "hyal. Zyl." U +"Kalium (SU)" K U +"Kalium (SU)/die" K SU Kalium K S -Ketonkörper (U)" KK U -Lactat Dehydrogenase" LDH S -LDL - Cholesterin" LDL S -Leukozyten (U)" Leu U +"Ketonkörper (U)" KK U +"Lactat Dehydrogenase" LDH S +"LDL - Cholesterin" LDL S +"Leukozyten (U)" Leu U Leukozyten Leu E Lymphozyten Lym E -MCH (HbE) MCH E +"MCH (HbE)" MCH E MCHC MCHC E MCV MCV E -Mittleres Plättchenvolumen MPV E +"Mittleres Plättchenvolumen" MPV E Monozyten Mon E -Natrium (SU)" Na SU -Natrium (SU)/die" Na SU +"Natrium (SU)" Na SU +"Natrium (SU)/die" Na SU Natrium Na S Neutrophile Neu E -Nitrit (U)" Nitrit U +"Nitrit (U)" Nitrit U NT-proBNP NT-proBNP S -PCO2 (art.)" pCO2 BGA -pH (U)" pH U -pH" pH BGA -Plattenepithelien (U)" Plattenep U -PO2 (art.)" pO2 BGA -Protein (U)" Protein U -Sammelmenge (U)" Volumen SU -Sammelzeit (U)" Zeit SU -Sauerstoffsättigung (art.)" SO2 BGA +"PCO2 (art.)" pCO2 BGA +"pH (U)" pH U +"pH" pH BGA +"Plattenepithelien (U)" Plattenep U +"PO2 (art.)" pO2 BGA +"Protein (U)" Protein U +"Sammelmenge (U)" Volumen SU +"Sammelzeit (U)" Zeit SU +"Sauerstoffsättigung (art.)" SO2 BGA Sirolimus SIR S -spezifisches Gewicht (U)" "spez. Gew." U -Standard Bicarbonat" "Std.-Bic." BGA -Tacrolimus (FK506)" TAC S +"spezifisches Gewicht (U)" "spez. Gew." U +"Standard Bicarbonat" "Std.-Bic." BGA +"Tacrolimus (FK506)" TAC S Thrombozyten Thr E Transferrin Transferrin S -Transferrinsättigung" Tf.-Sätt. S -Triglyceride" TG S -Troponin T (high sensitive)" hsTnT S +"Transferrinsättigung" Tf.-Sätt. S +"Triglyceride" TG S +"Troponin T (high sensitive)" hsTnT S Unreife Granulozyten" Gran E -Urobilinogen (U)" Urobilinogen U -Niedermol. Heparin (Anti-Xa)" Anti-Xa Z -Thromboplastinzeit n. Quick" Quick Z +"Urobilinogen (U)" Urobilinogen U +"Niedermol. Heparin (Anti-Xa)" Anti-Xa Z +"Thromboplastinzeit n. Quick" Quick Z PTT aPTT Z -Ratio int. norm." INR Z -Komplementfaktor C3c C3c S X -Komplementfaktor C4 C4 S X -Anti-DNAse B "Anti-DNAse B" S +"Ratio int. norm." INR Z +"Komplementfaktor C3c" C3c S X +"Komplementfaktor C4" C4 S X +"Anti-DNAse B" "Anti-DNAse B" S Anti-Streptolysin ASL S -PTH intakt" iPTH S +"PTH intakt" iPTH S TSH TSH S HAPTOGLOBIN Haptoglobin S FRAGMENTOZYTEN Fragmentozyten E diff --git a/zaaReloaded2/Thesaurus/Defaults/units.txt b/zaaReloaded2/Thesaurus/Defaults/units.txt index 48a907b..51a44d8 100755 --- a/zaaReloaded2/Thesaurus/Defaults/units.txt +++ b/zaaReloaded2/Thesaurus/Defaults/units.txt @@ -4,4 +4,5 @@ # weil (bislang) keine Umrechnung der Werte vorgesehen ist! "ml/min/ 1,73qm" "ml/min/1,73 m²" ng/ml µg/l -mmol/l mM \ No newline at end of file +mmol/l mM +n*1000/µl /nl \ No newline at end of file diff --git a/zaaReloaded2/Thesaurus/ThesaurusBase.cs b/zaaReloaded2/Thesaurus/ThesaurusBase.cs index ecc0baf..d8ee58b 100755 --- a/zaaReloaded2/Thesaurus/ThesaurusBase.cs +++ b/zaaReloaded2/Thesaurus/ThesaurusBase.cs @@ -242,7 +242,7 @@ namespace zaaReloaded2.Thesaurus #region Fields - private static readonly Regex _dashes = new Regex("-+"); + private static readonly Regex _dashes = new Regex("^-+$"); #endregion } From edaf88c4cd4b489f29f712ab9c3caf8786306170 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Thu, 6 Aug 2015 07:47:44 +0200 Subject: [PATCH 17/49] Implement blacklisting feature. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NEU: Parameter können im Thesaurus als "blacklisted" markiert werden und werden dann bei Verwendung einer Wildcard ("*") nicht berücksichtigt. --- zaaReloaded2/Controller/Elements/Items.cs | 2 +- zaaReloaded2/Formatter/ItemFormatter.cs | 22 ++++++++++++++++++- .../Importer/ZaaImporter/LaurisItem.cs | 3 ++- zaaReloaded2/LabModel/LabItem.cs | 6 +++++ .../Thesaurus/Defaults/parameters.txt | 16 +++++++------- zaaReloaded2/Thesaurus/Parameters.cs | 12 ++++++++++ 6 files changed, 50 insertions(+), 11 deletions(-) diff --git a/zaaReloaded2/Controller/Elements/Items.cs b/zaaReloaded2/Controller/Elements/Items.cs index b1d258b..77fc34d 100755 --- a/zaaReloaded2/Controller/Elements/Items.cs +++ b/zaaReloaded2/Controller/Elements/Items.cs @@ -148,7 +148,7 @@ namespace zaaReloaded2.Controller.Elements foreach (TimePointFormatter tpf in formatter.WorkingTimePoints.Values) { List newItems = tpf.ItemFormatters.Values - .Where(i => !i.HasBeenUsed && i.LabItem.QualifiedName.StartsWith(material)) + .Where(i => !i.HasBeenUsed && !i.IsBlacklisted && i.LabItem.QualifiedName.StartsWith(material)) .ToList(); newItems.ForEach(i => i.HasBeenUsed = true); items.AddRange(newItems); diff --git a/zaaReloaded2/Formatter/ItemFormatter.cs b/zaaReloaded2/Formatter/ItemFormatter.cs index df8796c..17d0bf3 100755 --- a/zaaReloaded2/Formatter/ItemFormatter.cs +++ b/zaaReloaded2/Formatter/ItemFormatter.cs @@ -48,6 +48,23 @@ namespace zaaReloaded2.Formatter /// public bool HasBeenUsed { get; set; } + /// + /// Gets or sets a flag that tells the formatter to include or + /// not include the material indicator in the formatted output. + /// Default is true. + /// + /// + /// For example, items that are selected with a wildcard may + /// contain the material info, while expressly chosen items + /// may not. + /// + public bool IncludeMaterial { get; set; } + + /// + /// Gets whether the Item is marked as blacklisted in the thesaurus. + /// + public bool IsBlacklisted { get { return LabItem.IsBlacklisted; } } + #endregion #region Constructor @@ -58,6 +75,7 @@ namespace zaaReloaded2.Formatter /// LabItem to wrap in this ItemFormatter. public ItemFormatter(LabItem labItem, ReferenceStyle referenceStyle) { + IncludeMaterial = true; LabItem = labItem; ReferenceStyle = referenceStyle; } @@ -133,11 +151,13 @@ namespace zaaReloaded2.Formatter value = LabItem.Value; } + string name = IncludeMaterial ? LabItem.QualifiedName : LabItem.Name; + // Insert the formatted text into the document. formatter.Write( String.Format( "{0} {1}{2}{3}", - LabItem.QualifiedName, + name, value, unit, reference diff --git a/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs b/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs index 244c73f..ccff09a 100755 --- a/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs +++ b/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs @@ -74,7 +74,8 @@ namespace zaaReloaded2.Importer.ZaaImporter if (parameterDictionary != null) { Name = parameterDictionary.GetCanonicalName(OriginalName); - AlwaysPrintLimits = parameterDictionary.GetForceReferenceDisplay(Name); + AlwaysPrintLimits = parameterDictionary.GetForceReferenceDisplay(OriginalName); + IsBlacklisted = parameterDictionary.GetIsBlacklisted(OriginalName); } if (unitDictionary != null) { diff --git a/zaaReloaded2/LabModel/LabItem.cs b/zaaReloaded2/LabModel/LabItem.cs index 7600fa2..3fc6c7e 100755 --- a/zaaReloaded2/LabModel/LabItem.cs +++ b/zaaReloaded2/LabModel/LabItem.cs @@ -205,6 +205,12 @@ namespace zaaReloaded2.LabModel public Material Material { get; protected set; } + /// + /// Gets whether the LabItem is marked as blacklisted + /// in the thesaurus. + /// + public bool IsBlacklisted { get; protected set; } + #endregion #region Constructors diff --git a/zaaReloaded2/Thesaurus/Defaults/parameters.txt b/zaaReloaded2/Thesaurus/Defaults/parameters.txt index 6b5c777..a2b837f 100755 --- a/zaaReloaded2/Thesaurus/Defaults/parameters.txt +++ b/zaaReloaded2/Thesaurus/Defaults/parameters.txt @@ -1,5 +1,5 @@ -# LAURIS-NAME "KANONISCHER NAME" MATERIAL "IMMER REFERENZBEREICH" -# =========== ================== ======== ======================= +# LAURIS-NAME "KANONISCHER NAME" MATERIAL "IMMER REFERENZBEREICH" "BLACKLIST" +# =========== ================== ======== ======================= =========== "Übergangsepithelien (U)" Übergangsep. U "a1-Microglobulin (SU)" a1-Microglobulin SU "a1-Microglobulin (SU)/die" a1-Microglobulin SU @@ -28,8 +28,8 @@ CK gesamt" CK S "Creatinin (SU)" Krea SU Creatinin Krea S "Creatinin-Clearance (SU)/min" CrCl SU -"Cyclosporin-A vor Gabe" "CsA (C0)" S X -"Cystatin C (N Latex)" "Cystatin C" S X +"Cyclosporin-A vor Gabe" "CsA (C0)" S X +"Cystatin C (N Latex)" "Cystatin C" S X Eisen Fe S Eosinophile Eos E "Erythrozyten (U)" Ery U @@ -43,7 +43,7 @@ Gesamt-Eiweiss Protein S "Gesamt-Eiweiss/Creatinin (PU)" TPCR U GGT GGT S "glomeruläre Filtrationsrate" GFR SU -"glomerul. Filtrationsr. (MDRD)" "eGFR (MDRD)" S +"glomerul. Filtrationsr. (MDRD)" "eGFR (MDRD)" S --- X "glomerul. Filtrationsr. CKD-EP" "eGFR (CKD-EPI)" S "Glucose (U)" Glukose U Glucose Glukose S @@ -51,7 +51,7 @@ Glucose Glukose S "GPT (ALAT)" GPT S Hämatokrit Hkt E Hämoglobin Hb E -Haptoglobin Haptoglobin S X +Haptoglobin Haptoglobin S X Harnsäure Harnsäure S "Harnstoff (SU)" Hst SU "Harnstoff (SU)/die" Hst/Tag SU @@ -104,8 +104,8 @@ Unreife Granulozyten" Gran E "Thromboplastinzeit n. Quick" Quick Z PTT aPTT Z "Ratio int. norm." INR Z -"Komplementfaktor C3c" C3c S X -"Komplementfaktor C4" C4 S X +"Komplementfaktor C3c" C3c S X +"Komplementfaktor C4" C4 S X "Anti-DNAse B" "Anti-DNAse B" S Anti-Streptolysin ASL S "PTH intakt" iPTH S diff --git a/zaaReloaded2/Thesaurus/Parameters.cs b/zaaReloaded2/Thesaurus/Parameters.cs index 18ebfbc..e6444f2 100755 --- a/zaaReloaded2/Thesaurus/Parameters.cs +++ b/zaaReloaded2/Thesaurus/Parameters.cs @@ -95,6 +95,18 @@ namespace zaaReloaded2.Thesaurus return LookUpValue(laurisName, 3, false); } + /// + /// Checks whether an item is marked as blacklisted in the + /// thesaurus. + /// + /// Laboratory item to lok up; + /// this must be an original Lauris string. + /// True if the item is marked as blacklisted, + /// false if not. Default is false. + public bool GetIsBlacklisted(string laurisName) + { + return LookUpValue(laurisName, 4, false); + } #endregion #region Overrides From bde85e56515f7052b9d6875a5f77d638de2163f8 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Fri, 7 Aug 2015 21:37:14 +0200 Subject: [PATCH 18/49] Fix views and view models. --- Tests/Controller/SettingsTest.cs | 53 +++++++++++ Tests/Tests.csproj | 1 + zaaReloaded2/Properties/AssemblyInfo.cs | 4 +- zaaReloaded2/Ribbon.cs | 4 - .../ViewModels/ElementPickerViewModel.cs | 27 ++++-- .../ViewModels/SettingsRepositoryViewModel.cs | 4 + zaaReloaded2/Views/ElementPickerView.xaml | 55 +++++++++++ zaaReloaded2/Views/ElementPickerView.xaml.cs | 33 +++++++ zaaReloaded2/Views/ElementView.xaml | 40 ++++++++ zaaReloaded2/Views/ElementView.xaml.cs | 33 +++++++ .../Views/SettingsRepositoryView.xaml | 11 ++- zaaReloaded2/Views/SettingsView.xaml | 92 +++++++++++++++++++ zaaReloaded2/Views/SettingsView.xaml.cs | 33 +++++++ zaaReloaded2/zaaReloaded2.csproj | 48 +++++++++- 14 files changed, 419 insertions(+), 19 deletions(-) create mode 100755 Tests/Controller/SettingsTest.cs create mode 100755 zaaReloaded2/Views/ElementPickerView.xaml create mode 100755 zaaReloaded2/Views/ElementPickerView.xaml.cs create mode 100755 zaaReloaded2/Views/ElementView.xaml create mode 100755 zaaReloaded2/Views/ElementView.xaml.cs create mode 100755 zaaReloaded2/Views/SettingsView.xaml create mode 100755 zaaReloaded2/Views/SettingsView.xaml.cs diff --git a/Tests/Controller/SettingsTest.cs b/Tests/Controller/SettingsTest.cs new file mode 100755 index 0000000..b59821b --- /dev/null +++ b/Tests/Controller/SettingsTest.cs @@ -0,0 +1,53 @@ +/* SettingsTest.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.Controller; +using zaaReloaded2.Controller.Elements; + +namespace Tests.Controller +{ + [TestFixture] + class SettingsTest + { + [Test] + public void CloneSettings() + { + SelectEachDay selectEachDay = new SelectEachDay(); + Items items = new Items("some text"); + Settings source = new Settings( + "hello world", + new List() { selectEachDay, items }); + source.ReferenceStyle = zaaReloaded2.Formatter.ReferenceStyle.IfSpecialOrAbnormal; + Settings clone = source.Clone() as Settings; + Assert.AreNotSame(source, clone); + Assert.AreEqual("Kopie von " + source.Name, clone.Name, + "Name"); + Assert.AreNotSame(source.Elements, clone.Elements); + Assert.AreEqual(source.ReferenceStyle, clone.ReferenceStyle, + "ReferenceStyle"); + Assert.AreEqual( + ((Items)source.Elements[1]).Content, + ((Items)clone.Elements[1]).Content, + "Items content"); + } + } +} diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index fc37c1f..c9404eb 100755 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -79,6 +79,7 @@ + diff --git a/zaaReloaded2/Properties/AssemblyInfo.cs b/zaaReloaded2/Properties/AssemblyInfo.cs index 1b66dcf..0d946e1 100755 --- a/zaaReloaded2/Properties/AssemblyInfo.cs +++ b/zaaReloaded2/Properties/AssemblyInfo.cs @@ -9,9 +9,9 @@ using System.Security; [assembly: AssemblyTitle("zaaReloaded2")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyCompany("Daniel Kraus")] [assembly: AssemblyProduct("zaaReloaded2")] -[assembly: AssemblyCopyright("Copyright © Microsoft 2015")] +[assembly: AssemblyCopyright("Copyright © Daniel Kraus 2015")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: InternalsVisibleTo("Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010061ecc0718277dee13e7dae2dad33787a981c9883ba80a659bebbdbec76563e201a7a3a6a5852e01bb5eb328d24d5889244b4626da9af9f93db663565441a3120e3985789e6f2a39289f4eed063725b84152cbef472d9dd2f7495f51dad6c91f4dc6fb7c72cb6bd9381335ac9878ec0a6369e880f35b8eca3063e16468d7704eb")] diff --git a/zaaReloaded2/Ribbon.cs b/zaaReloaded2/Ribbon.cs index 37305bf..958e328 100755 --- a/zaaReloaded2/Ribbon.cs +++ b/zaaReloaded2/Ribbon.cs @@ -202,10 +202,6 @@ namespace zaaReloaded2 Properties.Settings.Default.LastSettings = settings.Uid; Properties.Settings.Default.Save(); }; - vm.RequestCloseView += (sender, args) => - { - repository.Save(); - }; vm.InjectInto().ShowDialog(); } diff --git a/zaaReloaded2/ViewModels/ElementPickerViewModel.cs b/zaaReloaded2/ViewModels/ElementPickerViewModel.cs index 6b2ecbb..557119c 100755 --- a/zaaReloaded2/ViewModels/ElementPickerViewModel.cs +++ b/zaaReloaded2/ViewModels/ElementPickerViewModel.cs @@ -96,7 +96,7 @@ namespace zaaReloaded2.ViewModels Categories = new List(); if (allowControlElements) { - Categories.Add( + AddCategory( new CategoryViewModel( "Kontroll-Elemente", new Collection() @@ -108,7 +108,7 @@ namespace zaaReloaded2.ViewModels ) ); } - Categories.Add( + AddCategory( new CategoryViewModel( "Ausgabe-Elemente", new Collection() @@ -124,6 +124,12 @@ namespace zaaReloaded2.ViewModels #region Private methods + void AddCategory(CategoryViewModel category) + { + category.PropertyChanged += ElementViewModel_PropertyChanged; + Categories.Add(category); + } + /// /// Creates a new ControlElementViewModel that wraps a ControlElementBase /// object. The display string of the ControlElementViewModel is taken @@ -152,13 +158,16 @@ namespace zaaReloaded2.ViewModels void ElementViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { - if (e.PropertyName == "IsSelected") + // Casting sender as ElementViewModel may result in Null, but this + // is a desired effect because category headings may be selected + // in a view, but are not valid ElementViewModels that we could + // 'choose'. In addition, when an element is selected, another one + // is deselected, and the event triggers twice, once for the selected + // element and once for the deselected one. + ElementViewModel elementVM = sender as ElementViewModel; + if (elementVM != null && elementVM.IsSelected && e.PropertyName == "IsSelected") { - // Casting sender as ElementViewModel may result in Null, but this - // is a desired effect because category headings may be selected - // in a view, but are not valid ElementViewModels that we could - // 'choose'. - Selected = sender as ElementViewModel; + Selected = elementVM; } } @@ -172,7 +181,7 @@ namespace zaaReloaded2.ViewModels bool CanChooseElement() { - return Selected != null; + return Selected != null && Selected.IsSelected == true; } #endregion diff --git a/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs b/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs index 3822f8e..6277d1f 100755 --- a/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs @@ -199,6 +199,10 @@ namespace zaaReloaded2.ViewModels SettingsViewModel vm = new SettingsViewModel(s); AddSettingsViewModel(vm); } + RequestCloseView += (sender, args) => + { + _repository.Store(); + }; } #endregion diff --git a/zaaReloaded2/Views/ElementPickerView.xaml b/zaaReloaded2/Views/ElementPickerView.xaml new file mode 100755 index 0000000..e845457 --- /dev/null +++ b/zaaReloaded2/Views/ElementPickerView.xaml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + - public IList FormatElements { get; private set; } + public IList Children { get; internal set; } public ControlElementBase() : this(new List()) { } - public ControlElementBase(IList formatElements) + public ControlElementBase(IList children) { - FormatElements = formatElements; + Children = children; } - public ControlElementBase(FormatElementBase formatElement) - : this(new List() { formatElement }) + public ControlElementBase(FormatElementBase children) + : this(new List() { children }) { } + + /// + /// Creates a clone of the Children list (for use in cloning + /// derived classes). + /// + /// Clone of the Children list. + protected IList CloneChildren() + { + return Children.Select(child => child.Clone() as FormatElementBase).ToList(); + } } } diff --git a/zaaReloaded2/Controller/Elements/SelectEachDay.cs b/zaaReloaded2/Controller/Elements/SelectEachDay.cs index eed0093..824b116 100755 --- a/zaaReloaded2/Controller/Elements/SelectEachDay.cs +++ b/zaaReloaded2/Controller/Elements/SelectEachDay.cs @@ -26,7 +26,7 @@ namespace zaaReloaded2.Controller.Elements { public override string Label { - get { return "Jeden Tag auswählen"; } + get { return "Jeder Tag"; } } public override void Run(Formatter.Formatter formatter) @@ -46,7 +46,7 @@ namespace zaaReloaded2.Controller.Elements protected override ElementBase CreateInstance() { - return new SelectEachDay(); + return new SelectEachDay(CloneChildren()); } } } diff --git a/zaaReloaded2/Controller/Elements/SelectFirstDay.cs b/zaaReloaded2/Controller/Elements/SelectFirstDay.cs index f635948..6771800 100755 --- a/zaaReloaded2/Controller/Elements/SelectFirstDay.cs +++ b/zaaReloaded2/Controller/Elements/SelectFirstDay.cs @@ -30,7 +30,7 @@ namespace zaaReloaded2.Controller.Elements { public override string Label { - get { return "Ersten Tag auswählen"; } + get { return "Erster Tag"; } } public override void Run(Formatter.Formatter formatter) @@ -50,7 +50,7 @@ namespace zaaReloaded2.Controller.Elements protected override ElementBase CreateInstance() { - return new SelectFirstDay(); + return new SelectFirstDay(CloneChildren()); } } } diff --git a/zaaReloaded2/Controller/Elements/SelectLastDay.cs b/zaaReloaded2/Controller/Elements/SelectLastDay.cs index 2f5107a..e2d048e 100755 --- a/zaaReloaded2/Controller/Elements/SelectLastDay.cs +++ b/zaaReloaded2/Controller/Elements/SelectLastDay.cs @@ -31,7 +31,7 @@ namespace zaaReloaded2.Controller.Elements { public override string Label { - get { return "Letzten Tag auswählen"; } + get { return "Letzter Tag"; } } public override void Run(Formatter.Formatter formatter) @@ -51,7 +51,7 @@ namespace zaaReloaded2.Controller.Elements protected override ElementBase CreateInstance() { - return new SelectLastDay(); + return new SelectLastDay(CloneChildren()); } } } diff --git a/zaaReloaded2/Controller/Settings.cs b/zaaReloaded2/Controller/Settings.cs index cb81027..594ed15 100755 --- a/zaaReloaded2/Controller/Settings.cs +++ b/zaaReloaded2/Controller/Settings.cs @@ -87,6 +87,7 @@ namespace zaaReloaded2.Controller Uid = Guid.NewGuid(); Name = name; Elements = initialElements; + ReferenceStyle = Properties.Settings.Default.ReferenceStyle; if (Elements == null) { Elements = new List(); diff --git a/zaaReloaded2/Formatter/Formatter.cs b/zaaReloaded2/Formatter/Formatter.cs index b7bc89c..5ef0276 100755 --- a/zaaReloaded2/Formatter/Formatter.cs +++ b/zaaReloaded2/Formatter/Formatter.cs @@ -205,7 +205,7 @@ namespace zaaReloaded2.Formatter /// FormatElementBase children to process. public void ProcessAllTimePoints(ControlElementBase controlElement) { - ProcessAllTimePoints(controlElement.FormatElements); + ProcessAllTimePoints(controlElement.Children); } /// @@ -297,7 +297,7 @@ namespace zaaReloaded2.Formatter throw new ArgumentNullException("workingTimePoints"); WorkingTimePoints = workingTimePoints; - ProcessElements(controlElement.FormatElements); + ProcessElements(controlElement.Children); if (_primaryBuffer.HasBufferedText) { _primaryBuffer.Prepend( diff --git a/zaaReloaded2/Formatter/ReferenceStyle.cs b/zaaReloaded2/Formatter/ReferenceStyle.cs index 0e6c8f7..2d47b33 100755 --- a/zaaReloaded2/Formatter/ReferenceStyle.cs +++ b/zaaReloaded2/Formatter/ReferenceStyle.cs @@ -32,31 +32,31 @@ namespace zaaReloaded2.Formatter /// /// Never write normal ranges /// - [Description("Referenzbereich nie ausgeben")] + [Description("Niemals")] Never, /// /// Write normal range if item is marked in dictionary /// - [Description("Referenzbereich ausgeben, falls Parameter so markiert ist")] + [Description("Bei speziellen Parametern immer")] IfSpecialItem, /// /// Write normal range if value is abnormal /// - [Description("Referenzbereich ausgeben, falls Wert nicht normal ist")] + [Description("Bei pathologischem Wert")] IfAbnormal, /// /// Write normal range if item is marked in dictionary, or if value is abnormal /// - [Description("Referenzbereich ausgeben, falls Parameter so markiert ist oder falls Wert nicht normal ist")] + [Description("Bei pathologischem Wert oder speziellem Parameter")] IfSpecialOrAbnormal, /// /// Always write normal range reference /// - [Description("Referenzbereich immer ausgeben")] + [Description("Immer")] Always } } diff --git a/zaaReloaded2/Properties/Settings.Designer.cs b/zaaReloaded2/Properties/Settings.Designer.cs index 1a3bccc..cd3968c 100755 --- a/zaaReloaded2/Properties/Settings.Designer.cs +++ b/zaaReloaded2/Properties/Settings.Designer.cs @@ -254,5 +254,32 @@ namespace zaaReloaded2.Properties { return ((string)(this["ParagraphStyleName"])); } } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("IfSpecialOrAbnormal")] + public global::zaaReloaded2.Formatter.ReferenceStyle ReferenceStyle { + get { + return ((global::zaaReloaded2.Formatter.ReferenceStyle)(this["ReferenceStyle"])); + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Ausgabe-Elemente")] + public string FormatElementLabel { + get { + return ((string)(this["FormatElementLabel"])); + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Steuerungs-Elemente")] + public string ControlElementLabel { + get { + return ((string)(this["ControlElementLabel"])); + } + } } } diff --git a/zaaReloaded2/Properties/Settings.settings b/zaaReloaded2/Properties/Settings.settings index d8eee1d..2cecf23 100755 --- a/zaaReloaded2/Properties/Settings.settings +++ b/zaaReloaded2/Properties/Settings.settings @@ -77,5 +77,14 @@ zaaReloaded2-Laborwerte + + IfSpecialOrAbnormal + + + Ausgabe-Elemente + + + Steuerungs-Elemente + \ No newline at end of file diff --git a/zaaReloaded2/ViewModels/ControlElementViewModel.cs b/zaaReloaded2/ViewModels/ControlElementViewModel.cs index 88b3969..826968a 100755 --- a/zaaReloaded2/ViewModels/ControlElementViewModel.cs +++ b/zaaReloaded2/ViewModels/ControlElementViewModel.cs @@ -47,7 +47,7 @@ namespace zaaReloaded2.ViewModels Elements = new ObservableCollection(); if (controlElement != null) { - foreach (FormatElementBase childElement in controlElement.FormatElements) + foreach (FormatElementBase childElement in controlElement.Children) { FormatElementViewModel childVM = new FormatElementViewModel(childElement); Elements.Add(childVM); @@ -55,6 +55,22 @@ namespace zaaReloaded2.ViewModels } } + /// + /// Creates a new instance by copying the Elements property from another + /// ControlElementViewModel and the Children property from the other + /// view model's model. + /// + /// Other ControlElementViewModel to copy + /// from. + public ControlElementViewModel( + ControlElementBase controlElement, + ControlElementViewModel copyFrom) + : this(controlElement) + { + Elements = copyFrom.Elements; + ((ControlElementBase)Element).Children = controlElement.Children; + } + #endregion #region Public methods @@ -64,7 +80,7 @@ namespace zaaReloaded2.ViewModels Elements.Add(viewModel); viewModel.Parent = this; ControlElementBase e = Element as ControlElementBase; - e.FormatElements.Add(viewModel.RevealModelObject() as FormatElementBase); + e.Children.Add(viewModel.RevealModelObject() as FormatElementBase); } public void RemoveChildElement(FormatElementViewModel viewModel) @@ -75,13 +91,15 @@ namespace zaaReloaded2.ViewModels "Cannot remove child view model that is not in the collection."); } Elements.Remove(viewModel); - ((ControlElementBase)Element).FormatElements + ((ControlElementBase)Element).Children .Remove(viewModel.RevealModelObject() as FormatElementBase); viewModel.Parent = null; } #endregion + #region Overrides + public override object Clone() { ControlElementViewModel clone = new ControlElementViewModel(); @@ -94,5 +112,7 @@ namespace zaaReloaded2.ViewModels } return clone; } + + #endregion } } diff --git a/zaaReloaded2/ViewModels/ElementPickerViewModel.cs b/zaaReloaded2/ViewModels/ElementPickerViewModel.cs index 557119c..a54c550 100755 --- a/zaaReloaded2/ViewModels/ElementPickerViewModel.cs +++ b/zaaReloaded2/ViewModels/ElementPickerViewModel.cs @@ -29,7 +29,9 @@ namespace zaaReloaded2.ViewModels { /// /// View model that presents a list of zaaReloaded2.Contorller.Elements.ElementBase - /// classes to choose from. + /// classes to choose from. Depending on which constructor is used, this view model + /// presents either a list of ControlElementBase-derived classes, a list of + /// FormatElementBase-derived classes, or both. /// class ElementPickerViewModel : ViewModelBase { @@ -98,7 +100,7 @@ namespace zaaReloaded2.ViewModels { AddCategory( new CategoryViewModel( - "Kontroll-Elemente", + Properties.Settings.Default.ControlElementLabel, new Collection() { CreateControlElementViewModel(new SelectFirstDay()), @@ -108,13 +110,27 @@ namespace zaaReloaded2.ViewModels ) ); } + CreateFormatElementViewModels(); + } + + /// + /// Creates a new element picker for control elements only; + /// the selected ControlElementVieModel will copy the Elements + /// property from a source view model. + /// + /// ControlElementViewModel + /// whose Elements property to copy. + public ElementPickerViewModel(ControlElementViewModel copyFromViewModel) + { + Categories = new List(); AddCategory( new CategoryViewModel( - "Ausgabe-Elemente", + Properties.Settings.Default.ControlElementLabel, new Collection() { - CreateFormatElementViewModel("Laborparameter", new Items()), - CreateFormatElementViewModel("Beliebiger Text", new CustomText()), + CreateControlElementViewModel(new SelectFirstDay(), copyFromViewModel), + CreateControlElementViewModel(new SelectLastDay(), copyFromViewModel), + CreateControlElementViewModel(new SelectEachDay(), copyFromViewModel) } ) ); @@ -124,6 +140,20 @@ namespace zaaReloaded2.ViewModels #region Private methods + void CreateFormatElementViewModels() + { + AddCategory( + new CategoryViewModel( + Properties.Settings.Default.FormatElementLabel, + new Collection() + { + CreateFormatElementViewModel("Laborparameter", new Items()), + CreateFormatElementViewModel("Beliebiger Text", new CustomText()), + } + ) + ); + } + void AddCategory(CategoryViewModel category) { category.PropertyChanged += ElementViewModel_PropertyChanged; @@ -143,6 +173,15 @@ namespace zaaReloaded2.ViewModels return vm; } + ViewModelBase CreateControlElementViewModel( + ControlElementBase element, ControlElementViewModel copyFromViewModel) + { + ControlElementViewModel vm = new ControlElementViewModel(element, copyFromViewModel); + vm.DisplayString = element.Label; + vm.PropertyChanged += ElementViewModel_PropertyChanged; + return vm; + } + /// /// Creates a new FormatElementViewModel that wraps a FormatElementBase /// object and has a custom display string. The custom display string @@ -176,6 +215,7 @@ namespace zaaReloaded2.ViewModels if (CanChooseElement()) { ElementChosenMessage.Send(new ViewModelMessageContent(Selected)); + DoCloseView(); } } diff --git a/zaaReloaded2/ViewModels/ElementViewModel.cs b/zaaReloaded2/ViewModels/ElementViewModel.cs index add2138..f8b4c7c 100755 --- a/zaaReloaded2/ViewModels/ElementViewModel.cs +++ b/zaaReloaded2/ViewModels/ElementViewModel.cs @@ -42,6 +42,14 @@ namespace zaaReloaded2.ViewModels } } + public override string DisplayString + { + get + { + return Label; + } + } + #endregion #region Constructors diff --git a/zaaReloaded2/ViewModels/FormatElementViewModel.cs b/zaaReloaded2/ViewModels/FormatElementViewModel.cs index 21483f2..373e79e 100755 --- a/zaaReloaded2/ViewModels/FormatElementViewModel.cs +++ b/zaaReloaded2/ViewModels/FormatElementViewModel.cs @@ -20,6 +20,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; +using Bovender.Mvvm; using zaaReloaded2.Controller.Elements; namespace zaaReloaded2.ViewModels @@ -31,10 +32,10 @@ namespace zaaReloaded2.ViewModels public string Content { [DebuggerStepThrough] - get { return ((FormatElementBase)Element).Content; } + get { return _content; } set { - ((FormatElementBase)Element).Content = value; + _content = value; OnPropertyChanged("Content"); } } @@ -47,6 +48,24 @@ namespace zaaReloaded2.ViewModels #endregion + #region Commands + + public DelegatingCommand SaveCommand + { + get + { + if (_saveCommand == null) + { + _saveCommand = new DelegatingCommand( + param => DoSave(), + param => CanSave()); + } + return _saveCommand; + } + } + + #endregion + #region Constructors public FormatElementViewModel() : base() { } @@ -55,10 +74,13 @@ namespace zaaReloaded2.ViewModels : this() { Element = formatElement; + Content = ((FormatElementBase)Element).Content; } #endregion + #region Overrides + public override object Clone() { FormatElementViewModel clone = new FormatElementViewModel(); @@ -66,5 +88,52 @@ namespace zaaReloaded2.ViewModels clone.Element = Element; return clone; } + + public override string DisplayString + { + get + { + if (String.IsNullOrEmpty(Content)) + { + return _displayString; + } + else + { + return base.DisplayString; + } + } + set + { + _displayString = value; + } + } + + #endregion + + #region Private methods + + void DoSave() + { + ((FormatElementBase)Element).Content = _content; + // Writing the _content field to the model affects our + // DisplayString property; this is a hack to make it known. + OnPropertyChanged("DisplayString"); + CloseViewCommand.Execute(null); + } + + bool CanSave() + { + return !String.IsNullOrEmpty(Content); + } + + #endregion + + #region Fields + + DelegatingCommand _saveCommand; + string _displayString; + string _content; + + #endregion } } diff --git a/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs b/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs index 6277d1f..c398e57 100755 --- a/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs @@ -238,8 +238,9 @@ namespace zaaReloaded2.ViewModels Settings s = new Settings("Neu"); SettingsViewModel vm = new SettingsViewModel(s); _repository.SettingsList.Add(s); - SettingsList.Add(vm); + AddSettingsViewModel(vm); vm.IsSelected = true; + EditSettingsMessage.Send(new ViewModelMessageContent(vm)); } bool CanDeleteSettings() @@ -291,6 +292,7 @@ namespace zaaReloaded2.ViewModels AddSettingsViewModel(copy); LastSelected.IsSelected = false; copy.IsSelected = true; + EditSettingsMessage.Send(new ViewModelMessageContent(copy)); } } diff --git a/zaaReloaded2/ViewModels/SettingsViewModel.cs b/zaaReloaded2/ViewModels/SettingsViewModel.cs index 8eb178c..3308203 100755 --- a/zaaReloaded2/ViewModels/SettingsViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsViewModel.cs @@ -26,6 +26,7 @@ using Bovender.Mvvm.Messaging; using zaaReloaded2.Controller; using zaaReloaded2.Controller.Elements; using zaaReloaded2.Formatter; +using System.Collections.ObjectModel; namespace zaaReloaded2.ViewModels { @@ -104,7 +105,12 @@ namespace zaaReloaded2.ViewModels { if (_referenceStyle == null) { - _referenceStyle = new EnumProvider(_settings.ReferenceStyle); + _referenceStyle = new EnumProvider(); + _referenceStyle.AsEnum = _settings.ReferenceStyle; + _referenceStyle.PropertyChanged += (sender, args) => + { + _settings.ReferenceStyle = _referenceStyle.AsEnum; + }; } return _referenceStyle; } @@ -122,7 +128,7 @@ namespace zaaReloaded2.ViewModels : base() { _settings = settings; - Elements = new List(); + Elements = new ObservableCollection(); foreach (ElementBase element in settings.Elements) { ElementViewModel vm; @@ -177,6 +183,30 @@ namespace zaaReloaded2.ViewModels } } + public Message EditElementMessage + { + get + { + if (_editElementMessage == null) + { + _editElementMessage = new Message(); + } + return _editElementMessage; + } + } + + public Message ChangeControlElementMessage + { + get + { + if (_changeControlElementMessage == null) + { + _changeControlElementMessage = new Message(); + } + return _changeControlElementMessage; + } + } + #endregion #region Commands @@ -208,6 +238,20 @@ namespace zaaReloaded2.ViewModels } } + public DelegatingCommand EditElementCommand + { + get + { + if (_editElementCommand == null) + { + _editElementCommand = new DelegatingCommand( + param => DoEditElement(), + param => CanEditElement()); + } + return _editElementCommand; + } + } + public DelegatingCommand DeleteElementCommand { get @@ -275,6 +319,8 @@ namespace zaaReloaded2.ViewModels { ElementViewModel newVM = args.Content.ViewModel as ElementViewModel; AddElementViewModel(newVM); + newVM.IsSelected = true; + DoEditElement(); }; AddElementMessage.Send(new ViewModelMessageContent(picker)); } @@ -290,6 +336,8 @@ namespace zaaReloaded2.ViewModels { FormatElementViewModel newVM = args.Content.ViewModel as FormatElementViewModel; AddChildElementViewModel(LastSelectedElement as ControlElementViewModel, newVM); + newVM.IsSelected = true; + DoEditElement(); }; AddChildElementMessage.Send(new ViewModelMessageContent(picker)); } @@ -300,6 +348,41 @@ namespace zaaReloaded2.ViewModels return LastSelectedElement is ControlElementViewModel; } + void DoEditElement() + { + if (CanEditElement()) + { + if (LastSelectedElement is ControlElementViewModel) + { + ElementPickerViewModel picker = new ElementPickerViewModel( + LastSelectedElement as ControlElementViewModel); + picker.ElementChosenMessage.Sent += (sender, args) => + { + // Replace the previously selected element with the new + // one that we get from the ElementPickerViewModel. + ElementViewModel newVM = args.Content.ViewModel as ElementViewModel; + Elements.Insert( + Elements.IndexOf(LastSelectedElement), + newVM); + Elements.Remove(LastSelectedElement); + newVM.PropertyChanged += ElementViewModel_PropertyChanged; + newVM.IsSelected = true; + }; + ChangeControlElementMessage.Send( + new ViewModelMessageContent(picker)); + } + else + { + EditElementMessage.Send(new ViewModelMessageContent(LastSelectedElement)); + } + } + } + + bool CanEditElement() + { + return LastSelectedElement != null && LastSelectedElement.IsSelected; + } + /// /// Deletes the selected element. /// @@ -403,10 +486,13 @@ namespace zaaReloaded2.ViewModels Settings _settings; DelegatingCommand _addElementCommand; DelegatingCommand _addChildElementCommand; + DelegatingCommand _editElementCommand; DelegatingCommand _deleteElementCommand; DelegatingCommand _copyElementCommand; Message _addElementMessage; Message _addChildElementMessage; + Message _editElementMessage; + Message _changeControlElementMessage; ElementViewModel _selectedElement; EnumProvider _referenceStyle; diff --git a/zaaReloaded2/Views/ElementPickerView.xaml b/zaaReloaded2/Views/ElementPickerView.xaml index e845457..24d865a 100755 --- a/zaaReloaded2/Views/ElementPickerView.xaml +++ b/zaaReloaded2/Views/ElementPickerView.xaml @@ -26,28 +26,25 @@ Title="Neues Element auswählen" > - - - - - - - - - - - - - + public abstract class ControlElementBase : ElementBase { + #region Propertis + /// /// Gets a list of child elements, all of which must be derived /// from FormatElementBase. /// public IList Children { get; internal set; } + /// + /// Informs whether this control element can have child elements. + /// + public virtual bool CanHaveChildren { get { return true; } } + + #endregion + + #region Constructors + public ControlElementBase() : this(new List()) { } @@ -45,6 +56,10 @@ namespace zaaReloaded2.Controller.Elements : this(new List() { children }) { } + #endregion + + #region Protected methods + /// /// Creates a clone of the Children list (for use in cloning /// derived classes). @@ -54,5 +69,7 @@ namespace zaaReloaded2.Controller.Elements { return Children.Select(child => child.Clone() as FormatElementBase).ToList(); } + + #endregion } } diff --git a/zaaReloaded2/Controller/Elements/FormatElementBase.cs b/zaaReloaded2/Controller/Elements/FormatElementBase.cs index c47d4ad..b28c523 100755 --- a/zaaReloaded2/Controller/Elements/FormatElementBase.cs +++ b/zaaReloaded2/Controller/Elements/FormatElementBase.cs @@ -32,7 +32,6 @@ namespace zaaReloaded2.Controller.Elements set { _content = value; - } } diff --git a/zaaReloaded2/Controller/Elements/NextColumn.cs b/zaaReloaded2/Controller/Elements/NextColumn.cs new file mode 100755 index 0000000..a3e534c --- /dev/null +++ b/zaaReloaded2/Controller/Elements/NextColumn.cs @@ -0,0 +1,54 @@ +/* NextColumn.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.Controller.Elements +{ + /// + /// Format element that causes a Formatter object to move the + /// insertion point to the next colum in a layout table. + /// + class NextColumn : ControlElementBase + { + public override string Label + { + get { return "Nächste Spalte"; } + } + + public override bool CanHaveChildren + { + get + { + return false; + } + } + + public override void Run(Formatter.Formatter formatter) + { + formatter.NextColumn(); + } + + protected override ElementBase CreateInstance() + { + return new NextColumn(); + } + } +} diff --git a/zaaReloaded2/Controller/Elements/TwoColumns.cs b/zaaReloaded2/Controller/Elements/TwoColumns.cs new file mode 100755 index 0000000..0b9ab12 --- /dev/null +++ b/zaaReloaded2/Controller/Elements/TwoColumns.cs @@ -0,0 +1,54 @@ +/* TwoColumns.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.Controller.Elements +{ + /// + /// Format element that causes a Formatter object to insert a table with + /// two columns and one row into the documents. + /// + class TwoColumns : ControlElementBase + { + public override string Label + { + get { return "Zwei Spalten einfügen"; } + } + + public override bool CanHaveChildren + { + get + { + return false; + } + } + + public override void Run(Formatter.Formatter formatter) + { + formatter.InsertTwoColumns(); + } + + protected override ElementBase CreateInstance() + { + return new TwoColumns(); + } + } +} diff --git a/zaaReloaded2/Controller/SettingsRepository.cs b/zaaReloaded2/Controller/SettingsRepository.cs index a8e55cb..37402c2 100755 --- a/zaaReloaded2/Controller/SettingsRepository.cs +++ b/zaaReloaded2/Controller/SettingsRepository.cs @@ -89,6 +89,7 @@ namespace zaaReloaded2.Controller { return SettingsList.FirstOrDefault(s => s.Uid == uid); } + #endregion #region Private methods @@ -127,7 +128,9 @@ namespace zaaReloaded2.Controller def.SettingsNameWard, new List() { + new TwoColumns(), new SelectFirstDay(defaultItems), + new NextColumn(), new SelectLastDay(defaultItems) } ) diff --git a/zaaReloaded2/Formatter/DocumentWriter.cs b/zaaReloaded2/Formatter/DocumentWriter.cs index 51dad02..2c11113 100755 --- a/zaaReloaded2/Formatter/DocumentWriter.cs +++ b/zaaReloaded2/Formatter/DocumentWriter.cs @@ -119,13 +119,14 @@ namespace zaaReloaded2.Formatter if (Document != null) { - Document.ActiveWindow.Selection.ClearCharacterDirectFormatting(); - Document.ActiveWindow.Selection.ClearParagraphDirectFormatting(); + Selection s = Document.ActiveWindow.Selection; + s.ClearCharacterDirectFormatting(); + s.ClearParagraphDirectFormatting(); if (!string.IsNullOrEmpty(ParagraphStyle)) { - Document.ActiveWindow.Selection.set_Style(ParagraphStyle); + s.set_Style(ParagraphStyle); } - Document.ActiveWindow.Selection.Range.Text = _buffer.ToString(); + s.Range.Text = _buffer.ToString(); } if (Parent != null) { diff --git a/zaaReloaded2/Formatter/Formatter.cs b/zaaReloaded2/Formatter/Formatter.cs index 5ef0276..83c2d02 100755 --- a/zaaReloaded2/Formatter/Formatter.cs +++ b/zaaReloaded2/Formatter/Formatter.cs @@ -121,6 +121,8 @@ namespace zaaReloaded2.Formatter { if (!CanRun) throw new InvalidOperationException("No laboratory data to format."); + CreateParagraphStyle(); + _secondaryBuffer.ParagraphStyle = zaaReloaded2.Properties.Settings.Default.ParagraphStyleName; int current = 0; while (current < Settings.Elements.Count) { @@ -145,8 +147,6 @@ namespace zaaReloaded2.Formatter current++; } } - CreateParagraphStyle(); - _secondaryBuffer.ParagraphStyle = zaaReloaded2.Properties.Settings.Default.ParagraphStyleName; _secondaryBuffer.Flush(); } @@ -208,6 +208,39 @@ namespace zaaReloaded2.Formatter ProcessAllTimePoints(controlElement.Children); } + /// + /// Inserts a table with two columns into the document. + /// + public void InsertTwoColumns() + { + _secondaryBuffer.Flush(); + if (Document != null) + { + Range r = Document.ActiveWindow.Selection.Range; + _table = Document.Tables.Add(r, NumRows: 1, NumColumns: 2); + _table.AllowAutoFit = true; + _table.AutoFitBehavior(WdAutoFitBehavior.wdAutoFitWindow); + _table.PreferredWidthType = WdPreferredWidthType.wdPreferredWidthPercent; + _table.PreferredWidth = 100; + _table.Borders.Enable = 0; + } + } + + /// + /// Moves the insertion point to the next column in a layout + /// table. + /// + public void NextColumn() + { + if (_table == null) + { + throw new InvalidOperationException( + "Kann nicht zur nächsten Spalte wechseln, da bislang keine Tabelle eingefügt wurde."); + } + _secondaryBuffer.Flush(); + Document.ActiveWindow.Selection.MoveRight(WdUnits.wdCell); + } + /// /// Creates a zaaReloaded2 paragraph style in the document. /// @@ -324,6 +357,7 @@ namespace zaaReloaded2.Formatter Laboratory _laboratory; DocumentWriter _primaryBuffer; DocumentWriter _secondaryBuffer; + Table _table; #endregion } diff --git a/zaaReloaded2/ViewModels/ControlElementViewModel.cs b/zaaReloaded2/ViewModels/ControlElementViewModel.cs index 826968a..c0c09eb 100755 --- a/zaaReloaded2/ViewModels/ControlElementViewModel.cs +++ b/zaaReloaded2/ViewModels/ControlElementViewModel.cs @@ -35,6 +35,18 @@ namespace zaaReloaded2.ViewModels /// public ObservableCollection Elements { get; protected set; } + /// + /// Gets information whether this control element view model can + /// have child elements; + /// + public virtual bool CanHaveChildren + { + get + { + return ((ControlElementBase)Element).CanHaveChildren; + } + } + #endregion #region Constructors diff --git a/zaaReloaded2/ViewModels/ElementPickerViewModel.cs b/zaaReloaded2/ViewModels/ElementPickerViewModel.cs index a54c550..e00ff57 100755 --- a/zaaReloaded2/ViewModels/ElementPickerViewModel.cs +++ b/zaaReloaded2/ViewModels/ElementPickerViewModel.cs @@ -105,7 +105,9 @@ namespace zaaReloaded2.ViewModels { CreateControlElementViewModel(new SelectFirstDay()), CreateControlElementViewModel(new SelectLastDay()), - CreateControlElementViewModel(new SelectEachDay()) + CreateControlElementViewModel(new SelectEachDay()), + CreateControlElementViewModel(new TwoColumns()), + CreateControlElementViewModel(new NextColumn()), } ) ); @@ -130,7 +132,9 @@ namespace zaaReloaded2.ViewModels { CreateControlElementViewModel(new SelectFirstDay(), copyFromViewModel), CreateControlElementViewModel(new SelectLastDay(), copyFromViewModel), - CreateControlElementViewModel(new SelectEachDay(), copyFromViewModel) + CreateControlElementViewModel(new SelectEachDay(), copyFromViewModel), + CreateControlElementViewModel(new TwoColumns()), + CreateControlElementViewModel(new NextColumn()), } ) ); diff --git a/zaaReloaded2/ViewModels/ElementViewModel.cs b/zaaReloaded2/ViewModels/ElementViewModel.cs index f8b4c7c..f67274c 100755 --- a/zaaReloaded2/ViewModels/ElementViewModel.cs +++ b/zaaReloaded2/ViewModels/ElementViewModel.cs @@ -30,23 +30,11 @@ namespace zaaReloaded2.ViewModels { #region Properties - /// - /// Gets the label of the wrapped element. - /// - public virtual string Label - { - [DebuggerStepThrough] - get - { - return Element.Label; - } - } - public override string DisplayString { get { - return Label; + return Element.Label; } } diff --git a/zaaReloaded2/ViewModels/FormatElementViewModel.cs b/zaaReloaded2/ViewModels/FormatElementViewModel.cs index 373e79e..fef5d4b 100755 --- a/zaaReloaded2/ViewModels/FormatElementViewModel.cs +++ b/zaaReloaded2/ViewModels/FormatElementViewModel.cs @@ -89,25 +89,6 @@ namespace zaaReloaded2.ViewModels return clone; } - public override string DisplayString - { - get - { - if (String.IsNullOrEmpty(Content)) - { - return _displayString; - } - else - { - return base.DisplayString; - } - } - set - { - _displayString = value; - } - } - #endregion #region Private methods diff --git a/zaaReloaded2/ViewModels/SettingsViewModel.cs b/zaaReloaded2/ViewModels/SettingsViewModel.cs index 3308203..fdf149b 100755 --- a/zaaReloaded2/ViewModels/SettingsViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsViewModel.cs @@ -345,7 +345,8 @@ namespace zaaReloaded2.ViewModels bool CanAddChildElement() { - return LastSelectedElement is ControlElementViewModel; + return LastSelectedElement is ControlElementViewModel && + ((ControlElementViewModel)LastSelectedElement).CanHaveChildren; } void DoEditElement() diff --git a/zaaReloaded2/zaaReloaded2.csproj b/zaaReloaded2/zaaReloaded2.csproj index e73c60c..fb816a0 100755 --- a/zaaReloaded2/zaaReloaded2.csproj +++ b/zaaReloaded2/zaaReloaded2.csproj @@ -193,10 +193,12 @@ + + From 51bfdc760eb998dfb96f28f7f2a9965bceb69392 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Sun, 9 Aug 2015 21:12:07 +0200 Subject: [PATCH 26/49] Remove superfluous field from FormatElementViewModel. --- zaaReloaded2/ViewModels/FormatElementViewModel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/zaaReloaded2/ViewModels/FormatElementViewModel.cs b/zaaReloaded2/ViewModels/FormatElementViewModel.cs index fef5d4b..00810fa 100755 --- a/zaaReloaded2/ViewModels/FormatElementViewModel.cs +++ b/zaaReloaded2/ViewModels/FormatElementViewModel.cs @@ -112,7 +112,6 @@ namespace zaaReloaded2.ViewModels #region Fields DelegatingCommand _saveCommand; - string _displayString; string _content; #endregion From 6c9f003134cb0117455e03bc897a341e9b029986 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Sun, 9 Aug 2015 21:21:56 +0200 Subject: [PATCH 27/49] Do not build headers from empty time stamps. --- zaaReloaded2/Formatter/TimePointFormatter.cs | 32 ++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/zaaReloaded2/Formatter/TimePointFormatter.cs b/zaaReloaded2/Formatter/TimePointFormatter.cs index ab5ba9b..da8b41e 100755 --- a/zaaReloaded2/Formatter/TimePointFormatter.cs +++ b/zaaReloaded2/Formatter/TimePointFormatter.cs @@ -30,14 +30,36 @@ namespace zaaReloaded2.Formatter { #region Static methods + /// + /// Builds a header paragraph from a Date, + /// but only if the Date structure has a value. + /// public static string DateHeader(DateTime date) { - return FormatHeader(date.ToShortDateString()); + if (date != _nullTimeStamp.Date) + { + return FormatHeader(date.ToShortDateString()); + } + else + { + return String.Empty; + } } + /// + /// Builds a header paragraph from a DateTime structure, + /// but only if the DateTime structure has a value. + /// public static string DateAndTimeHeader(DateTime dateTime) { - return FormatHeader(dateTime.ToString()); + if (dateTime != _nullTimeStamp) + { + return FormatHeader(dateTime.ToString()); + } + else + { + return String.Empty; + } } #endregion @@ -119,5 +141,11 @@ namespace zaaReloaded2.Formatter } #endregion + + #region Fields + + static readonly DateTime _nullTimeStamp = new DateTime(); + + #endregion } } \ No newline at end of file From 947b53cf97cd267f79a3eedf53a26054ed36fa53 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Mon, 10 Aug 2015 06:55:57 +0200 Subject: [PATCH 28/49] Fix material convertsion and units detection. --- Tests/Formatter/FormatterTest.cs | 19 +++++++++++++++++++ zaaReloaded2/Controller/Elements/Items.cs | 4 +++- .../Importer/ZaaImporter/LaurisItem.cs | 11 +++++++---- zaaReloaded2/Thesaurus/Defaults/units.txt | 5 +++-- zaaReloaded2/Thesaurus/Parameters.cs | 17 ++++++++++++----- 5 files changed, 44 insertions(+), 12 deletions(-) diff --git a/Tests/Formatter/FormatterTest.cs b/Tests/Formatter/FormatterTest.cs index 7437d6b..871dab9 100755 --- a/Tests/Formatter/FormatterTest.cs +++ b/Tests/Formatter/FormatterTest.cs @@ -99,6 +99,25 @@ namespace Tests.Formatter _document.Range().Text); } + [Test] + public void FormatSpecialItems() + { + ZaaImporter importer = new ZaaImporter(); + importer.Import( + "Klinische Chemie: glomerul. Filtrationsr. CKD-EP: 36 ml/min /1,73qm; " + + "Sammelmenge (U): 12300 ml; " + ); + Document document = new Document(); + f.Formatter formatter = new f.Formatter(document); + formatter.Laboratory = importer.Laboratory; + formatter.Settings = new zaaReloaded2.Controller.Settings( + new List() { new Items("*") } ); + formatter.Run(); + Assert.AreEqual( + "eGFR (CKD-EPI) 36 ml/min/1,73 m², SU-Volumen 12300 ml\r\r", + document.Range().Text); + } + string GetResourceText(string resource) { try diff --git a/zaaReloaded2/Controller/Elements/Items.cs b/zaaReloaded2/Controller/Elements/Items.cs index b5710da..9baf10a 100755 --- a/zaaReloaded2/Controller/Elements/Items.cs +++ b/zaaReloaded2/Controller/Elements/Items.cs @@ -139,7 +139,6 @@ namespace zaaReloaded2.Controller.Elements { items.AddRange(CollectByName(formatter, itemName)); } - } return items; } @@ -158,6 +157,9 @@ namespace zaaReloaded2.Controller.Elements .Where(i => !i.HasBeenUsed && !i.IsBlacklisted && i.LabItem.QualifiedName.StartsWith(material)) .ToList(); newItems.ForEach(i => i.HasBeenUsed = true); + // Include the material prefix only if this item was collected by a + // general wildcard ("*" rather than "SU-*" etc.). + newItems.ForEach(i => i.IncludeMaterial = String.IsNullOrEmpty(material)); items.AddRange(newItems); } return items; diff --git a/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs b/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs index ccff09a..71d7e19 100755 --- a/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs +++ b/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs @@ -76,6 +76,7 @@ namespace zaaReloaded2.Importer.ZaaImporter Name = parameterDictionary.GetCanonicalName(OriginalName); AlwaysPrintLimits = parameterDictionary.GetForceReferenceDisplay(OriginalName); IsBlacklisted = parameterDictionary.GetIsBlacklisted(OriginalName); + Material = parameterDictionary.GetMaterial(OriginalName, Material); } if (unitDictionary != null) { @@ -165,10 +166,12 @@ namespace zaaReloaded2.Importer.ZaaImporter } /// - /// Analyses the Lauris name for a material abbreviation. - /// If the parameter does not refer to blood (serum, whole - /// blood, etc.), Lauris appends an abbreviation in parentheses - /// to the parameter name. + /// Parses the original Lauris name for a material abbreviation. + /// This may be misleading in certain cases, e.g. "Sammelmenge (U)" + /// appears to be spot urine ("U"), but is collected urine instead + /// ("SU"). Therefore, in the constructor that takes the thesaurus + /// parameters, the material is looked up in the Parameters thesaurus. + /// ("Sammelmenge (U)" is contained in the Parameters thesaurus.) /// /// /// Gesamt-Eiweiss (SU), Albumin (SU)/die, Gesamt-Eiweiss (PU) diff --git a/zaaReloaded2/Thesaurus/Defaults/units.txt b/zaaReloaded2/Thesaurus/Defaults/units.txt index 51a44d8..8981e90 100755 --- a/zaaReloaded2/Thesaurus/Defaults/units.txt +++ b/zaaReloaded2/Thesaurus/Defaults/units.txt @@ -2,7 +2,8 @@ # ============== ==================== # WICHTIG: Nur direkt austauschbare Einheiten verwenden, # weil (bislang) keine Umrechnung der Werte vorgesehen ist! -"ml/min/ 1,73qm" "ml/min/1,73 m²" +"ml/min /1,73qm" "ml/min/1,73 m²" ng/ml µg/l mmol/l mM -n*1000/µl /nl \ No newline at end of file +n*1000/µl /nl +Bak/µl /µl diff --git a/zaaReloaded2/Thesaurus/Parameters.cs b/zaaReloaded2/Thesaurus/Parameters.cs index e6444f2..981e7d8 100755 --- a/zaaReloaded2/Thesaurus/Parameters.cs +++ b/zaaReloaded2/Thesaurus/Parameters.cs @@ -69,16 +69,23 @@ namespace zaaReloaded2.Thesaurus /// Lauris item name to look up. /// enum; if no material is /// found in the dictionary, the default material "S" (serum) is returned. - public Material GetMaterial(string laurisName) + public Material GetMaterial(string laurisName, Material def) { string textValue = LookUpValue(laurisName, 2); - try + if (String.IsNullOrEmpty(textValue)) { - return MaterialFactory.FromAbbreviation(textValue); + return def; } - catch + else { - return Material.B; + try + { + return MaterialFactory.FromAbbreviation(textValue); + } + catch + { + return Material.B; + } } } From 53d13b394e4816d17baf64eeaa2265cdd59c1c0f Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Mon, 10 Aug 2015 09:18:36 +0200 Subject: [PATCH 29/49] Do not expand elements tree by default. --- zaaReloaded2/Views/SettingsView.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaaReloaded2/Views/SettingsView.xaml b/zaaReloaded2/Views/SettingsView.xaml index 39540b6..9ccb8c2 100755 --- a/zaaReloaded2/Views/SettingsView.xaml +++ b/zaaReloaded2/Views/SettingsView.xaml @@ -75,7 +75,7 @@ From c9fc4a5fae412f5f032e4e8a2d3cd43db9fbb6e3 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Tue, 11 Aug 2015 16:12:39 +0200 Subject: [PATCH 30/49] Implement serialization of Settings and SettingsRepository. - FIX: Stile werden jetzt gespeichert. --- Tests/Controller/SettingsRepositoryTest.cs | 2 +- Tests/Controller/SettingsTest.cs | 17 ++ Tests/SerializationTest.cs | 169 ++++++++++++++++++ Tests/Tests.csproj | 3 + .../Controller/Elements/ControlElementBase.cs | 46 ++++- .../Controller/Elements/CustomText.cs | 19 +- .../Controller/Elements/ElementBase.cs | 2 +- .../Controller/Elements/FormatElementBase.cs | 14 +- zaaReloaded2/Controller/Elements/Items.cs | 10 +- .../Controller/Elements/NextColumn.cs | 17 +- .../Controller/Elements/SelectEachDay.cs | 23 ++- .../Controller/Elements/SelectFirstDay.cs | 23 ++- .../Controller/Elements/SelectLastDay.cs | 23 ++- .../Controller/Elements/TwoColumns.cs | 19 +- zaaReloaded2/Controller/Settings.cs | 105 ++++++++++- zaaReloaded2/Controller/SettingsRepository.cs | 118 ++++++++++-- zaaReloaded2/Formatter/ReferenceStyle.cs | 1 - zaaReloaded2/Properties/Settings.Designer.cs | 14 +- zaaReloaded2/Properties/Settings.settings | 5 +- zaaReloaded2/app.config | 6 + zaaReloaded2/zaaReloaded2.csproj | 1 + 21 files changed, 593 insertions(+), 44 deletions(-) create mode 100755 Tests/SerializationTest.cs diff --git a/Tests/Controller/SettingsRepositoryTest.cs b/Tests/Controller/SettingsRepositoryTest.cs index 39f04d1..6e11358 100755 --- a/Tests/Controller/SettingsRepositoryTest.cs +++ b/Tests/Controller/SettingsRepositoryTest.cs @@ -27,7 +27,7 @@ namespace Tests.Controller [TestFixture] class SettingsRepositoryTest { - SettingsRepository _savedSettings; + string _savedSettings; [SetUp] public void SetUp() diff --git a/Tests/Controller/SettingsTest.cs b/Tests/Controller/SettingsTest.cs index b59821b..7395825 100755 --- a/Tests/Controller/SettingsTest.cs +++ b/Tests/Controller/SettingsTest.cs @@ -22,6 +22,7 @@ using System.Text; using NUnit.Framework; using zaaReloaded2.Controller; using zaaReloaded2.Controller.Elements; +using System.IO; namespace Tests.Controller { @@ -49,5 +50,21 @@ namespace Tests.Controller ((Items)clone.Elements[1]).Content, "Items content"); } + + [Test] + public void PersistSettings() + { + string name = "hello world"; + Settings persisting = new Settings(name, new List() { new Items() }); + persisting.ReferenceStyle = zaaReloaded2.Formatter.ReferenceStyle.IfSpecialItem; + MemoryStream s = new MemoryStream(); + persisting.Persist(s); + s.Position = 0; + Settings retrieved = Settings.Unpersist(s); + + Assert.AreEqual(persisting.Name, retrieved.Name, "Name"); + Assert.AreEqual(persisting.ReferenceStyle, retrieved.ReferenceStyle, "ReferenceStyle"); + + } } } diff --git a/Tests/SerializationTest.cs b/Tests/SerializationTest.cs new file mode 100755 index 0000000..f6f0821 --- /dev/null +++ b/Tests/SerializationTest.cs @@ -0,0 +1,169 @@ +/* SelectFirstDayTest.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.IO; +using System.Linq; +using System.Text; +using System.Xml.Serialization; +using zaaReloaded2.Controller.Elements; +using NUnit.Framework; +using System.Runtime.Serialization.Formatters.Binary; +using System.Runtime.Serialization.Formatters.Soap; +using zaaReloaded2.Controller; + +namespace Tests.Controller +{ + [TestFixture] + class SerializationTest + { + [Test] + public void SerializeControlElement() + { + SelectFirstDay element = new SelectFirstDay( + new List() + { + new Items("hello"), + new Items("world") + } + ); + MemoryStream stream = new MemoryStream(); + // XmlSerializer does not work with interface properties... + // XmlSerializer serializer = new XmlSerializer(typeof(SelectFirstDay)); + // BinaryFormatter serializer = new BinaryFormatter(); + SoapFormatter serializer = new SoapFormatter(); + serializer.Serialize(stream, element); + stream.Position = 0; + StreamReader sr = new StreamReader(stream); + Console.WriteLine(sr.ReadToEnd()); + stream.Position = 0; + SelectFirstDay deserialized = serializer.Deserialize(stream) as SelectFirstDay; + Assert.IsNotNull(deserialized); + Assert.AreEqual(element.Children.Count, deserialized.Children.Count); + Assert.AreEqual(element.Children[0].Content, deserialized.Children[0].Content); + Assert.AreEqual(element.Children[1].Content, deserialized.Children[1].Content); + } + + [Test] + public void SerializeFormatElement() + { + string testString = "Hello World"; + CustomText element = new CustomText(); + element.Content = testString; + MemoryStream stream = new MemoryStream(); + SoapFormatter serializer = new SoapFormatter(); + serializer.Serialize(stream, element); + stream.Position = 0; + CustomText deserialized = serializer.Deserialize(stream) as CustomText; + Assert.IsNotNull(deserialized); + Assert.AreEqual(element.Content, deserialized.Content); + } + + [Test] + public void SerializeSettings() + { + Settings settings = CreateTestSettings1(); + MemoryStream stream = new MemoryStream(); + SoapFormatter serializer = new SoapFormatter(); + serializer.Serialize(stream, settings); + stream.Position = 0; + Settings deserialized = serializer.Deserialize(stream) as Settings; + Assert.IsNotNull(deserialized); + Assert.AreEqual(settings.Name, deserialized.Name); + Assert.AreEqual(settings.ReferenceStyle, deserialized.ReferenceStyle); + Assert.AreEqual(settings.Elements.Count, deserialized.Elements.Count); + Assert.AreEqual(settings.Elements[0].GetType(), deserialized.Elements[0].GetType()); + Assert.AreEqual(settings.Elements[1].GetType(), deserialized.Elements[1].GetType()); + } + + [Test] + public void SerializeSettingsRepository() + { + SettingsRepository repository = new SettingsRepository(); + repository.SettingsList.Add(CreateTestSettings1()); + repository.SettingsList.Add(CreateTestSettings2()); + MemoryStream stream = new MemoryStream(); + SoapFormatter serializer = new SoapFormatter(); + serializer.Serialize(stream, repository); + stream.Position = 0; + SettingsRepository deserialized = serializer.Deserialize(stream) as SettingsRepository; + Assert.IsNotNull(deserialized); + Assert.AreEqual(repository.SettingsList.Count, deserialized.SettingsList.Count); + Assert.AreEqual( + repository.SettingsList[1].Elements.Count, + deserialized.SettingsList[1].Elements.Count + ); + Assert.AreEqual( + repository.SettingsList[0].Name, + deserialized.SettingsList[0].Name + ); + Assert.AreEqual( + repository.SettingsList[0].ReferenceStyle, + deserialized.SettingsList[0].ReferenceStyle + ); + } + + Settings CreateTestSettings1() + { + string testName = "test name..."; + string testString = "Hello World"; + SelectFirstDay controlElement = new SelectFirstDay( + new List() + { + new Items("hello"), + new Items("world") + } + ); + CustomText formatElement = new CustomText(); + formatElement.Content = testString; + Settings settings = new Settings(testName, new List() + { + controlElement, + formatElement + } + ); + settings.ReferenceStyle = zaaReloaded2.Formatter.ReferenceStyle.IfSpecialItem; + return settings; + } + + Settings CreateTestSettings2() + { + string testName = "another test name"; + SelectFirstDay controlElement1 = new SelectFirstDay( + new List() + { + new CustomText(), + } + ); + SelectLastDay controlElement2 = new SelectLastDay( + new List() + { + new Items("Items content"), + } + ); + Settings settings = new Settings(testName, new List() + { + controlElement1, + controlElement2 + } + ); + settings.ReferenceStyle = zaaReloaded2.Formatter.ReferenceStyle.IfSpecialOrAbnormal; + return settings; + } + } +} diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index c9404eb..644547a 100755 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -58,11 +58,13 @@ 3.5 + ..\packages\Expression.Blend.Sdk.1.0.2\lib\net40-client\System.Windows.Interactivity.dll True + @@ -78,6 +80,7 @@ + diff --git a/zaaReloaded2/Controller/Elements/ControlElementBase.cs b/zaaReloaded2/Controller/Elements/ControlElementBase.cs index ef58c9c..9681a2c 100755 --- a/zaaReloaded2/Controller/Elements/ControlElementBase.cs +++ b/zaaReloaded2/Controller/Elements/ControlElementBase.cs @@ -1,4 +1,5 @@ -/* ControlElementBase.cs +using System; +/* ControlElementBase.cs * part of zaaReloaded2 * * Copyright 2015 Daniel Kraus @@ -17,6 +18,7 @@ */ using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; namespace zaaReloaded2.Controller.Elements { @@ -24,9 +26,10 @@ namespace zaaReloaded2.Controller.Elements /// Base class for control elements that control e.g. the working /// set of time points in a Formatter object. /// - public abstract class ControlElementBase : ElementBase + [Serializable] + public abstract class ControlElementBase : ElementBase, ISerializable { - #region Propertis + #region Properties /// /// Gets a list of child elements, all of which must be derived @@ -58,6 +61,43 @@ namespace zaaReloaded2.Controller.Elements #endregion + #region Serialization + + /// + /// Deserialization constructor. + /// + protected ControlElementBase(SerializationInfo info, StreamingContext context) + { + int version = info.GetInt32("Version"); + int childrenCount = info.GetInt32("ChildrenCount"); + Children = new List(); + for (int i = 0; i < childrenCount; i++) + { + Type typeOfChild = info.GetValue(SerializedChildName(i, "Type"), typeof(Type)) as Type; + Children.Add(info.GetValue(SerializedChildName(i, "Object"), typeOfChild) as FormatElementBase); + } + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Version", Properties.Settings.Default.SerializationVersion); + info.AddValue("ChildrenCount", Children.Count); + int i = 0; + foreach (FormatElementBase child in Children) + { + info.AddValue(SerializedChildName(i, "Type"), child.GetType()); + info.AddValue(SerializedChildName(i, "Object"), child); + i++; + } + } + + private string SerializedChildName(int index, string info) + { + return String.Format("Child{0}{1}", index, info); + } + + #endregion + #region Protected methods /// diff --git a/zaaReloaded2/Controller/Elements/CustomText.cs b/zaaReloaded2/Controller/Elements/CustomText.cs index cbe1576..e6bd78c 100755 --- a/zaaReloaded2/Controller/Elements/CustomText.cs +++ b/zaaReloaded2/Controller/Elements/CustomText.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; using System.Text; namespace zaaReloaded2.Controller.Elements @@ -25,7 +26,8 @@ namespace zaaReloaded2.Controller.Elements /// /// Controller element that writes arbitrary text to the document. /// - class CustomText : FormatElementBase + [Serializable] + public class CustomText : FormatElementBase, ISerializable { public override string Label { @@ -43,5 +45,20 @@ namespace zaaReloaded2.Controller.Elements clone.Content = Content; return clone; } + + #region Constructors + + public CustomText () { } + + /// + /// Deserialization constructor. + /// + protected CustomText(SerializationInfo info, StreamingContext context) + { + int version = info.GetInt32("Version"); + Content = info.GetString("Content"); + } + + #endregion } } diff --git a/zaaReloaded2/Controller/Elements/ElementBase.cs b/zaaReloaded2/Controller/Elements/ElementBase.cs index ce13628..a5d1ecc 100755 --- a/zaaReloaded2/Controller/Elements/ElementBase.cs +++ b/zaaReloaded2/Controller/Elements/ElementBase.cs @@ -21,13 +21,13 @@ using System.Linq; using System.Text; using Microsoft.Office.Interop.Word; using zaaReloaded2.LabModel; +using System.Runtime.Serialization; namespace zaaReloaded2.Controller.Elements { /// /// Base class for formatting elements. /// - [Serializable] public abstract class ElementBase : ICloneable { /// diff --git a/zaaReloaded2/Controller/Elements/FormatElementBase.cs b/zaaReloaded2/Controller/Elements/FormatElementBase.cs index b28c523..015e0b1 100755 --- a/zaaReloaded2/Controller/Elements/FormatElementBase.cs +++ b/zaaReloaded2/Controller/Elements/FormatElementBase.cs @@ -16,12 +16,14 @@ * limitations under the License. */ +using System; +using System.Runtime.Serialization; namespace zaaReloaded2.Controller.Elements { /// /// Base class for elements that perform actual formatting. /// - public abstract class FormatElementBase : ElementBase + public abstract class FormatElementBase : ElementBase, ISerializable { /// /// Gets or sets the content of this format element. @@ -41,5 +43,15 @@ namespace zaaReloaded2.Controller.Elements protected virtual void OnContentChanged() {} private string _content; + + #region Serialization + + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Version", Properties.Settings.Default.SerializationVersion); + info.AddValue("Content", Content); + } + + #endregion } } diff --git a/zaaReloaded2/Controller/Elements/Items.cs b/zaaReloaded2/Controller/Elements/Items.cs index 9baf10a..880795d 100755 --- a/zaaReloaded2/Controller/Elements/Items.cs +++ b/zaaReloaded2/Controller/Elements/Items.cs @@ -23,6 +23,7 @@ using System.Diagnostics; using Microsoft.Office.Interop.Word; using zaaReloaded2.LabModel; using zaaReloaded2.Formatter; +using System.Runtime.Serialization; namespace zaaReloaded2.Controller.Elements { @@ -31,7 +32,7 @@ namespace zaaReloaded2.Controller.Elements /// to a Word document. /// [Serializable] - class Items : CustomText + public class Items : CustomText { #region ElementBase implementation @@ -93,6 +94,13 @@ namespace zaaReloaded2.Controller.Elements Content = content; } + /// + /// Deserialization constructor. + /// + protected Items(SerializationInfo info, StreamingContext context) + : base(info, context) + { } + #endregion #region Private methods diff --git a/zaaReloaded2/Controller/Elements/NextColumn.cs b/zaaReloaded2/Controller/Elements/NextColumn.cs index a3e534c..a59e427 100755 --- a/zaaReloaded2/Controller/Elements/NextColumn.cs +++ b/zaaReloaded2/Controller/Elements/NextColumn.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; using System.Text; namespace zaaReloaded2.Controller.Elements @@ -26,7 +27,8 @@ namespace zaaReloaded2.Controller.Elements /// Format element that causes a Formatter object to move the /// insertion point to the next colum in a layout table. /// - class NextColumn : ControlElementBase + [Serializable] + public class NextColumn : ControlElementBase, ISerializable { public override string Label { @@ -50,5 +52,18 @@ namespace zaaReloaded2.Controller.Elements { return new NextColumn(); } + + #region Constructors + + public NextColumn() : base() { } + + /// + /// Deserialization constructor. + /// + protected NextColumn(SerializationInfo info, StreamingContext context) + : base(info, context) + { } + + #endregion } } diff --git a/zaaReloaded2/Controller/Elements/SelectEachDay.cs b/zaaReloaded2/Controller/Elements/SelectEachDay.cs index 824b116..6f46976 100755 --- a/zaaReloaded2/Controller/Elements/SelectEachDay.cs +++ b/zaaReloaded2/Controller/Elements/SelectEachDay.cs @@ -18,11 +18,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; using System.Text; namespace zaaReloaded2.Controller.Elements { - class SelectEachDay : ControlElementBase + [Serializable] + public class SelectEachDay : ControlElementBase { public override string Label { @@ -34,6 +36,13 @@ namespace zaaReloaded2.Controller.Elements formatter.ProcessEachDay(this); } + protected override ElementBase CreateInstance() + { + return new SelectEachDay(CloneChildren()); + } + + #region Constructors + public SelectEachDay() : base() { } public SelectEachDay(FormatElementBase formatElement) @@ -44,9 +53,13 @@ namespace zaaReloaded2.Controller.Elements : base(formatElements) { } - protected override ElementBase CreateInstance() - { - return new SelectEachDay(CloneChildren()); - } + /// + /// Deserialization constructor. + /// + protected SelectEachDay(SerializationInfo info, StreamingContext context) + : base(info, context) + { } + + #endregion } } diff --git a/zaaReloaded2/Controller/Elements/SelectFirstDay.cs b/zaaReloaded2/Controller/Elements/SelectFirstDay.cs index 6771800..7887287 100755 --- a/zaaReloaded2/Controller/Elements/SelectFirstDay.cs +++ b/zaaReloaded2/Controller/Elements/SelectFirstDay.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; using System.Text; namespace zaaReloaded2.Controller.Elements @@ -26,7 +27,8 @@ namespace zaaReloaded2.Controller.Elements /// Selects the time points of the first day in a given Formatter /// object. /// - class SelectFirstDay : ControlElementBase + [Serializable] + public class SelectFirstDay : ControlElementBase, ISerializable { public override string Label { @@ -38,6 +40,13 @@ namespace zaaReloaded2.Controller.Elements formatter.ProcessFirstDay(this); } + protected override ElementBase CreateInstance() + { + return new SelectFirstDay(CloneChildren()); + } + + #region Constructors + public SelectFirstDay() : base() { } public SelectFirstDay(FormatElementBase formatElement) @@ -48,9 +57,13 @@ namespace zaaReloaded2.Controller.Elements : base(formatElements) { } - protected override ElementBase CreateInstance() - { - return new SelectFirstDay(CloneChildren()); - } + /// + /// Deserialization constructor. + /// + protected SelectFirstDay(SerializationInfo info, StreamingContext context) + : base(info, context) + { } + + #endregion } } diff --git a/zaaReloaded2/Controller/Elements/SelectLastDay.cs b/zaaReloaded2/Controller/Elements/SelectLastDay.cs index e2d048e..3ef2f1e 100755 --- a/zaaReloaded2/Controller/Elements/SelectLastDay.cs +++ b/zaaReloaded2/Controller/Elements/SelectLastDay.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; using System.Text; namespace zaaReloaded2.Controller.Elements @@ -27,7 +28,8 @@ namespace zaaReloaded2.Controller.Elements /// Selects the time points of the last day in a given Formatter /// object. /// - class SelectLastDay : ControlElementBase + [Serializable] + public class SelectLastDay : ControlElementBase, ISerializable { public override string Label { @@ -39,6 +41,13 @@ namespace zaaReloaded2.Controller.Elements formatter.ProcessLastDay(this); } + protected override ElementBase CreateInstance() + { + return new SelectLastDay(CloneChildren()); + } + + #region Constructors + public SelectLastDay() : base() { } public SelectLastDay(FormatElementBase formatElement) @@ -49,9 +58,13 @@ namespace zaaReloaded2.Controller.Elements : base(formatElements) { } - protected override ElementBase CreateInstance() - { - return new SelectLastDay(CloneChildren()); - } + /// + /// Deserialization constructor. + /// + protected SelectLastDay(SerializationInfo info, StreamingContext context) + : base(info, context) + { } + + #endregion } } diff --git a/zaaReloaded2/Controller/Elements/TwoColumns.cs b/zaaReloaded2/Controller/Elements/TwoColumns.cs index 0b9ab12..20ec064 100755 --- a/zaaReloaded2/Controller/Elements/TwoColumns.cs +++ b/zaaReloaded2/Controller/Elements/TwoColumns.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.Serialization; using System.Text; namespace zaaReloaded2.Controller.Elements @@ -26,7 +27,8 @@ namespace zaaReloaded2.Controller.Elements /// Format element that causes a Formatter object to insert a table with /// two columns and one row into the documents. /// - class TwoColumns : ControlElementBase + [Serializable] + public class TwoColumns : ControlElementBase, ISerializable { public override string Label { @@ -50,5 +52,18 @@ namespace zaaReloaded2.Controller.Elements { return new TwoColumns(); } - } + + #region Constructors + + public TwoColumns() : base() { } + + /// + /// Deserialization constructor. + /// + protected TwoColumns(SerializationInfo info, StreamingContext context) + : base(info, context) + { } + + #endregion +} } diff --git a/zaaReloaded2/Controller/Settings.cs b/zaaReloaded2/Controller/Settings.cs index 594ed15..46eb1b4 100755 --- a/zaaReloaded2/Controller/Settings.cs +++ b/zaaReloaded2/Controller/Settings.cs @@ -21,6 +21,9 @@ using System.Linq; using System.Text; using zaaReloaded2.Formatter; using zaaReloaded2.Controller.Elements; +using System.IO; +using System.Text.RegularExpressions; +using System.Runtime.Serialization; namespace zaaReloaded2.Controller { @@ -28,8 +31,43 @@ namespace zaaReloaded2.Controller /// Holds settings related to controlling laboratory output. /// [Serializable] - public class Settings : ICloneable + public class Settings : ICloneable, ISerializable { + #region Persistence + + /// + /// Custom serialization method that writes this settings + /// to a human-readable text file. + /// + /// + public void Persist(Stream stream) + { + StreamWriter w = new StreamWriter(stream); + w.WriteLine(String.Format("[{0}]", Name)); + + w.WriteLine(ReferenceStyle.ToString()); + w.Flush(); + } + + public static Settings Unpersist(Stream stream) + { + StreamReader r = new StreamReader(stream); + string line = r.ReadLine(); + Match m = _persistenceHeaderRegex.Match(line); + if (!m.Success) + throw new InvalidDataException("Settings header line does not match expected format."); + + Settings settings = new Settings(); + settings.Name = m.Groups["name"].Value; + + line = r.ReadLine(); + settings.ReferenceStyle = (ReferenceStyle)Enum.Parse(typeof(ReferenceStyle), line); + + return settings; + } + + #endregion + #region Properties /// @@ -94,6 +132,39 @@ namespace zaaReloaded2.Controller } } + /// + /// Creates a new Settings object with an initial + /// set of elements, a name, and a unique ID. + /// + /// Set of ElementBase + /// object (or derived ones). + /// Name of these settings. + /// Unique ID for this Settings object. + public Settings(string name, IList initialElements, Guid uid) + : this(name, initialElements) + { + Uid = uid; + } + + /// + /// Deserialization constructor. + /// + protected Settings(SerializationInfo info, StreamingContext context) + { + int version = info.GetInt32("Version"); + Uid = (Guid)info.GetValue("Uid", typeof(Guid)); + Name = info.GetString("Name"); + ReferenceStyle = (ReferenceStyle)info.GetValue("ReferenceStyle", typeof(ReferenceStyle)); + int elementsCount = info.GetInt32("ElementsCount"); + Elements = new List(); + for (int i = 0; i < elementsCount; i++) + { + Type type = info.GetValue(SerializationElementName(i, "Type"), typeof(Type)) as Type; + ElementBase element = info.GetValue(SerializationElementName(i, "Object"), type) as ElementBase; + Elements.Add(element); + } + } + #endregion #region Public methods @@ -122,5 +193,37 @@ namespace zaaReloaded2.Controller } #endregion + + #region Implementation of ISerializable + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Version", Properties.Settings.Default.SerializationVersion); + info.AddValue("Uid", Uid); + info.AddValue("Name", Name); + info.AddValue("ReferenceStyle", ReferenceStyle); + info.AddValue("ElementsCount", Elements.Count); + int i = 0; + foreach (ElementBase e in Elements) + { + info.AddValue(SerializationElementName(i, "Type"), e.GetType()); + info.AddValue(SerializationElementName(i, "Object"), e); + i++; + } + } + + string SerializationElementName(int index, string info) + { + return String.Format("Element{0}{1}", index, info); + } + + #endregion + + #region Fields + + // Defines header lines for the persistence file: "[My name]" and so on. + static readonly Regex _persistenceHeaderRegex = new Regex(@"^\[(?.+?)]$"); + + #endregion } } diff --git a/zaaReloaded2/Controller/SettingsRepository.cs b/zaaReloaded2/Controller/SettingsRepository.cs index 37402c2..6b261f8 100755 --- a/zaaReloaded2/Controller/SettingsRepository.cs +++ b/zaaReloaded2/Controller/SettingsRepository.cs @@ -18,10 +18,15 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Configuration; +using System.IO; using System.Linq; using System.Text; +using System.Xml.Serialization; +using System.Reflection; using zaaReloaded2.Controller.Elements; +using System.Runtime.Serialization.Formatters.Soap; +using System.Web; +using System.Runtime.Serialization; namespace zaaReloaded2.Controller { @@ -35,30 +40,45 @@ namespace zaaReloaded2.Controller /// different from zaaReloaded's Settings. /// [Serializable] - public class SettingsRepository : ApplicationSettingsBase + public class SettingsRepository : ISerializable { - #region Persistence + #region Properties persistence public static SettingsRepository Load() { - return - zaaReloaded2.Properties.Settings.Default.SettingsRepository ?? - new SettingsRepository(); + string s = Properties.Settings.Default.SettingsRepository; + if (String.IsNullOrEmpty(s)) + { + return null; + } + else + { + MemoryStream stream = new MemoryStream(); + string encoded = Properties.Settings.Default.SettingsRepository; + byte[] bytes = Convert.FromBase64String(encoded); + stream.Write(bytes, 0, bytes.Length); + stream.Position = 0; + SoapFormatter serializer = new SoapFormatter(); + return serializer.Deserialize(stream) as SettingsRepository; + } } public void Store() { - zaaReloaded2.Properties.Settings.Default.SettingsRepository = this; - zaaReloaded2.Properties.Settings.Default.Save(); + MemoryStream stream = new MemoryStream(); + SoapFormatter serializer = new SoapFormatter(); + serializer.Serialize(stream, this); + stream.Position = 0; + string encoded = Convert.ToBase64String(stream.ToArray()); + Properties.Settings.Default.SettingsRepository = encoded; + Properties.Settings.Default.Save(); } #endregion #region Properties - [UserScopedSetting()] - [SettingsSerializeAs(SettingsSerializeAs.Xml)] - public IList SettingsList { get; private set; } + public IList SettingsList { get; protected set; } #endregion @@ -76,6 +96,41 @@ namespace zaaReloaded2.Controller #endregion + #region Serialization + + protected SettingsRepository(SerializationInfo info, StreamingContext context) + { + int version = info.GetInt32("Version"); + int settingsCount = info.GetInt32("SettingsCount"); + SettingsList = new List(); + for (int i = 0; i < settingsCount; i++) + { + Type type = info.GetValue(SerializationSettingsName(i, "Type"), typeof(Type)) as Type; + Settings s = info.GetValue(SerializationSettingsName(i, "Object"), typeof(Settings)) as Settings; + SettingsList.Add(s); + } + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Version", Properties.Settings.Default.SerializationVersion); + info.AddValue("SettingsCount", SettingsList.Count); + int i = 0; + foreach (Settings s in SettingsList) + { + info.AddValue(SerializationSettingsName(i, "Type"), s.GetType()); + info.AddValue(SerializationSettingsName(i, "Object"), s); + i++; + } + } + + string SerializationSettingsName(int index, string info) + { + return String.Format("Settings{0}{1}", index, info); + } + + #endregion + #region Public methods /// @@ -90,6 +145,27 @@ namespace zaaReloaded2.Controller return SettingsList.FirstOrDefault(s => s.Uid == uid); } + /// + /// Imports one Settings object by deserialization + /// from a stream. + /// + /// Stream to read from. + /// True if the import was successful. + bool Import(Stream s) + { + XmlSerializer ser = new XmlSerializer(typeof(Settings)); + Settings settings = ser.Deserialize(s) as Settings; + if (settings != null) + { + SettingsList.Add(settings); + return true; + } + else + { + return false; + } + } + #endregion #region Private methods @@ -132,7 +208,8 @@ namespace zaaReloaded2.Controller new SelectFirstDay(defaultItems), new NextColumn(), new SelectLastDay(defaultItems) - } + }, + Guid.Parse(DEFAULT_SETTINGS_1_UID) ) ); @@ -143,11 +220,26 @@ namespace zaaReloaded2.Controller new List() { new SelectEachDay(defaultItems), - } + }, + Guid.Parse(DEFAULT_SETTINGS_2_UID) ) ); } #endregion + + #region Private constants + + /// + /// Constant GUID for the first default Settings. + /// + const string DEFAULT_SETTINGS_1_UID = "EA79DE6C-E999-44F1-9122-929A8AA404CB"; + + /// + /// Constant GUID for the second default Settings. + /// + const string DEFAULT_SETTINGS_2_UID = "783C63B5-A964-4368-B2D0-D4595DCCB952"; + + #endregion } } diff --git a/zaaReloaded2/Formatter/ReferenceStyle.cs b/zaaReloaded2/Formatter/ReferenceStyle.cs index 2d47b33..6e19a3f 100755 --- a/zaaReloaded2/Formatter/ReferenceStyle.cs +++ b/zaaReloaded2/Formatter/ReferenceStyle.cs @@ -26,7 +26,6 @@ namespace zaaReloaded2.Formatter /// /// Describes the style of normal range references. /// - [Serializable] public enum ReferenceStyle { /// diff --git a/zaaReloaded2/Properties/Settings.Designer.cs b/zaaReloaded2/Properties/Settings.Designer.cs index cd3968c..625959a 100755 --- a/zaaReloaded2/Properties/Settings.Designer.cs +++ b/zaaReloaded2/Properties/Settings.Designer.cs @@ -25,9 +25,10 @@ namespace zaaReloaded2.Properties { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - public global::zaaReloaded2.Controller.SettingsRepository SettingsRepository { + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string SettingsRepository { get { - return ((global::zaaReloaded2.Controller.SettingsRepository)(this["SettingsRepository"])); + return ((string)(this["SettingsRepository"])); } set { this["SettingsRepository"] = value; @@ -281,5 +282,14 @@ namespace zaaReloaded2.Properties { return ((string)(this["ControlElementLabel"])); } } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public int SerializationVersion { + get { + return ((int)(this["SerializationVersion"])); + } + } } } diff --git a/zaaReloaded2/Properties/Settings.settings b/zaaReloaded2/Properties/Settings.settings index 2cecf23..e5ba5b7 100755 --- a/zaaReloaded2/Properties/Settings.settings +++ b/zaaReloaded2/Properties/Settings.settings @@ -2,7 +2,7 @@ - + @@ -86,5 +86,8 @@ Steuerungs-Elemente + + 1 + \ No newline at end of file diff --git a/zaaReloaded2/app.config b/zaaReloaded2/app.config index d4345e1..4267d0b 100755 --- a/zaaReloaded2/app.config +++ b/zaaReloaded2/app.config @@ -104,10 +104,16 @@ Steuerungs-Elemente + + 1 + + + + diff --git a/zaaReloaded2/zaaReloaded2.csproj b/zaaReloaded2/zaaReloaded2.csproj index fb816a0..8153108 100755 --- a/zaaReloaded2/zaaReloaded2.csproj +++ b/zaaReloaded2/zaaReloaded2.csproj @@ -143,6 +143,7 @@ + ..\packages\Expression.Blend.Sdk.1.0.2\lib\net40-client\System.Windows.Interactivity.dll From 6af2243660d48bf56481e0d5cc6af7a3cf22e024 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Tue, 11 Aug 2015 16:16:26 +0200 Subject: [PATCH 31/49] Do not return Null from SettingsRepository.Load(). --- zaaReloaded2/Controller/SettingsRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zaaReloaded2/Controller/SettingsRepository.cs b/zaaReloaded2/Controller/SettingsRepository.cs index 6b261f8..5f465f8 100755 --- a/zaaReloaded2/Controller/SettingsRepository.cs +++ b/zaaReloaded2/Controller/SettingsRepository.cs @@ -49,7 +49,7 @@ namespace zaaReloaded2.Controller string s = Properties.Settings.Default.SettingsRepository; if (String.IsNullOrEmpty(s)) { - return null; + return new SettingsRepository(); } else { From 00c60ba0ad629ed7e447fcafc2e9ed7b6364da53 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Tue, 11 Aug 2015 19:51:23 +0200 Subject: [PATCH 32/49] Comment on serialization. --- README.md | 27 +++++++++++++++++++ zaaReloaded2/Controller/SettingsRepository.cs | 22 +++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/README.md b/README.md index e5dec80..6278f40 100644 --- a/README.md +++ b/README.md @@ -174,4 +174,31 @@ oder nicht-normalen Parametern oder immer mit ausgegeben werden: Die Markierung besonderer Parameter erfolgt in der Textdatei, aus der das `zaaReloaded2.Thesaurus.Parameters` generiert wird (s.o.). + +## Serialisierung + +Um das Stil-Repositorium (`zaaReloaded2.Controller.SettingsRepository`) +in den Assembly-Properties speichern zu können und für den Im- und +Export von Stilen werden die in .NET eingebauten +Serialisierungs-Strukturen genutzt. Weil es mit der Verwendung eines +`XmlSerializer`s Probleme gab, weil der `XmlSerializer` nicht mit +Interface-Eigenschaften umgehen kann, wurde zunächst für alle Klassen, +die serialisiert werden müssen (`SettingsRepository`, +`zaaReloaded2.Controller.Settings` und die von +`zaaReloaded2.Controller.Elements.ElementBase` abgeleiteten Klassen) das +Interface `ISerializable` implementiert. Das hat aber auch nicht dazu +geführt, daß der `XmlSerializer` die Klassen serialisierte, so daß +jetzt der `SoapFormatter` verwendet wird. Der stört sich nicht an +Interface-Eigenschaften und produziert auch XML, wenngleich im etwas +komplizierten [SOAP][]-Format. + +Um das `SettingsRepository` in den Assembly-Properties zu persistieren, +muß das serialisierte XML noch in einen Base64-kodierten String +umgewandelt werden, damit das SOAP-XML nicht mit dem XML der +Assembly-Properties ins Gehege kommt (siehe +`zaaReloaded2.Controller.SettingsRepository.Load()` und +`zaaReloaded2.Controller.SettingsRepository.Store()`). + +[SOAP]: http://de.wikipedia.org/wiki/SOAP + diff --git a/zaaReloaded2/Controller/SettingsRepository.cs b/zaaReloaded2/Controller/SettingsRepository.cs index 5f465f8..2bc9960 100755 --- a/zaaReloaded2/Controller/SettingsRepository.cs +++ b/zaaReloaded2/Controller/SettingsRepository.cs @@ -44,6 +44,19 @@ namespace zaaReloaded2.Controller { #region Properties persistence + /// + /// Loads the SettingsRepository instance that was persisted in + /// the assembly properties. + /// + /// SettingsRepository that was last stored in the + /// assembly properties, or a newly created SettingsRepository + /// if no previously stored exists. + /// + /// The SettingsRepository is serialized using a SoapFormatter, which + /// creates SOAP XML. Since the assembly properties are stored as + /// XML as well, the serialized SettingsRepository is converted to + /// a base-64 string, which does not mess with the properties XML. + /// public static SettingsRepository Load() { string s = Properties.Settings.Default.SettingsRepository; @@ -63,6 +76,15 @@ namespace zaaReloaded2.Controller } } + /// + /// Stores the SettingsRepository in the assembly properties. + /// + /// + /// The SettingsRepository is serialized using a SoapFormatter, which + /// creates SOAP XML. Since the assembly properties are stored as + /// XML as well, the serialized SettingsRepository is converted to + /// a base-64 string, which does not mess with the properties XML. + /// public void Store() { MemoryStream stream = new MemoryStream(); From 0708f2a2b397cceb5559dc416f588af46b4ed72c Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Tue, 11 Aug 2015 21:22:34 +0200 Subject: [PATCH 33/49] Implement import and export of Settings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NEU: Stile können importiert und exportiert werden. --- Tests/Controller/SettingsTest.cs | 16 -- zaaReloaded2/Controller/Settings.cs | 47 ++--- zaaReloaded2/Properties/Settings.Designer.cs | 12 ++ zaaReloaded2/Properties/Settings.settings | 3 + zaaReloaded2/ViewModels/IoErrorViewModel.cs | 46 +++++ .../ViewModels/SettingsRepositoryViewModel.cs | 173 ++++++++++++++++++ zaaReloaded2/Views/IoErrorView.xaml | 52 ++++++ zaaReloaded2/Views/IoErrorView.xaml.cs | 33 ++++ .../Views/SettingsRepositoryView.xaml | 35 +++- zaaReloaded2/app.config | 3 + zaaReloaded2/zaaReloaded2.csproj | 8 + 11 files changed, 379 insertions(+), 49 deletions(-) create mode 100755 zaaReloaded2/ViewModels/IoErrorViewModel.cs create mode 100755 zaaReloaded2/Views/IoErrorView.xaml create mode 100755 zaaReloaded2/Views/IoErrorView.xaml.cs diff --git a/Tests/Controller/SettingsTest.cs b/Tests/Controller/SettingsTest.cs index 7395825..912992c 100755 --- a/Tests/Controller/SettingsTest.cs +++ b/Tests/Controller/SettingsTest.cs @@ -50,21 +50,5 @@ namespace Tests.Controller ((Items)clone.Elements[1]).Content, "Items content"); } - - [Test] - public void PersistSettings() - { - string name = "hello world"; - Settings persisting = new Settings(name, new List() { new Items() }); - persisting.ReferenceStyle = zaaReloaded2.Formatter.ReferenceStyle.IfSpecialItem; - MemoryStream s = new MemoryStream(); - persisting.Persist(s); - s.Position = 0; - Settings retrieved = Settings.Unpersist(s); - - Assert.AreEqual(persisting.Name, retrieved.Name, "Name"); - Assert.AreEqual(persisting.ReferenceStyle, retrieved.ReferenceStyle, "ReferenceStyle"); - - } } } diff --git a/zaaReloaded2/Controller/Settings.cs b/zaaReloaded2/Controller/Settings.cs index 46eb1b4..8a965d9 100755 --- a/zaaReloaded2/Controller/Settings.cs +++ b/zaaReloaded2/Controller/Settings.cs @@ -24,6 +24,7 @@ using zaaReloaded2.Controller.Elements; using System.IO; using System.Text.RegularExpressions; using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Soap; namespace zaaReloaded2.Controller { @@ -36,34 +37,34 @@ namespace zaaReloaded2.Controller #region Persistence /// - /// Custom serialization method that writes this settings - /// to a human-readable text file. + /// Deserializes a Settings object from SOAP XML that is read from a file. /// - /// - public void Persist(Stream stream) + /// File to read. + /// Deserialized Settings object with new unique ID. + /// if the file does not contain + /// SOAP XML that can be deserialized to a Settings object. + public static Settings LoadFromFile(string fileName) { - StreamWriter w = new StreamWriter(stream); - w.WriteLine(String.Format("[{0}]", Name)); - - w.WriteLine(ReferenceStyle.ToString()); - w.Flush(); + StreamReader reader = new StreamReader(fileName); + SoapFormatter formatter = new SoapFormatter(); + Settings settings = formatter.Deserialize(reader.BaseStream) as Settings; + if (settings == null) + { + throw new InvalidDataException("Datei enthält keine Stil-Daten oder ist beschädigt."); + } + settings.Uid = Guid.NewGuid(); + return settings; } - public static Settings Unpersist(Stream stream) + /// + /// Serializes a Settings object to SOAP XML that is written to a file. + /// + /// File to write to. + public void SaveToFile(string fileName) { - StreamReader r = new StreamReader(stream); - string line = r.ReadLine(); - Match m = _persistenceHeaderRegex.Match(line); - if (!m.Success) - throw new InvalidDataException("Settings header line does not match expected format."); - - Settings settings = new Settings(); - settings.Name = m.Groups["name"].Value; - - line = r.ReadLine(); - settings.ReferenceStyle = (ReferenceStyle)Enum.Parse(typeof(ReferenceStyle), line); - - return settings; + StreamWriter writer = new StreamWriter(fileName); + SoapFormatter formatter = new SoapFormatter(); + formatter.Serialize(writer.BaseStream, this); } #endregion diff --git a/zaaReloaded2/Properties/Settings.Designer.cs b/zaaReloaded2/Properties/Settings.Designer.cs index 625959a..bf379f7 100755 --- a/zaaReloaded2/Properties/Settings.Designer.cs +++ b/zaaReloaded2/Properties/Settings.Designer.cs @@ -291,5 +291,17 @@ namespace zaaReloaded2.Properties { return ((int)(this["SerializationVersion"])); } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ImportExportPath { + get { + return ((string)(this["ImportExportPath"])); + } + set { + this["ImportExportPath"] = value; + } + } } } diff --git a/zaaReloaded2/Properties/Settings.settings b/zaaReloaded2/Properties/Settings.settings index e5ba5b7..45f429b 100755 --- a/zaaReloaded2/Properties/Settings.settings +++ b/zaaReloaded2/Properties/Settings.settings @@ -89,5 +89,8 @@ 1 + + + \ No newline at end of file diff --git a/zaaReloaded2/ViewModels/IoErrorViewModel.cs b/zaaReloaded2/ViewModels/IoErrorViewModel.cs new file mode 100755 index 0000000..ad26bfd --- /dev/null +++ b/zaaReloaded2/ViewModels/IoErrorViewModel.cs @@ -0,0 +1,46 @@ +/* IoErrorViewModel.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 Bovender.Mvvm.ViewModels; + +namespace zaaReloaded2.ViewModels +{ + /// + /// Simple view model for I/O error messages, for use in conjunction + /// with Bovender's ShowViewDialogAction and zaaReloaded2.Views.IoErrorView. + /// + class IoErrorViewModel : ViewModelBase + { + public Exception Exception { get; private set; } + + public string Message { get { return Exception.Message; } } + + public IoErrorViewModel(Exception e) + { + Exception = e; + } + + public override object RevealModelObject() + { + return Exception; + } + } +} diff --git a/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs b/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs index c398e57..837e7fa 100755 --- a/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsRepositoryViewModel.cs @@ -24,6 +24,8 @@ using Bovender.Mvvm.ViewModels; using Bovender.Mvvm.Messaging; using zaaReloaded2.Controller; using System.Collections.ObjectModel; +using System.Runtime.Serialization.Formatters.Soap; +using System.IO; namespace zaaReloaded2.ViewModels { @@ -134,6 +136,33 @@ namespace zaaReloaded2.ViewModels } } + public DelegatingCommand ExportSettingsCommand + { + get + { + if (_exportSettingsCommand == null) + { + _exportSettingsCommand = new DelegatingCommand( + param => DoExportSettings(), + param => CanExportSettings()); + } + return _exportSettingsCommand; + } + } + public DelegatingCommand ImportSettingsCommand + { + get + { + if (_importSettingsCommand == null) + { + _importSettingsCommand = new DelegatingCommand( + param => DoImportSettings(), + param => CanImportSettings()); + } + return _importSettingsCommand; + } + } + #endregion #region Messages @@ -186,6 +215,54 @@ namespace zaaReloaded2.ViewModels } } + public Message ChooseExportFileNameMessage + { + get + { + if (_chooseExportFileNameMessage == null) + { + _chooseExportFileNameMessage = new Message(); + } + return _chooseExportFileNameMessage; + } + } + + public Message ChooseImportFileNameMessage + { + get + { + if (_chooseImportFileNameMessage == null) + { + _chooseImportFileNameMessage = new Message(); + } + return _chooseImportFileNameMessage; + } + } + + public Message ExportErrorMessage + { + get + { + if (_exportErrorMessage == null) + { + _exportErrorMessage = new Message(); + } + return _exportErrorMessage; + } + } + + public Message ImportErrorMessage + { + get + { + if (_importErrorMessage == null) + { + _importErrorMessage = new Message(); + } + return _importErrorMessage; + } + } + #endregion #region Constructors @@ -334,6 +411,86 @@ namespace zaaReloaded2.ViewModels SettingsList.Add(settingsViewModel); } + void DoExportSettings() + { + if (CanExportSettings()) + { + ChooseExportFileNameMessage.Send( + new FileNameMessageContent(SuggestImportExportPath(), FILE_FILTER), + msg => ConfirmExportSettings(msg)); + } + } + + bool CanExportSettings() + { + return LastSelected != null && LastSelected.IsSelected && !IsDefaultSettings(); + } + + void ConfirmExportSettings(FileNameMessageContent message) + { + if (message.Confirmed) + { + Properties.Settings.Default.ImportExportPath = message.Value; + Properties.Settings.Default.Save(); + Settings settings = LastSelected.RevealModelObject() as Settings; + try + { + settings.SaveToFile(message.Value); + } + catch (Exception e) + { + ExportErrorMessage.Send( + new ViewModelMessageContent(new IoErrorViewModel(e)) + ); + } + } + } + + void DoImportSettings() + { + ChooseImportFileNameMessage.Send( + new FileNameMessageContent(SuggestImportExportPath(), FILE_FILTER), + msg => ConfirmImportSettings(msg)); + } + + bool CanImportSettings() + { + return true; + } + + void ConfirmImportSettings(FileNameMessageContent message) + { + if (message.Confirmed) + { + Properties.Settings.Default.ImportExportPath = message.Value; + Properties.Settings.Default.Save(); + try + { + Settings settings = Settings.LoadFromFile(message.Value); + AddSettingsViewModel(new SettingsViewModel(settings)); + } + catch (Exception e) + { + ImportErrorMessage.Send( + new ViewModelMessageContent(new IoErrorViewModel(e)) + ); + } + } + } + + string SuggestImportExportPath() + { + string path = Properties.Settings.Default.ImportExportPath; + if (String.IsNullOrEmpty(path)) + { + return Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + } + else + { + return path; + } + } + #endregion #region Implementation of ViewModelBase @@ -354,10 +511,26 @@ namespace zaaReloaded2.ViewModels DelegatingCommand _deleteSettingsCommand; DelegatingCommand _resetSettingsCommand; DelegatingCommand _copySettingsCommand; + DelegatingCommand _exportSettingsCommand; + DelegatingCommand _importSettingsCommand; Message _confirmDeleteSettingsMessage; Message _confirmResetSettingsMessage; Message _editSettingsMessage; Message _useSettingsMessage; + Message _chooseExportFileNameMessage; + Message _chooseImportFileNameMessage; + Message _exportErrorMessage; + Message _importErrorMessage; + + #endregion + + #region Constant + + /// + /// File filter that is used for the import and export + /// of settings. + /// + const string FILE_FILTER = "zaaReloaded-Stildatei (*.zaaReloaded)|*.zaaReloaded"; #endregion } diff --git a/zaaReloaded2/Views/IoErrorView.xaml b/zaaReloaded2/Views/IoErrorView.xaml new file mode 100755 index 0000000..3e52132 --- /dev/null +++ b/zaaReloaded2/Views/IoErrorView.xaml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + - - - - + + + + + + + + + + diff --git a/zaaReloaded2/zaaReloaded2.csproj b/zaaReloaded2/zaaReloaded2.csproj index 853000a..56cf87f 100755 --- a/zaaReloaded2/zaaReloaded2.csproj +++ b/zaaReloaded2/zaaReloaded2.csproj @@ -401,6 +401,10 @@ + + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) From a9d0e8ee7eac8fd4ca02f474aaa4c231021d3c75 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 12 Aug 2015 22:29:31 +0200 Subject: [PATCH 42/49] Add IsExpanded property to ElementViewModel. --- zaaReloaded2/ViewModels/ElementViewModel.cs | 12 +++++++++++- zaaReloaded2/ViewModels/SettingsViewModel.cs | 2 ++ zaaReloaded2/Views/SettingsView.xaml | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/zaaReloaded2/ViewModels/ElementViewModel.cs b/zaaReloaded2/ViewModels/ElementViewModel.cs index 2fe3d61..a6757f1 100755 --- a/zaaReloaded2/ViewModels/ElementViewModel.cs +++ b/zaaReloaded2/ViewModels/ElementViewModel.cs @@ -41,6 +41,16 @@ namespace zaaReloaded2.ViewModels public abstract string ToolTip { get; } + public bool IsExpanded + { + get { return _isExpanded; } + set + { + _isExpanded = value; + OnPropertyChanged("IsExpanded"); + } + } + #endregion #region Constructors @@ -77,7 +87,7 @@ namespace zaaReloaded2.ViewModels #region Fields - string _displayString; + bool _isExpanded; #endregion } diff --git a/zaaReloaded2/ViewModels/SettingsViewModel.cs b/zaaReloaded2/ViewModels/SettingsViewModel.cs index 2ead6db..152ce75 100755 --- a/zaaReloaded2/ViewModels/SettingsViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsViewModel.cs @@ -503,6 +503,7 @@ namespace zaaReloaded2.ViewModels ControlElementViewModel controlElementAbove = Elements[index - 1] as ControlElementViewModel; Elements.RemoveAt(index); + controlElementAbove.IsExpanded = true; controlElementAbove.AddChildElement( lastSelectedElement as FormatElementViewModel); FormatElementBase model = lastSelectedElement.RevealModelObject() as FormatElementBase; @@ -599,6 +600,7 @@ namespace zaaReloaded2.ViewModels ControlElementViewModel controlElementBelow = Elements[index + 1] as ControlElementViewModel; Elements.RemoveAt(index); + controlElementBelow.IsExpanded = true; controlElementBelow.Elements.Insert( 0, lastSelectedElement as FormatElementViewModel); diff --git a/zaaReloaded2/Views/SettingsView.xaml b/zaaReloaded2/Views/SettingsView.xaml index 7870078..3e77e7b 100755 --- a/zaaReloaded2/Views/SettingsView.xaml +++ b/zaaReloaded2/Views/SettingsView.xaml @@ -95,7 +95,7 @@ From 3a615eda8b783fab12ce5c733dbb8e56cb217c42 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Wed, 12 Aug 2015 22:54:29 +0200 Subject: [PATCH 43/49] Fix demoting elements while moving up. --- zaaReloaded2/ViewModels/SettingsViewModel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/zaaReloaded2/ViewModels/SettingsViewModel.cs b/zaaReloaded2/ViewModels/SettingsViewModel.cs index 152ce75..f4e13c1 100755 --- a/zaaReloaded2/ViewModels/SettingsViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsViewModel.cs @@ -509,7 +509,6 @@ namespace zaaReloaded2.ViewModels FormatElementBase model = lastSelectedElement.RevealModelObject() as FormatElementBase; ControlElementBase modelAbove = _settings.Elements[index - 1] as ControlElementBase; _settings.Elements.RemoveAt(index); - modelAbove.Children.Add(model); } } else From 63447def9b7dcd0dc62e02aa4f203b2483500ab9 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Thu, 13 Aug 2015 17:59:23 +0200 Subject: [PATCH 44/49] Multiple fixes for Views and ViewModels. --- zaaReloaded2/Style.xaml | 7 +++ .../Thesaurus/Defaults/parameters.txt | 7 ++- zaaReloaded2/Thesaurus/Defaults/units.txt | 1 + zaaReloaded2/ViewModels/SettingsViewModel.cs | 44 ++++++++++++++----- .../Views/SettingsRepositoryView.xaml | 22 +++++++--- zaaReloaded2/Views/SettingsView.xaml | 18 +++++--- zaaReloaded2/Views/SettingsView.xaml.cs | 5 +++ 7 files changed, 79 insertions(+), 25 deletions(-) diff --git a/zaaReloaded2/Style.xaml b/zaaReloaded2/Style.xaml index 3b42593..5a415d1 100755 --- a/zaaReloaded2/Style.xaml +++ b/zaaReloaded2/Style.xaml @@ -49,6 +49,13 @@ + diff --git a/zaaReloaded2/Thesaurus/Defaults/parameters.txt b/zaaReloaded2/Thesaurus/Defaults/parameters.txt index a2b837f..a2cc569 100755 --- a/zaaReloaded2/Thesaurus/Defaults/parameters.txt +++ b/zaaReloaded2/Thesaurus/Defaults/parameters.txt @@ -11,7 +11,7 @@ Albumin Alb S Albumin/Creatinin (PU)" ACR U "Alk. Phosphatase" AP S Amylase Amylase S -"anorg. Phosphat" Phosphat S +"anorg. Phosphat" P S "Bakterien (U)" Bakt U Basenabweichung BE BGA Basophile Baso E @@ -69,6 +69,7 @@ Kalium K S "Leukozyten (U)" Leu U Leukozyten Leu E Lymphozyten Lym E +Magnesium Mg S X "MCH (HbE)" MCH E MCHC MCHC E MCV MCV E @@ -82,7 +83,7 @@ Neutrophile Neu E NT-proBNP NT-proBNP S "PCO2 (art.)" pCO2 BGA "pH (U)" pH U -"pH" pH BGA +pH pH BGA "Plattenepithelien (U)" Plattenep U "PO2 (art.)" pO2 BGA "Protein (U)" Protein U @@ -112,3 +113,5 @@ Anti-Streptolysin ASL S TSH TSH S HAPTOGLOBIN Haptoglobin S FRAGMENTOZYTEN Fragmentozyten E +"HBc-Antikörper (gesamt)" Anti-HBc S +HBs-Antikörper Anti-HBs S \ No newline at end of file diff --git a/zaaReloaded2/Thesaurus/Defaults/units.txt b/zaaReloaded2/Thesaurus/Defaults/units.txt index 8981e90..1e8a350 100755 --- a/zaaReloaded2/Thesaurus/Defaults/units.txt +++ b/zaaReloaded2/Thesaurus/Defaults/units.txt @@ -6,4 +6,5 @@ ng/ml µg/l mmol/l mM n*1000/µl /nl +n*10E6/µl /fl Bak/µl /µl diff --git a/zaaReloaded2/ViewModels/SettingsViewModel.cs b/zaaReloaded2/ViewModels/SettingsViewModel.cs index f4e13c1..7bceeb5 100755 --- a/zaaReloaded2/ViewModels/SettingsViewModel.cs +++ b/zaaReloaded2/ViewModels/SettingsViewModel.cs @@ -342,13 +342,24 @@ namespace zaaReloaded2.ViewModels { // Create a new element picker; it will automatically create and // send us a new element view model if one is chosen by the view. - ElementPickerViewModel picker = new ElementPickerViewModel(true); + ElementPickerViewModel picker = new ElementPickerViewModel( + allowControlElements: IsTopLevelElement()); picker.ElementChosenMessage.Sent += (sender, args) => { ElementViewModel newVM = args.Content.ViewModel as ElementViewModel; - AddElementViewModel(newVM); + if (IsTopLevelElement()) + { + AddElementViewModel(newVM); + } + else + { + // If the selected element is on the second level, it + // must be a FormatElementViewModel. + ControlElementViewModel parent = ((FormatElementViewModel)LastSelectedElement).Parent; + AddChildElementViewModel(parent, newVM as FormatElementViewModel); + } newVM.IsSelected = true; - DoEditElement(); + if (newVM is FormatElementViewModel) DoEditElement(); }; AddElementMessage.Send(new ViewModelMessageContent(picker)); } @@ -389,11 +400,16 @@ namespace zaaReloaded2.ViewModels { // Replace the previously selected element with the new // one that we get from the ElementPickerViewModel. + int index = Elements.IndexOf(LastSelectedElement); ElementViewModel newVM = args.Content.ViewModel as ElementViewModel; - Elements.Insert( - Elements.IndexOf(LastSelectedElement), - newVM); - Elements.Remove(LastSelectedElement); + ControlElementBase oldModel = LastSelectedElement.RevealModelObject() as ControlElementBase; + ControlElementBase newModel = newVM.RevealModelObject() as ControlElementBase; + // Caveat: once we modify the Elements collection, LastSelectedElement will change! + Elements.RemoveAt(index); + Elements.Insert(index, newVM); + newModel.Children = oldModel.Children; + _settings.Elements.RemoveAt(index); + _settings.Elements.Insert(index, newModel); newVM.PropertyChanged += ElementViewModel_PropertyChanged; newVM.IsSelected = true; }; @@ -452,12 +468,16 @@ namespace zaaReloaded2.ViewModels { if (IsTopLevelElement()) { - AddElementViewModel(LastSelectedElement.Clone() as ElementViewModel); + ElementViewModel newControlVM = LastSelectedElement.Clone() as ElementViewModel; + AddElementViewModel(newControlVM); + newControlVM.IsSelected = true; } else { - FormatElementViewModel formatVM = LastSelectedElement as FormatElementViewModel; - formatVM.Parent.AddChildElement(formatVM.Clone() as FormatElementViewModel); + FormatElementViewModel originalVM = LastSelectedElement as FormatElementViewModel; + FormatElementViewModel newFormatVM = originalVM.Clone() as FormatElementViewModel; + originalVM.Parent.AddChildElement(newFormatVM); + newFormatVM.IsSelected = true; } } } @@ -492,7 +512,7 @@ namespace zaaReloaded2.ViewModels _settings.Elements.RemoveAt(index); _settings.Elements.Insert( index - 1, - LastSelectedElement.RevealModelObject() as ElementBase); + lastSelectedElement.RevealModelObject() as ElementBase); } else { @@ -588,7 +608,7 @@ namespace zaaReloaded2.ViewModels _settings.Elements.RemoveAt(index); _settings.Elements.Insert( index + 1, - LastSelectedElement.RevealModelObject() as ElementBase); + lastSelectedElement.RevealModelObject() as ElementBase); } else { diff --git a/zaaReloaded2/Views/SettingsRepositoryView.xaml b/zaaReloaded2/Views/SettingsRepositoryView.xaml index dd7bcb1..47fdc0a 100755 --- a/zaaReloaded2/Views/SettingsRepositoryView.xaml +++ b/zaaReloaded2/Views/SettingsRepositoryView.xaml @@ -61,7 +61,8 @@ - - - - - - - - - - + + + + + + + +

zaaReloaded2

-

+

Erweiterung für Microsoft® Word 2010-2013, das die Zentrale Arztbriefablage (ZAA) um Funktionen für das Formatieren von Laborwerten erweitert.

+ + + +

Anleitung

+ +

+ zaaReloaded2-Ribbon +

+ +
    +
  1. Laborwerte auf üblichem Wege in einen Arztbrief einfügen + (alternativ kann der Befehl "Demo" ausgeführt werden, dann wird + ein Beispieldokument mit Laborwerten geöffnet).
  2. +
  3. Im zaaReloaded-Tab (s.o.) den linken Knopf "Formatieren" + klicken.
  4. +

    + Stil auswählen +

    +
  5. + Beim ersten Mal erscheint eine Liste mit Stilen, aus der man den + gewünschten Stil auswählt (siehe Abbildung). Beim nächsten Mal, + wenn man auf diesen Knopf drückt, wird der zuletzt verwendete Stil + genommen.
  6. +
  7. Einen Moment warten... Voilà.
  8. +
+ + +

Anpassen

+

+ Das Addin enthält einen Stil-Editor, mit dem die eingebauten Stile + bearbeitet und neue Stile entworfen werden können: +

+

+ Stil bearbeiten +

+ +

+ Elemente + Jeder Stil setzt sich aus sog. Elementen zusammen. Es gibt + Steuerelemente, die zum Beispiel für die Auswahl eines Tages + (erster Tag/letzter Tag) zuständig sind, und es gibt + Ausgabeelemente, die die eigentliche Formatierung und Ausgabe + der Laborwerte übernehmen. +

+ +

+ Steuerelemente: +

+ +
    +
  • Ersten Tag auswählen
  • +
  • Letzten Tag auswählen
  • +
  • Jeden Tag nacheinander auswählen
  • +
  • Zwei Spalten einfügen/zur nächsten Spalte wechseln
  • +
+ +

+ Ausgabeelemente: +

+ +
    +
  • Laborparameter
  • +
  • Beliebiger Text
  • +
+ +

+ "Beliebiger Text" kann, wie der Name schon sagt, beliebigen Text + enthalten, der nicht weiter bearbeitet wird. Auf diese Weise kann + man z.B. Platzhalter für Urinsedimente ausgeben lassen, die dann + noch händisch vervollständigt werden. +

+

+ "Laborparameter" enthalten eine durch Kommata getrennte Auflistung + von Parametern, die ausgegeben werden sollen. Jedes Element + "Laborparameter" kann optional einen Titel enthalten: +

+

+ Beispiel: +

+
+

+ Element bearbeiten +

+

+ Klinische Chemie: Na, K, Cl, (usw.) +

+
+

+ In diesem Fall ist "Klinische Chemie:" der optionale Titel. +

+

+ Die Bezeichnungen der einzelnen Parameter sind fest einprogrammiert. + Teils decken sie sich mit den Bezeichnungen, wie sie in der + Zentralen Arztbriefablage erscheinen; teils sind sie aber auch + Surrogate (z.B. "eGFR (CKD-EPI)" anstatt "glomerul. Filtrationsr. + CKD-EP"). +

+

+ Die Platzhalter "SU-*", "U-*" und "*" geben die noch nicht + verwendeten Werte für Sammelurin, Spontanurin und alles weitere aus. + "*" sollte daher als letztes verwendet werden. +

+ Die Liste der möglichen Parameternamen ist momentan fest + einprogrammiert und kann auch noch nicht angezeigt werden. Man kann + beim Entwickeln eines Stils aber probehalber den Platzhalter "*" + verwenden und sieht dann, welche Parameternamen noch verwendet + werden könnten. +

+ +

Updates

+

+ Das Addin sucht täglich nach Updates und lädt und installiert diese + bei Bedarf im Hintergrund. +

+

Weitere Informationen

Versionsgeschichte

Quellcode-Dokumentation

+
-

© 2015 Daniel Kraus

+

© 2015 Daniel Kraus

From 193a83a769c3d6b08334c44ecb980ad3fab4b085 Mon Sep 17 00:00:00 2001 From: Daniel Kraus Date: Thu, 13 Aug 2015 20:50:53 +0200 Subject: [PATCH 49/49] Prepare release 2.0.0-beta.1 --- HISTORY.md | 19 ++++++++++++++++++- www/versioninfo.txt | 6 +++--- zaaReloaded2/VERSION | 4 ++-- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 6394dbd..f30eef4 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,4 +1,21 @@ -Version 2.0.0-alpha.5 (2015-08-04) +Version 2.0.0-beta.1 (2015-08-13) +======================================================================== + +- FIX: Datenimport funktioniert jetzt auch mit Word-Text und nicht nur in den Tests. +- FIX: Mehrere Thesaurus-Fehler behoben. +- FIX: Stile werden jetzt gespeichert. +- NEU: Auf Werkseinstellungen zurücksetzen. +- NEU: Datumsangaben werden in eine Zeile "Laborwerte vom ..." eingebettet. +- NEU: Elemente können verschoben werden. +- NEU: Formatierung der Ausgabe mit Absatzvorlage. +- NEU: Parameter können im Thesaurus als "blacklisted" markiert werden und werden dann bei Verwendung einer Wildcard ("*") nicht berücksichtigt. +- NEU: Steuer-Elemente für Spalten. +- NEU: Stile können importiert und exportiert werden. + +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + + +Version 2.0.0-alpha.5. (2015-08-04) ======================================================================== - FIX: Daniels Spezial löscht nicht mehr die Unterschriften. diff --git a/www/versioninfo.txt b/www/versioninfo.txt index 4db5701..9823b85 100644 --- a/www/versioninfo.txt +++ b/www/versioninfo.txt @@ -1,4 +1,4 @@ -2.0.0-alpha.5 -http://zaa.nephrowiki.de/downloads/zaaReloaded-2.0.0-alpha.5.exe -423cbe81b42ddfc293cf5b387c1d8d269dce3f61 publish/release/zaaReloaded-2.0.0-alpha.5.exe +2.0.0-beta.1 +http://zaa.nephrowiki.de/downloads/zaaReloaded-2.0.0-beta.1.exe +4fe0f2bd8ba1c3852c02c6220bddf53b5fd98ef8 publish/release/zaaReloaded-2.0.0-beta.1.exe diff --git a/zaaReloaded2/VERSION b/zaaReloaded2/VERSION index 7d655d4..03310dd 100755 --- a/zaaReloaded2/VERSION +++ b/zaaReloaded2/VERSION @@ -1,2 +1,2 @@ -2.0.0-alpha.5 -2.0.0.5 +2.0.0-beta.1 +2.0.0.6