diff --git a/res/main.less b/res/main.less index 6011089..364e5d9 100644 --- a/res/main.less +++ b/res/main.less @@ -64,13 +64,13 @@ margin-bottom: 1em; #headword { - font-size: 1.2em; + font-size: 1.2em; color: @darkred; font-weight: bold; } #grammar { - color: @gray; + color: @gray; font-style: italic; margin-left: 1em; } @@ -90,8 +90,8 @@ } #sense-container { - border-top: 1px dotted #999999; - padding-top: 0.5em; + border-top: 1px dotted #999999; + padding-top: 0.5em; margin-top: 0.5em; .sense-num { @@ -156,16 +156,7 @@ vertical-align: super; font-size: 0.7em; } - .translation-explanation:not(:empty) { - font-style: italic; - - &:before { - content: '['; - } - &:after { - content: ']'; - } - } + } .translation-add { @@ -175,10 +166,24 @@ } } + .explanations:not(:empty) { + font-style: italic; + + &:not(.solo) { + &:before { + content: '['; + } + + &:after { + content: ']'; + } + } + } + .example { clear: left; margin-left: 1em; - + .example-dot, .example-rest { float: left; max-width: 90%; @@ -242,7 +247,7 @@ // if span left of input it is just too high, this is a fix .span-left-of-input { - margin-top: 0.3em; + margin-top: 0.3em; } @@ -257,7 +262,7 @@ .example-cluster { color: @blue; } - .example-logdice { + .example-logdice { color: @gray; } .example-frequency { @@ -289,7 +294,7 @@ .example-component-none { color: @gray; } - + .example-component-no-space { color: @silver; } @@ -313,7 +318,7 @@ background-color: rgba(0,0, 0, 0.01); padding: 0.5em; border: 2px solid gray; - margin: 1em; + margin: 1em; .ske-line { display: block; @@ -325,13 +330,13 @@ .grey1 { margin-left: 0.3em; - background-color: @silver; + background-color: @silver; color: @black; } .grey2 { margin-left: 0.3em; background-color: @black; - color: @silver; + color: @silver; } .no-gf2-info { @@ -374,7 +379,7 @@ // overflow: scroll; // border: 2px solid red; // height: 10em; -// +// // :nth-last-child(odd) { // background-color: lightgray; // } diff --git a/src/export.py b/src/export.py index ed28df7..c5be07d 100644 --- a/src/export.py +++ b/src/export.py @@ -5,7 +5,7 @@ from model.tags import export_tag def export_to_xml(model): xml_document = export_entry(model.entry) serializer = __new__(XMLSerializer()) - return serializer.serializeToString(xml_document); + return serializer.serializeToString(xml_document) def export_entry(entry): @@ -23,9 +23,14 @@ def export_entry(entry): headword = doc.createElement("headword") headword_lemma = doc.createElement("lemma") + +# headword_lemma = entry.original_xml.querySelector("head headword lemma") + headword_lemma.textContent = entry.headword if entry.headword_type is not None: headword_lemma.setAttribute("type", entry.headword_type) + if entry.headword_audio is not None: + headword_lemma.setAttribute("audio", entry.headword_audio) headword.appendChild(headword_lemma) head.appendChild(headword) @@ -52,11 +57,18 @@ def export_entry(entry): lexunit.appendChild(lexeme) head.appendChild(lexunit) - grammar = doc.createElement("grammar") - grammar_category = doc.createElement("category") +# Example of keeping original xml and adding changes to it only + grammar_category = entry.original_xml.querySelector("head grammar category") + if grammar_category is None: + grammar = doc.createElement("grammar") + grammar_category = doc.createElement("category") + grammar.appendChild(grammar_category) + entry.original_xml.querySelector("head").appendChild(grammar_category) + grammar_category.textContent = entry.grammar - grammar.appendChild(grammar_category) - head.appendChild(grammar) + + head.appendChild(entry.original_xml.querySelector("head grammar")) + if len(entry.measure) > 0: measure_list = doc.createElement("measureList") @@ -125,7 +137,9 @@ def export_sense(doc, sense): for example in sense.examples: example_container = example.export(doc) - export_translation_list(doc, example, example_container) + translation_container_list = doc.createElement("translationContainerList") + export_translation_list(doc, example, translation_container_list) + example_container.appendChild(translation_container_list) example_container_list.appendChild(example_container) return sense_xml @@ -146,26 +160,27 @@ def export_translation(doc, translation): actual_t.textContent = translation.translation actual_t.setAttribute("targetLang", translation.targetLang) + if translation.audio: + actual_t.setAttribute("audio", translation.audio) + if translation.source: actual_t.setAttribute("source", translation.source) translation_xml.appendChild(actual_t) - explanationList = doc.createElement("explanationList") - - for explanation in translation.explanationList: - el = doc.createElement("explanation") - el.textContent = explanation - explanationList.appendChild(el) - - translation_xml.appendChild(explanationList) + if len(translation.explanationList) > 0 : + explanationList = _export_explanation_list(doc, translation.explanationList) + translation_xml.appendChild(explanationList) - explanation = doc.createElement("explanation") - explanation.textContent = translation.explanation - translation_xml.appendChild(explanation) return translation_xml +def _export_explanation_list(doc, lst): + result = doc.createElement('explanationList') + for explanation in lst: + result.appendChild(explanation.export(doc)) + + return result def _export_label_list(doc, lst): result = doc.createElement("labelList") diff --git a/src/message/common_accessors.py b/src/message/common_accessors.py index 6618233..f71e6e9 100644 --- a/src/message/common_accessors.py +++ b/src/message/common_accessors.py @@ -10,16 +10,19 @@ def generic_list_getter(): return result # Formats data from inputs to name-value objects -def homonymy_list_getter(): +def double_list_getter(firstParameter, secondParameter, allowEmptyField = False): result = [] - for row in document.getElementsByClassName("label-list-row"): - value = row.querySelector(".value-input").value - name = row.querySelector(".name-input").value + for row in document.getElementsByClassName("double-list-row"): + firstValue = row.querySelector("." + firstParameter + "-input").value + secondValue = row.querySelector("." + secondParameter + "-input").value + + if (allowEmptyField is False and '' in [firstValue, secondValue]): + continue - if ("" in [name, value]): + if (allowEmptyField is True and all('' == value or value.isspace() for value in [firstValue, secondValue])): continue - result.append({"name": name, "value": value}) + result.append({firstParameter: firstValue, secondParameter: secondValue}) return result diff --git a/src/message/simple_edits.py b/src/message/simple_edits.py index 0ba1d03..927c819 100644 --- a/src/message/simple_edits.py +++ b/src/message/simple_edits.py @@ -90,7 +90,7 @@ class EditVariants(Message): class EditHomonymy(Message): def update_model(self, model): - homonymy = common_accessors.homonymy_list_getter() + homonymy = common_accessors.double_list_getter("value", "name") model.entry.homonymy = homonymy diff --git a/src/message/translation_edit.py b/src/message/translation_edit.py index daa07bd..33c5f74 100644 --- a/src/message/translation_edit.py +++ b/src/message/translation_edit.py @@ -4,6 +4,7 @@ import message.common_accessors as common_accessors from browser import document, window from model.translation import Translation from model.sense import Sense +from model.explanation import Explanation @@ -13,9 +14,14 @@ class EditTranslation(DataChgClickMessage): self.old_cluster_idx = self.get_arg(1, int) self.translation.translation = document.getElementById("etv").value -# This could be dangerous if generic_list_getter is getting data from any other list as well. - self.translation.explanationList = common_accessors.generic_list_getter() - +# This could be dangerous if double_list_getter is getting data from any other list as well. + explanations = common_accessors.double_list_getter('value', 'language', True) + self.translation.explanationList = [] + for entry in explanations: + explanation = Explanation() + explanation.value = entry.value + explanation.language = entry.language + self.translation.explanationList.append(explanation) # common_accessors.label_list_getter() self.translation.tags = common_accessors.label_list_getter() diff --git a/src/model/__init__.py b/src/model/__init__.py index 11bc8b3..2f9785f 100644 --- a/src/model/__init__.py +++ b/src/model/__init__.py @@ -2,3 +2,4 @@ from model.model import Model from model.sense import Sense from model.translation import Translation from model.example import Example +from model.explanation import Explanation diff --git a/src/model/entry.py b/src/model/entry.py index 46dc5f4..5e60489 100644 --- a/src/model/entry.py +++ b/src/model/entry.py @@ -14,6 +14,7 @@ class Entry(Data): self.headword = "" self.homonymy = [] self.headword_type = None + self.headword_audio = None self.grammar = "" self.comment = "" self.variants = [] @@ -22,16 +23,20 @@ class Entry(Data): self.measure = {} self.labels = [] self.senses = [] + self.original_xml = None def import_xml(self, entry_xml): + self.original_xml = entry_xml.cloneNode(True) status = entry_xml.querySelector("head status") + headword = entry_xml.querySelector("head headword lemma") grammar = entry_xml.querySelector("head grammar category") comment = entry_xml.querySelector("head comment") self.status = status.textContent if status else "" self.headword = headword.textContent if headword else "" - self.headword_type = headword.getAttribute("type") if headword else None + self.headword_type = headword.getAttribute("type") if headword and headword.hasAttribute("type") else None + self.headword_audio = headword.getAttribute("audio") if headword and headword.hasAttribute("audio") else None self.grammar = grammar.textContent if grammar else "" self.comment = comment.textContent if comment else "" self.variants = [v.textContent for v in entry_xml.querySelectorAll("head variantList variant")] diff --git a/src/model/example/example.py b/src/model/example/example.py index e33df8f..5488877 100644 --- a/src/model/example/example.py +++ b/src/model/example/example.py @@ -18,12 +18,12 @@ class Example(Data): self.components = [] self.edited = False self.newly_created = False - + # removes space from last component if multiword example def check_multiword_components(self): if self.is_multiword(): self.components[len(self.components) - 1].no_space = ComponentLexeme.LAST_COMPONENT_SPACE - + @staticmethod def new_multiword(): example = Example() @@ -32,105 +32,105 @@ class Example(Data): example.inner = MultiwordExample() example.inner.cluster = ExampleClusters.first_empty_cluster() example.inner.type = "grammaticalCombination" - + empty_component = ComponentLexeme() empty_component.role = "headword" example.components.append(empty_component) - + return example - + def import_xml(self, example_xml): - self.translations = from_container_list(example_xml.querySelectorAll("translationContainer")) - + self.translations = from_container_list(example_xml.querySelectorAll("translationContainerList translationContainer")) + if example_xml.hasAttribute("modified"): self.edited = example_xml.getAttribute("modified") == "true" - + inner_xml = example_xml.querySelector("corpusExample") if inner_xml is not None: self.inner = CorpusExample() else: - inner_xml = example_xml.querySelector("multiwordExample") + inner_xml = example_xml.querySelector("multiwordExample") self.inner = MultiwordExample() - + self.inner.import_xml(inner_xml) - + for idx, comp_xml in enumerate(inner_xml.childNodes): comp = ComponentLexeme() comp.import_xml(comp_xml) if comp.isValid(): self.components.append(comp) - + self.check_multiword_components() - + def export(self, doc): self.check_multiword_components() - + result = doc.createElement("exampleContainer") - + inner = self.inner.export(doc, self.edited) # TODO: bad quick fix for comp in self.components: inner.appendChild(comp.export(doc)) - + result.appendChild(inner) return result - + def view(self, model, sense): example_tag = "div.example-rest" if self in model.chosen_examples: example_tag += ".example-chosen" - + cluster = self.get_cluster() dot_attr = {"style": { "visibility": "visible" if cluster is None else "hidden"}} - + example_content = [] if cluster is not None: example_content.append(h("span.example-cluster", {}, str(cluster + 1))) - + example_text_inner_tag = "span.example-text-{}".format(self.get_view_type()) example_content.append(h(example_text_inner_tag, {}, self.inner.view(self.components))) - + other_attributes = self.get_other_attributes() if "frequency" in other_attributes: example_content.append(h("span.example-frequency", {}, other_attributes["frequency"])) if "logDice" in other_attributes: example_content.append(h("span.example-logdice", {}, other_attributes["logDice"])) - + parent_display = "none" if model.examples_shown or self.is_multiword() or len(self.translations) > 0: parent_display = "inherit" - + clusters_display = "inherit" if not model.clusters_shown: clusters_display = "none" - + return h("div.example", {"style": {"display": parent_display}}, [ h("div.example-dot", dot_attr, "▣"), h(example_tag, {}, [ h("span.example-text", {"on": {"click": M.msg(M.ShowExampleMenu, self)} }, example_content), h("div.example-translation-list", {}, [ h("div.example-translation", {}, View.view_translations(self.translations, self, model))]), - h("div.example-clusters", + h("div.example-clusters", {"style": {"display": clusters_display }}, show_toggle_cluster_buttons(sense, self))])]) - + def simple_view(self): return self.inner.view(self.components) - + def get_cluster(self): return self.inner.get_cluster() - + def set_cluster(self, cluster): self.inner.cluster = cluster - + def get_structure(self): return self.inner.get_structure() - + def is_collocation(self): return self.get_view_type() == 2 - + def is_multiword(self): return self.get_view_type() != 1 - + def get_view_type(self): # as per the bosses, these are the rules for different colors if type(self.inner) is CorpusExample: @@ -139,8 +139,8 @@ class Example(Data): return 2 else: return 3 - + def get_other_attributes(self): return self.inner.other_attributes - - + + diff --git a/src/model/explanation.py b/src/model/explanation.py new file mode 100644 index 0000000..3a3db81 --- /dev/null +++ b/src/model/explanation.py @@ -0,0 +1,21 @@ +from model.data import Data + +from lib.snabbdom import h + + +class Explanation(Data): + def __init__(self): + self.value = "" + self.language = "" + + def import_dom(self, explanation_dom): + + self.value = explanation_dom.textContent if explanation_dom else "" + self.language = explanation_dom.getAttribute("language") if explanation_dom.hasAttribute("language") else "" + + def export(self, doc): + result = doc.createElement("explanation") + result.textContent = self.value + if self.language != "": result.setAttribute('language', self.language) + + return result diff --git a/src/model/sense.py b/src/model/sense.py index a1d51e2..e1580a9 100644 --- a/src/model/sense.py +++ b/src/model/sense.py @@ -15,37 +15,37 @@ class Sense(Data): self.labels = [] self.translations = [] self.examples = [] - + def import_xml(self, sense_xml, idx): self.original_idx = idx + for definition in sense_xml.querySelectorAll("definitionList definition"): key = definition.getAttribute("type") self.definition[key] = definition.textContent - + self.labels = import_label_list("sense > labelList label", sense_xml) self.translations = from_container_list( - sense_xml.querySelectorAll("translationContainerList translationContainer")) - + sense_xml.querySelectorAll('sense > translationContainerList translationContainer')) for example_xml in sense_xml.querySelectorAll("exampleContainerList exampleContainer"): example = Example() example.import_xml(example_xml) self.examples.append(example) - - + + def merge_labels(self): return ", ".join(val for _, val in self.labels) - - + + def view(self, model, sense_num): examples = [example.view(model, self) for example in self.examples] - + result = h("div.elm-div", {}, [ h("div.sense-num", {"on": {"click": M.msg(M.ShowSenseMenu, self)}}, str(sense_num + 1)), h("div.sense", {}, [ h("span.sense-label-list", { "on": { "click": M.msg(M.ShowSenseLabelEdit, self) }}, [ - h("span.sense-label", {}, clean_label(slabel)) for _, slabel in self.labels ]), + h("span.sense-label", {}, clean_label(slabel)) for _, slabel in self.labels ]), h("span.sense-definition", { "on": { "click": M.msg(M.ShowSenseDefinitionEdit, self) }}, self.definition["indicator"]), h("div", {}, View.view_translations(self.translations, self, model)), h("div", {}, examples)])]) return result - + diff --git a/src/model/translation.py b/src/model/translation.py index d1f10d3..e030a10 100644 --- a/src/model/translation.py +++ b/src/model/translation.py @@ -1,4 +1,5 @@ from model.tags import import_label_list +from model.explanation import Explanation from model.data import Data from lib.snabbdom import h @@ -32,8 +33,8 @@ class Translation(Data): self.translation = "" self.source = "" self.targetLang = "" - self.explanation = "" - self.explanationList = [] + self.audio = "" + self.explanationList = set() self.tags = [] def import_xml(self, translation_xml): @@ -43,14 +44,14 @@ class Translation(Data): self.translation = translation.textContent self.source = translation.getAttribute("source") if translation.hasAttribute("source") else "" self.targetLang = translation.getAttribute("targetLang") if translation.hasAttribute("targetLang") else "" + self.audio = translation.getAttribute("audio") if translation.hasAttribute("audio") else "" explanationList = translation_xml.querySelectorAll("explanationList explanation") + for explanation_dom in explanationList: + explanation = Explanation() + explanation.import_dom(explanation_dom) + self.explanationList.append(explanation) - for explanation in explanationList: - self.explanationList.append(explanation.textContent if explanation else "") - - explanation = translation_xml.querySelector("explanation") - self.explanation = explanation.textContent if explanation else "" self.tags = import_label_list("labelList label", translation_xml) def view(self, model): @@ -66,10 +67,10 @@ class Translation(Data): if self.source: elements.append(h("span.translation-source", {}, self.source)) - explanation_class = ".translation-explanation" if self.translation else "" - -# elements.append(h("span{}".format(explanation_class), {}, self.explanations)) - elements.append(h("span{}".format(explanation_class), {}, ", ".join(self.explanationList))) + if (self.explanationList): + explanation_class = ".explanations" if self.translation else ".explanations.solo" + explanations = [explanation.value for explanation in self.explanationList] + elements.append(h("span{}".format(explanation_class), {}, ", ".join(explanations))) return h("div.translation-div", {"on": {"click": M.msg(M.ShowTranslationMenu, self) }}, elements) @@ -80,6 +81,6 @@ class Translation(Data): # next two are not checked as the also can not be deleted via gui # result = result and self.source == "" # result = result and self.targetLang == "" - result = result and self.explanation == "" + result = result and len(self.explanationList) == 0 result = result and len(self.tags) == 0 return result diff --git a/src/view/modal_templates.py b/src/view/modal_templates.py index 82a7e71..562deb6 100644 --- a/src/view/modal_templates.py +++ b/src/view/modal_templates.py @@ -49,7 +49,7 @@ def generic_list_editor(title, element_list_getter): def homonymy_editor(title, current_labels): def split_line2(left, right): - cls = "flex.two{}".format(".label-list-row") + cls = "flex.two{}".format(".double-list-row") return h("div.{}".format(cls), {}, [ h("div.half", {}, left), h("div.half", {}, right)]) @@ -67,6 +67,28 @@ def homonymy_editor(title, current_labels): return content +def explanation_editor(title, current_labels): + def split_line2(left, right): + cls = "flex.two{}".format(".double-list-row") + return h("div.{}".format(cls), {}, [ + h("div.four-fifth", {}, left), h("div.fifth", {}, right)]) + + content = [h("p", {}, title)] + for i, explanation in enumerate(current_labels()): + language = [] + value = [] + language.append(h("label", {"attrs": {"for": i}}, "Language:")) + language.append(h("input.language-input", {"props": {"type": "text", "value": explanation["language"], "id": i}}, "")) + value.append(h("label", {"attrs": {"for": i + "-value"}}, "Value:")) + value.append(h("input.value-input", {"props": {"type": "text", "value": explanation["value"], "id": i + "-value"}}, "")) + + content.append(split_line2(value, language)) + content.append(h("button", {"on": {"click": message.msg(message.AddToGenericList, current_labels)}}, "+")) + + return content + + + def label_list_editor(current_labels, add_label_message_class): def split_line3(left, center, right, is_llr=True): cls = "flex.three{}".format(".label-list-row" if is_llr else "") diff --git a/src/view/modals.py b/src/view/modals.py index d9ca6e2..c6fd6c1 100644 --- a/src/view/modals.py +++ b/src/view/modals.py @@ -21,7 +21,7 @@ def edit_translation(translation, parent, cluster_idx, num_clusters, cls): content.extend([ split_line2("Prevedek:", h("textarea#etv", {"props": {"value": translation.translation}}, ""))]) - content.extend(generic_list_editor("Razlage:", lambda: translation.explanationList)) + content.extend(explanation_editor("Razlage:", lambda: translation.explanationList)) # cluster number options = [h("option", {"props": {"selected": idx == cluster_idx}}, str(idx + 1)) for idx in range(num_clusters + 1)]