From 8acdac67da2d1aaf48acf6b996dd4eb8e0f46b5f Mon Sep 17 00:00:00 2001
From: Fabian Gallenkamp <fabian.gallenkamp@uni-hamburg.de>
Date: Thu, 21 Feb 2019 12:17:06 +0100
Subject: [PATCH] Fixed small issues

---
 app.py                            | 244 ++++++++++++------------------
 sample_db.sqlite                  | Bin 65536 -> 69632 bytes
 templates/export/softwares.jinja2 |   6 +
 3 files changed, 99 insertions(+), 151 deletions(-)

diff --git a/app.py b/app.py
index 0937225..2936940 100644
--- a/app.py
+++ b/app.py
@@ -189,7 +189,7 @@ class AdvancedSoftwareView(sqla.ModelView):
             # 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/Softwareliste.asciidoc', encoding='utf-8')
+            template.stream(softwareincategory=softwareincategory).dump('../digitale-Methoden-wiki/SoftwareToolsList.asciidoc', encoding='utf-8')
 
             base_path = pathlib.Path('../digitale-Methoden-wiki/')
             data = io.BytesIO()
@@ -203,139 +203,25 @@ class AdvancedSoftwareView(sqla.ModelView):
                 as_attachment=True,
                 attachment_filename='data.zip'
             )
-            flash("Done")
+            flash("Files generated!")
         except Exception as ex:
             if not self.handle_view_exception(ex):
                 raise
-
             flash("Not done")
 
 
-'''
-class UserAdmin(sqla.ModelView):
-    action_disallowed_list = ['delete', ]
-    column_display_pk = True
-    column_list = [
-        'id',
-        'last_name',
-        'first_name',
-        'email',
-        'pets',
-    ]
-    column_default_sort = [('last_name', False), ('first_name', False)]  # sort on multiple columns
-
-    # custom filter: each filter in the list is a filter operation (equals, not equals, etc)
-    # filters with the same name will appear as operations under the same filter
-    column_filters = [
-        FilterEqual(column=User.last_name, name='Last Name'),
-        FilterLastNameBrown(column=User.last_name, name='Last Name',
-                            options=(('1', 'Yes'), ('0', 'No')))
-    ]
-    inline_models = [(UserInfo, inline_form_options), ]
-
-    # setup create & edit forms so that only 'available' pets can be selected
-    def create_form(self):
-        return self._use_filtered_parent(
-            super(UserAdmin, self).create_form()
-        )
-
-    def edit_form(self, obj):
-        return self._use_filtered_parent(
-            super(UserAdmin, self).edit_form(obj)
-        )
-
-    def _use_filtered_parent(self, form):
-        form.pets.query_factory = self._get_parent_list
-        return form
-
-    def _get_parent_list(self):
-        # only show available pets in the form
-        return Pet.query.filter_by(available=True).all()
-
-# Customized Post model admin
-class PostAdmin(sqla.ModelView):
-    column_list = ['id', 'user', 'title', 'date', 'tags']
-    column_default_sort = ('date', True)
-    column_sortable_list = [
-        'id',
-        'title',
-        'date',
-        ('user', ('user.last_name', 'user.first_name')),  # sort on multiple columns
-    ]
-    column_labels = dict(title='Post Title')  # Rename 'title' column in list view
-    column_searchable_list = [
-        'title',
-        'tags.name',
-        'user.first_name',
-        'user.last_name',
-    ]
-    column_labels = {
-        'title': 'Title',
-        'tags.name': 'tags',
-        'user.first_name': 'user\'s first name',
-        'user.last_name': 'last name',
-    }
-    column_filters = [
-        'user',
-        'title',
-        'date',
-        'tags',
-        filters.FilterLike(Post.title, 'Fixed Title', options=(('test1', 'Test 1'), ('test2', 'Test 2'))),
-    ]
-    can_export = True
-    export_max_rows = 1000
-    export_types = ['csv', 'xls']
-
-    # Pass arguments to WTForms. In this case, change label for text field to
-    # be 'Big Text' and add required() validator.
-    form_args = dict(
-                    text=dict(label='Big Text', validators=[validators.data_required()])
-                )
-
-    form_ajax_refs = {
-        'user': {
-            'fields': (User.first_name, User.last_name)
-        },
-        'tags': {
-            'fields': (Tag.name,),
-            'minimum_input_length': 0,  # show suggestions, even before any user input
-            'placeholder': 'Please select',
-            'page_size': 5,
-        },
-    }
-
-    def __init__(self, session):
-        # Just call parent class with predefined model.
-        super(PostAdmin, self).__init__(Post, session)
-
-
-
-
-class TreeView(sqla.ModelView):
-    form_excluded_columns = ['children', ]
-
-
-class ScreenView(sqla.ModelView):
-    column_list = ['id', 'width', 'height',
-                   'number_of_pixels']  # not that 'number_of_pixels' is a hybrid property, not a field
-    column_sortable_list = ['id', 'width', 'height', 'number_of_pixels']
-
-    # Flask-admin can automatically detect the relevant filters for hybrid properties.
-    column_filters = ('number_of_pixels',)
-'''
-
 # Create admin
-admin = admin.Admin(app, name='Softwaresammlung: Digitale Methoden', template_mode='bootstrap3')
+admin = admin.Admin(app, name='SoftwareTools: digital methods', template_mode='bootstrap3')
 
 # Add views
-admin.add_view(AdvancedSoftwareView(Software, db.session))
-admin.add_view(sqla.ModelView(Feature, db.session, category="Weiteres"))
-admin.add_view(sqla.ModelView(License, db.session, category="Weiteres"))
-admin.add_view(sqla.ModelView(Link, db.session, category="Weiteres"))
-admin.add_view(sqla.ModelView(SoftwareCategory, db.session, category="Weiteres"))
-admin.add_sub_category(name="Andere Sammlungen", parent_name="Weiteres")
-admin.add_link(MenuLink(name="CRAN-R", url='https://cran.r-project.org/web/views/', category='Andere Sammlungen', target="_blank"))
-admin.add_link(MenuLink(name="ROpenSci", url='https://ropensci.org/packages/', category='Andere Sammlungen', target="_blank"))
+admin.add_view(AdvancedSoftwareView(Software, db.session, name="SoftwareTool"))
+admin.add_view(sqla.ModelView(Feature, db.session, category="Misc"))
+admin.add_view(sqla.ModelView(License, db.session, category="Misc"))
+admin.add_view(sqla.ModelView(Link, db.session, category="Misc"))
+admin.add_view(sqla.ModelView(SoftwareCategory, db.session, category="Misc"))
+admin.add_sub_category(name="Other collections", parent_name="Misc")
+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"))
 
 
 
@@ -344,12 +230,9 @@ def build_sample_db():
     Populate a small db with some example entries.
     """
 
-    import random
-    import datetime
-
     db.drop_all()
     db.create_all()
-    lic_unknown = License(name="Unbekannt")
+    lic_unknown = License(name="Unknown")
     lic_bsd = License(name="BSD")
     lic_gpl2 = License(name="GPL", version="2.0")
     lic_gpl3 = License(name="GPL", version="3.0")
@@ -359,7 +242,7 @@ def build_sample_db():
     lic_mit = License(name="MIT")
     lic_byncnd3 = License(name="CC BY-NC-ND", version="3.0")
     lic_ccdl = License(name="CCDL", version="1.0")
-    lic_prop = License(name="Proprietär")
+    lic_prop = License(name="Proprietary")
     db.session.add(lic_gpl3)
     db.session.add(lic_gpl3)
     db.session.add(lic_agpl3)
@@ -383,25 +266,27 @@ def build_sample_db():
     db.session.add(prol_js)
     db.session.add(prol_c)
 
-    cat_tracking = SoftwareCategory(name="datenschutzkonformes Tracking", short_description="Sammlung von Sensordaten/Logdaten oder Nutzungsdaten mit expliziter Einverständnis mittels Software auf dem Gerät.")
-    cat_scraping = SoftwareCategory(name="Scraping", short_description="Tools im Zusammenhang mit Web-Scraping.")
-    cat_int = SoftwareCategory(name="Korpusanalyse", short_description="Integrierte Plattformen für die Analyse großer Textcorpora.")
-    cat_qda = SoftwareCategory(name="QDA-Software", short_description="Computer-gestützte Analyse qualitativer Daten.")
-    cat_tm = SoftwareCategory(name="Automatisierte Inhaltsanalyse/Text Mining", short_description="")
-
-    cat_senti = SoftwareCategory(name="Sentiment Analysis", short_description="")
-    cat_topic = SoftwareCategory(name="Topic-Modellierung", short_description="")
-    cat_visu = SoftwareCategory(name="Visualisierung", short_description="")
-    cat_kollab_anno = SoftwareCategory(name="Kollaboratives Annotieren", short_description="")
-    cat_kollab_write = SoftwareCategory(name="Kollaboratives Schreiben", short_description="")
-    cat_stat = SoftwareCategory(name="Statisik-Programme", short_description="Zur statistischen Modellierung einsetzbare Software.")
-    cat_repo = SoftwareCategory(name="Forschungsdatenspeicherung", short_description="")
-    cat_now = SoftwareCategory(name="Nowcasting", short_description="")
-    cat_net = SoftwareCategory(name="Netzwerkanalysen", short_description="social network analysis")
-    cat_esmema = SoftwareCategory(name="ESM/EMA-Studien", short_description="Datenerhebung in 'natürlicher' Umgebung.")
-    cat_transkript = SoftwareCategory(name="Audio-Transkription", short_description="Transkriptionssoftware")
-    cat_search = SoftwareCategory(name="Suche", short_description="Software im Zusammenhang von erweiterter Suche in großen Textmengen")
-    cat_misc = SoftwareCategory(name="Weiteres", short_description="Zu speziell zum Einordnen..")
+    cat_tracking = SoftwareCategory(name="user-consented tracking", short_description="Collection of sensor data on (mobile) devices in accordance with data protection laws.")
+    cat_scraping = SoftwareCategory(name="scraping", short_description="Tools in the area of web-scraping")
+    cat_int = SoftwareCategory(name="tools for corpus linguistics", short_description="Integrated platforms for corpus analysis and processing.")
+    cat_qda = SoftwareCategory(name="computer assisted/aided qualitative data analysis software (CAQDAS)", short_description="assist with qualitative research such as transcription analysis, coding and text interpretation, recursive abstraction, content analysis, discourse analysis, grounded theory methodology, etc.")
+    cat_tm = SoftwareCategory(name="text mining/natuaral language processing(NLP)", short_description="")
+    cat_senti = SoftwareCategory(name="sentiment analysis", short_description="")
+    cat_topic = SoftwareCategory(name="topic-models", short_description="")
+    cat_visu = SoftwareCategory(name="visualization", short_description="")
+    cat_kollab_anno = SoftwareCategory(name="collaborative annotation", short_description="")
+    cat_kollab_write = SoftwareCategory(name="collaborative writing", short_description="")
+    cat_stat = SoftwareCategory(name="statistical software", short_description="software that helps calcualting with specific statistical models")
+    cat_repo = SoftwareCategory(name="research data archiving", short_description="")
+    cat_now = SoftwareCategory(name="nowcasting", short_description="")
+    cat_net = SoftwareCategory(name="network analysis", short_description="social network analysis")
+    cat_esmema = SoftwareCategory(name="ESM/EMA surveys", short_description="Datenerhebung in 'natürlicher' Umgebung.")
+    cat_transkript = SoftwareCategory(name="audio-transcriptions", short_description="software that converts speech into electronic text document.")
+    cat_search = SoftwareCategory(name="search", short_description="information retrieval in large datasets.")
+    cat_ocr = SoftwareCategory(name="optical character recognition (OCR)",short_description="OCR is the mechanical or electronic conversion of images of typed, handwritten or printed text into machine-encoded text.")
+    cat_oe = SoftwareCategory(name="online experiments", short_description="")
+    cat_misc = SoftwareCategory(name="miscellaneous", short_description="")
+
 
     db.session.add(cat_tracking)
     db.session.add(cat_scraping)
@@ -449,6 +334,17 @@ def build_sample_db():
     db.session.add(Link(software=tool, type="repository", url="https://github.com/audaciouscode/PassiveDataKit-Android", comment="android"))
     db.session.add(Link(software=tool, type="repository", url="https://github.com/audaciouscode/PassiveDataKit-iOS", comment="iOS"))
 
+    tool = Software(name="facepager",
+                    developer="Jakob Jünger and Till Keyling",
+                    maintainer="Jakob Jünger",
+                    softwarecategory=cat_scraping,
+                    architecture="package",
+                    license=lic_mit,
+                    programminglanguages=[prol_py])
+    db.session.add(tool)
+    db.session.add(Link(software=tool, type="wiki", url="https://github.com/strohne/Facepager", comment=""))
+    db.session.add(Link(software=tool, type="repository", url="https://github.com/strohne/Facepager", comment=""))
+
     tool = Software(name="Scrapy",
                     developer="",
                     maintainer="",
@@ -857,6 +753,18 @@ def build_sample_db():
     db.session.add(Link(software=tool, type="website", url="https://gephi.org/", comment=""))
     db.session.add(Link(software=tool, type="repository", url="https://github.com/gephi/gephi/", comment=""))
 
+    tool = Software(name="scikit-image",
+                    short_description="scikit-image is a collection of algorithms for image processing. It is available free of charge and free of restriction. We pride ourselves on high-quality, peer-reviewed code, written by an active community of volunteers.",
+                    developer="Stéfan van der Walt, Johannes L. Schönberger, Juan Nunez-Iglesias, François Boulogne, Joshua D. Warner, Neil Yager, Emmanuelle Gouillart, Tony Yu, and the scikit-image contributors",
+                    maintainer="Stéfan van der Walt, Johannes L. Schönberger, Juan Nunez-Iglesias, François Boulogne, Joshua D. Warner, Neil Yager, Emmanuelle Gouillart, Tony Yu, and the scikit-image contributors",
+                    softwarecategory=cat_visu,
+                    architecture="package",
+                    license=lic_bsd,
+                    programminglanguages=[prol_py])
+    db.session.add(tool)
+    db.session.add(Link(software=tool, type="website", url="https://scikit-image.org/", comment=""))
+    db.session.add(Link(software=tool, type="repository", url="https://github.com/scikit-image/scikit-image", comment=""))
+
     tool = Software(name="CATMA",
                     short_description="CATMA (Computer Assisted Text Markup and Analysis) is a practical and intuitive tool for text researchers. In CATMA users can combine the hermeneutic, ‘undogmatic’ and the digital, taxonomy based approach to text and corpora—as a single researcher, or in real-time collaboration with other team members.",
                     developer="",
@@ -939,7 +847,7 @@ def build_sample_db():
                     license=lic_prop,
                     programminglanguages=[])
     db.session.add(tool)
-    db.session.add(Link(software=tool, type="website", url="https://www.rrz.uni-hamburg.de/services/software/software-thematisch/statistik/stata.html", comment = "uhh"))
+    db.session.add(Link(software=tool, type="website", url="https://www.rrz.uni-hamburg.de/services/software/software-thematisch/statistik/spss-netzlizenz.html", comment = "uhh"))
 
 
     tool = Software(name="STATA",
@@ -951,7 +859,7 @@ def build_sample_db():
                     license=lic_prop,
                     programminglanguages=[])
     db.session.add(tool)
-    db.session.add(Link(software=tool, type="website", url="https://www.rrz.uni-hamburg.de/services/software/software-thematisch/statistik/spss-netzlizenz.html", comment = "uhh"))
+    db.session.add(Link(software=tool, type="website", url="https://www.rrz.uni-hamburg.de/services/software/software-thematisch/statistik/stata.html", comment = "uhh"))
 
     tool = Software(name="Nowcasting",
                     short_description="",
@@ -1079,6 +987,40 @@ def build_sample_db():
     db.session.add(Link(software=tool, type="website", url="", comment=""))
     db.session.add(Link(software=tool, type="repository", url="https://github.com/EXMARaLDA/exmaralda", comment=""))
 
+    tool = Software(name="tesseract",
+                    short_description="Tesseract is an open source text recognizer (OCR) Engine, available under the Apache 2.0 license. It can be used directly, or (for programmers) using an API to extract printed text from images. It supports a wide variety of languages.",
+                    developer="Google, HP Inc.",
+                    maintainer="Ray Smith u. a. ",
+                    softwarecategory=cat_ocr,
+                    architecture="package",
+                    license=lic_apache2,
+                    programminglanguages=[prol_py])
+    db.session.add(tool)
+    db.session.add(Link(software=tool, type="repository", url="https://github.com/tesseract-ocr/tesseract", comment=""))
+
+    tool = Software(name="nodeGame",
+                    short_description="NodeGame is a free, open source JavaScript/HTML5 framework for conducting synchronous experiments online and in the lab directly in the browser window. It is specifically designed to support behavioral research along three dimensions: larger group sizes, real-time (but also discrete time) experiments, batches of simultaneous experiments.",
+                    developer="Stefan Balietti",
+                    maintainer="Stefan Balietti",
+                    softwarecategory=cat_oe,
+                    architecture="package",
+                    license=lic_mit,
+                    programminglanguages=[prol_js])
+    db.session.add(tool)
+    db.session.add(Link(software=tool, type="website", url="https://nodegame.org/", comment=""))
+    db.session.add(Link(software=tool, type="repository", url="https://github.com/nodeGame", comment=""))
+
+    tool = Software(name="scikit-learn",
+                    short_description="Scikit-learn is a free software machine learning library for the Python programming language. It features various classification, regression and clustering algorithms including support vector machines, random forests, gradient boosting, k-means and DBSCAN, and is designed to interoperate with the Python numerical and scientific libraries NumPy and SciPy.",
+                    developer="Pedregosa, F. and Varoquaux, G. and Gramfort, A. and Michel, V. and Thirion, B. and Grisel, O. and Blondel, M. and Prettenhofer, P. and Weiss, R. and Dubourg, V. and Vanderplas, J. and Passos, A. and Cournapeau, D. and Brucher, M. and Perrot, M. and Duchesnay, E.",
+                    maintainer="Pedregosa, F. and Varoquaux, G. and Gramfort, A. and Michel, V. and Thirion, B. and Grisel, O. and Blondel, M. and Prettenhofer, P. and Weiss, R. and Dubourg, V. and Vanderplas, J. and Passos, A. and Cournapeau, D. and Brucher, M. and Perrot, M. and Duchesnay, E.",
+                    softwarecategory=cat_misc,
+                    architecture="package",
+                    license=lic_bsd,
+                    programminglanguages=[prol_py])
+    db.session.add(tool)
+    db.session.add(Link(software=tool, type="website", url="https://scikit-learn.org/stable/index.html", comment=""))
+    db.session.add(Link(software=tool, type="repository", url="https://github.com/scikit-learn/scikit-learn", comment=""))
 
     '''
      tool = Software(name="",
diff --git a/sample_db.sqlite b/sample_db.sqlite
index 70a3bda9555a978394356aff339af7e3870bd091..71c57ac22da4c8b03f75d19b5e73b215f671f0a8 100644
GIT binary patch
delta 7836
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
S%$g}alfxG#uxOTMWB>p$i<ajA

delta 4411
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
K;A7-x)C2(C)8SbF

diff --git a/templates/export/softwares.jinja2 b/templates/export/softwares.jinja2
index d96d39f..c355f74 100644
--- a/templates/export/softwares.jinja2
+++ b/templates/export/softwares.jinja2
@@ -1,3 +1,9 @@
+:toc:
+:toclevels: 8
+:toc-title: Categories (work in progress: this list is preliminary and will be updated continuously)
+:sectnums:
+:sectnumlevels: 8
+
 {% for softwarecategory in softwareincategory %}
 == {{ softwarecategory[0].name }}
 _{{softwarecategory[0].short_description}}_
-- 
GitLab