From aa1726ca248d2a227979f57aaf5123684fa855de Mon Sep 17 00:00:00 2001
From: Fabian Gallenkamp <fabian.gallenkamp@uni-hamburg.de>
Date: Fri, 1 Mar 2019 16:12:11 +0100
Subject: [PATCH] prettify export mechanism

---
 app.py           | 305 ++++++++++++++++++++++++++++-------------------
 sample_db.sqlite | Bin 90112 -> 94208 bytes
 2 files changed, 184 insertions(+), 121 deletions(-)

diff --git a/app.py b/app.py
index f49b10d..d69cfcb 100644
--- a/app.py
+++ b/app.py
@@ -174,6 +174,66 @@ class Reference(db.Model):
 #@app.route('/')
 #def index():
 #    return redirect(url_for("admin.index"))
+@app.route('/wiki-export')
+def index():
+    # Generate sub pages for tools
+    with open('templates/export/software.jinja2', "r", encoding="utf-8") as file_:
+        template = Template(file_.read())
+    softwares = Software.query.all()
+    for software_tool in softwares:
+        template.stream(software=software_tool).dump(
+            '../digitale-Methoden-wiki/Tool_' + software_tool.name.replace(' ', '') + '.asciidoc', encoding='utf-8')
+
+    softwareincategory = []
+    software_categorys = SoftwareCategory.query.all()
+    for software_category in software_categorys:
+        softwares = Software.query.filter(Software.softwarecategory_id == software_category.id)
+        softwareincategory.append((software_category, softwares))
+
+    # Generate tools overview page
+    with open('templates/export/softwares.jinja2', "r", encoding="utf-8") as file_:
+        template = Template(file_.read())
+    template.stream(softwareincategory=softwareincategory).dump('../digitale-Methoden-wiki/SoftwareToolsList.asciidoc', encoding='utf-8')
+
+    # Generate methods overview page
+    hierarchy = db.session.query(Method, literal(0).label('level')).filter(Method.parent_id == null()) \
+        .cte(name="hierarchy", recursive=True)
+
+    parent = aliased(hierarchy, name="p")
+    children = aliased(Method, name="c")
+    hierarchy = hierarchy.union_all(
+        db.session.query(
+            children,
+            (parent.c.level + 1).label("level"))
+            .filter(children.parent_id == parent.c.id))
+
+    result = db.session.query(hierarchy.c.level, hierarchy.c.id, hierarchy.c.parent_id, hierarchy.c.name,
+                              hierarchy.c.description) \
+        .select_entity_from(hierarchy).order_by(hierarchy.c.id) \
+        .all()
+    references = db.session.query(Reference).order_by(Reference.name).all()
+
+    # Generate sub pages
+    with open('templates/export/MethodsList.jinja2', "r", encoding="utf-8") as file_:
+        template = Template(file_.read())
+    template.stream(methods=result, references=references).dump('../digitale-Methoden-wiki/MethodsList.asciidoc',
+                                                                encoding='utf-8')
+
+    base_path = pathlib.Path('../digitale-Methoden-wiki/')
+    #data = io.BytesIO()
+    #with zipfile.ZipFile(data, mode='w') as z:
+    #    for f_name in base_path.iterdir():
+    #        z.write(f_name)
+    #data.seek(0)
+
+    #return send_file(
+    #    data,
+    #    mimetype='application/zip',
+    #    as_attachment=True,
+    #    attachment_filename='data.zip'
+    #)
+    flash("Wiki-pages exported to " + str(base_path))
+    return redirect(url_for("admin.index"))
 
 
 class AdvancedSoftwareView(sqla.ModelView):
@@ -196,74 +256,6 @@ class AdvancedSoftwareView(sqla.ModelView):
         'links': _links_formatter
     }
 
-    @action('advancedexport', 'AdvancedExport')
-    def action_advancedexport(self, ids):
-        try:
-            # Generate sub pages
-            with open('templates/export/software.jinja2', "r", encoding="utf-8") as file_:
-                template = Template(file_.read())
-            softwares = Software.query.filter(Software.id.in_(ids))
-            for software_tool in softwares:
-                template.stream(software=software_tool).dump('../digitale-Methoden-wiki/Tool_' + software_tool.name.replace(' ','') + '.asciidoc', encoding='utf-8')
-
-            softwareincategory = []
-            software_categorys = SoftwareCategory.query.all()
-            for software_category in software_categorys:
-                softwares = Software.query.filter(Software.softwarecategory_id == software_category.id)
-                softwareincategory.append((software_category,softwares))
-
-            # Generate overview page
-            with open('templates/export/softwares.jinja2', "r", encoding="utf-8") as file_:
-                template = Template(file_.read())
-            template.stream(softwareincategory=softwareincategory).dump('../digitale-Methoden-wiki/SoftwareToolsList.asciidoc', encoding='utf-8')
-
-            base_path = pathlib.Path('../digitale-Methoden-wiki/')
-            data = io.BytesIO()
-            with zipfile.ZipFile(data, mode='w') as z:
-                for f_name in base_path.iterdir():
-                    z.write(f_name)
-            data.seek(0)
-            return send_file(
-                data,
-                mimetype='application/zip',
-                as_attachment=True,
-                attachment_filename='data.zip'
-            )
-            flash("Files generated!")
-        except Exception as ex:
-            if not self.handle_view_exception(ex):
-                raise
-            flash("Not done")
-
-    @action('advancedexport2', 'AdvancedExport2')
-    def action_advancedexport2(self, ids):
-        try:
-            hierarchy = db.session.query(Method, literal(0).label('level')).filter(Method.parent_id == null()) \
-            .cte(name="hierarchy", recursive=True)
-
-            parent = aliased(hierarchy, name="p")
-            children = aliased(Method, name="c")
-            hierarchy = hierarchy.union_all(
-            db.session.query(
-                children,
-                (parent.c.level + 1).label("level"))
-                .filter(children.parent_id == parent.c.id))
-
-            result = db.session.query(hierarchy.c.level, hierarchy.c.id, hierarchy.c.parent_id, hierarchy.c.name, hierarchy.c.description)\
-                .select_entity_from(hierarchy).order_by(hierarchy.c.id)\
-                .all()
-            references = db.session.query(Reference).order_by(Reference.name).all()
-
-            # Generate sub pages
-            with open('templates/export/MethodsList.jinja2', "r", encoding="utf-8") as file_:
-                template = Template(file_.read())
-            template.stream(methods=result, references=references).dump('../digitale-Methoden-wiki/MethodsList.asciidoc', encoding='utf-8')
-            flash("Files generated!")
-        except Exception as ex:
-            if not self.handle_view_exception(ex):
-                raise
-            flash("Not done")
-
 # Create admin
 admin = admin.Admin(app, name='digital methods:software-tools', template_mode='bootstrap3', index_view=AdminIndexView(
         name='home',
@@ -274,13 +266,14 @@ admin = admin.Admin(app, name='digital methods:software-tools', template_mode='b
 # Add views
 admin.add_view(AdvancedSoftwareView(Software, db.session, name="software-tools"))
 admin.add_view(sqla.ModelView(Method, db.session, name="methods"))
-admin.add_view(sqla.ModelView(Feature, db.session, category="miscellaneous"))
-admin.add_view(sqla.ModelView(License, db.session, category="miscellaneous"))
-admin.add_view(sqla.ModelView(Link, db.session, category="miscellaneous"))
-admin.add_view(sqla.ModelView(SoftwareCategory, db.session, category="miscellaneous"))
+admin.add_view(sqla.ModelView(Feature, db.session, name="software-features", category="miscellaneous"))
+admin.add_view(sqla.ModelView(License, db.session, name="licenses", category="miscellaneous"))
+admin.add_view(sqla.ModelView(Link, db.session, name="links", category="miscellaneous"))
+admin.add_view(sqla.ModelView(SoftwareCategory, db.session, name="software categories", category="miscellaneous"))
 admin.add_sub_category(name="other collections", parent_name="miscellaneous")
 admin.add_link(MenuLink(name="CRAN-R", url='https://cran.r-project.org/web/views/', category='other collections', target="_blank"))
 admin.add_link(MenuLink(name="ROpenSci", url='https://ropensci.org/packages/', category='other collections', target="_blank"))
+admin.add_link(MenuLink(name="wiki-export", url='/wiki-export', category="miscellaneous"))
 
 
 
@@ -415,115 +408,167 @@ Furthermore the server-client-model is the established communication paradigms f
                      parent=method3)
     db.session.add(method3)
     method2 = Method(id=25,name="information retrieval",
-                     description="",
-                     parent=method1)
-    db.session.add(method2)
-    method2 = Method(id=26,name="indexing",
-                     description="",
-                     parent=method1)
-    db.session.add(method2)
-    method2 = Method(id=27,name="searching",
-                     description="",
+                     description="Retrieve relevant informations in response to the information requests.",
                      parent=method1)
     db.session.add(method2)
+    method3 = Method(id=26,name="indexing",
+                     description="'organize data in such a way that it can be easily retrieved later on'(<<Ignatow_etal2017>>,137)",
+                     parent=method2)
+    db.session.add(method3)
+    method3 = Method(id=27,name="searching/querying",
+                     description="'take information requests in the form of queries and return relevant documents'(<<Ignatow_etal2017>>,137). There are different models in order to estimate the similarity between records and the search queries (e.g. boolean, vector space or a probabilistic model)(ibid).",
+                     parent=method2)
+    db.session.add(method3)
     method2 = Method(id=28,name="statistical analysis",
                      description="",
                      parent=method1)
     db.session.add(method2)
     method3 = Method(id=29,name="frequency analysis",
-                     description="Descriptiv statistical analysis by using text fragments",
+                     description="Descriptiv statistical analysis by using specific text abundances.",
                      parent=method2)
     db.session.add(method3)
-    method3 = Method(id=30,name="classification/machine learning",
-                     description="",
-                     parent=method2)
-    db.session.add(method3)
-    method4 = Method(id=31,name="supervised classification",
-                     description="",
+    method4 = Method(id=30,name="word frequencies/dictionary analysis",
+                     description="Analyse statistical significant occurence of words/word-groups. Can also be combined with meta-data (e.g. creation time of document).",
                      parent=method3)
     db.session.add(method4)
-    method4 = Method(id=32,name="topic modelling",
-                     description="",
+    method4 = Method(id=31,name="co-occurence analysis",
+                     description="Analyse statistical significant co-occurence of words in different contextual units.",
                      parent=method3)
     db.session.add(method4)
-    method4 = Method(id=33,name="latent dirichlet allocation",
-                     description="",
+    method3 = Method(id=32,name="classification/machine learning",
+                     description="Various techniques to (semi-)automatically identify specific classes. ",
+                     parent=method2)
+    db.session.add(method3)
+    method4 = Method(id=33,name="supervised classification",
+                     description="Use given training examples in order to classify certain entities.",
                      parent=method3)
     db.session.add(method4)
-    method4 = Method(id=34,name="structural topic modelling",
-                     description="",
+    method4 = Method(id=34,name="latent semantic analysis",
+                     description="'The basic idea of latent semantic analysis (LSA) is, that text do have a higher order (=latent semantic) structure which, however, is obscured by word usage (e.g. through the use of synonyms or polysemy). By using conceptual indices that are derived statistically via a truncated singular value decomposition (a two-mode factor analysis) over a given document-term matrix, this variability problem can be overcome.'(link:https://cran.r-project.org/web/packages/lsa/lsa.pdf[CRAN-R])",
                      parent=method3)
     db.session.add(method4)
-    method4 = Method(id=35,name="latent semantic analysis",
-                     description="",
+    method4 = Method(id=35,name="topic modelling",
+                     description="Probabilistic models to infer semantic clusters. See especially <<Papilloud_etal2018>>.",
                      parent=method3)
     db.session.add(method4)
-    method4 = Method(id=36,name="sentiment analysis",
+    method5 = Method(id=36,name="latent dirichlet allocation",
+                     description="""'The application of LDA is based on three nested concepts: the text collection to be modelled is referred to as the corpus; one item within the corpus is a document, with words within a document called terms.(...) +
+The aim of the LDA algorithm is to model a comprehensive representation of the corpus by inferring latent content variables, called topics. Regarding the level of analysis, topics are heuristically located on an intermediate level between the corpus and the documents and can be imagined as content-related categories, or clusters. (...) Since topics are hidden in the first place, no information about them is directly observable in the data. The LDA algorithm solves this problem by inferring topics from recurring patterns of word occurrence in documents.'(<<Maier_etal2018>>,94)""",
+                     parent=method4)
+    db.session.add(method5)
+    method5 = Method(id=37,name="non-negative-matrix-factorization",
+                     description="Inclusion of non-negative constraint.",
+                     parent=method4)
+    db.session.add(method5)
+    method5 = Method(id=38,name="structural topic modelling",
+                     description="Inclusion of meta-data. Refer especially to <<roberts2013>>.",
+                     parent=method4)
+    db.session.add(method5)
+    method4 = Method(id=39,name="sentiment analysis",
                      description="'Subjectivity and sentiment analysis focuses on the automatic identification of private states, such as opinions, emotions, sentiments, evaluations, beliefs, and speculations in natural language. While subjectivity classification labels text as either subjective or objective, sentiment classification adds an additional level of granularity, by further classifying subjective text as either positive, negative, or neutral.' (<<Ignatow_etal2017>> pp. 148)",
                      parent=method3)
     db.session.add(method4)
-    method4 = Method(id=37,name="automated narrative, argumentative structures, irony, metaphor detection/extraction",
-                     description="",
+    method4 = Method(id=40,name="automated narrative, argumentative structures, irony, metaphor detection/extraction",
+                     description="For automated narrative methapor analysis see (<<Ignatow_etal2017>>, 89-106. For argumentative structures(Task: Retrieving sentential arguments for any given controversial topic) <<Stab_etal2018>> .Refer for a current overview <<Cabrio2018>>.",
                      parent=method3)
     db.session.add(method4)
-    method3 = Method(id=38,name="network analysis",
-                     description="",
+    method3 = Method(id=41,name="network analysis/modelling",
+                     description="Generate networks out of text/relationships between text.",
                      parent=method2)
     db.session.add(method3)
-    method4 = Method(id=39, name="knowledge graph construction",
-                     description="finding factual information in free text",
+    method4 = Method(id=42, name="knowledge graph construction",
+                     description="Modelling entities and their relationships.",
                      parent=method3)
     db.session.add(method4)
 
-    method2 = Method(id=40,name="data visualization",
-                     description="",
+    method2 = Method(id=43,name="data visualization",
+                     description="Visualize the mined informations.",
                      parent=method1)
     db.session.add(method2)
-    method3 = Method(id=41,name="dynamic visualizations",
+    method3 = Method(id=44,name="word relationships",
                      description="",
                      parent=method2)
     db.session.add(method3)
-
-    method1 = Method(id=42,name="science practice",
+    method3 = Method(id=45,name="networks",
+                     description="",
+                     parent=method2)
+    db.session.add(method3)
+    method3 = Method(id=46,name="geo-referenced",
                      description="",
+                     parent=method2)
+    db.session.add(method3)
+    method3 = Method(id=47,name="dynamic visualizations",
+                     description="Visualizations with user interaction or time frames.",
+                     parent=method2)
+    db.session.add(method3)
+    method1 = Method(id=48,name="science practice",
+                     description="General science practice",
                      parent=method)
     db.session.add(method1)
-    method2 = Method(id=43,name="digital research design",
-                     description="",
+    method2 = Method(id=49,name="digital research design",
+                     description="New possibilities in surveys or data aquisition techniques.",
                      parent=method1)
     db.session.add(method2)
-    method2 = Method(id=44,name="collaborative work",
+    method3 = Method(id=50,name="ecological momentary assessments (EMA)/Experience Sampling Method (ESM)",
+                     description="Mostly equivalent. EMA focusses on medical questions or measurements in a natural environment; ESM more on subjective Questions in the real life. Four characteristics: 1) data collection in natural environments 2) Focussing on near events/impressions/actions 3) questions triggered randomly or event-based 4) multiple questions over a certain period of time [Citation after Stone and Shiffmann 1994] (<<Salganik2018>>,109)",
+                     parent=method2)
+    db.session.add(method3)
+    method3 = Method(id=51,name="wiki surveys",
+                     description="Guide open-answer questions with user feedback.",
+                     parent=method2)
+    db.session.add(method3)
+    method3 = Method(id=52,name="survey data linked to big data sources",
+                     description="",
+                     parent=method2)
+    db.session.add(method3)
+    method4 = Method(id=53,name="Enriched asking",
+                     description="",
+                     parent=method3)
+    db.session.add(method4)
+    method4 = Method(id=54,name="Amplified asking",
+                     description="",
+                     parent=method3)
+    db.session.add(method4)
+    method2 = Method(id=55,name="collaborative work",
                      description="",
                      parent=method1)
     db.session.add(method2)
-    method2 = Method(id=45,name="digital communication",
+    method3 = Method(id=56,name="open call projects",
+                     description="(e.g. annotation).",
+                     parent=method2)
+    db.session.add(method3)
+    method3 = Method(id=57,name="distributed data collection",
+                     description="",
+                     parent=method2)
+    db.session.add(method3)
+    method2 = Method(id=58,name="digital communication",
                      description="",
                      parent=method1)
     db.session.add(method2)
-    method1 = Method(id=46,name="statistical modeling",
+    method1 = Method(id=59,name="statistical modeling",
                      description="",
                      parent=method)
     db.session.add(method1)
-    method2 = Method(id=47,name="regression analysis",
+    method2 = Method(id=60,name="regression analysis",
                      description="",
                      parent=method1)
     db.session.add(method2)
-    method2 = Method(id=48,name="time-series analysis",
+    method2 = Method(id=61,name="time-series analysis",
                      description="",
                      parent=method1)
     db.session.add(method2)
-    method2 = Method(id=49,name="agent-based modeling",
+    method2 = Method(id=62,name="agent-based modeling",
                      description="",
                      parent=method1)
     db.session.add(method2)
-    method2 = Method(id=50,name="digital communication",
-                     description="",
-                     parent=method1)
-    db.session.add(method2)
-    method1 = Method(id=51,name="social complexity modeling/ social simulation",
+    method1 = Method(id=63,name="social complexity modeling/ social simulation",
                      description="",
                      parent=method)
     db.session.add(method1)
+    method2 = Method(id=64,name="nowcasting",
+                     description="Using methods to predict the future for estimation of current values. (Example: predict influenza epidemiology combining CDC Data and Google Trends(<<Salganik2018>>,46–50)).",
+                     parent=method1)
+    db.session.add(method2)
 
     reference = Reference(name="Rogers2013",
                           cited="Rogers, R. (2013). Digital methods. Cambridge, Massachusetts, London, England: The MIT Press.")
@@ -540,6 +585,24 @@ Furthermore the server-client-model is the established communication paradigms f
     reference = Reference(name="Wickham_etal2017",
                           cited="Wickham, H., & Grolemund, G. (2017). R for Data Science: Import, tidy, transform, visualize, and model data. Beijing, Boston, Farnham, Sebastopol, Tokyo: O’Reilly UK Ltd.")
     db.session.add(reference)
+    reference = Reference(name="Papilloud_etal2018",
+                          cited="Papilloud, C., & Hinneburg, A. (Eds.). (2018). Studienskripten zur Soziologie. Qualitative Textanalyse mit Topic-Modellen: Eine Einführung für Sozialwissenschaftler. Wiesbaden: Springer VS.")
+    db.session.add(reference)
+    reference = Reference(name="Maier_etal2018",
+                          cited="Maier, D., Waldherr, A., Miltner, P., Wiedemann, G., Niekler, A., Keinert, A., . . . Adam, S. (2018). Applying LDA Topic Modeling in Communication Research: Toward a Valid and Reliable Methodology. Communication Methods and Measures, 12(2-3), 93–118. https://doi.org/10.1080/19312458.2018.1430754")
+    db.session.add(reference)
+    reference = Reference(name="Roberts2013",
+                          cited="Roberts, M. E., Stewart, B. M., Tingley, D., Airoldi, E. M., & others (2013). The structural topic model and applied social science. In Advances in neural information processing systems workshop on topic models: computation, application, and evaluation (pp. 1–20).")
+    db.session.add(reference)
+    reference = Reference(name="Salganik2018",
+                          cited="Salganik, M. J. (2018). Bit by bit: Social research in the digital age.")
+    db.session.add(reference)
+    reference = Reference(name="Cabrio2018",
+                          cited="Cabrio, E., & Villata, S. (2018). Five years of argument mining: a data-driven analysis. In Proceedings of the 27th International Joint Conference on Artificial Intelligence (pp. 5427–5433).")
+    db.session.add(reference)
+    reference = Reference(name="Stab_etal2018",
+                          cited="Stab, C., Daxenberger, J., Stahlhut, C., Miller, T., Schiller, B., Tauchmann, C., . . . Gurevych, I. (2018). ArgumenText: Searching for Arguments in Heterogeneous Sources. In Proceedings of the 2018 Conference of the North American Chapter of the Association for Computational Linguistics: Demonstrations (pp. 21–25).")
+    db.session.add(reference)
 
 
 
diff --git a/sample_db.sqlite b/sample_db.sqlite
index 5d3af6d1c13fadaf563d0433f45e9ec3486501ad..5062764ab07c1a65ebd949829019a7a2a00a8134 100644
GIT binary patch
delta 8066
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

delta 2100
zcmZp8z}j$tb%K--=U)Z}1~CRW;G8p2$B2pZ@5Y2B{!xtlzd;gW{J+`Hu{W|aGrnMq
zV(aH}X47Qd&-seAfmN2}0ZTQD5%Wog-OQDo^-QlADmjxlgE$>IRheco6){C|ax>ZR
z7IM7dxWuuQH;iKzM;%8JhbynrL`UiA`}i51Cg1x!fAWVPkJw~1Ss5&Or`zi?ipy(=
zsj)KX@-pV7mXzlgWh*4+CFWEXXBNw-%CRzd^G@H%&8WaG<1fp~U|2ofhnLZYza+Jy
zL?NvxF+DdmucTOJ^NPS|Rwikq$<h&0@_JH+tPG~SjEU)~c_q3@iN&cY3c2|ysX3W>
z>3UKI(;YP#C7Jak^(RG0FzZR{g7h$JN$5=fugNH`rX{P*%3#gQn39>ES(2EeP?TDn
znpl*ap^%bVoSB}dC85E}V8}apV}!5^lavhDp5l_klFZ_g%;dxzxXYRNrCAx|c^M&^
zGxO4!_@r1FWO*4g^HNePKzwOQRt9t4=?VOdVw_BwVr&eSjjD{(9ReA}Chz&gvAH+0
zj+xPT`bP~$BPL0`$quoS?4nwrK;qn75nIQ^1$Gz%1A_q9rUVuNK34AS4E&RLFY=o4
zRC90NtSGRQd-L|{5Ee$k%{&c{`2~gT7#J8tH5nKfgcKMU82I=$DX=KmS@5+m@c-oh
z%>Rb}8UKC$oBWsfPxBw)-^ah5e<S~D{w4f#`KR$u;P2#b;;-f};m_kw=TGF1;t%2X
z<9FwGnC!Gcop~$Yy3IKoWclj3*jde)85!-_Sk0Ij8Clp^O_>=PC7D=Fm>C%@m{^UO
z85!l7S&f((8MT>M44D}@Ragy}85t#*SoN718Ra-w^_UqM9hg{knHd@Fm{@g~85vcX
zS+$uN8MT;LwU`+hm04LeK@=0K1~Vfg2NSD0Gb5udGpiaiBcnchJ*z4+BcnJos|qtC
zBQrCrGKgYgRbpmjlx1dBWM*U(V`f!gW@OZ4W0hxSWMpDym1AaPRAOe8WoBg5U}lwJ
zW@OZ3W|3xQ<YZ@&VrJx&VU=WNWK?8gkzi)zbYc}}W@I#BW)Wj%<m6!$WoBgLVPX{l
zQOvBu%#4in*37Iz%#4iIEG&Y|jGXGM0?dqzx@@fcAc~2VkC~B?jfsVqnUT|&g@>7u
zQ<{~VnUPVAnT3m)laZN~lbMlGhl!PgnUT?vla(DrF|)8SGjfWsvNAI=vNEx-Ff(#;
zvobR?GIBGsFflW7DzGv#Gcrmsu`)0)F!Hm3Gp@*Ho&#U?W0`CixEP}t`0RN*xL(m9
zCv!2qVt8mA%~+J0Gr94z#PkRJj0!AFuNdY}e)M@ZICpcnvIJYgayKV8izz9&TNo{O
zbE+~goxCQnn}xHUS!}X(kPi#zE2d+cyMsP4vT(j)xUjkLOEVL*663$gvJn!bWPesY
z-Y~{KkS^j20Nz4I3s?cbz`!VH%F9TF0)T;m;Rs3rFu6C<m4$(UA#(GzpXSVrX_NV4
z4U`xd7>rDLIg#yy6>JO)4C1=Hkiuc|xBq<0SsWM@ma`Zz3T$UdVEn_sO@NU_AWW0h
zfq{P(zX9KMz8QR`ygzs+^Lp_-<Z<Hu#NE#IgKHbtOs-C@3a%I~Zq6s1hdB&5c-ddG
zpJsi*+QYe;)q!&&XEA3Cr!A*4CmY9Wjtd-HIA(BEbGUDgI`E8z%}bq?!8LX|j~=6}
zVOnNhN@iZVLRw;SNoit^LS|lCeo<~>NoIbYLS~*qT2X4M0w|KzqEy)!8jX1wrwi&c
z3Qs?)$LPQ!*PNiTeFrn+eMSyBIb~J`TVBTLb)1Y6>~ac9tPGC4)A^YhrP<{a6<Haa
zd8aR6Wt5SU6IEbk(B@?<$uG!EhLrW7!d}h@T7Z&MF+d7kCbas&mQeiwEu@Q+GgI@D
zQxyt|5|c|ZlT(puJ9Z639U{+!tLBrJXJxSGoqmUjQHos-S2f573PVISIDH{6qpUh9
jHP7aSpKKW?n+FAet7ZAWnVUZZ^)gPr6d5_WHsk^TJy+@$

-- 
GitLab