From 9904e54c51f7f47012856f71cd4f8da78b023425 Mon Sep 17 00:00:00 2001 From: Patrick Jentsch <p.jentsch@uni-bielefeld.de> Date: Thu, 15 Dec 2022 09:53:19 +0100 Subject: [PATCH] Add dashboard2 --- app/main/routes.py | 6 + app/models.py | 16 ++- app/templates/main/dashboard2.html.j2 | 178 ++++++++++++++++++++++++++ 3 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 app/templates/main/dashboard2.html.j2 diff --git a/app/main/routes.py b/app/main/routes.py index 1e7665a3..7e42d60b 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -30,6 +30,12 @@ def dashboard(): return render_template('main/dashboard.html.j2', title='Dashboard') +@bp.route('/dashboard2') +@login_required +def dashboard2(): + return render_template('main/dashboard2.html.j2', title='Dashboard') + + @bp.route('/user_manual') def user_manual(): return render_template('main/user_manual.html.j2', title='User manual') diff --git a/app/models.py b/app/models.py index d28331a0..aa5b2faa 100644 --- a/app/models.py +++ b/app/models.py @@ -226,6 +226,7 @@ class Role(HashidMixin, db.Model): db.session.add(role) db.session.commit() + class Token(db.Model): __tablename__ = 'tokens' # Primary key @@ -249,31 +250,33 @@ class Token(db.Model): yesterday = datetime.utcnow() - timedelta(days=1) Token.query.filter(Token.refresh_expiration < yesterday).delete() + class Avatar(HashidMixin, FileMixin, db.Model): __tablename__ = 'avatars' # Primary key id = db.Column(db.Integer, primary_key=True) # Foreign keys user_id = db.Column(db.Integer, db.ForeignKey('users.id')) + # Backrefs: user: User @property def path(self): return os.path.join(self.user.path, 'avatar') - + def delete(self): try: os.remove(self.path) except OSError as e: current_app.logger.error(e) db.session.delete(self) - + + class User(HashidMixin, UserMixin, db.Model): __tablename__ = 'users' # Primary key id = db.Column(db.Integer, primary_key=True) # Foreign keys role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) - # privacy_id = db.Column(db.Integer, db.ForeignKey('privacies.id')) # Fields email = db.Column(db.String(254), index=True, unique=True) username = db.Column(db.String(64), index=True, unique=True) @@ -555,7 +558,6 @@ class User(HashidMixin, UserMixin, db.Model): 'show_email': self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_EMAIL), 'show_last_seen': self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_LAST_SEEN), 'show_member_since': self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_MEMBER_SINCE) - } if backrefs: json_serializeable['role'] = \ @@ -579,6 +581,7 @@ class User(HashidMixin, UserMixin, db.Model): } return json_serializeable + class TesseractOCRPipelineModel(FileMixin, HashidMixin, db.Model): __tablename__ = 'tesseract_ocr_pipeline_models' # Primary key @@ -869,7 +872,7 @@ class JobInput(FileMixin, HashidMixin, db.Model): @property def user_id(self): - return self.job.user_id + return self.job.user.id def to_json_serializeable(self, backrefs=False, relationships=False): json_serializeable = { @@ -925,7 +928,7 @@ class JobResult(FileMixin, HashidMixin, db.Model): @property def user_id(self): - return self.job.user_id + return self.job.user.id def to_json_serializeable(self, backrefs=False, relationships=False): json_serializeable = { @@ -1166,6 +1169,7 @@ class CorpusFile(FileMixin, HashidMixin, db.Model): self.corpus.to_json_serializeable(backrefs=True) return json_serializeable + class Corpus(HashidMixin, db.Model): ''' Class to define a corpus. diff --git a/app/templates/main/dashboard2.html.j2 b/app/templates/main/dashboard2.html.j2 new file mode 100644 index 00000000..47a7e236 --- /dev/null +++ b/app/templates/main/dashboard2.html.j2 @@ -0,0 +1,178 @@ +{% extends "base.html.j2" %} +{% from "main/_breadcrumbs.html.j2" import breadcrumbs with context %} + +{% block page_content %} +<div class="corpus-list no-autoinit" id="corpus-list" data-user-id="{{ current_user.hashid }}"> + <div class="parallax-container"> + <div class="parallax"><img src="{{ url_for('static', filename='images/parallax_hq/canvas.png') }}"></div> + <div style="position: absolute; bottom: 0; width: 100%;"> + <div class="container"> + <div class="white" style="padding: 1px 35px 0 10px; border-radius: 35px;"> + <div class="input-field"> + <i class="material-icons prefix">search</i> + <input class="search" id="corpus-list-search" type="text"> + <label for="corpus-list-search">Search Corpus</label> + </div> + </div> + </div> + </div> + </div> + + <div class="section"> + <div class="row"> + <div class="col s1"></div> + <div class="col s3"> + <h2 id="corpora">My Corpora</h1> + <p>Create a corpus to interactively perform linguistic analysis.</p> + <p>Or browse our users public corpora.<span class="new badge"></span></p> + </div> + <div class="col s7"> + <div class="card"> + <div class="card-content"> + <div> + <table> + <thead> + <tr> + <th></th> + <th>Title and Description</th> + <th>Status</th> + <th></th> + </tr> + </thead> + <tbody class="list"></tbody> + </table> + <ul class="pagination"></ul> + </div> + </div> + <div class="card-action right-align"> + <a class="btn disabled waves-effect waves-light" href="{{ url_for('corpora.import_corpus') }}">Import Corpus<i class="material-icons right">import_export</i></a> + <a class="btn waves-effect waves-light" href="{{ url_for('corpora.create_corpus') }}">Create corpus<i class="material-icons right">add</i></a> + </div> + </div> + </div> + </div> + </div> +</div> + +<div class="job-list no-autoinit" id="job-list" data-user-id="{{ current_user.hashid }}"> + <div class="parallax-container"> + <div class="parallax"><img src="{{ url_for('static', filename='images/parallax_hq/canvas.png') }}"></div> + <div style="position: absolute; bottom: 0; width: 100%;"> + <div class="container"> + <div class="white" style="padding: 1px 35px 0 10px; border-radius: 35px;"> + <div class="input-field"> + <i class="material-icons prefix">search</i> + <input class="search" id="job-list-search" type="text"> + <label for="job-list-search">Search Job</label> + </div> + </div> + </div> + </div> + </div> + + <div class="section"> + <div class="row"> + <div class="col s1"></div> + <div class="col s3"> + <h2 id="jobs">My Jobs</h1> + <p> + A job is the execution of a service provided by nopaque. You can + create any number of jobs and let them be processed simultaneously. We + <b>strongly recommend</b> that you create a folder on your computer where you + save the various files that nopaque provides you with after each + pre-processing step. You will need the result of each step for the + next step. + </p> + <p><b>Where is my Job data?</b> Don't worry, please read <a href="{{ url_for('main.news', _anchor='april-2022-update') }}">this news</a> entry</p> + </div> + <div class="col s7"> + <div class="card"> + <div class="card-content"> + <div> + <table> + <thead> + <tr> + <th></th> + <th>Title and Description</th> + <th>Status</th> + <th></th> + </tr> + </thead> + <tbody class="list"></tbody> + </table> + <ul class="pagination"></ul> + </div> + </div> + <div class="card-action right-align"> + <a class="btn modal-trigger waves-effect waves-light" data-target="create-job-modal"><i class="material-icons left">add</i>Create job</a> + </div> + </div> + </div> + </div> + </div> +</div> +{% endblock page_content %} + +{% block modals %} +{{ super() }} +<div id="create-job-modal" class="modal"> + <div class="modal-content"> + <h4>Select a service</h4> + <p> </p> + <div class="row"> + <div class="col s12 m4"> + <div class="card-panel center-align hoverable"> + <br> + <a href="{{ url_for('services.file_setup_pipeline') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);"> + <i class="nopaque-icons service-color darken service-icon" data-service="file-setup-pipeline"></i> + </a> + <br><br> + <p class="service-color-text darken" data-service="file-setup-pipeline"><b>File setup</b></p> + <p class="light">Digital copies of text based research data (books, letters, etc.) often comprise various files and formats. nopaque converts and merges those files to facilitate further processing.</p> + <a href="{{ url_for('services.file_setup_pipeline') }}" class="waves-effect waves-light btn service-color darken" data-service="file-setup-pipeline">Create Job</a> + </div> + </div> + <div class="col s12 m4"> + <div class="card-panel center-align hoverable"> + <br> + <a href="{{ url_for('services.tesseract_ocr_pipeline') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);"> + <i class="nopaque-icons service-color darken service-icon" data-service="tesseract-ocr-pipeline" style="font-size: 2.5rem;"></i> + </a> + <br><br> + <p class="service-color-text darken" data-service="tesseract-ocr-pipeline"><b>Optical Character Recognition</b></p> + <p class="light">nopaque converts your image data – like photos or scans – into text data through a process called OCR. This step enables you to proceed with further computational analysis of your documents.</p> + <a href="{{ url_for('services.tesseract_ocr_pipeline') }}" class="waves-effect waves-light btn service-color darken" data-service="tesseract-ocr-pipeline">Create Job</a> + </div> + </div> + <div class="col s12 m4"> + <div class="card-panel center-align hoverable"> + <br> + <a href="{{ url_for('services.spacy_nlp_pipeline') }}" class="btn-floating btn-large waves-effect waves-light" style="transform: scale(2);"> + <i class="nopaque-icons service-color darken service-icon" data-service="spacy-nlp-pipeline" style="font-size: 2.5rem;"></i> + </a> + <br><br> + <p class="service-color-text darken" data-service="spacy-nlp-pipeline"><b>Natural Language Processing</b></p> + <p class="light">By means of computational linguistic data processing (tokenization, lemmatization, part-of-speech tagging and named-entity recognition) nopaque extracts additional information from your text.</p> + <a href="{{ url_for('services.spacy_nlp_pipeline') }}" class="waves-effect waves-light btn service-color darken" data-service="spacy-nlp-pipeline">Create Job</a> + </div> + </div> + </div> + </div> + <div class="modal-footer"> + <a class="btn-flat modal-close waves-effect waves-light">Close</a> + </div> +</div> +{% endblock modals %} + + +{% block scripts %} +{{ super() }} +<script> + let corpusListElement = document.querySelector('#corpus-list'); + let corpusListOptions = {initialHtmlGenerator: null}; + let corpusList = new CorpusList(corpusListElement, corpusListOptions); + let jobListElement = document.querySelector('#job-list'); + let jobListOptions = {initialHtmlGenerator: null}; + let jobList = new JobList(jobListElement, jobListOptions); +</script> +{% endblock scripts %} -- GitLab