Rework corpora package

This commit is contained in:
Patrick Jentsch
2023-03-10 10:33:11 +01:00
parent 3789f61ca4
commit aaf14a9952
27 changed files with 654 additions and 731 deletions

View File

@ -14,7 +14,7 @@
<li class="tab"><a{%if request.path == url_for('corpora.create_corpus') %} class="active"{% endif %} href="{{ url_for('corpora.create_corpus') }}" target="_self">Create corpus</a></li>
<li class="tab disabled"><i class="material-icons">navigate_next</i></li>
{% if corpus %}
<li class="tab"><a{%if request.path == url_for('corpora.create_corpus_file', corpus_id=corpus.id) %} class="active"{% endif %} href="{{ url_for('corpora.create_corpus_file', corpus_id=corpus.id) }}" target="_self">Create corpus file(s)</a></li>
<li class="tab"><a{%if request.path == url_for('corpora.files.create_corpus_file', corpus_id=corpus.id) %} class="active"{% endif %} href="{{ url_for('corpora.files.create_corpus_file', corpus_id=corpus.id) }}" target="_self">Create corpus file(s)</a></li>
{% else %}
<li class="tab disabled tooltipped" data-tooltip="Select a corpus first" target="_self"><a>Create corpus file(s)</a></li>
{% endif %}

View File

@ -49,4 +49,8 @@
{% block scripts %}
{{ super() }}
{% include "_scripts.html.j2" %}
{% set page_script = self._TemplateReference__context.name|replace('.html.j2', '.js.j2') %}
<script>
{% include page_script ignore missing %}
</script>
{% endblock scripts %}

View File

@ -79,37 +79,3 @@
</div>
</div>
{% endblock page_content %}
{% block modals %}
{{ super() }}
<div id="models-modal" class="modal">
<div class="modal-content">
<h4>Tesseract OCR Pipeline models</h4>
<table>
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Biblio</th>
</tr>
</thead>
<tbody>
{% for m in tesseract_ocr_pipeline_models %}
<tr id="tesseract-ocr-pipeline-model-{{ m.hashid }}">
<td>{{ m.title }}</td>
{% if m.description == '' %}
<td>Description is not available.</td>
{% else %}
<td>{{ m.description }}</td>
{% endif %}
<td><a href="{{ m.publisher_url }}">{{ m.publisher }}</a> ({{ m.publishing_year }}), {{ m.title }} {{ m.version}}, <a href="{{ m.publishing_url }}">{{ m.publishing_url }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="modal-footer">
<a href="#!" class="modal-close waves-effect waves-light btn">Close</a>
</div>
</div>
{% endblock modals %}

View File

@ -1,4 +1,4 @@
{% set breadcrumbs %}
{# {% set breadcrumbs %}
<li class="tab disabled"><i class="material-icons">navigate_next</i></li>
<li class="tab"><a href="{{ url_for('main.dashboard', _anchor='jobs') }}" target="_self">My corpora</a></li>
<li class="tab disabled"><i class="material-icons">navigate_next</i></li>
@ -25,4 +25,4 @@
<li class="tab disabled"><i class="material-icons">navigate_next</i></li>
<li class="tab"><a class="active" href="{{ url_for('.corpus_file', corpus_file_id=corpus_file.id, corpus_id=corpus.id) }}" target="_self">{{ corpus_file.author }}: {{ corpus_file.title }} ({{ corpus_file.publishing_year }})</a></li>
{% endif %}
{% endset %}
{% endset %} #}

View File

@ -91,7 +91,7 @@
<div class="corpus-file-list" data-user-id="{{ corpus.user.hashid }}" data-corpus-id="{{ corpus.hashid }}"></div>
</div>
<div class="card-action right-align">
<a href="{{ url_for('corpora.create_corpus_file', corpus_id=corpus.id) }}" class="btn waves-effect waves-light"><i class="material-icons left">add</i>Add corpus file</a>
<a href="{{ url_for('corpora.files.create_corpus_file', corpus_id=corpus.id) }}" class="btn waves-effect waves-light"><i class="material-icons left">add</i>Add corpus file</a>
</div>
</div>
</div>
@ -215,132 +215,3 @@
</div>
</div>
{% endblock modals %}
{% block scripts %}
{{ super() }}
<script>
let corpusId = {{ corpus.hashid|tojson }};
let corpusDisplay = new CorpusDisplay(document.querySelector('#corpus-display'));
// #region publishing_modal_js
let publishingModalIsPublicSwitchElement = document.querySelector('#publishing-modal-is-public-switch');
publishingModalIsPublicSwitchElement.addEventListener('change', (event) => {
let newIsPublic = publishingModalIsPublicSwitchElement.checked;
Requests.corpora.entity.isPublic.update(corpusId, newIsPublic)
.catch((response) => {
publishingModalIsPublicSwitchElement.checked = !newIsPublic;
});
});
// #endregion publishing_modal_js
// #region delete_modal_js
let deleteModalDeleteButtonElement = document.querySelector('#delete-modal-delete-button');
deleteModalDeleteButtonElement.addEventListener('click', (event) => {
Requests.corpora.entity.delete(corpusId)
.then((response) => {window.location.href = '/dashboard';});
});
// #endregion delete_modal_js
// #region invite_user_modal_js
let inviteUserModalElement = document.querySelector('#invite-user-modal');
let inviteUserModalSearchElement = document.querySelector('#invite-user-modal-search');
let inviteUserModalInviteButtonElement = document.querySelector('#invite-user-modal-invite-button');
let inviteUserModalSearch = M.Chips.init(
inviteUserModalSearchElement,
{
autocompleteOptions: {
data: {
'nopaque': '/users/3V8Aqpg74JvxOd9o/avatar',
'pjentsch': '/users/3V8Aqpg74JvxOd9o/avatar',
'pjentsch2': '/users/3V8Aqpg74JvxOd9o/avatar'
}
},
limit: 3,
onChipAdd: (a, chipElement) => {
if (!(chipElement.firstChild.data in inviteUserModalSearch.autocomplete.options.data)) {
chipElement.firstElementChild.click();
}
},
placeholder: 'Enter a username',
secondaryPlaceholder: 'Add more users'
}
);
M.Modal.init(
inviteUserModalElement,
{
onOpenStart: (modalElement, modalTriggerElement) => {
while (inviteUserModalSearch.chipsData.length > 0) {
inviteUserModalSearch.deleteChip(0);
}
}
}
)
inviteUserModalInviteButtonElement.addEventListener('click', (event) => {
let usernames = inviteUserModalSearch.chipsData.map((chipData) => chipData.tag);
Requests.corpora.entity.followers.add(corpusId, usernames);
});
// #endregion invite_user_modal_js
// #region share_link_modal_js
let shareLinkModalElement = document.querySelector('#share-link-modal');
let shareLinkModalCorpusFollowerRoleSelectElement = document.querySelector('#share-link-modal-corpus-follower-role-select');
let shareLinkModalExpirationDateDatepickerElement = document.querySelector('#share-link-modal-expiration-date-datepicker');
let shareLinkModalCreateButtonElement = document.querySelector('#share-link-modal-create-button');
let shareLinkModalOutputContainerElement = document.querySelector('#share-link-modal-output-container');
let shareLinkModalOutputFieldElement = document.querySelector('#share-link-modal-output-field');
let shareLinkModalOutputCopyButtonElement = document.querySelector('#share-link-modal-output-copy-button');
let today = new Date();
let tomorrow = new Date();
tomorrow.setDate(today.getDate() + 1);
let oneWeekLater = new Date();
oneWeekLater.setDate(today.getDate() + 7);
let fourWeeksLater = new Date();
fourWeeksLater.setDate(today.getDate() + 28);
M.Datepicker.init(
shareLinkModalExpirationDateDatepickerElement,
{
container: document.querySelector('main'),
defaultDate: oneWeekLater,
setDefaultDate: true,
minDate: tomorrow,
maxDate: fourWeeksLater
}
);
M.Modal.init(
shareLinkModalElement,
{
onOpenStart: (modalElement, modalTriggerElement) => {
shareLinkModalOutputFieldElement.value = '';
shareLinkModalOutputContainerElement.classList.add('hide');
}
}
)
shareLinkModalCreateButtonElement.addEventListener('click', (event) => {
Requests.corpora.entity.generateShareLink(corpusId, shareLinkModalCorpusFollowerRoleSelectElement.value, shareLinkModalExpirationDateDatepickerElement.value)
.then((response) => {
response.json()
.then((json) => {
shareLinkModalOutputContainerElement.classList.remove('hide');
shareLinkModalOutputFieldElement.value = json.corpusShareLink;
});
});
});
shareLinkModalOutputCopyButtonElement.addEventListener('click', (event) => {
navigator.clipboard.writeText(shareLinkModalOutputFieldElement.value)
.then(
() => {app.flash('Copied!');},
() => {app.flash('Could not copy to clipboard. Please copy manually.', 'error');}
);
});
// #endregion share_link_modal_js
</script>
{% endblock scripts %}

View File

@ -0,0 +1,127 @@
let corpusId = {{ corpus.hashid|tojson }};
let corpusDisplay = new CorpusDisplay(document.querySelector('#corpus-display'));
// #region Publishing
let publishingModalIsPublicSwitchElement = document.querySelector('#publishing-modal-is-public-switch');
publishingModalIsPublicSwitchElement.addEventListener('change', (event) => {
let newIsPublic = publishingModalIsPublicSwitchElement.checked;
Requests.corpora.entity.isPublic.update(corpusId, newIsPublic)
.catch((response) => {
publishingModalIsPublicSwitchElement.checked = !newIsPublic;
});
});
// #endregion Publishing
// #region Delete
let deleteModalDeleteButtonElement = document.querySelector('#delete-modal-delete-button');
deleteModalDeleteButtonElement.addEventListener('click', (event) => {
Requests.corpora.entity.delete(corpusId)
.then((response) => {
window.location.href = {{ url_for('main.dashboard')|tojson }};
});
});
// #endregion Delete
// #region Invite users
let inviteUserModalElement = document.querySelector('#invite-user-modal');
let inviteUserModalSearchElement = document.querySelector('#invite-user-modal-search');
let inviteUserModalInviteButtonElement = document.querySelector('#invite-user-modal-invite-button');
let inviteUserModalSearch = M.Chips.init(
inviteUserModalSearchElement,
{
autocompleteOptions: {
data: {
'nopaque': '/users/3V8Aqpg74JvxOd9o/avatar',
'pjentsch': '/users/3V8Aqpg74JvxOd9o/avatar',
'pjentsch2': '/users/3V8Aqpg74JvxOd9o/avatar'
}
},
limit: 3,
onChipAdd: (a, chipElement) => {
if (!(chipElement.firstChild.data in inviteUserModalSearch.autocomplete.options.data)) {
chipElement.firstElementChild.click();
}
},
placeholder: 'Enter a username',
secondaryPlaceholder: 'Add more users'
}
);
M.Modal.init(
inviteUserModalElement,
{
onOpenStart: (modalElement, modalTriggerElement) => {
while (inviteUserModalSearch.chipsData.length > 0) {
inviteUserModalSearch.deleteChip(0);
}
}
}
)
inviteUserModalInviteButtonElement.addEventListener('click', (event) => {
let usernames = inviteUserModalSearch.chipsData.map((chipData) => chipData.tag);
Requests.corpora.entity.followers.add(corpusId, usernames);
});
// #endregion Invite users
// #region Share link
let shareLinkModalElement = document.querySelector('#share-link-modal');
let shareLinkModalCorpusFollowerRoleSelectElement = document.querySelector('#share-link-modal-corpus-follower-role-select');
let shareLinkModalExpirationDateDatepickerElement = document.querySelector('#share-link-modal-expiration-date-datepicker');
let shareLinkModalCreateButtonElement = document.querySelector('#share-link-modal-create-button');
let shareLinkModalOutputContainerElement = document.querySelector('#share-link-modal-output-container');
let shareLinkModalOutputFieldElement = document.querySelector('#share-link-modal-output-field');
let shareLinkModalOutputCopyButtonElement = document.querySelector('#share-link-modal-output-copy-button');
let today = new Date();
let tomorrow = new Date();
tomorrow.setDate(today.getDate() + 1);
let oneWeekLater = new Date();
oneWeekLater.setDate(today.getDate() + 7);
let fourWeeksLater = new Date();
fourWeeksLater.setDate(today.getDate() + 28);
M.Datepicker.init(
shareLinkModalExpirationDateDatepickerElement,
{
container: document.querySelector('main'),
defaultDate: oneWeekLater,
setDefaultDate: true,
minDate: tomorrow,
maxDate: fourWeeksLater
}
);
M.Modal.init(
shareLinkModalElement,
{
onOpenStart: (modalElement, modalTriggerElement) => {
shareLinkModalOutputFieldElement.value = '';
shareLinkModalOutputContainerElement.classList.add('hide');
}
}
)
shareLinkModalCreateButtonElement.addEventListener('click', (event) => {
let role = shareLinkModalCorpusFollowerRoleSelectElement.value;
let expiration = shareLinkModalExpirationDateDatepickerElement.value
Requests.corpora.entity.generateShareLink(corpusId, role, expiration)
.then((response) => {
response.json()
.then((json) => {
shareLinkModalOutputContainerElement.classList.remove('hide');
shareLinkModalOutputFieldElement.value = json.corpusShareLink;
});
});
});
shareLinkModalOutputCopyButtonElement.addEventListener('click', (event) => {
navigator.clipboard.writeText(shareLinkModalOutputFieldElement.value)
.then(
() => {app.flash('Copied!');},
() => {app.flash('Could not copy to clipboard. Please copy manually.', 'error');}
);
});
// #endregion Share link

View File

@ -36,7 +36,7 @@
</div>
</div>
<div class="card-action right-align">
<a class="waves-effect waves-light btn red" href="{{ url_for('.corpus', corpus_id=corpus.id) }}"><i class="material-icons left">close</i>Cancel</a>
<a class="waves-effect waves-light btn red" href="{{ url_for('corpora.corpus', corpus_id=corpus.id) }}"><i class="material-icons left">close</i>Cancel</a>
{{ wtf.render_field(form.submit, material_icon='send') }}
</div>
</div>

View File

@ -1,72 +0,0 @@
{% extends "base.html.j2" %}
{% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %}
{% block page_content %}
<div class="corpus-list no-autoinit" id="corpus-list">
<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-text">
<h1 id="title"><i class="nopaque-icons" style="font-size: inherit;">I</i>Corpora</h1>
</div>
<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="row">
<div class="col s12" id="corpora">
<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>
</div>
</div>
</div>
{% endblock page_content %}
{% block scripts %}
{{ super() }}
<script>
let corpusListElement = document.querySelector('#corpus-list');
let corpusListOptions = {
initialHtmlGenerator: null,
item: `
<tr class="clickable hoverable">
<td><a class="btn-floating disabled"><i class="material-icons service-color darken" data-service="corpus-analysis">book</i></a></td>
<td><b class="title"></b><br><i class="description"></i></td>
<td><span class="status badge new corpus-status-color corpus-status-text" data-badge-caption=""></span></td>
<td class="right-align">
<a class="action-button btn-floating service-color darken waves-effect waves-light" data-action="view" data-service="corpus-analysis"><i class="material-icons">send</i></a>
</td>
</tr>
`.trim(),
};
let corpusList = new CorpusList(corpusListElement, corpusListOptions);
corpusList._init({{ corpora|tojson }});
</script>
{% endblock scripts %}

View File

@ -101,30 +101,3 @@
</div>
</div>
{% endblock page_content %}
{% block scripts %}
{{ super() }}
<script>
let corpusFileList = new PublicCorpusFileList(document.querySelector('.corpus-file-list'));
corpusFileList.add({{ corpus_files|tojson }});
let unfollowRequestElement = document.querySelector('.action-button[data-action="unfollow-request"]');
unfollowRequestElement.addEventListener('click', () => {
return new Promise((resolve, reject) => {
fetch('{{ url_for("corpora.unfollow_corpus", corpus_id=corpus.id, follower_id=current_user.id) }}', {method: 'POST', headers: {Accept: 'application/json'}})
.then(
(response) => {
if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);}
if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);}
resolve(response);
window.location.href = '{{ url_for("main.dashboard") }}';
},
(response) => {
app.flash('Something went wrong', 'error');
reject(response);
}
);
});
});
</script>
{% endblock scripts %}

View File

@ -0,0 +1,11 @@
let corpusId = {{ corpus.hashid|tojson }};
let corpusFileList = new PublicCorpusFileList(document.querySelector('.corpus-file-list'));
corpusFileList.add({{ corpus_files|tojson }});
let unfollowRequestElement = document.querySelector('.action-button[data-action="unfollow-request"]');
unfollowRequestElement.addEventListener('click', () => {
Requests.corpora.entity.followers.entity.delete(corpusId, currentUserId)
.then((response) => {
window.location.href = {{ url_for('main.dashboard')|tojson }};
});
});

View File

@ -77,7 +77,7 @@
{{ form.model.label }}
<span class="helper-text">
<a class="modal-trigger tooltipped" href="#models-modal" data-position="bottom" data-tooltip="See more information about models"><i class="material-icons" style="color:#0064A3;">help_outline</i></a>
<a class="tooltipped" href="{{ url_for('contributions.create_spacy_nlp_pipeline_model') }}" data-position="bottom" data-tooltip="Add your own spaCy NLP models"><i class="material-icons" style="color:#0064A3">new_label</i></a>
<a class="tooltipped" href="{{ url_for('contributions.spacy_nlp_pipeline_models.create_spacy_nlp_pipeline_model') }}" data-position="bottom" data-tooltip="Add your own spaCy NLP models"><i class="material-icons" style="color:#0064A3">new_label</i></a>
</span>
</div>
</div>

View File

@ -59,7 +59,7 @@
{{ form.model.label }}
<span class="helper-text">
<a class="modal-trigger tooltipped" href="#models-modal" data-position="bottom" data-tooltip="See more information about models"><i class="material-icons" style="color:#00A58B;">help_outline</i></a>
<a class="tooltipped" href="{{ url_for('contributions.create_tesseract_ocr_pipeline_model') }}" data-position="bottom" data-tooltip="Add your own Tesseract OCR models"><i class="material-icons" style="color:#00A58B">new_label</i></a>
<a class="tooltipped" href="{{ url_for('contributions.tesseract_ocr_pipeline_models.create_tesseract_ocr_pipeline_model') }}" data-position="bottom" data-tooltip="Add your own Tesseract OCR models"><i class="material-icons" style="color:#00A58B">new_label</i></a>
</span>
{% for error in form.model.errors %}
<span class="helper-text error-color-text">{{ error }}</span>