Skip to content
Snippets Groups Projects
Commit 59de68e6 authored by Patrick Jentsch's avatar Patrick Jentsch
Browse files

More rework

parent fdad1099
No related branches found
No related tags found
No related merge requests found
......@@ -116,20 +116,6 @@ def analyse_corpus(corpus_id):
)
@bp.route('/<hashid:corpus_id>/generate-corpus-share-link', methods=['GET', 'POST'])
@login_required
@corpus_follower_permission_required('GENERATE_SHARE_LINK')
def generate_corpus_share_link(corpus_id):
corpus_hashid = hashids.encode(corpus_id)
data = request.get_json('data')
role_name = data['role']
exp_data = data['expiration']
expiration = datetime.strptime(exp_data, '%b %d, %Y')
token = current_user.generate_follow_corpus_token(corpus_hashid, role_name, expiration)
link = url_for('corpora.follow_corpus', corpus_id=corpus_id, token=token, _external=True)
return link
@bp.route('/<hashid:corpus_id>/follow/<token>')
@login_required
def follow_corpus(corpus_id, token):
......@@ -210,6 +196,43 @@ def build_corpus(corpus_id):
return response
@bp.route('/<hashid:corpus_id>/generate-corpus-share-link', methods=['POST'])
@login_required
@corpus_follower_permission_required('GENERATE_SHARE_LINK')
@content_negotiation(consumes='application/json', produces='application/json')
def generate_corpus_share_link(corpus_id):
corpus_hashid = hashids.encode(corpus_id)
data = request.json
if not isinstance(data, dict):
abort(400)
expiration = data.get('expiration')
if not isinstance(expiration, str):
abort(400)
role_name = data.get('role')
if not isinstance(role_name, str):
abort(400)
expiration_date = datetime.strptime(expiration, '%b %d, %Y')
cfr = CorpusFollowerRole.query.filter_by(name=role_name).first()
if cfr is None:
abort(400)
token = current_user.generate_follow_corpus_token(corpus_hashid, role_name, expiration_date)
corpus_share_link = url_for(
'corpora.follow_corpus',
corpus_id=corpus_id,
token=token,
_external=True
)
response_data = {
'message': 'Corpus share link generated',
'category': 'corpus',
'corpusShareLink': corpus_share_link
}
response = jsonify(response_data)
response.status_code = 200
return response
@bp.route('/<hashid:corpus_id>/is_public', methods=['PUT'])
@login_required
@corpus_owner_or_admin_required
......@@ -405,7 +428,9 @@ def add_permission(corpus_id, follower_id):
role_name = request.json
if not isinstance(role_name, str):
abort(400)
cfr = CorpusFollowerRole.query.filter_by(name=role_name).first_or_404()
cfr = CorpusFollowerRole.query.filter_by(name=role_name).first()
if cfr is None:
abort(400)
cfa = CorpusFollowerAssociation.query.filter_by(corpus_id=corpus_id, follower_id=follower_id).first_or_404()
cfa.role = cfr
db.session.commit()
......
......@@ -11,6 +11,14 @@ Requests.JSONfetch = (input, init={}) => {
fetch(input, Utils.mergeObjectsDeep(init, fixedInit))
.then(
(response) => {
if (response.ok) {
resolve(response.clone());
} else {
reject(response);
}
if (response.status === 204) {
return;
}
response.json()
.then(
(json) => {
......@@ -22,11 +30,6 @@ Requests.JSONfetch = (input, init={}) => {
app.flash(`[${response.status}]: ${response.statusText}`, 'error');
}
);
if (response.ok) {
resolve(response);
} else {
reject(response);
}
},
(response) => {
app.flash('Something went wrong', 'error');
......
......@@ -4,9 +4,9 @@
*****************************************************************************/
Requests.corpora = {};
Requests.corpora.ent = {};
Requests.corpora.entity = {};
Requests.corpora.ent.delete = (corpusId) => {
Requests.corpora.entity.delete = (corpusId) => {
let input = `/corpora/${corpusId}`;
let init = {
method: 'DELETE'
......@@ -14,7 +14,7 @@ Requests.corpora.ent.delete = (corpusId) => {
return Requests.JSONfetch(input, init);
};
Requests.corpora.ent.build = (corpusId) => {
Requests.corpora.entity.build = (corpusId) => {
let input = `/corpora/${corpusId}/build`;
let init = {
method: 'POST',
......@@ -22,57 +22,25 @@ Requests.corpora.ent.build = (corpusId) => {
return Requests.JSONfetch(input, init);
};
Requests.corpora.ent.isPublic = {};
Requests.corpora.ent.isPublic.update = (corpusId, value) => {
let input = `/corpora/${corpusId}/is_public`;
let init = {
method: 'PUT',
body: JSON.stringify(value)
};
return Requests.JSONfetch(input, init);
};
Requests.corpora.ent.files = {};
Requests.corpora.ent.files.ent = {};
Requests.corpora.ent.files.ent.delete = (corpusId, corpusFileId) => {
let input = `/corpora/${corpusId}/files/${corpusFileId}`;
let init = {
method: 'DELETE',
};
return Requests.JSONfetch(input, init);
};
Requests.corpora.ent.followers = {};
Requests.corpora.ent.followers.add = (corpusId, usernames) => {
let input = `/corpora/${corpusId}/followers`;
Requests.corpora.entity.generateShareLink = (corpusId, role, expiration) => {
let input = `/corpora/${corpusId}/generate-corpus-share-link`;
let init = {
method: 'POST',
body: JSON.stringify(usernames)
body: JSON.stringify({role: role, expiration: expiration})
};
return Requests.JSONfetch(input, init);
};
Requests.corpora.ent.followers.ent = {};
Requests.corpora.entity.isPublic = {};
Requests.corpora.ent.followers.ent.delete = (corpusId, followerId) => {
let input = `/corpora/${corpusId}/followers/${followerId}`;
let init = {
method: 'DELETE',
};
return Requests.JSONfetch(input, init);
};
Requests.corpora.ent.followers.ent.role = {};
Requests.corpora.ent.followers.ent.role.update = (corpusId, followerId, value) => {
let input = `/corpora/${corpusId}/followers/${followerId}/role`;
Requests.corpora.entity.isPublic.update = (corpusId, value) => {
let input = `/corpora/${corpusId}/is_public`;
let init = {
method: 'PUT',
body: JSON.stringify(value)
};
return Requests.JSONfetch(input, init);
};
/*****************************************************************************
* Corpora *
* Fetch requests for /corpora/<entity>/files routes *
*****************************************************************************/
Requests.corpora.entity.files = {};
Requests.corpora.entity.files.ent = {};
Requests.corpora.entity.files.ent.delete = (corpusId, corpusFileId) => {
let input = `/corpora/${corpusId}/files/${corpusFileId}`;
let init = {
method: 'DELETE',
};
return Requests.JSONfetch(input, init);
};
/*****************************************************************************
* Corpora *
* Fetch requests for /corpora/<entity>/followers routes *
*****************************************************************************/
Requests.corpora.entity.followers = {};
Requests.corpora.entity.followers.add = (corpusId, usernames) => {
let input = `/corpora/${corpusId}/followers`;
let init = {
method: 'POST',
body: JSON.stringify(usernames)
};
return Requests.JSONfetch(input, init);
};
Requests.corpora.entity.followers.entity = {};
Requests.corpora.entity.followers.entity.delete = (corpusId, followerId) => {
let input = `/corpora/${corpusId}/followers/${followerId}`;
let init = {
method: 'DELETE',
};
return Requests.JSONfetch(input, init);
};
Requests.corpora.entity.followers.entity.role = {};
Requests.corpora.entity.followers.entity.role.update = (corpusId, followerId, value) => {
let input = `/corpora/${corpusId}/followers/${followerId}/role`;
let init = {
method: 'PUT',
body: JSON.stringify(value)
};
return Requests.JSONfetch(input, init);
};
......@@ -5,7 +5,7 @@ class CorpusDisplay extends ResourceDisplay {
this.displayElement
.querySelector('.action-button[data-action="build-request"]')
.addEventListener('click', (event) => {
Requests.corpora.corpus.build(this.corpusId);
Requests.corpora.entity.build(this.corpusId);
});
}
......
......@@ -122,7 +122,7 @@ class CorpusFollowerList extends ResourceList {
case 'update-role': {
let followerId = listItemElement.dataset.followerId;
let roleName = event.target.value;
Utils.updateCorpusFollowerRole(this.corpusId, followerId, roleName);
Requests.corpora.entity.followers.entity.role.update(this.corpusId, followerId, roleName);
break;
}
default: {
......@@ -141,7 +141,7 @@ class CorpusFollowerList extends ResourceList {
switch (listAction) {
case 'unfollow-request': {
let followerId = listItemElement.dataset.followerId;
Utils.unfollowCorpusRequest(this.corpusId, followerId);
Requests.corpora.entity.followers.entity.delete(this.corpusId, followerId);
break;
}
case 'view': {
......
......@@ -95,7 +95,7 @@ class CorpusList extends ResourceList {
let listAction = listActionElement === null ? 'view' : listActionElement.dataset.listAction;
switch (listAction) {
case 'delete-request': {
Utils.deleteCorpusRequest(this.userId, itemId);
Requests.corpora.entity.delete(this.userId, itemId);
break;
}
case 'view': {
......
......@@ -69,151 +69,6 @@ class Utils {
return Utils.mergeObjectsDeep(mergedObject, ...objects.slice(2));
}
static updateCorpusIsPublicRequest(corpusId, isPublic) {
return new Promise((resolve, reject) => {
let fetchRessource = `/corpora/${corpusId}/is_public`;
let fetchOptions = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(isPublic)
};
fetch(fetchRessource, fetchOptions)
.then(
(response) => {
if (response.ok) {
app.flash(`Corpus is now ${isPublic ? 'public' : 'private'}`, 'corpus');
resolve(response);
} else {
app.flash(`${response.statusText}`, 'error');
reject(response);
}
},
(response) => {
app.flash('Something went wrong', 'error');
reject(response);
}
);
});
}
static updateCorpusFollowerRole(corpusId, followerId, roleName) {
return new Promise((resolve, reject) => {
let fetchRessource = `/corpora/${corpusId}/followers/${followerId}/role`;
let fetchOptions = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({role: roleName})
};
fetch(fetchRessource, fetchOptions)
.then(
(response) => {
if (response.ok) {
app.flash('Role updated', 'corpus');
resolve(response);
} else {
app.flash(`${response.statusText}`, 'error');
reject(response);
}
},
(response) => {
app.flash('Something went wrong', 'error');
reject(response);
}
);
});
}
static addCorpusFollowersRequest(corpusId, usernames) {
return new Promise((resolve, reject) => {
let fetchRessource = `/corpora/${corpusId}/followers/add`;
let fetchOptions = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(usernames)
};
fetch(fetchRessource, fetchOptions)
.then(
(response) => {
if (response.ok) {
app.flash(`${usernames.length > 1 ? 'Users are' : 'User is'} following now`, 'corpus');
resolve(response);
} else {
app.flash(`${response.statusText}`, 'error');
reject(response);
}
},
(response) => {
app.flash('Something went wrong', 'error');
reject(response);
}
);
});
}
static buildCorpusRequest(userId, corpusId) {
return new Promise((resolve, reject) => {
let corpus;
try {
corpus = app.data.users[userId].corpora[corpusId];
} catch (error) {
corpus = {};
}
fetch(`/corpora/${corpusId}/build`, {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);}
if (response.status === 409) {app.flash('Conflict', 'error'); reject(response);}
app.flash(`Corpus "${corpus?.title}" marked for building`, 'corpus');
resolve(response);
},
(response) => {
app.flash('Something went wrong', 'error');
reject(response);
}
);
});
}
static unfollowCorpusRequest(corpusId, followerId) {
return new Promise((resolve, reject) => {
let fetchRessource = `/corpora/${corpusId}/followers/${followerId}/unfollow`;
let fetchOptions = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
};
fetch(fetchRessource, fetchOptions)
.then(
(response) => {
if (response.ok) {
app.flash(`User unfollowed from Corpus`, 'corpus');
resolve(response);
} else {
app.flash(`${response.statusText}`, 'error');
reject(response);
}
},
(response) => {
app.flash('Something went wrong', 'error');
reject(response);
}
);
});
}
static deleteCorpusRequest(userId, corpusId) {
return new Promise((resolve, reject) => {
let corpus;
......@@ -326,116 +181,6 @@ class Utils {
});
}
static deleteSpaCyNLPPipelineModelRequest(userId, spaCyNLPPipelineModelId) {
return new Promise((resolve, reject) => {
let spaCyNLPPipelineModel;
try {
spaCyNLPPipelineModel = app.data.users[userId].spacy_nlp_pipeline_models[spaCyNLPPipelineModelId];
} catch (error) {
spaCyNLPPipelineModel = {};
}
let modalElement = Utils.HTMLToElement(
`
<div class="modal">
<div class="modal-content">
<h4>Confirm SpaCy NLP Pipeline Model deletion</h4>
<p>Do you really want to delete the SpaCy NLP Pipeline Model <b>${spaCyNLPPipelineModel?.title}</b>? All files will be permanently deleted!</p>
</div>
<div class="modal-footer">
<a class="action-button btn modal-close waves-effect waves-light" data-action="cancel">Cancel</a>
<a class="action-button btn modal-close red waves-effect waves-light" data-action="confirm">Delete</a>
</div>
</div>
`
);
document.querySelector('#modals').appendChild(modalElement);
let modal = M.Modal.init(
modalElement,
{
dismissible: false,
onCloseEnd: () => {
modal.destroy();
modalElement.remove();
}
}
);
let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]');
confirmElement.addEventListener('click', (event) => {
let spaCyNLPPipelineModelTitle = spaCyNLPPipelineModel?.title;
fetch(`/contributions/spacy-nlp-pipeline-models/${spaCyNLPPipelineModelId}`, {method: 'DELETE'})
.then(
(response) => {
if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);}
if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);}
app.flash(`SpaCy NLP Pipeline Model "${spaCyNLPPipelineModelTitle}" marked for deletion`);
resolve(response);
},
(response) => {
app.flash('Something went wrong', 'error');
reject(response);
}
);
});
modal.open();
});
}
static deleteTesseractOCRPipelineModelRequest(userId, tesseractOCRPipelineModelId) {
return new Promise((resolve, reject) => {
let tesseractOCRPipelineModel;
try {
tesseractOCRPipelineModel = app.data.users[userId].tesseract_ocr_pipeline_models[tesseractOCRPipelineModelId];
} catch (error) {
tesseractOCRPipelineModel = {};
}
let modalElement = Utils.HTMLToElement(
`
<div class="modal">
<div class="modal-content">
<h4>Confirm Tesseract OCR Pipeline Model deletion</h4>
<p>Do you really want to delete the Tesseract OCR Pipeline Model <b>${tesseractOCRPipelineModel?.title}</b>? All files will be permanently deleted!</p>
</div>
<div class="modal-footer">
<a class="action-button btn modal-close waves-effect waves-light" data-action="cancel">Cancel</a>
<a class="action-button btn modal-close red waves-effect waves-light" data-action="confirm">Delete</a>
</div>
</div>
`
);
document.querySelector('#modals').appendChild(modalElement);
let modal = M.Modal.init(
modalElement,
{
dismissible: false,
onCloseEnd: () => {
modal.destroy();
modalElement.remove();
}
}
);
let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]');
confirmElement.addEventListener('click', (event) => {
let tesseractOCRPipelineModelTitle = tesseractOCRPipelineModel?.title;
fetch(`/contributions/tesseract-ocr-pipeline-models/${tesseractOCRPipelineModelId}`, {method: 'DELETE'})
.then(
(response) => {
if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);}
if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);}
app.flash(`Tesseract OCR Pipeline Model "${tesseractOCRPipelineModelTitle}" marked for deletion`);
resolve(response);
},
(response) => {
app.flash('Something went wrong', 'error');
reject(response);
}
);
});
modal.open();
});
}
static deleteProfileAvatarRequest(userId) {
return new Promise((resolve, reject) => {
let modalElement = Utils.HTMLToElement(
......@@ -698,94 +443,4 @@ class Utils {
modal.open();
});
}
static updateTesseractOCRPipelineModelIsPublicRequest(tesseractOCRPipelineModelId, newIsPublicValue) {
return new Promise((resolve, reject) => {
let fetchRessource = `/contributions/tesseract-ocr-pipeline-models/${tesseractOCRPipelineModelId}/is_public`;
let fetchOptions = {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(newIsPublicValue)
};
fetch(fetchRessource, fetchOptions)
.then(
(response) => {
if (response.ok) {
response.json().then((data) => {app.flash(data);});
resolve(response);
} else {
app.flash(`${response.statusText}`, 'error');
reject(response);
}
},
(response) => {
app.flash('Something went wrong', 'error');
reject(response);
}
);
});
}
static updateSpaCyNLPPipelineModelIsPublicRequest(SpaCyNLPPipelineModelId, newIsPublicValue) {
return new Promise((resolve, reject) => {
let fetchRessource = `/contributions/spacy-nlp-pipeline-models/${SpaCyNLPPipelineModelId}/is_public`;
let fetchOptions = {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(newIsPublicValue)
};
fetch(fetchRessource, fetchOptions)
.then(
(response) => {
if (response.ok) {
response.json().then((data) => {app.flash(data);});
resolve(response);
} else {
app.flash(`${response.statusText}`, 'error');
reject(response);
}
},
(response) => {
app.flash('Something went wrong', 'error');
reject(response);
}
);
});
}
static generateCorpusShareLinkRequest(corpusId, role, expiration) {
return new Promise((resolve, reject) => {
const data = {role: role, expiration: expiration};
fetch(`/corpora/${corpusId}/generate-corpus-share-link`, {method: 'POST', headers: {Accept: 'text/plain'}, body: JSON.stringify(data)})
.then(
(response) => {
if (!response.ok) {
app.flash(`Something went wrong: ${response.status} ${response.statusText}`, 'error');
reject(response);
return;
}
return response.text();
},
(response) => {
// Something went wrong during the HTTP request
app.flash('Something went wrong', 'error');
reject(response);
}
)
.then(
(corpusShareLink) => {resolve(corpusShareLink);},
(error) => {
// Something went wrong during ReadableStream processing
app.flash('Something went wrong', 'error');
reject(error);
}
);
});
}
}
......@@ -61,7 +61,9 @@
'js/Requests/contributions/contributions.js',
'js/Requests/contributions/spacy_nlp_pipeline_models.js',
'js/Requests/contributions/tesseract_ocr_pipeline_models.js',
'js/Requests/Corpora.js'
'js/Requests/corpora/corpora.js',
'js/Requests/corpora/files.js',
'js/Requests/corpora/followers.js'
%}
<script src="{{ ASSET_URL }}"></script>
{%- endassets %}
......
......@@ -226,7 +226,7 @@
let publishingModalIsPublicSwitchElement = document.querySelector('#publishing-modal-is-public-switch');
publishingModalIsPublicSwitchElement.addEventListener('change', (event) => {
let newIsPublic = publishingModalIsPublicSwitchElement.checked;
Requests.corpora.corpus.isPublic.update(corpusId, newIsPublic)
Requests.corpora.entity.isPublic.update(corpusId, newIsPublic)
.catch((response) => {
publishingModalIsPublicSwitchElement.checked = !newIsPublic;
});
......@@ -236,7 +236,7 @@
// #region delete_modal_js
let deleteModalDeleteButtonElement = document.querySelector('#delete-modal-delete-button');
deleteModalDeleteButtonElement.addEventListener('click', (event) => {
Requests.corpora.corpus.delete(corpusId)
Requests.corpora.entity.delete(corpusId)
.then((response) => {window.location.href = '/dashboard';});
});
// #endregion delete_modal_js
......@@ -280,7 +280,7 @@
inviteUserModalInviteButtonElement.addEventListener('click', (event) => {
let usernames = inviteUserModalSearch.chipsData.map((chipData) => chipData.tag);
Utils.addCorpusFollowersRequest(corpusId, usernames);
Requests.corpora.entity.followers.add(corpusId, usernames);
});
// #endregion invite_user_modal_js
......@@ -323,10 +323,13 @@
)
shareLinkModalCreateButtonElement.addEventListener('click', (event) => {
Utils.generateCorpusShareLinkRequest(corpusId, shareLinkModalCorpusFollowerRoleSelectElement.value, shareLinkModalExpirationDateDatepickerElement.value)
.then((shareLink) => {
shareLinkModalOutputContainerElement.classList.remove('hide');
shareLinkModalOutputFieldElement.value = shareLink;
Requests.corpora.entity.generateShareLink(corpusId, shareLinkModalCorpusFollowerRoleSelectElement.value, shareLinkModalExpirationDateDatepickerElement.value)
.then((response) => {
response.json()
.then((json) => {
shareLinkModalOutputContainerElement.classList.remove('hide');
shareLinkModalOutputFieldElement.value = json.corpusShareLink;
});
});
});
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment