17 Commits

Author SHA1 Message Date
c07d4c8976 Merge pull request #3 from CCC-MF/hide_form_fields
Hide form fields in OSC file using profile configuration
2023-09-01 12:40:13 +02:00
3ec51099c7 Extract method to apply form field profile part 2023-09-01 12:31:48 +02:00
1e7a95bb09 Always save form field content for hidden form fields 2023-09-01 09:07:08 +02:00
abdef90e90 Set filter to "false" if form field should be hidden 2023-08-31 22:20:19 +02:00
a2df2650ed Use empty entry lists if they did not exist in file 2023-08-31 17:30:47 +02:00
f947395c54 Update dependencies 2023-08-31 17:00:32 +02:00
d3d4ec2646 Add a section for experimental functions in README.md 2023-08-31 17:00:00 +02:00
f507893b4d Show required dependencies not included in OSC file 2023-08-31 16:46:40 +02:00
af4ec8898a Strip content that is included in system library 2023-08-31 16:10:19 +02:00
7d6a6ee9b3 Add information about user library flag 2023-08-31 15:54:38 +02:00
a605018176 Do not modify forms included in system library 2023-08-31 12:08:34 +02:00
a8851c5e4f Mark system library content 2023-08-31 11:32:51 +02:00
0b0188bd30 Print info_xml content if using diff sub command 2023-08-30 17:57:53 +02:00
1e553aad58 Add add line break to very long argument description 2023-08-30 17:57:44 +02:00
f9c66cfdb1 Merge pull request #2 from CCC-MF/show_requirements
Add sub command 'tree' to show dependencies
2023-08-29 17:38:51 +02:00
103075ab78 Add sub command 'tree' to show dependencies 2023-08-29 17:34:02 +02:00
37c8b47d1f Update dependencies 2023-08-25 20:53:05 +02:00
13 changed files with 563 additions and 52 deletions

42
Cargo.lock generated
View File

@@ -4,15 +4,15 @@ version = 3
[[package]]
name = "anstyle"
version = "1.0.1"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
checksum = "15c4c2c83f81532e5845a733998b6971faca23490340a418e9b72a3ec9de12ea"
[[package]]
name = "clap"
version = "4.3.19"
version = "4.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d"
checksum = "7c8d502cbaec4595d2e7d5f61e318f05417bd2b66fdc3809498f0d3fdf0bea27"
dependencies = [
"clap_builder",
"clap_derive",
@@ -21,9 +21,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.3.19"
version = "4.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1"
checksum = "5891c7bc0edb3e1c2204fc5e94009affabeb1821c9e5fdc3959536c5c0bb984d"
dependencies = [
"anstyle",
"clap_lex",
@@ -31,9 +31,9 @@ dependencies = [
[[package]]
name = "clap_derive"
version = "4.3.12"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
checksum = "c9fd1a5729c4548118d7d70ff234a44868d00489a4b6597b0b020918a0e91a1a"
dependencies = [
"heck",
"proc-macro2",
@@ -43,9 +43,9 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.5.0"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
name = "console"
@@ -114,9 +114,9 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "memchr"
version = "2.5.0"
version = "2.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e"
[[package]]
name = "once_cell"
@@ -126,7 +126,7 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "osc-variant"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"clap",
"console",
@@ -157,9 +157,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.32"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@@ -172,18 +172,18 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "serde"
version = "1.0.182"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdb30a74471f5b7a1fa299f40b4bf1be93af61116df95465b2b5fc419331e430"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.182"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f4c2c6ea4bc09b5c419012eafcdb0fcef1d9119d626c8f3a0708a5b92d38a70"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [
"proc-macro2",
"quote",
@@ -205,9 +205,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.28"
version = "2.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -1,6 +1,6 @@
[package]
name = "osc-variant"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
authors = ["Paul-Christian Volkmer <volkmer_p@ukw.de>"]
description = "Anwendung zum Anpassen einer OSC-Datei an einen Standort"

View File

@@ -22,6 +22,15 @@ Zum Auflisten der Inhalte einer Datei wird folgender Befehl verwendet:
osc-variant list meine-beispieldatei.osc
```
Zum Auflisten der Inhalte mit allen Abhängigkeiten, z.B. Daten- und Merkmalkataloge und bei Formularen wird der Befehl
`tree` verwendet:
```
osc-variant tree meine-beispieldatei.osc
```
Achtung! Dies erzeugt eine sehr umfangreiche Ausgabe.
Zum Vergleich zweier OSC-Dateien wird der Unterbefehl `diff` verwendet.
Der optionale Parameter `--strict` vergleicht auch den Inhalt der OSC-Datei.
Ohne diesen wird nur das Vorhandensein von Inhalten und die Revision verglichen.
@@ -58,13 +67,23 @@ Hierzu ist die Option `--compact` vorgesehen. Es können, je nach Datei, bis zu
Bei der Auflistung der Inhalte, kann die Option `--sorted` dazu verwendet werden, die angezeigten Einträge alphabetisch zu sortieren.
Die Sortierung erfolgt dabei nach Namen des Katalogs oder des Formulars.
##### Experimentell: Sortierung nach Modifikation
#### Experimentelle Funktionen
Neben den gebräuchlichen Funktionen gibt es weitere, derzeit noch experimentelle, Funktionen.
##### Sortierung bei Modifikation
Beim Modifizieren der Inhalte kann die experimentelle Option `--x-sorted` dazu verwendet werden, die Einträge im Anschluss an die Modifikation
nach Namen zu sortieren.
Dies erlaubt eine konsistente Reihenfolge der Einträge, wodurch ein direkter Vergleich mit Vorversionen ermöglicht wird.
ACHTUNG: Es kann sein, dass dadurch ein Import der resultierenden OSC-Datei nicht mehr möglich ist, da das genaue Verhalten des Imports aktuell noch nicht bekannt ist.
##### Entfernen von Inhalten der Systembibliothek bei Modifikation
Mit der die experimentelle Option `--x-strip` ist es möglich, die in der OSC-Datei enthaltenen und beim Import nicht genutzten Inhalte aus der Systembibliothek zu entfernen.
Hierbei werden alle Inhalte entfernt, die im Ordner "ONKOSTAR Bibliothek" enthalten sind, beim Import jedoch ignoriert werden.
## Profile
Zum Erstellen von Varianten einer OSC-Datei wird eine Profildatei im YAML-Format verwendet.
@@ -74,6 +93,9 @@ In ihr sind die durchzuführenden Änderungen definiert. Eine Profildatei hat di
```
forms:
- name: "ExampleForm"
form_field:
- name: "formularfeld"
hide: true
form_references:
- name: "ref_first_mtb"
referenced_data_form: "Formularverweis.Variante"
@@ -100,6 +122,12 @@ und dabei die vorhandenen Angaben für den Formularverweis zu ersetzen.
Die Angaben für `referenced_data_form`, `anzeige_auswahl`, `anzeige` und `scripts_code` sind optional.
Wird keine Angabe gemacht, wird der bestehende Wert beibehalten.
Zudem wird im Formular "ExampleForm" das Formularfeld "formularfeld" ausgeblendet, indem der Filter auf "false" gesetzt wird.
Dadurch wird das Formularfeld nie angezeigt.
Ein zuvor bestehender Filter wird ersetzt.
Weiterhin wird die Eigenschaft "Speichern" des Formularfelds auf "Immer speichern" gesetzt um sicherzustellen, dass zuvor
enthaltene Daten weiterhin gespeichert bleiben und werden, auch wenn das Formularfeld nicht sichtbar ist.
**Achtung!** Diese Anwendung überprüft keine Scripts und verwendet angegebene Scripts als "valid" im resultierenden OSC-File.
Zudem kann die Menükategorie angepasst werden.

View File

@@ -43,6 +43,15 @@ pub enum Command {
)]
sorted: bool,
},
#[command(about = "Zeigt Kataloge und Formulare mit Revision und Abhängigkeiten an.")]
Tree {
inputfile: String,
#[arg(
long = "sorted",
help = "Sortiere Kataloge und Formulare nach Name (Optional)"
)]
sorted: bool,
},
#[command(about = "Modifiziert die angegebene Datei anhand der Profildatei")]
Modify {
inputfile: String,
@@ -54,9 +63,14 @@ pub enum Command {
compact: bool,
#[arg(
long = "x-sorted",
help = "EXPERIMENTELL: Sortiere Kataloge und Formulare nach Name (Optional). Kann negative Auswirkungen auf den ordnungsgemäßen Import haben."
help = "EXPERIMENTELL: Sortiere Kataloge und Formulare nach Name (Optional).\nKann negative Auswirkungen auf den ordnungsgemäßen Import haben."
)]
sorted: bool,
#[arg(
long = "x-strip",
help = "EXPERIMENTELL: Entferne Einträge aus der Systembibliothek die nicht importiert werden (Optional).\nKann negative Auswirkungen auf den ordnungsgemäßen Import haben."
)]
strip: bool,
},
#[command(about = "Vergleiche zwei Dateien anhand der Revision der enthaltenen Inhalte")]
Diff {

View File

@@ -117,12 +117,20 @@ fn main() -> Result<(), Box<dyn Error>> {
}
data.print_list();
}
Command::Tree { inputfile, sorted } => {
let mut data = read_inputfile(inputfile)?;
if sorted {
data.sorted()
}
OnkostarEditor::print_tree(&data);
}
Command::Modify {
inputfile,
profile,
outputfile,
compact,
sorted,
strip,
} => {
let data = &mut read_inputfile(inputfile)?;
@@ -137,6 +145,10 @@ fn main() -> Result<(), Box<dyn Error>> {
data.sorted();
}
if strip {
data.strip_system_library_content();
}
let mut buf = String::new();
let mut serializer = Serializer::new(&mut buf);

View File

@@ -22,10 +22,14 @@
* SOFTWARE.
*/
use std::collections::HashSet;
use console::style;
use serde::{Deserialize, Serialize};
use crate::model::{Comparable, Listable, Ordner, Sortable};
use crate::model::onkostar_editor::OnkostarEditor;
use crate::model::requirements::{Requirement, Requires};
use crate::model::{Comparable, FolderContent, Listable, Ordner, Sortable};
#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
@@ -59,7 +63,11 @@ pub struct DataCatalogue {
impl Listable for DataCatalogue {
fn to_listed_string(&self) -> String {
format!(
"Datenkatalog '{}' in Revision '{}'",
"Datenkatalog ({}) '{}' in Revision '{}'",
match self.is_system_library_content() {
true => style("S").yellow(),
_ => style("u"),
},
style(&self.name).yellow(),
style(&self.revision).yellow()
)
@@ -92,6 +100,54 @@ impl Comparable for DataCatalogue {
}
}
impl Requires for DataCatalogue {
fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec<Requirement> {
self.entries
.entry
.iter()
.filter(|&entry| entry.property_catalogue.is_some())
.map(|entry| match &entry.property_catalogue {
Some(entry) => entry.to_string(),
_ => String::new(),
})
.collect::<HashSet<_>>()
.into_iter()
.map(|entry| match all.find_property_catalogue(entry.as_str()) {
Some(contained) => Requirement::PropertyCatalogue(contained),
None => Requirement::ExternalPropertyCatalogue(entry),
})
.collect::<Vec<_>>()
}
fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String {
format!(
"{}\n{}",
self.to_listed_string(),
self.get_required_entries(all)
.iter()
.map(|entry| match entry {
Requirement::PropertyCatalogue(_) => {
Some(format!(" + {}\n", entry.to_string()))
}
Requirement::ExternalPropertyCatalogue(_) => {
Some(format!(" + {}\n", entry.to_string()))
}
_ => None,
})
.filter(Option::is_some)
.flatten()
.collect::<Vec<_>>()
.join("")
)
}
}
impl FolderContent for DataCatalogue {
fn get_library_folder(&self) -> String {
self.ordner.bibliothek.name.to_string()
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct Entries {

View File

@@ -22,12 +22,17 @@
* SOFTWARE.
*/
use std::collections::HashSet;
use console::style;
use serde::{Deserialize, Serialize};
use crate::model::onkostar_editor::OnkostarEditor;
use crate::model::requirements::{Requirement, Requires};
use crate::model::{
apply_profile_to_form_entry, Ansichten, Comparable, Entries, Filter, FormEntry,
FormEntryContainer, Listable, MenuCategory, PlausibilityRules, Script, Sortable,
apply_profile_to_form_entry, apply_profile_to_form_field, Ansichten, Comparable, Entries,
Filter, FolderContent, FormEntry, FormEntryContainer, Listable, MenuCategory,
PlausibilityRules, RefEntries, Script, Sortable,
};
use crate::model::{Haeufigkeiten, Ordner};
use crate::profile::Profile;
@@ -165,6 +170,12 @@ impl FormEntryContainer for DataForm {
apply_profile_to_form_entry(entry, form_reference)
});
// Hide form field using filter set to "false" if requested
profile_form
.form_fields
.iter()
.for_each(|form_field| apply_profile_to_form_field(entry, form_field));
if let Some(menu_category) = &profile_form.menu_category {
self.menu_category = Some(MenuCategory {
name: menu_category.name.clone(),
@@ -181,7 +192,11 @@ impl FormEntryContainer for DataForm {
impl Listable for DataForm {
fn to_listed_string(&self) -> String {
format!(
"Formular '{}' in Revision '{}'",
"Formular ({}) '{}' in Revision '{}'",
match self.is_system_library_content() {
true => style("S").yellow(),
_ => style("u"),
},
style(&self.name).yellow(),
style(&self.revision).yellow()
)
@@ -227,6 +242,69 @@ impl Comparable for DataForm {
}
}
impl Requires for DataForm {
fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec<Requirement> {
self.data_catalogues
.data_catalogue
.iter()
.collect::<HashSet<_>>()
.into_iter()
.map(|entry| match all.find_data_catalogue(entry.as_str()) {
Some(contained) => Requirement::DataCatalogue(contained),
None => Requirement::ExternalDataCatalogue(entry.to_string()),
})
.collect::<Vec<_>>()
}
fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String {
format!(
"{}\n{}",
self.to_listed_string(),
self.get_required_entries(all)
.iter()
.map(|entry| match entry {
Requirement::DataCatalogue(x) => {
let inner = x
.get_required_entries(all)
.iter()
.map(|inner_entry| match inner_entry {
Requirement::PropertyCatalogue(y) => Some(y.to_listed_string()),
Requirement::ExternalPropertyCatalogue(name) => Some(format!(
"Merkmalskatalog (-) '{}' - hier nicht enthalten",
style(name).yellow()
)),
_ => None,
})
.filter(Option::is_some)
.map(|item| format!(" - {}\n", item.unwrap()))
.collect::<Vec<_>>()
.join("");
if inner.is_empty() {
Some(format!(" + {}\n", x.to_listed_string()))
} else {
Some(format!(" + {}\n{}", x.to_listed_string(), inner))
}
}
Requirement::ExternalDataCatalogue(_) => {
Some(format!(" + {}\n", entry.to_string()))
}
_ => None,
})
.filter(Option::is_some)
.flatten()
.collect::<Vec<_>>()
.join("")
)
}
}
impl FolderContent for DataForm {
fn get_library_folder(&self) -> String {
self.ordner.bibliothek.name.to_string()
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct DataCatalogues {
@@ -454,6 +532,15 @@ impl FormEntry for Entry {
valid: true,
});
}
fn hide(&mut self) {
self.filter = Some(Filter {
condition: "false".into(),
valid: true,
ref_entries: Some(RefEntries { ref_entry: None }),
});
self.speichern = "0".into()
}
}
impl Sortable for Entry {

View File

@@ -22,16 +22,19 @@
* SOFTWARE.
*/
use crate::profile::{FormReference, Profile};
use serde::{Deserialize, Serialize};
use std::collections::hash_map::DefaultHasher;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use serde::{Deserialize, Serialize};
use crate::profile::{FormField, FormReference, Profile};
pub mod data_catalogue;
pub mod data_form;
pub mod onkostar_editor;
pub mod property_catalogue;
pub mod requirements;
pub mod unterformular;
#[derive(Serialize, Deserialize, Debug)]
@@ -229,6 +232,15 @@ where
}
}
fn apply_profile_to_form_field<E>(entry: &mut E, form_field: &FormField)
where
E: FormEntry,
{
if entry.get_name() == form_field.name && form_field.hide {
entry.hide()
}
}
pub trait FormEntryContainer {
fn apply_profile(&mut self, profile: &Profile);
}
@@ -264,4 +276,13 @@ pub trait FormEntry {
fn update_anzeige(&mut self, value: String);
fn update_anzeige_auswahl(&mut self, value: String);
fn update_scripts_code(&mut self, value: String);
fn hide(&mut self);
}
pub trait FolderContent {
fn get_library_folder(&self) -> String;
fn is_system_library_content(&self) -> bool {
"ONKOSTAR Bibliothek" == self.get_library_folder()
}
}

View File

@@ -22,18 +22,20 @@
* SOFTWARE.
*/
use console::style;
use quick_xml::de::from_str;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::fmt::Debug;
use std::str::FromStr;
use console::style;
use quick_xml::de::from_str;
use serde::{Deserialize, Serialize};
use crate::model::data_catalogue::DataCatalogue;
use crate::model::data_form::DataForm;
use crate::model::property_catalogue::PropertyCatalogue;
use crate::model::requirements::Requires;
use crate::model::unterformular::Unterformular;
use crate::model::{Comparable, FormEntryContainer, Listable, Sortable};
use crate::model::{Comparable, FolderContent, FormEntryContainer, Listable, Sortable};
use crate::profile::Profile;
#[derive(Serialize, Deserialize, Debug)]
@@ -46,13 +48,47 @@ pub struct OnkostarEditor {
}
impl OnkostarEditor {
pub fn find_property_catalogue<'a>(&'a self, name: &str) -> Option<&'a PropertyCatalogue> {
match self
.editor
.property_catalogue
.iter()
.filter(|&item| item.get_name().eq_ignore_ascii_case(name))
.nth(0)
{
Some(x) => Some(x),
_ => None,
}
}
pub fn find_data_catalogue<'a>(&'a self, name: &str) -> Option<&'a DataCatalogue> {
match self
.editor
.data_catalogue
.iter()
.filter(|&item| item.get_name().eq_ignore_ascii_case(name))
.nth(0)
{
Some(x) => Some(x),
_ => None,
}
}
pub fn apply_profile(&mut self, profile: &Profile) {
self.editor.data_form.iter_mut().for_each(|data_form| {
data_form.apply_profile(profile);
});
self.editor.unterformular.iter_mut().for_each(|data_form| {
data_form.apply_profile(profile);
})
self.editor
.data_form
.iter_mut()
.filter(|data_form| !data_form.is_system_library_content())
.for_each(|data_form| {
data_form.apply_profile(profile);
});
self.editor
.unterformular
.iter_mut()
.filter(|data_form| !data_form.is_system_library_content())
.for_each(|data_form| {
data_form.apply_profile(profile);
});
}
pub fn print_list(&self) {
@@ -69,11 +105,39 @@ impl OnkostarEditor {
}
fn print_items(title: &str, list: &[impl Listable]) {
println!("\n{} {}", list.len(), style(title).underlined());
print!("\n{} {}", list.len(), style(title).underlined());
println!(
" - Inhalte der Systembibliothek sind mit ({}), der Benutzerbibliothek mit (u) markiert",
style("S").yellow()
);
list.iter()
.for_each(|entry| println!("{}", entry.to_listed_string()));
}
pub fn print_tree(&self) {
println!(
"Die Datei wurde am {} mit {} in Version {} erstellt.\n\nFolgende Inhalte sind gespeichert",
style(&self.info_xml.datum_xml).yellow(),
style(&self.info_xml.name).yellow(),
style(&self.info_xml.version).yellow()
);
Self::print_items("Merkmalskataloge", &self.editor.property_catalogue);
self.print_items_tree("Datenkataloge", &self.editor.data_catalogue);
self.print_items_tree("Formulare", &self.editor.data_form);
self.print_items_tree("Unterformulare", &self.editor.unterformular);
}
fn print_items_tree(&self, title: &str, list: &[impl Requires]) {
print!("\n{} {}", list.len(), style(title).underlined());
println!(
" - Inhalte der Systembibliothek sind mit ({}), der Benutzerbibliothek mit (u) markiert",
style("S").yellow()
);
list.iter()
.for_each(|entry| println!("{}", entry.to_requirement_string(self)));
}
pub fn sorted(&mut self) {
self.editor
.property_catalogue
@@ -108,7 +172,41 @@ impl OnkostarEditor {
});
}
pub fn strip_system_library_content(&mut self) {
self.editor
.property_catalogue
.retain(|e| !e.is_system_library_content());
self.editor
.data_catalogue
.retain(|e| !e.is_system_library_content());
self.editor
.data_form
.retain(|e| !e.is_system_library_content());
self.editor
.unterformular
.retain(|e| !e.is_system_library_content());
}
pub fn print_diff(&mut self, other: &mut Self, strict: bool) {
println!();
println!(
"Datei A wurde am {} mit {} in Version {} erstellt.",
style(&self.info_xml.datum_xml).yellow(),
style(&self.info_xml.name).yellow(),
style(&self.info_xml.version).yellow()
);
println!(
"Datei B wurde am {} mit {} in Version {} erstellt.",
style(&other.info_xml.datum_xml).yellow(),
style(&other.info_xml.name).yellow(),
style(&other.info_xml.version).yellow()
);
self.sorted();
other.sorted();
@@ -243,12 +341,12 @@ pub struct InfoXML {
#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
struct Editor {
#[serde(rename = "PropertyCatalogue")]
#[serde(rename = "PropertyCatalogue", default)]
property_catalogue: Vec<PropertyCatalogue>,
#[serde(rename = "DataCatalogue")]
#[serde(rename = "DataCatalogue", default)]
data_catalogue: Vec<DataCatalogue>,
#[serde(rename = "Unterformular")]
#[serde(rename = "Unterformular", default)]
unterformular: Vec<Unterformular>,
#[serde(rename = "DataForm")]
#[serde(rename = "DataForm", default)]
data_form: Vec<DataForm>,
}

View File

@@ -25,7 +25,7 @@
use console::style;
use serde::{Deserialize, Serialize};
use crate::model::{Comparable, Listable, Ordner, Sortable};
use crate::model::{Comparable, FolderContent, Listable, Ordner, Sortable};
#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
@@ -56,7 +56,11 @@ pub struct PropertyCatalogue {
impl Listable for PropertyCatalogue {
fn to_listed_string(&self) -> String {
format!(
"Merkmalskatalog '{}' in Revision '{}'",
"Merkmalskatalog ({}) '{}' in Revision '{}'",
match self.is_system_library_content() {
true => style("S").yellow(),
_ => style("u"),
},
style(&self.name).yellow(),
style(&self.revision).yellow()
)
@@ -89,6 +93,12 @@ impl Comparable for PropertyCatalogue {
}
}
impl FolderContent for PropertyCatalogue {
fn get_library_folder(&self) -> String {
self.ordner.bibliothek.name.to_string()
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct Versions {

57
src/model/requirements.rs Normal file
View File

@@ -0,0 +1,57 @@
/*
* MIT License
*
* Copyright (c) 2023 Comprehensive Cancer Center Mainfranken
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
use crate::model::data_catalogue::DataCatalogue;
use crate::model::onkostar_editor::OnkostarEditor;
use crate::model::property_catalogue::PropertyCatalogue;
use crate::model::Listable;
#[allow(clippy::enum_variant_names)]
pub enum Requirement<'a> {
PropertyCatalogue(&'a PropertyCatalogue),
DataCatalogue(&'a DataCatalogue),
ExternalPropertyCatalogue(String),
ExternalDataCatalogue(String),
}
impl ToString for Requirement<'_> {
fn to_string(&self) -> String {
match self {
Requirement::PropertyCatalogue(item) => item.to_listed_string(),
Requirement::DataCatalogue(item) => item.to_listed_string(),
Requirement::ExternalPropertyCatalogue(name) => {
format!("Merkmalskatalog (-) '{}' - hier nicht enthalten", name)
}
Requirement::ExternalDataCatalogue(name) => {
format!("Datenkatalog (-) '{}' - hier nicht enthalten", name)
}
}
}
}
pub trait Requires {
fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec<Requirement>;
fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String;
}

View File

@@ -22,12 +22,17 @@
* SOFTWARE.
*/
use std::collections::HashSet;
use console::style;
use serde::{Deserialize, Serialize};
use crate::model::onkostar_editor::OnkostarEditor;
use crate::model::requirements::{Requirement, Requires};
use crate::model::{
apply_profile_to_form_entry, Ansichten, Comparable, Entries, Filter, FormEntry,
FormEntryContainer, Listable, MenuCategory, PlausibilityRules, Script, Sortable,
apply_profile_to_form_entry, apply_profile_to_form_field, Ansichten, Comparable, Entries,
Filter, FolderContent, FormEntry, FormEntryContainer, Listable, MenuCategory,
PlausibilityRules, RefEntries, Script, Sortable,
};
use crate::model::{Haeufigkeiten, Ordner};
use crate::profile::Profile;
@@ -176,6 +181,12 @@ impl FormEntryContainer for Unterformular {
apply_profile_to_form_entry(entry, form_reference)
});
// Hide form field using filter set to "false" if requested
profile_form
.form_fields
.iter()
.for_each(|form_field| apply_profile_to_form_field(entry, form_field));
if let Some(menu_category) = &profile_form.menu_category {
self.menu_category = Some(MenuCategory {
name: menu_category.name.clone(),
@@ -193,14 +204,22 @@ impl Listable for Unterformular {
fn to_listed_string(&self) -> String {
if self.hat_unterformulare {
return format!(
"Unterformular '{}' in Revision '{}' {}",
"Unterformular ({}) '{}' in Revision '{}' {}",
match self.is_system_library_content() {
true => style("S").yellow(),
_ => style("u"),
},
style(&self.name).yellow(),
style(&self.revision).yellow(),
style("Unterformular mit Markierung 'hat Unterformulare'!").red()
);
}
format!(
"Unterformular '{}' in Revision '{}'",
"Unterformular ({}) '{}' in Revision '{}'",
match self.is_system_library_content() {
true => style("S").yellow(),
_ => style("u"),
},
style(&self.name).yellow(),
style(&self.revision).yellow()
)
@@ -246,6 +265,68 @@ impl Comparable for Unterformular {
}
}
impl Requires for Unterformular {
fn get_required_entries<'a>(&'a self, all: &'a OnkostarEditor) -> Vec<Requirement> {
self.data_catalogues
.data_catalogue
.iter()
.collect::<HashSet<_>>()
.into_iter()
.map(|entry| match all.find_data_catalogue(entry.as_str()) {
Some(contained) => Requirement::DataCatalogue(contained),
None => Requirement::ExternalDataCatalogue(entry.to_string()),
})
.collect::<Vec<_>>()
}
fn to_requirement_string<'a>(&'a self, all: &'a OnkostarEditor) -> String {
format!(
"{}\n{}",
self.to_listed_string(),
self.get_required_entries(all)
.iter()
.map(|entry| match entry {
Requirement::DataCatalogue(x) => {
let inner = x
.get_required_entries(all)
.iter()
.map(|inner_entry| match inner_entry {
Requirement::PropertyCatalogue(_) => Some(inner_entry.to_string()),
Requirement::ExternalPropertyCatalogue(_) => {
Some(inner_entry.to_string())
}
_ => None,
})
.filter(Option::is_some)
.map(|item| format!(" - {}\n", item.unwrap()))
.collect::<Vec<_>>()
.join("");
if inner.is_empty() {
Some(format!(" + {}\n", x.to_listed_string()))
} else {
Some(format!(" + {}\n{}", x.to_listed_string(), inner))
}
}
Requirement::ExternalDataCatalogue(_) => {
Some(format!(" + {}\n", entry.to_string()))
}
_ => None,
})
.filter(Option::is_some)
.flatten()
.collect::<Vec<_>>()
.join("")
)
}
}
impl FolderContent for Unterformular {
fn get_library_folder(&self) -> String {
self.ordner.bibliothek.name.to_string()
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(deny_unknown_fields)]
pub struct DataCatalogues {
@@ -474,6 +555,15 @@ impl FormEntry for Entry {
valid: true,
});
}
fn hide(&mut self) {
self.filter = Some(Filter {
condition: "false".into(),
valid: true,
ref_entries: Some(RefEntries { ref_entry: None }),
});
self.speichern = "0".into()
}
}
impl Sortable for Entry {

View File

@@ -50,6 +50,8 @@ pub struct Form {
pub name: String,
#[serde(default)]
pub form_references: Vec<FormReference>,
#[serde(default)]
pub form_fields: Vec<FormField>,
pub menu_category: Option<MenuCategory>,
}
@@ -68,6 +70,13 @@ impl FormReference {
}
}
#[derive(Deserialize)]
pub struct FormField {
pub name: String,
#[serde(default)]
pub hide: bool,
}
#[derive(Deserialize)]
pub struct MenuCategory {
pub name: String,
@@ -93,6 +102,9 @@ mod tests {
referenced_data_form: 'OS.Tumorkonferenz.VarianteUKW'
anzeige: 'Datum: {Datum}'
anzeige_auswahl: 'TK vom {Datum}'
form_fields:
- name: eingabefeld
hide: true
";
match Profile::from_str(content) {
@@ -101,6 +113,7 @@ mod tests {
assert_eq!(profile.forms[0].name, "DNPM Therapieplan");
assert!(profile.forms[0].menu_category.is_some());
assert_eq!(profile.forms[0].form_references.len(), 1);
assert_eq!(profile.forms[0].form_fields.len(), 1)
}
Err(e) => panic!("Cannot deserialize profile: {}", e),
}
@@ -212,4 +225,29 @@ mod tests {
let actual = Profile::from_str(content);
assert!(actual.is_err());
}
#[test]
fn should_deserialize_form_fields() {
let content = "forms:
- name: 'DNPM Therapieplan'
form_fields:
- name: formularfeld_to_keep
hide: false
- name: formularfeld_to_hide
hide: true
";
match Profile::from_str(content) {
Ok(profile) => {
assert_eq!(profile.forms.len(), 1);
assert_eq!(profile.forms[0].name, "DNPM Therapieplan");
assert_eq!(profile.forms[0].form_fields.len(), 2);
assert_eq!(profile.forms[0].form_fields[0].name, "formularfeld_to_keep");
assert!(!profile.forms[0].form_fields[0].hide);
assert_eq!(profile.forms[0].form_fields[1].name, "formularfeld_to_hide");
assert!(profile.forms[0].form_fields[1].hide);
}
Err(e) => panic!("Cannot deserialize profile: {}", e),
}
}
}