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/README.md b/README.md index e5dec80..e754da3 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,11 @@ folgende Teile aufgeteilt: existieren verschiedene Klassen, die die Klassen des Labor-Modells (`zaaReloaded2.LabModel`) umschließen und um entsprechende Funktionalitäten erweitern. + - __Kontrolle der Formatierung:__ Der Namensraum + `zaaReloaded2.Controller` umfaßt Klassen, mit denen ein + `zaaReloaded2.Formatter.Formatter` gesteuert werden kann. Diese + Klassen spielen eine große Rolle bei den benutzerdefinierten + Anpassungen von Stilen. ## Parsen der Lauris-Ausgabe @@ -174,4 +179,49 @@ 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.). + +## User Interface + +Das User Interface wurde mit der Windows Presentation Foundation nach +dem [MVVM][]-Muster implementiert. Dabei wurde auf das Hilfs-Framework +[Bovender][bovender-framework] zurückgegriffen. + + +## Updates + +Das Add-in sucht täglich nach einem Update; dabei wird die Datei + heruntergeladen. Updates +werden automatisch im Hintergrund geladen und beim Schließen von Word +(bzw. der Zentralen Arztbriefablage) installiert und stehen dann beim +nächsten Starten der Anwendung zur Verfügung. + + +## 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 +[MVVM]: http://de.wikipedia.org/wiki/MVVM +[bovender-framework]: https://github.com/bovender/bovender + diff --git a/Tests/Controller/Elements/ItemsTest.cs b/Tests/Controller/Elements/ItemsTest.cs index 7fa49d3..8b3b861 100755 --- a/Tests/Controller/Elements/ItemsTest.cs +++ b/Tests/Controller/Elements/ItemsTest.cs @@ -61,7 +61,10 @@ 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); + string expected = ( + TimePointFormatter.DateAndTimeHeader(new DateTime(2015, 07, 13, 13, 31, 00)) + + "Na 133, K 6 (5)\r\r").Replace(Environment.NewLine, "\r"); + Assert.AreEqual(expected, _document.Range().Text); } [Test] @@ -78,7 +81,10 @@ 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); + string expected = ( + TimePointFormatter.DateAndTimeHeader(new DateTime(2015, 07, 13, 13, 31, 00)) + + "Klinische Chemie: Na 133, K 6 (5)\r\r").Replace(Environment.NewLine, "\r"); + Assert.AreEqual(expected, _document.Range().Text); } [Test] @@ -113,8 +119,10 @@ 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", - _document.Range().Text); + string expected = ( + TimePointFormatter.DateAndTimeHeader(new DateTime(2015, 07, 13, 13, 31, 00)) + + "Klinische Chemie: Na 133, Cl 110, K 6\r\r").Replace(Environment.NewLine, "\r"); + Assert.AreEqual(expected, _document.Range().Text); } [Test] @@ -133,8 +141,10 @@ 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", - _document.Range().Text); + string expected = ( + TimePointFormatter.DateAndTimeHeader(new DateTime(2015, 07, 13, 13, 31, 00)) + + "Klinische Chemie: Na 133, SU-Protein 2,8\r\r").Replace(Environment.NewLine, "\r"); + Assert.AreEqual(expected, _document.Range().Text); } [Test] @@ -153,8 +163,11 @@ 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", - _document.Range().Text); + string expected = ( + TimePointFormatter.DateAndTimeHeader(new DateTime(2015, 07, 13, 13, 31, 00)) + + "Klinische Chemie: Na 133, SU-Protein 2,8, Cl 110, U-Na 99\r\r") + .Replace(Environment.NewLine, "\r"); + Assert.AreEqual(expected, _document.Range().Text); } } } diff --git a/Tests/Controller/SettingsRepositoryTest.cs b/Tests/Controller/SettingsRepositoryTest.cs index e6b789a..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() @@ -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/Tests/Controller/SettingsTest.cs b/Tests/Controller/SettingsTest.cs new file mode 100755 index 0000000..912992c --- /dev/null +++ b/Tests/Controller/SettingsTest.cs @@ -0,0 +1,54 @@ +/* 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; +using System.IO; + +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/Formatter/FormatterTest-all.txt b/Tests/Formatter/FormatterTest-all.txt index 1db9686..e112ed6 100755 --- a/Tests/Formatter/FormatterTest-all.txt +++ b/Tests/Formatter/FormatterTest-all.txt @@ -1,7 +1,7 @@  -04.07.2015 12:31:00: +Laborwerte vom 04.07.2015 12:31:00: Klinische Chemie: Na 144 mM, K 4,3 mM -06.07.2015 10:28:00: +Laborwerte vom 06.07.2015 10:28:00: Klinische Chemie: Na 138 mM, K 4,6 mM diff --git a/Tests/Formatter/FormatterTest-eachday.txt b/Tests/Formatter/FormatterTest-eachday.txt index d7ddc76..2bad5e3 100755 --- a/Tests/Formatter/FormatterTest-eachday.txt +++ b/Tests/Formatter/FormatterTest-eachday.txt @@ -1,7 +1,7 @@  -04.07.2015: +Laborwerte vom 04.07.2015: Klinische Chemie: Na 144 mM, K 4,3 mM -06.07.2015: +Laborwerte vom 06.07.2015: Klinische Chemie: Na 138 mM, K 4,6 mM diff --git a/Tests/Formatter/FormatterTest-firstday.txt b/Tests/Formatter/FormatterTest-firstday.txt index 352e4ff..0aa36d1 100755 --- a/Tests/Formatter/FormatterTest-firstday.txt +++ b/Tests/Formatter/FormatterTest-firstday.txt @@ -1,4 +1,4 @@  -04.07.2015: +Laborwerte vom 04.07.2015: Klinische Chemie: Na 144 mM, K 4,3 mM diff --git a/Tests/Formatter/FormatterTest-lastday.txt b/Tests/Formatter/FormatterTest-lastday.txt index a69af35..b95373f 100755 --- a/Tests/Formatter/FormatterTest-lastday.txt +++ b/Tests/Formatter/FormatterTest-lastday.txt @@ -1,4 +1,4 @@  -06.07.2015: +Laborwerte vom 06.07.2015: Klinische Chemie: Na 138 mM, K 4,6 mM 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/Tests/Importer/ZaaImporter/TimePointTest.cs b/Tests/Importer/ZaaImporter/TimePointTest.cs index e07c035..81e33d7 100755 --- a/Tests/Importer/ZaaImporter/TimePointTest.cs +++ b/Tests/Importer/ZaaImporter/TimePointTest.cs @@ -40,7 +40,7 @@ namespace Tests.Importer.ZaaImporter [Test] public void ParseInvalidLaurisTimePoints() { - LaurisTimePoint tp = new LaurisTimePoint("Aerobe Kultur: Enterokokken ;  Wachstum: 10 ;"); + LaurisTimePoint tp = new LaurisTimePoint("Mit freundlichen Grüßen"); Assert.IsFalse(tp.IsValidTimePoint, "Bogus paragraph should be invalid LaurisTimePoint"); 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/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 fc37c1f..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,7 +80,9 @@ + + diff --git a/Tests/ViewModels/SettingsViewModelTest.cs b/Tests/ViewModels/SettingsViewModelTest.cs index 695e62c..dec16f7 100755 --- a/Tests/ViewModels/SettingsViewModelTest.cs +++ b/Tests/ViewModels/SettingsViewModelTest.cs @@ -69,7 +69,7 @@ namespace Tests.ViewModels _settingsVM.AddElementViewModel(viewModel); viewModel.IsSelected = true; int oldViewModelChildrenCount = viewModel.Elements.Count; - int oldModelChildrenCount = model.FormatElements.Count; + int oldModelChildrenCount = model.Children.Count; _settingsVM.AddChildElementMessage.Sent += (sender, args) => { messageSent = true; @@ -84,7 +84,7 @@ namespace Tests.ViewModels Assert.IsTrue(messageSent, "Message was not sent"); Assert.AreEqual(oldViewModelChildrenCount + 1, viewModel.Elements.Count, "Count of children in ViewModel was not increased by 1"); - Assert.AreEqual(oldModelChildrenCount + 1, model.FormatElements.Count, + Assert.AreEqual(oldModelChildrenCount + 1, model.Children.Count, "Count of children in Model was not increased by 1"); } @@ -106,13 +106,83 @@ 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] + 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]).Children.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]).Children.Contains(element), + "Settings' collection of FormatElement children still contains the element model."); } [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/gimp/down.xcf b/gimp/down.xcf new file mode 100644 index 0000000..2c325f8 Binary files /dev/null and b/gimp/down.xcf differ diff --git a/gimp/duplicate.xcf b/gimp/duplicate.xcf new file mode 100644 index 0000000..759a39e Binary files /dev/null and b/gimp/duplicate.xcf differ diff --git a/gimp/exit.xcf b/gimp/exit.xcf new file mode 100644 index 0000000..80747ce Binary files /dev/null and b/gimp/exit.xcf differ diff --git a/gimp/export.xcf b/gimp/export.xcf new file mode 100644 index 0000000..2967dd3 Binary files /dev/null and b/gimp/export.xcf differ diff --git a/gimp/gear.xcf b/gimp/gear.xcf new file mode 100644 index 0000000..c06bb85 Binary files /dev/null and b/gimp/gear.xcf differ diff --git a/gimp/import.xcf b/gimp/import.xcf new file mode 100644 index 0000000..f8cea86 Binary files /dev/null and b/gimp/import.xcf differ diff --git a/gimp/minus.xcf b/gimp/minus.xcf new file mode 100644 index 0000000..6609709 Binary files /dev/null and b/gimp/minus.xcf differ diff --git a/gimp/pen.xcf b/gimp/pen.xcf new file mode 100644 index 0000000..2c168ab Binary files /dev/null and b/gimp/pen.xcf differ diff --git a/gimp/plus-child.xcf b/gimp/plus-child.xcf new file mode 100644 index 0000000..7eeefc5 Binary files /dev/null and b/gimp/plus-child.xcf differ diff --git a/gimp/plus.xcf b/gimp/plus.xcf new file mode 100644 index 0000000..d2b470c Binary files /dev/null and b/gimp/plus.xcf differ diff --git a/gimp/reset.xcf b/gimp/reset.xcf new file mode 100644 index 0000000..12c5b44 Binary files /dev/null and b/gimp/reset.xcf differ diff --git a/gimp/up.xcf b/gimp/up.xcf new file mode 100644 index 0000000..cd242b6 Binary files /dev/null and b/gimp/up.xcf differ diff --git a/www/img/elementbearbeiten.png b/www/img/elementbearbeiten.png new file mode 100644 index 0000000..6a6b382 Binary files /dev/null and b/www/img/elementbearbeiten.png differ diff --git a/www/img/elementpicker.png b/www/img/elementpicker.png new file mode 100644 index 0000000..daf743b Binary files /dev/null and b/www/img/elementpicker.png differ diff --git a/www/img/nachher.png b/www/img/nachher.png new file mode 100644 index 0000000..a311ec9 Binary files /dev/null and b/www/img/nachher.png differ diff --git a/www/img/ribbon.png b/www/img/ribbon.png new file mode 100644 index 0000000..a7aae9b Binary files /dev/null and b/www/img/ribbon.png differ diff --git a/www/img/stilauswahl.png b/www/img/stilauswahl.png new file mode 100644 index 0000000..060a955 Binary files /dev/null and b/www/img/stilauswahl.png differ diff --git a/www/img/stilbearbeiten.png b/www/img/stilbearbeiten.png new file mode 100644 index 0000000..76daaee Binary files /dev/null and b/www/img/stilbearbeiten.png differ diff --git a/www/img/vorher.png b/www/img/vorher.png new file mode 100644 index 0000000..b50e76e Binary files /dev/null and b/www/img/vorher.png differ diff --git a/www/index.html b/www/index.html index ed714f9..7e0345f 100644 --- a/www/index.html +++ b/www/index.html @@ -1,20 +1,178 @@ zaaReloaded2 by Daniel Kraus + + +

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

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/Controller/Elements/ControlElementBase.cs b/zaaReloaded2/Controller/Elements/ControlElementBase.cs index 65a4564..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 @@ -16,6 +17,8 @@ * limitations under the License. */ using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; namespace zaaReloaded2.Controller.Elements { @@ -23,25 +26,90 @@ 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 Properties + /// /// Gets a list of child elements, all of which must be derived /// from FormatElementBase. /// - public IList FormatElements { get; private set; } + 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()) { } - 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 }) { } + + #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 + + /// + /// 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(); + } + + #endregion } } diff --git a/zaaReloaded2/Controller/Elements/CustomText.cs b/zaaReloaded2/Controller/Elements/CustomText.cs index cbe1576..afc1d22 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,11 +26,22 @@ 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 { - get { return String.Format("\"{0}\"", Content); } + get + { + if (String.IsNullOrEmpty(Content)) + { + return "Beliebiger Text"; + } + else + { + return String.Format("\"{0}\"", Content); + } + } } public override void Run(Formatter.Formatter formatter) @@ -43,5 +55,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 c47d4ad..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. @@ -32,7 +34,6 @@ namespace zaaReloaded2.Controller.Elements set { _content = value; - } } @@ -42,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 b1d258b..151237e 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,13 +32,23 @@ namespace zaaReloaded2.Controller.Elements /// to a Word document. /// [Serializable] - class Items : CustomText + public class Items : CustomText { #region ElementBase implementation public override string Label { - get { return Content; } + get + { + if (String.IsNullOrEmpty(Content)) + { + return "Laborparameter"; + } + else + { + return Content; + } + } } public override void Run(zaaReloaded2.Formatter.Formatter formatter) @@ -74,6 +85,13 @@ namespace zaaReloaded2.Controller.Elements } } + protected override ElementBase CreateInstance() + { + Items clone = new Items(); + clone.Content = Content; + return clone; + } + #endregion #region Constructors @@ -86,6 +104,13 @@ namespace zaaReloaded2.Controller.Elements Content = content; } + /// + /// Deserialization constructor. + /// + protected Items(SerializationInfo info, StreamingContext context) + : base(info, context) + { } + #endregion #region Private methods @@ -132,7 +157,6 @@ namespace zaaReloaded2.Controller.Elements { items.AddRange(CollectByName(formatter, itemName)); } - } return items; } @@ -148,9 +172,12 @@ 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); + // 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/Controller/Elements/NextColumn.cs b/zaaReloaded2/Controller/Elements/NextColumn.cs new file mode 100755 index 0000000..a59e427 --- /dev/null +++ b/zaaReloaded2/Controller/Elements/NextColumn.cs @@ -0,0 +1,69 @@ +/* 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.Runtime.Serialization; +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. + /// + [Serializable] + public class NextColumn : ControlElementBase, ISerializable + { + 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(); + } + + #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 eed0093..6f46976 100755 --- a/zaaReloaded2/Controller/Elements/SelectEachDay.cs +++ b/zaaReloaded2/Controller/Elements/SelectEachDay.cs @@ -18,15 +18,17 @@ 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 { - get { return "Jeden Tag auswählen"; } + get { return "Jeder Tag"; } } public override void Run(Formatter.Formatter formatter) @@ -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(); - } + /// + /// 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 f635948..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,11 +27,12 @@ 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 { - get { return "Ersten Tag auswählen"; } + get { return "Erster Tag"; } } public override void Run(Formatter.Formatter formatter) @@ -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(); - } + /// + /// 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 2f5107a..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,11 +28,12 @@ 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 { - get { return "Letzten Tag auswählen"; } + get { return "Letzter Tag"; } } public override void Run(Formatter.Formatter formatter) @@ -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(); - } + /// + /// 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 new file mode 100755 index 0000000..20ec064 --- /dev/null +++ b/zaaReloaded2/Controller/Elements/TwoColumns.cs @@ -0,0 +1,69 @@ +/* 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.Runtime.Serialization; +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. + /// + [Serializable] + public class TwoColumns : ControlElementBase, ISerializable + { + 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(); + } + + #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 5691988..561e561 100755 --- a/zaaReloaded2/Controller/Settings.cs +++ b/zaaReloaded2/Controller/Settings.cs @@ -21,6 +21,10 @@ using System.Linq; using System.Text; using zaaReloaded2.Formatter; using zaaReloaded2.Controller.Elements; +using System.IO; +using System.Text.RegularExpressions; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Soap; namespace zaaReloaded2.Controller { @@ -28,8 +32,43 @@ namespace zaaReloaded2.Controller /// Holds settings related to controlling laboratory output. /// [Serializable] - public class Settings : ICloneable + public class Settings : ICloneable, ISerializable { + #region Persistence + + /// + /// Deserializes a Settings object from SOAP XML that is read from a file. + /// + /// 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) + { + 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; + } + + /// + /// Serializes a Settings object to SOAP XML that is written to a file. + /// + /// File to write to. + public void SaveToFile(string fileName) + { + StreamWriter writer = new StreamWriter(fileName); + SoapFormatter formatter = new SoapFormatter(); + formatter.Serialize(writer.BaseStream, this); + } + + #endregion + #region Properties /// @@ -51,14 +90,14 @@ namespace zaaReloaded2.Controller /// Gets the unique ID of this Settings object. The unique /// ID is not included in deep-copying (cloning). /// - public Guid Uid { get; private set; } + public Guid Uid { get; internal set; } #endregion #region Constructors public Settings() - : this(string.Empty, new List()) + : this(string.Empty, null) { } public Settings(string name) @@ -87,8 +126,46 @@ namespace zaaReloaded2.Controller Uid = Guid.NewGuid(); Name = name; Elements = initialElements; + ReferenceStyle = Properties.Settings.Default.ReferenceStyle; + if (Elements == null) + { + Elements = new List(); + } } + /// + /// 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 @@ -117,5 +194,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 a8e55cb..7b009b3 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,67 @@ namespace zaaReloaded2.Controller /// different from zaaReloaded's Settings. /// [Serializable] - public class SettingsRepository : ApplicationSettingsBase + public class SettingsRepository : ISerializable { - #region Persistence + #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() { - return - zaaReloaded2.Properties.Settings.Default.SettingsRepository ?? - new SettingsRepository(); + string s = Properties.Settings.Default.SettingsRepository; + if (String.IsNullOrEmpty(s)) + { + return new SettingsRepository(); + } + 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; + } } + /// + /// 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() { - 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 +118,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 /// @@ -89,6 +166,13 @@ namespace zaaReloaded2.Controller { return SettingsList.FirstOrDefault(s => s.Uid == uid); } + + public static bool IsDefaultSettings(Settings settings) + { + return (settings.Name == Properties.Settings.Default.SettingsNameClinic + BUILTIN_LABEL || + settings.Name == Properties.Settings.Default.SettingsNameWard + BUILTIN_LABEL); + } + #endregion #region Private methods @@ -101,50 +185,40 @@ namespace zaaReloaded2.Controller { SettingsList.Clear(); - // Shortcut to assembly properties - zaaReloaded2.Properties.Settings def = zaaReloaded2.Properties.Settings.Default; + Assembly myAssembly = this.GetType().Assembly; + SoapFormatter deserializer = new SoapFormatter(); + Stream stream = myAssembly.GetManifestResourceStream("zaaReloaded2.Defaults.ward.zaaReloaded"); + Settings settings = deserializer.Deserialize(stream) as Settings; + settings.Name = Properties.Settings.Default.SettingsNameWard + BUILTIN_LABEL; + settings.Uid = Guid.Parse(DEFAULT_SETTINGS_1_UID); + SettingsList.Add(settings); - // TODO: May want to create deep copies of this list below - List defaultItems = new List() - { - new Items(def.DefaultItemsClinicalChem), - new Items(def.DefaultItemsInflammation), - new Items(def.DefaultItemsCardio), - new Items(def.DefaultItemsKidney), - new Items(def.DefaultItemsLiver), - new Items(def.DefaultItemsLipids), - new Items(def.DefaultItemsHematology), - new Items(def.DefaultItemsCoagulation), - new Items(def.DefaultItemsDrugs), - new Items(def.DefaultItemsCollectedUrine), - new Items(def.DefaultItemsSpotUrine), - new Items(def.DefaultItemsOther), - }; - - // Configure the settings for the ward - SettingsList.Add( - new Settings( - def.SettingsNameWard, - new List() - { - new SelectFirstDay(defaultItems), - new SelectLastDay(defaultItems) - } - ) - ); - - // Configure the settings for the outpatient clinic - SettingsList.Add( - new Settings( - def.SettingsNameClinic, - new List() - { - new SelectEachDay(defaultItems), - } - ) - ); + stream = myAssembly.GetManifestResourceStream("zaaReloaded2.Defaults.clinic.zaaReloaded"); + settings = deserializer.Deserialize(stream) as Settings; + settings.Name = Properties.Settings.Default.SettingsNameClinic + BUILTIN_LABEL; + settings.Uid = Guid.Parse(DEFAULT_SETTINGS_2_UID); + SettingsList.Add(settings); } #endregion + + #region Constants + + /// + /// Label that is appended to the default Settings' names. + /// + public const string BUILTIN_LABEL = " (eingebaut)"; + + /// + /// 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/Defaults/clinic.zaaReloaded b/zaaReloaded2/Defaults/clinic.zaaReloaded new file mode 100755 index 0000000..97fc7d3 --- /dev/null +++ b/zaaReloaded2/Defaults/clinic.zaaReloaded @@ -0,0 +1,158 @@ + + + +1 + +<_a>-97947944 +<_b>-28017 +<_c>17434 +<_d>191 +<_e>72 +<_f>82 +<_g>56 +<_h>48 +<_i>158 +<_j>105 +<_k>95 + +NepA neu +IfSpecialItem +1 + + + + +zaaReloaded2.Controller.Elements.SelectEachDay +4 +zaaReloaded2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6ec8d075a1ab1383 + + +1 +19 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +zaaReloaded2.Controller.Elements.Items +4 + + + +1 +Klinische Chemie: Na, K, Cl, Mg, Ca, P, CaxP, Alb, Prot, Haptoglobin, LDH, Glukose, Harnsäure + + +1 +Entzündung/Immunsystem: CRP, Pct, C3c, C4, Anti-DNAse B, ASL + + +1 +Kardiale Marker: CK, CKMB, Trop, NTproBNP + + +1 +Niere: Krea, Hst, eGFR (CKD-EPI) + + +1 +Sammelurin: SU-Proteinurie, SU-Alb, SU-CrCl, SU-HstCl, SU-GFR, SU-Natrium, SU-Zeit, SU-Volumen + + +1 +Spot-Urin: U-* + + +1 +Leber: GOT, GGT, GPT, AP, Bilirubin, CHE + + +1 +Blutfette: TG, Chol, LDL, HDL, Lp(a) + + +1 +Hämatologie: Hb, Hkt, Reti, Leu, Thr, MCV, HbA1c, Retikulozyten, Fragmentozyten + + +1 +Gerinnung: Quick, INR, PTT, Fibrinogen, ATIII, Anti-Xa + + +1 +Diff.-BB: Ery, Neu, Lym, Mon, Baso, Eos + + +1 +Hormone: iPTH, TSH + + +1 +Medikamente: TAC, CSA, SIR, Vancomycin, Gentamicin, Tobramicin + + +1 +Eisenhaushalt: Eisen, Ferritin, Transferrin, Tf.-Sätt. + + +1 +BGA: pH, Std.-Bic., BE + + +zaaReloaded2.Controller.Elements.CustomText +4 + + + +1 +Virologie (EDTA-Blut): CMV-PCR, BKV-PCR + + +1 +Nephrolog. Sediment: pH, Proteinurie, Ery /µl, Leu /µl, Plattenep. /µl, Bakt., Schleimfäden + + +1 +Autoantikörper: ANCA (IF), MPO-ANCA (ELISA), PR3-ANCA (ELISA), ANA (IF), AnDNA (ELISA), AnDNA (RIA) + + +1 +Hepatitis-Serologie: Anti-HBs, Anti-HBc + + + diff --git a/zaaReloaded2/Defaults/parameters.txt b/zaaReloaded2/Defaults/parameters.txt new file mode 100755 index 0000000..f20ee6b --- /dev/null +++ b/zaaReloaded2/Defaults/parameters.txt @@ -0,0 +1,117 @@ +# 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 +"aktuelles Bicarbonat" Bic BGA +"Albumin (PU)" Alb U +"Albumin (SU)" Alb SU +"Albumin (SU)/die" Alb SU +Albumin Alb S +Albumin/Creatinin (PU)" ACR U +"Alk. Phosphatase" AP S +Amylase Amylase S +"anorg. Phosphat" P 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 +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 +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 +Eisen Fe S +Eosinophile Eos E +"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 Protein S +"Gesamt-Eiweiss/Creatinin (PU)" TPCR U +GGT GGT S +"glomeruläre Filtrationsrate" GFR SU +"glomerul. Filtrationsr. (MDRD)" "eGFR (MDRD)" S --- X +"glomerul. Filtrationsr. CKD-EP" "eGFR (CKD-EPI)" S +"Glucose (U)" Glukose U +Glucose Glukose 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" 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 +Kalium K S +"Ketonkörper (U)" KK U +"Lactat Dehydrogenase" LDH S +"LDL - Cholesterin" LDL 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 +"Mittleres Plättchenvolumen" MPV E +Monozyten Mon E +"Natrium (SU)" Na SU +"Natrium (SU)/die" Na SU +Natrium Na S +Neutrophile Neu E +"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 +Sirolimus SIR 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 +Unreife Granulozyten" Gran E +"Urobilinogen (U)" Urobilinogen U +"Niedermol. Heparin (Anti-Xa)" Anti-Xa Z +"Thromboplastinzeit n. Quick" Quick Z +PTT PTT Z +"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 +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/Defaults/units.txt similarity index 62% rename from zaaReloaded2/Thesaurus/Defaults/units.txt rename to zaaReloaded2/Defaults/units.txt index 48a907b..1e8a350 100755 --- a/zaaReloaded2/Thesaurus/Defaults/units.txt +++ b/zaaReloaded2/Defaults/units.txt @@ -2,6 +2,9 @@ # ============== ==================== # 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 \ No newline at end of file +mmol/l mM +n*1000/µl /nl +n*10E6/µl /fl +Bak/µl /µl diff --git a/zaaReloaded2/Defaults/ward.zaaReloaded b/zaaReloaded2/Defaults/ward.zaaReloaded new file mode 100755 index 0000000..fe9677f --- /dev/null +++ b/zaaReloaded2/Defaults/ward.zaaReloaded @@ -0,0 +1,208 @@ + + + +1 + +<_a>-1732917544 +<_b>21822 +<_c>16721 +<_d>134 +<_e>250 +<_f>231 +<_g>106 +<_h>44 +<_i>93 +<_j>252 +<_k>234 + +Station neu +IfSpecialOrAbnormal +4 + + + + + + + + + + +zaaReloaded2.Controller.Elements.TwoColumns +4 +zaaReloaded2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6ec8d075a1ab1383 + + +1 +0 + + +zaaReloaded2.Controller.Elements.SelectFirstDay +4 + + + +1 +17 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +zaaReloaded2.Controller.Elements.NextColumn +4 + + + +1 +0 + + +zaaReloaded2.Controller.Elements.SelectLastDay +4 + + + +1 +17 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +zaaReloaded2.Controller.Elements.Items +4 + + + +1 +Klinische Chemie: Na, K, Cl, Mg, Ca, P, CaxP, Alb, Prot, Haptoglobin, LDH, Glukose, Harnsäure + + +1 +Entzündung/Immunsystem: CRP, Pct, C3c, C4, Anti-DNAse B, ASL + + +1 +Kardiale Marker: CK, CKMB, Trop, NTproBNP + + +1 +Niere: Krea, Hst, eGFR (CKD-EPI) + + +1 +Sammelurin: SU-Proteinurie, SU-Alb, SU-CrCl, SU-HstCl, SU-GFR, SU-Natrium, SU-Zeit, SU-Volumen + + +1 +Spot-Urin: U-* + + +1 +Leber: GOT, GGT, GPT, AP, Bilirubin, CHE + + +1 +Blutfette: TG, Chol, LDL, HDL, Lp(a) + + +1 +Hämatologie: Hb, Hkt, Reti, Leu, Thr, MCV, HbA1c, Retikulozyten, Fragmentozyten + + +1 +Gerinnung: Quick, INR, PTT, Fibrinogen, ATIII, Anti-Xa + + +1 +Diff.-BB: Ery, Neu, Lym, Mon, Baso, Eos + + +1 +Hormone: iPTH, TSH + + +1 +Medikamente: TAC, CSA, SIR, Vancomycin, Gentamicin, Tobramicin + + +1 +Eisenhaushalt: Eisen, Ferritin, Transferrin, Tf.-Sätt. + + +1 +BGA: pH, Std.-Bic., BE + + +1 +Hepatitis-Serologie: Anti-HBs, Anti-HBc + + +1 +Weitere Werte: * + + + diff --git a/zaaReloaded2/Demo/Demo.docx b/zaaReloaded2/Demo/Demo.docx index 6164d7b..ac0714c 100755 Binary files a/zaaReloaded2/Demo/Demo.docx and b/zaaReloaded2/Demo/Demo.docx differ diff --git a/zaaReloaded2/ExceptionHandler/ExceptionDetailView.xaml b/zaaReloaded2/ExceptionHandler/ExceptionDetailView.xaml index 88956db..f69b532 100755 --- a/zaaReloaded2/ExceptionHandler/ExceptionDetailView.xaml +++ b/zaaReloaded2/ExceptionHandler/ExceptionDetailView.xaml @@ -25,7 +25,8 @@ xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" xmlns:settings="clr-namespace:Bovender.Mvvm.Views.Settings;assembly=Bovender" xmlns:converter="clr-namespace:Bovender.Mvvm.Converters;assembly=Bovender" - SizeToContent="Height" Width="700" + SizeToContent="Height" Width="700" ShowInTaskbar="False" + WindowStyle="ToolWindow" Topmost="True" ResizeMode="CanResizeWithGrip" x:Name="exceptionDetailView" Title="Technische Fehlerdetails" settings:WindowState.Save="True" diff --git a/zaaReloaded2/ExceptionHandler/ExceptionView.xaml b/zaaReloaded2/ExceptionHandler/ExceptionView.xaml index 64e6e0e..7154fcd 100755 --- a/zaaReloaded2/ExceptionHandler/ExceptionView.xaml +++ b/zaaReloaded2/ExceptionHandler/ExceptionView.xaml @@ -27,6 +27,7 @@ xmlns:actions="clr-namespace:Bovender.Mvvm.Actions;assembly=Bovender" xmlns:settings="clr-namespace:Bovender.Mvvm.Views.Settings;assembly=Bovender" SizeToContent="WidthAndHeight" ResizeMode="CanResizeWithGrip" + WindowStyle="ToolWindow" ShowInTaskbar="False" Topmost="True" WindowStartupLocation="CenterScreen" Title="zaaReloaded2: Fehler!" settings:WindowState.Save="True" diff --git a/zaaReloaded2/ExceptionHandler/ExceptionViewModel.cs b/zaaReloaded2/ExceptionHandler/ExceptionViewModel.cs index bb16451..02b9ae5 100755 --- a/zaaReloaded2/ExceptionHandler/ExceptionViewModel.cs +++ b/zaaReloaded2/ExceptionHandler/ExceptionViewModel.cs @@ -32,8 +32,7 @@ namespace zaaReloaded2.ExceptionHandler { get { - // TODO: Get Word version - return "n/a"; + return Globals.ThisAddIn.Application.Build; } } @@ -84,7 +83,7 @@ namespace zaaReloaded2.ExceptionHandler protected override string DevPath() { - return @"x:\Code\zaaReloaded2\"; + return @"x:\Code\zaaReloaded2\zaaReloaded2\"; } #endregion diff --git a/zaaReloaded2/Formatter/DocumentWriter.cs b/zaaReloaded2/Formatter/DocumentWriter.cs index 6953f92..2c11113 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,14 @@ namespace zaaReloaded2.Formatter if (Document != null) { - Document.Range().Text = _buffer.ToString(); + Selection s = Document.ActiveWindow.Selection; + s.ClearCharacterDirectFormatting(); + s.ClearParagraphDirectFormatting(); + if (!string.IsNullOrEmpty(ParagraphStyle)) + { + s.set_Style(ParagraphStyle); + } + s.Range.Text = _buffer.ToString(); } if (Parent != null) { diff --git a/zaaReloaded2/Formatter/Formatter.cs b/zaaReloaded2/Formatter/Formatter.cs index 86fae29..83c2d02 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,10 @@ namespace zaaReloaded2.Formatter /// current position of the cursor). public void Run() { + 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) { @@ -195,7 +205,68 @@ namespace zaaReloaded2.Formatter /// FormatElementBase children to process. public void ProcessAllTimePoints(ControlElementBase controlElement) { - ProcessAllTimePoints(controlElement.FormatElements); + 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. + /// + 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 @@ -259,7 +330,7 @@ namespace zaaReloaded2.Formatter throw new ArgumentNullException("workingTimePoints"); WorkingTimePoints = workingTimePoints; - ProcessElements(controlElement.FormatElements); + ProcessElements(controlElement.Children); if (_primaryBuffer.HasBufferedText) { _primaryBuffer.Prepend( @@ -286,6 +357,7 @@ namespace zaaReloaded2.Formatter Laboratory _laboratory; DocumentWriter _primaryBuffer; DocumentWriter _secondaryBuffer; + Table _table; #endregion } 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/Formatter/ReferenceStyle.cs b/zaaReloaded2/Formatter/ReferenceStyle.cs index 0e6c8f7..6e19a3f 100755 --- a/zaaReloaded2/Formatter/ReferenceStyle.cs +++ b/zaaReloaded2/Formatter/ReferenceStyle.cs @@ -26,37 +26,36 @@ namespace zaaReloaded2.Formatter /// /// Describes the style of normal range references. /// - [Serializable] public enum ReferenceStyle { /// /// 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/Formatter/TimePointFormatter.cs b/zaaReloaded2/Formatter/TimePointFormatter.cs index 6248775..da8b41e 100755 --- a/zaaReloaded2/Formatter/TimePointFormatter.cs +++ b/zaaReloaded2/Formatter/TimePointFormatter.cs @@ -28,6 +28,55 @@ namespace zaaReloaded2.Formatter /// public class TimePointFormatter { + #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) + { + 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) + { + if (dateTime != _nullTimeStamp) + { + return FormatHeader(dateTime.ToString()); + } + else + { + return String.Empty; + } + } + + #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 +126,7 @@ namespace zaaReloaded2.Formatter /// public string GetDateHeader() { - return FormatHeader(TimeStamp.ToShortDateString()); + return DateHeader(TimeStamp); } @@ -88,21 +137,14 @@ namespace zaaReloaded2.Formatter /// public string GetDateAndTimeHeader() { - return FormatHeader(TimeStamp.ToString()); + return DateAndTimeHeader(TimeStamp); } #endregion - #region Private methods + #region Fields - string FormatHeader(string text) - { - return String.Format("{0}{1}:{2}", - Environment.NewLine, - text, - Environment.NewLine - ); - } + static readonly DateTime _nullTimeStamp = new DateTime(); #endregion } diff --git a/zaaReloaded2/Icons/down.png b/zaaReloaded2/Icons/down.png new file mode 100644 index 0000000..c7faaf3 Binary files /dev/null and b/zaaReloaded2/Icons/down.png differ diff --git a/zaaReloaded2/Icons/duplicate.png b/zaaReloaded2/Icons/duplicate.png new file mode 100644 index 0000000..d92993e Binary files /dev/null and b/zaaReloaded2/Icons/duplicate.png differ diff --git a/zaaReloaded2/Icons/exit.png b/zaaReloaded2/Icons/exit.png new file mode 100644 index 0000000..8668326 Binary files /dev/null and b/zaaReloaded2/Icons/exit.png differ diff --git a/zaaReloaded2/Icons/export.png b/zaaReloaded2/Icons/export.png new file mode 100644 index 0000000..559f57d Binary files /dev/null and b/zaaReloaded2/Icons/export.png differ diff --git a/zaaReloaded2/Icons/gear.png b/zaaReloaded2/Icons/gear.png new file mode 100644 index 0000000..e89a5de Binary files /dev/null and b/zaaReloaded2/Icons/gear.png differ diff --git a/zaaReloaded2/Icons/import.png b/zaaReloaded2/Icons/import.png new file mode 100644 index 0000000..5a03f66 Binary files /dev/null and b/zaaReloaded2/Icons/import.png differ diff --git a/zaaReloaded2/Icons/minus.png b/zaaReloaded2/Icons/minus.png new file mode 100644 index 0000000..79b06ce Binary files /dev/null and b/zaaReloaded2/Icons/minus.png differ diff --git a/zaaReloaded2/Icons/pen.png b/zaaReloaded2/Icons/pen.png new file mode 100644 index 0000000..945c7c6 Binary files /dev/null and b/zaaReloaded2/Icons/pen.png differ diff --git a/zaaReloaded2/Icons/plus-child.png b/zaaReloaded2/Icons/plus-child.png new file mode 100644 index 0000000..bccea0b Binary files /dev/null and b/zaaReloaded2/Icons/plus-child.png differ diff --git a/zaaReloaded2/Icons/plus.png b/zaaReloaded2/Icons/plus.png new file mode 100644 index 0000000..cd65550 Binary files /dev/null and b/zaaReloaded2/Icons/plus.png differ diff --git a/zaaReloaded2/Icons/reset.png b/zaaReloaded2/Icons/reset.png new file mode 100644 index 0000000..46656de Binary files /dev/null and b/zaaReloaded2/Icons/reset.png differ diff --git a/zaaReloaded2/Icons/up.png b/zaaReloaded2/Icons/up.png new file mode 100644 index 0000000..22ff0a0 Binary files /dev/null and b/zaaReloaded2/Icons/up.png differ 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/LaurisItem.cs b/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs index 244c73f..71d7e19 100755 --- a/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs +++ b/zaaReloaded2/Importer/ZaaImporter/LaurisItem.cs @@ -74,7 +74,9 @@ namespace zaaReloaded2.Importer.ZaaImporter if (parameterDictionary != null) { Name = parameterDictionary.GetCanonicalName(OriginalName); - AlwaysPrintLimits = parameterDictionary.GetForceReferenceDisplay(Name); + AlwaysPrintLimits = parameterDictionary.GetForceReferenceDisplay(OriginalName); + IsBlacklisted = parameterDictionary.GetIsBlacklisted(OriginalName); + Material = parameterDictionary.GetMaterial(OriginalName, Material); } if (unitDictionary != null) { @@ -164,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/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..18c751d 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 @@ -83,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. @@ -101,7 +111,7 @@ namespace zaaReloaded2.Importer.ZaaImporter { Paragraphs = value.Split( new string[] { Environment.NewLine }, - StringSplitOptions.None); + StringSplitOptions.None).ToList(); } } } @@ -128,19 +138,41 @@ namespace zaaReloaded2.Importer.ZaaImporter { } public LaurisTimePoint( - string[] paragraphs, + IList paragraphs, Parameters parameterDictionary, Units unitDictionary) + : this(parameterDictionary, unitDictionary) + { + Paragraphs = paragraphs; + } + + public LaurisTimePoint(IList paragraphs) + : this(paragraphs, null, null) + { + } + + public LaurisTimePoint( + Parameters parameterDictionary, + Units unitDictionary) : this() { _parameterDictionary = parameterDictionary; _unitDictionary = unitDictionary; - Paragraphs = paragraphs; } - public LaurisTimePoint(string[] 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 @@ -151,59 +183,43 @@ namespace zaaReloaded2.Importer.ZaaImporter /// 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 +237,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..aea8b8a 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. @@ -59,45 +67,45 @@ 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); - 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++; - } - - // TODO: Find an alternative to returning in the middle of the method. - if (i >= numParagraphs) return; - - if (LaurisTimePoint.IsTimeStampLine(paragraphs[i])) - { - // Remember the time stamp line's index - start = i; - - // Seek the next time stamp line - while (i + 1 < numParagraphs - && !LaurisTimePoint.IsTimeStampLine(paragraphs[i + 1])) + 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)) { - i++; + timePoint = existing as LaurisTimePoint; + } + else + { + Laboratory.AddTimePoint(timePoint); } } - - Laboratory.AddTimePoint( - new LaurisTimePoint( - paragraphs.Slice(start, i - start + 1), - _parameters, - _units - ) - ); + // 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)) + { + if (timePoint == null) + { + timePoint = new LaurisTimePoint(_parameters, _units); + Laboratory.AddTimePoint(timePoint); + } + timePoint.AddParagraph(paragraph); + } } } 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/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 } } 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/Properties/Settings.Designer.cs b/zaaReloaded2/Properties/Settings.Designer.cs index 902836a..bf379f7 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; @@ -245,5 +246,62 @@ 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"])); + } + } + + [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"])); + } + } + + [global::System.Configuration.ApplicationScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public int SerializationVersion { + get { + 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 0fab6e4..45f429b 100755 --- a/zaaReloaded2/Properties/Settings.settings +++ b/zaaReloaded2/Properties/Settings.settings @@ -2,7 +2,7 @@ - + @@ -74,5 +74,23 @@ 00000000-0000-0000-0000-000000000000 + + zaaReloaded2-Laborwerte + + + IfSpecialOrAbnormal + + + Ausgabe-Elemente + + + Steuerungs-Elemente + + + 1 + + + + \ No newline at end of file diff --git a/zaaReloaded2/Ribbon.cs b/zaaReloaded2/Ribbon.cs index f5428d8..958e328 100755 --- a/zaaReloaded2/Ribbon.cs +++ b/zaaReloaded2/Ribbon.cs @@ -200,10 +200,7 @@ namespace zaaReloaded2 Settings settings = settingsVM.RevealModelObject() as Settings; DoFormat(settings); Properties.Settings.Default.LastSettings = settings.Uid; - }; - vm.RequestCloseView += (sender, args) => - { - repository.Save(); + Properties.Settings.Default.Save(); }; vm.InjectInto().ShowDialog(); } 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 deleted file mode 100755 index 20cf176..0000000 --- a/zaaReloaded2/Thesaurus/Defaults/parameters.txt +++ /dev/null @@ -1,114 +0,0 @@ -# LAURIS-NAME "KANONISCHER NAME" MATERIAL "IMMER REFERENZBEREICH" -# =========== ================== ======== ======================= -"Übergangsepithelien (U)" Übergangsep. U -"a1-Microglobulin (SU)" a1-Microglobulin SU -"a1-Microglobulin (SU)/die" a1-Microglobulin SU -"aktuelles Bicarbonat" Bic BGA -"Albumin (PU)" Alb U -"Albumin (SU)" Alb SU -"Albumin (SU)/die" Alb SU -Albumin Alb S -Albumin/Creatinin (PU)" ACR U -"Alk. Phosphatase" AP S -Amylase Amylase S -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 -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 -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 -Eisen Fe S -Eosinophile Eos E -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 Protein S -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 -Glucose Glukose 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" 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 -Kalium K S -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 -MCHC MCHC E -MCV MCV E -Mittleres Plättchenvolumen MPV E -Monozyten Mon E -Natrium (SU)" Na SU -Natrium (SU)/die" Na SU -Natrium Na S -Neutrophile Neu E -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 -Sirolimus SIR 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 -Unreife Granulozyten" Gran E -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 -Anti-Streptolysin ASL S -PTH intakt" iPTH S -TSH TSH S -HAPTOGLOBIN Haptoglobin S -FRAGMENTOZYTEN Fragmentozyten E diff --git a/zaaReloaded2/Thesaurus/Parameters.cs b/zaaReloaded2/Thesaurus/Parameters.cs index 18ebfbc..7600e85 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; + } } } @@ -95,6 +102,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 @@ -105,7 +124,7 @@ namespace zaaReloaded2.Thesaurus protected override System.IO.Stream GetDefaultStream() { return Assembly.GetExecutingAssembly().GetManifestResourceStream( - "zaaReloaded2.Thesaurus.Defaults.parameters.txt"); + "zaaReloaded2.Defaults.parameters.txt"); } /// @@ -118,6 +137,5 @@ namespace zaaReloaded2.Thesaurus } #endregion - } } 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 } diff --git a/zaaReloaded2/Thesaurus/Units.cs b/zaaReloaded2/Thesaurus/Units.cs index 5f08df3..71f321d 100755 --- a/zaaReloaded2/Thesaurus/Units.cs +++ b/zaaReloaded2/Thesaurus/Units.cs @@ -72,7 +72,7 @@ namespace zaaReloaded2.Thesaurus protected override System.IO.Stream GetDefaultStream() { return Assembly.GetExecutingAssembly().GetManifestResourceStream( - "zaaReloaded2.Thesaurus.Defaults.units.txt"); + "zaaReloaded2.Defaults.units.txt"); } /// 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 diff --git a/zaaReloaded2/ViewModels/ControlElementViewModel.cs b/zaaReloaded2/ViewModels/ControlElementViewModel.cs index 58b0421..fa55d8f 100755 --- a/zaaReloaded2/ViewModels/ControlElementViewModel.cs +++ b/zaaReloaded2/ViewModels/ControlElementViewModel.cs @@ -35,6 +35,20 @@ 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; + } + } + + public override string ToolTip { get { return Element.Label; } } + #endregion #region Constructors @@ -47,7 +61,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 +69,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 @@ -62,8 +92,39 @@ 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); + e.Children.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).Children + .Remove(viewModel.RevealModelObject() as FormatElementBase); + viewModel.Parent = null; + } + + #endregion + + #region Overrides + + 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; } #endregion diff --git a/zaaReloaded2/ViewModels/ElementPickerViewModel.cs b/zaaReloaded2/ViewModels/ElementPickerViewModel.cs index 6b2ecbb..f5f48e3 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 { @@ -96,25 +98,43 @@ namespace zaaReloaded2.ViewModels Categories = new List(); if (allowControlElements) { - Categories.Add( + AddCategory( new CategoryViewModel( - "Kontroll-Elemente", + Properties.Settings.Default.ControlElementLabel, new Collection() { CreateControlElementViewModel(new SelectFirstDay()), CreateControlElementViewModel(new SelectLastDay()), - CreateControlElementViewModel(new SelectEachDay()) + CreateControlElementViewModel(new SelectEachDay()), + CreateControlElementViewModel(new TwoColumns()), + CreateControlElementViewModel(new NextColumn()), } ) ); } - Categories.Add( + 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), + CreateControlElementViewModel(new TwoColumns()), + CreateControlElementViewModel(new NextColumn()), } ) ); @@ -124,6 +144,26 @@ namespace zaaReloaded2.ViewModels #region Private methods + void CreateFormatElementViewModels() + { + AddCategory( + new CategoryViewModel( + Properties.Settings.Default.FormatElementLabel, + new Collection() + { + CreateFormatElementViewModel(new Items()), + CreateFormatElementViewModel(new CustomText()), + } + ) + ); + } + + 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 @@ -137,28 +177,39 @@ 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 /// is necessary because format elements do not have a canonical label. /// - ViewModelBase CreateFormatElementViewModel(string name, FormatElementBase element) + ViewModelBase CreateFormatElementViewModel(FormatElementBase element) { FormatElementViewModel vm = new FormatElementViewModel(element); - vm.DisplayString = name; vm.PropertyChanged += ElementViewModel_PropertyChanged; return vm; } 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; } } @@ -167,12 +218,13 @@ namespace zaaReloaded2.ViewModels if (CanChooseElement()) { ElementChosenMessage.Send(new ViewModelMessageContent(Selected)); + DoCloseView(); } } bool CanChooseElement() { - return Selected != null; + return Selected != null && Selected.IsSelected == true; } #endregion diff --git a/zaaReloaded2/ViewModels/ElementViewModel.cs b/zaaReloaded2/ViewModels/ElementViewModel.cs index a14a865..a6757f1 100755 --- a/zaaReloaded2/ViewModels/ElementViewModel.cs +++ b/zaaReloaded2/ViewModels/ElementViewModel.cs @@ -26,19 +26,28 @@ using System.Collections.ObjectModel; namespace zaaReloaded2.ViewModels { - public abstract class ElementViewModel : ViewModelBase + public abstract class ElementViewModel : ViewModelBase, ICloneable { #region Properties /// - /// Gets the label of the wrapped element. + /// Returns the wrapped Element's label or an explicitly + /// set display string. /// - public virtual string Label + public override string DisplayString { - [DebuggerStepThrough] - get + get { return Element.Label; } + } + + public abstract string ToolTip { get; } + + public bool IsExpanded + { + get { return _isExpanded; } + set { - return Element.Label; + _isExpanded = value; + OnPropertyChanged("IsExpanded"); } } @@ -69,5 +78,17 @@ namespace zaaReloaded2.ViewModels } #endregion + + #region Implementation of ICloneable + + public abstract object Clone(); + + #endregion + + #region Fields + + bool _isExpanded; + + #endregion } } diff --git a/zaaReloaded2/ViewModels/FormatElementViewModel.cs b/zaaReloaded2/ViewModels/FormatElementViewModel.cs index 5dbe16a..61ae81b 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,14 +32,40 @@ namespace zaaReloaded2.ViewModels public string Content { [DebuggerStepThrough] - get { return ((FormatElementBase)Element).Content; } + get { return _content; } set { - ((FormatElementBase)Element).Content = value; + _content = value; OnPropertyChanged("Content"); } } + /// + /// Gets or sets the parent ControlElementViewModel, if + /// there is any. + /// + public ControlElementViewModel Parent { get; set; } + + public override string ToolTip { get { return Element.Label; } } + + #endregion + + #region Commands + + public DelegatingCommand SaveCommand + { + get + { + if (_saveCommand == null) + { + _saveCommand = new DelegatingCommand( + param => DoSave(), + param => CanSave()); + } + return _saveCommand; + } + } + #endregion #region Constructors @@ -49,8 +76,46 @@ namespace zaaReloaded2.ViewModels : this() { Element = formatElement; + Content = ((FormatElementBase)Element).Content; } #endregion + + #region Overrides + + public override object Clone() + { + FormatElementViewModel clone = new FormatElementViewModel(); + clone.Parent = Parent; + clone.Element = Element; + return clone; + } + + #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 _content; + + #endregion } } 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 f04fbba..2c1b155 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 { @@ -33,7 +35,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 @@ -120,6 +136,34 @@ 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 @@ -172,6 +216,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 @@ -179,39 +271,50 @@ namespace zaaReloaded2.ViewModels public SettingsRepositoryViewModel(SettingsRepository repository) { _repository = repository; - SettingsList = new ObservableCollection(); - foreach (Settings s in repository.SettingsList) + BuildSettingsList(); + RequestCloseView += (sender, args) => { - SettingsViewModel vm = new SettingsViewModel(s); - AddSettingsViewModel(vm); - } + _repository.Store(); + }; } #endregion #region Private methods + void BuildSettingsList() + { + SettingsList = new ObservableCollection(); + foreach (Settings s in _repository.SettingsList) + { + SettingsViewModel vm = new SettingsViewModel(s); + AddSettingsViewModel(vm); + } + OnPropertyChanged("SettingsList"); + } + void DoEditSettings() { 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)); + CloseViewCommand.Execute(null); } bool CanUseSettings() { - return Selected != null; + return LastSelected != null && LastSelected.IsSelected; } void DoAddSettings() @@ -219,13 +322,14 @@ 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() { - return Selected != null && !IsDefaultSettings(); + return LastSelected != null && LastSelected.IsSelected && !IsDefaultSettings(); } void DoDeleteSettings() @@ -233,7 +337,7 @@ namespace zaaReloaded2.ViewModels if (CanDeleteSettings()) { ConfirmDeleteSettingsMessage.Send( - new ViewModelMessageContent(Selected), + new ViewModelMessageContent(LastSelected), param => ConfirmDeleteSettings(param)); } } @@ -259,19 +363,25 @@ namespace zaaReloaded2.ViewModels { if (content.Confirmed) { - + _repository = new SettingsRepository(); + BuildSettingsList(); } } void DoCopySettings() { - if (Selected != null) + if (LastSelected != null) { - SettingsViewModel copy = Selected.Clone() as SettingsViewModel; + SettingsViewModel copy = LastSelected.Clone() as SettingsViewModel; + if (IsDefaultSettings()) + { + copy.Name = copy.Name.Replace(SettingsRepository.BUILTIN_LABEL, String.Empty); + } _repository.SettingsList.Add(copy.RevealModelObject() as Settings); AddSettingsViewModel(copy); - Selected.IsSelected = false; + LastSelected.IsSelected = false; copy.IsSelected = true; + EditSettingsMessage.Send(new ViewModelMessageContent(copy)); } } @@ -280,7 +390,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 +400,9 @@ 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); + return SettingsRepository.IsDefaultSettings(LastSelected.RevealModelObject() as Settings); } else { @@ -313,6 +421,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 @@ -333,10 +521,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/ViewModels/SettingsViewModel.cs b/zaaReloaded2/ViewModels/SettingsViewModel.cs index b16f5d9..db33e3f 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 { @@ -76,7 +77,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 @@ -95,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; } @@ -113,7 +128,7 @@ namespace zaaReloaded2.ViewModels : base() { _settings = settings; - Elements = new List(); + Elements = new ObservableCollection(); foreach (ElementBase element in settings.Elements) { ElementViewModel vm; @@ -126,6 +141,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; } } @@ -167,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 @@ -198,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 @@ -226,6 +280,34 @@ namespace zaaReloaded2.ViewModels } } + public DelegatingCommand MoveElementUpCommand + { + get + { + if (_moveElementUpCommand == null) + { + _moveElementUpCommand = new DelegatingCommand( + param => DoMoveElementUp(), + param => CanMoveElementUp()); + } + return _moveElementUpCommand; + } + } + + public DelegatingCommand MoveElementDownCommand + { + get + { + if (_moveElementDownCommand == null) + { + _moveElementDownCommand = new DelegatingCommand( + param => DoMoveElementDown(), + param => CanMoveElementDown()); + } + return _moveElementDownCommand; + } + } + #endregion #region Public methods @@ -260,11 +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; + if (newVM is FormatElementViewModel) DoEditElement(); }; AddElementMessage.Send(new ViewModelMessageContent(picker)); } @@ -279,7 +374,9 @@ 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); + newVM.IsSelected = true; + DoEditElement(); }; AddChildElementMessage.Send(new ViewModelMessageContent(picker)); } @@ -287,30 +384,335 @@ namespace zaaReloaded2.ViewModels bool CanAddChildElement() { - return SelectedElement is ControlElementViewModel; + return LastSelectedElement is ControlElementViewModel && + ((ControlElementViewModel)LastSelectedElement).CanHaveChildren; } - void DoDeleteElement() { } + 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. + int index = Elements.IndexOf(LastSelectedElement); + ElementViewModel newVM = args.Content.ViewModel as ElementViewModel; + 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; + }; + ChangeControlElementMessage.Send( + new ViewModelMessageContent(picker)); + } + else + { + EditElementMessage.Send(new ViewModelMessageContent(LastSelectedElement)); + } + } + } - bool CanDeleteElement() { return _selectedElement != null; } - - void DoCopyElement() { } - - bool CanCopyElement() { return _selectedElement != null; } + bool CanEditElement() + { + return LastSelectedElement != null && LastSelectedElement.IsSelected; + } /// - /// Sets or unsets the SelectedElement property whenever the IsSelected - /// property of an ElementViewModel changes. + /// 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 (IsTopLevelElement()) + { + // First level of the hierarchy + int index = Elements.IndexOf(LastSelectedElement); + Elements.RemoveAt(index); + _settings.Elements.RemoveAt(index); + if (index == Elements.Count) index--; + LastSelectedElement = null; + if (Elements.Count > 0) Elements[index].IsSelected = true; + } + else + { + // Second level of the hierarchy + FormatElementViewModel formatVM = LastSelectedElement as FormatElementViewModel; + ControlElementViewModel parent = formatVM.Parent; + int index = parent.Elements.IndexOf(formatVM); + parent.RemoveChildElement(formatVM); + if (index == parent.Elements.Count) index--; + LastSelectedElement = null; + if (parent.Elements.Count > 0) parent.Elements[index].IsSelected = true; + } + } + } + + bool CanDeleteElement() { return LastSelectedElement != null && LastSelectedElement.IsSelected; } + + void DoCopyElement() + { + if (CanCopyElement()) + { + if (IsTopLevelElement()) + { + ElementViewModel newControlVM = LastSelectedElement.Clone() as ElementViewModel; + AddElementViewModel(newControlVM); + newControlVM.IsSelected = true; + } + else + { + FormatElementViewModel originalVM = LastSelectedElement as FormatElementViewModel; + FormatElementViewModel newFormatVM = originalVM.Clone() as FormatElementViewModel; + originalVM.Parent.AddChildElement(newFormatVM); + newFormatVM.IsSelected = true; + } + } + } + + bool CanCopyElement() { return LastSelectedElement != null && LastSelectedElement.IsSelected; } + + void DoMoveElementUp() + { + if (CanMoveElementUp()) + { + // We need to get a hold of the LastSelectedElement because a TreeView + // might reset the selection when we move elements around. + ElementViewModel lastSelectedElement = LastSelectedElement; + // Top-level elements are either control elements or format elements; + // child elements on the second level however are always format elements + // and must be treated differently. + if (IsTopLevelElement()) + { + int index = Elements.IndexOf(lastSelectedElement); + if (lastSelectedElement is ControlElementViewModel || + Elements[index - 1] is FormatElementViewModel || + !((ControlElementViewModel)Elements[index - 1]).CanHaveChildren + ) + { + // Simple case: top-level control element -- just move it up; + // if the selected element is a format element and the element + // above it is a format element too, just move it up as well. + // If the element above the selected element is a control element, + // but cannot have children, move the selected element up as well. + Elements.RemoveAt(index); + Elements.Insert(index - 1, lastSelectedElement); + _settings.Elements.RemoveAt(index); + _settings.Elements.Insert( + index - 1, + lastSelectedElement.RevealModelObject() as ElementBase); + } + else + { + // If we get here, the selected element is a format element + // and the element above it is a control element that can + // have child elements, i.e. the selected element is demoted + // to a child element of the control element above it. + ControlElementViewModel controlElementAbove = + Elements[index - 1] as ControlElementViewModel; + Elements.RemoveAt(index); + controlElementAbove.IsExpanded = true; + controlElementAbove.AddChildElement( + lastSelectedElement as FormatElementViewModel); + FormatElementBase model = lastSelectedElement.RevealModelObject() as FormatElementBase; + ControlElementBase modelAbove = _settings.Elements[index - 1] as ControlElementBase; + _settings.Elements.RemoveAt(index); + } + } + else + { + // The selected element is a child element. + // If it is at the top of the child elements list, promote it + // to a top-level element; if not, just move it up in the + // child elements list. + FormatElementViewModel selected = lastSelectedElement as FormatElementViewModel; + int index = selected.Parent.Elements.IndexOf(selected); + if (index == 0) + { + // Promote the element from the top of the children list + // to a top-level element above its parent. + int parentIndex = Elements.IndexOf(selected.Parent); + selected.Parent.Elements.RemoveAt(0); + Elements.Insert(parentIndex, selected); + FormatElementBase model = selected.RevealModelObject() as FormatElementBase; + ControlElementBase parentModel = selected.Parent.RevealModelObject() as ControlElementBase; + parentModel.Children.RemoveAt(0); + _settings.Elements.Insert(parentIndex, model); + selected.Parent = null; + } + else + { + selected.Parent.Elements.Move(index, index - 1); + ControlElementBase parentModel = selected.Parent.RevealModelObject() as ControlElementBase; + FormatElementBase selectedModel = parentModel.Children[index]; + parentModel.Children.RemoveAt(index); + parentModel.Children.Insert(index - 1, selectedModel); + } + } + // Select the last selected element again. + lastSelectedElement.IsSelected = true; + } + } + + bool CanMoveElementUp() + { + if (IsTopLevelElement()) + { + return Elements.IndexOf(LastSelectedElement) > 0; + } + else + { + // If the selected element is a child element, it can always be moved + // up before the parent element. + return LastSelectedElement != null && LastSelectedElement.IsSelected; + } + } + + void DoMoveElementDown() + { + if (CanMoveElementDown()) + { + // We need to get a hold of the LastSelectedElement because a TreeView + // might reset the selection when we move elements around. + ElementViewModel lastSelectedElement = LastSelectedElement; + // Top-level elements are either control elements or format elements; + // child elements on the second level however are always format elements + // and must be treated differently. + if (IsTopLevelElement()) + { + int index = Elements.IndexOf(lastSelectedElement); + if (lastSelectedElement is ControlElementViewModel || + Elements[index + 1 ] is FormatElementViewModel || + !((ControlElementViewModel)Elements[index + 1]).CanHaveChildren + ) + { + // Simple case: top-level control element -- just move it down; + // if the selected element is a format element and the element + // below it is a format element too, just move it down as well. + // If the element below the selected element is a control element, + // but cannot have children, move the selected element down as well. + Elements.RemoveAt(index); + Elements.Insert(index + 1, lastSelectedElement); + _settings.Elements.RemoveAt(index); + _settings.Elements.Insert( + index + 1, + lastSelectedElement.RevealModelObject() as ElementBase); + } + else + { + // If we get here, the selected element is a format element + // and the element below it is a control element that can + // have child elements, i.e. the selected element is demoted + // to a child element of the control element below it. + ControlElementViewModel controlElementBelow = + Elements[index + 1] as ControlElementViewModel; + Elements.RemoveAt(index); + controlElementBelow.IsExpanded = true; + controlElementBelow.Elements.Insert( + 0, + lastSelectedElement as FormatElementViewModel); + ((FormatElementViewModel)lastSelectedElement).Parent = controlElementBelow; + FormatElementBase model = lastSelectedElement.RevealModelObject() as FormatElementBase; + ControlElementBase modelBelow = _settings.Elements[index + 1] as ControlElementBase; + _settings.Elements.RemoveAt(index); + modelBelow.Children.Insert(0, model); + } + } + else + { + // The selected element is a child element. + // If it is at the bottom of the child elements list, promote it + // to a top-level element; if not, just move it down in the + // child elements list. + FormatElementViewModel selected = lastSelectedElement as FormatElementViewModel; + int index = selected.Parent.Elements.IndexOf(selected); + if (index == selected.Parent.Elements.Count - 1) + { + // Promote the element from the bottom of the children list + // to a top-level element below its parent. + int parentIndex = Elements.IndexOf(selected.Parent); + selected.Parent.Elements.RemoveAt(selected.Parent.Elements.Count - 1); + Elements.Insert(parentIndex + 1, selected); + FormatElementBase model = selected.RevealModelObject() as FormatElementBase; + ControlElementBase parentModel = selected.Parent.RevealModelObject() as ControlElementBase; + parentModel.Children.RemoveAt(parentModel.Children.Count - 1); + _settings.Elements.Insert(parentIndex + 1, model); + selected.Parent = null; + } + else + { + selected.Parent.Elements.Move(index, index + 1); + ControlElementBase parentModel = selected.Parent.RevealModelObject() as ControlElementBase; + FormatElementBase selectedModel = parentModel.Children[index]; + parentModel.Children.RemoveAt(index); + parentModel.Children.Insert(index + 1, selectedModel); + } + } + // Select the last selected element again. + lastSelectedElement.IsSelected = true; + } + + } + + bool CanMoveElementDown() + { + if (IsTopLevelElement()) + { + return Elements.IndexOf(LastSelectedElement) < Elements.Count - 1; + } + else + { + // If the selected element is a child element, it can always be moved + // down after the parent element. + return LastSelectedElement != null && LastSelectedElement.IsSelected; + } + } + + /// + /// 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; } } + /// + /// Returns true if the selected ElementViewModel is at the top + /// level of the hierarchy. + /// + bool IsTopLevelElement() + { + return LastSelectedElement != null && + (LastSelectedElement is ControlElementViewModel || + ((FormatElementViewModel)LastSelectedElement).Parent == null); + } + #endregion #region Implementation of ViewModelBase @@ -336,10 +738,15 @@ namespace zaaReloaded2.ViewModels Settings _settings; DelegatingCommand _addElementCommand; DelegatingCommand _addChildElementCommand; + DelegatingCommand _editElementCommand; DelegatingCommand _deleteElementCommand; DelegatingCommand _copyElementCommand; + DelegatingCommand _moveElementUpCommand; + DelegatingCommand _moveElementDownCommand; Message _addElementMessage; Message _addChildElementMessage; + Message _editElementMessage; + Message _changeControlElementMessage; ElementViewModel _selectedElement; EnumProvider _referenceStyle; diff --git a/zaaReloaded2/Views/ElementPickerView.xaml b/zaaReloaded2/Views/ElementPickerView.xaml new file mode 100755 index 0000000..947a719 --- /dev/null +++ b/zaaReloaded2/Views/ElementPickerView.xaml @@ -0,0 +1,52 @@ + + + + + + + + diff --git a/zaaReloaded2/Views/SettingsView.xaml b/zaaReloaded2/Views/SettingsView.xaml new file mode 100755 index 0000000..fb64868 --- /dev/null +++ b/zaaReloaded2/Views/SettingsView.xaml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/zaaReloaded2/Views/SettingsView.xaml.cs b/zaaReloaded2/Views/SettingsView.xaml.cs new file mode 100755 index 0000000..f2c7c61 --- /dev/null +++ b/zaaReloaded2/Views/SettingsView.xaml.cs @@ -0,0 +1,38 @@ +/* SettingsView.xaml.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.Windows; + +namespace zaaReloaded2.Views +{ + /// + /// Interaction logic for AboutView.xaml + /// + public partial class SettingsView : Window + { + public SettingsView() + { + InitializeComponent(); + } + + private void TreeView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e) + { + + } + } +} diff --git a/zaaReloaded2/app.config b/zaaReloaded2/app.config index a0c5181..5dc28f4 100755 --- a/zaaReloaded2/app.config +++ b/zaaReloaded2/app.config @@ -92,16 +92,37 @@ Medikamente: TAC, CSA, SIR, Vancomycin, Gentamicin, Tobramicin + + zaaReloaded2-Laborwerte + + + IfSpecialOrAbnormal + + + Ausgabe-Elemente + + + Steuerungs-Elemente + + + 1 + + + + 00000000-0000-0000-0000-000000000000 + + + \ No newline at end of file diff --git a/zaaReloaded2/zaaReloaded2.csproj b/zaaReloaded2/zaaReloaded2.csproj index cf5b500..c2c542e 100755 --- a/zaaReloaded2/zaaReloaded2.csproj +++ b/zaaReloaded2/zaaReloaded2.csproj @@ -27,11 +27,24 @@ false zaaReloaded2 zaaReloaded2 - 3 v4.0 VSTO40 true - HomeSite + publish\ + + en + 1.0.0.0 + true + true + 7 + days + False + zaaReloaded2 + + + zaaReloaded2 + + 3 @@ -39,6 +52,16 @@ Microsoft .NET Framework 4 %28x86 and x64%29 true + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + False Microsoft Visual Studio 2010 Tools for Office Runtime %28x86 and x64%29 @@ -120,6 +143,7 @@ + ..\packages\Expression.Blend.Sdk.1.0.2\lib\net40-client\System.Windows.Interactivity.dll @@ -170,10 +194,12 @@ + + @@ -225,7 +251,20 @@ + + + IoErrorView.xaml + + + ElementPickerView.xaml + + + ElementView.xaml + + + SettingsView.xaml + SettingsRepositoryView.xaml @@ -245,6 +284,8 @@ + + SettingsSingleFileGenerator @@ -269,8 +310,8 @@ - - + + @@ -297,6 +338,22 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + MSBuild:Compile Designer @@ -329,6 +386,28 @@ + + + + + + + + + + + + + + + + + + + + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)