Merge branch 'development' of gitlab.ub.uni-bielefeld.de:sfb1288inf/nopaque into development

This commit is contained in:
Inga Kirschnick 2022-12-13 15:05:20 +01:00
commit 5a396ce214
6 changed files with 260 additions and 114 deletions

View File

@ -1,70 +0,0 @@
class PublicCorporaList extends RessourceList {
static instances = [];
static getInstance(elem) {
return PublicCorporaList.instances.find((instance) => {
return instance.listjs.list === elem;
});
}
static autoInit() {
for (let publicCorporaListElement of document.querySelectorAll('.public-corpora-list:not(.no-autoinit)')) {
new PublicCorporaList(publicCorporaListElement);
}
}
static options = {
initialHtmlGenerator: (id) => {
return `
<div class="input-field">
<i class="material-icons prefix">search</i>
<input id="${id}-search" class="search" type="search"></input>
<label for="${id}-search">Search corpus</label>
</div>
<table>
<thead>
<tr>
<th></th>
<th>Title</th>
<th>Description</th>
<th></th>
</tr>
</thead>
<tbody class="list"></tbody>
</table>
<ul class="pagination"></ul>
`.trim();
},
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></td>
<td><i class="description"></i></td>
</tr>
`.trim(),
ressourceMapper: (corpus) => {
return {
'id': corpus.id,
'creation-date': corpus.creation_date,
'description': corpus.description,
'title': corpus.title
};
},
sortArgs: ['creation-date', {order: 'desc'}],
valueNames: [
{data: ['id']},
{data: ['creation-date']},
'description',
'title'
]
};
constructor(listElement, options = {}) {
super(listElement, {...PublicCorporaList.options, ...options});
PublicCorporaList.instances.push(this);
}
init(user) {
this._init(user.corpora.is_public);
}
}

View File

@ -10,7 +10,6 @@ class RessourceList {
JobList.autoInit(); JobList.autoInit();
JobInputList.autoInit(); JobInputList.autoInit();
JobResultList.autoInit(); JobResultList.autoInit();
PublicCorporaList.autoInit();
SpaCyNLPPipelineModelList.autoInit(); SpaCyNLPPipelineModelList.autoInit();
TesseractOCRPipelineModelList.autoInit(); TesseractOCRPipelineModelList.autoInit();
UserList.autoInit(); UserList.autoInit();
@ -31,11 +30,11 @@ class RessourceList {
...{pagination: {item: `<li><a class="page" href="#${listElement.id}"></a></li>`}}, ...{pagination: {item: `<li><a class="page" href="#${listElement.id}"></a></li>`}},
...options ...options
} }
if ('ressourceMapper' in options) { if ('ressourceMapper' in options && typeof options.ressourceMapper === 'function') {
this.ressourceMapper = options.ressourceMapper; this.ressourceMapper = options.ressourceMapper;
delete options.ressourceMapper; delete options.ressourceMapper;
} }
if ('initialHtmlGenerator' in options) { if ('initialHtmlGenerator' in options && typeof options.initialHtmlGenerator === 'function') {
this.initialHtmlGenerator = options.initialHtmlGenerator; this.initialHtmlGenerator = options.initialHtmlGenerator;
listElement.innerHTML = this.initialHtmlGenerator(listElement.id); listElement.innerHTML = this.initialHtmlGenerator(listElement.id);
delete options.initialHtmlGenerator; delete options.initialHtmlGenerator;

View File

@ -0,0 +1,102 @@
/**
* XMLtoObject - Converts XML into a JavaScript value or object.
* GitHub: https://github.com/Pevtrick/XMLtoObject
* by Patrick Jentsch: https://github.com/Pevtrick
*/
/**
* The XMLDocument.toObject() method converts the XMLDocument into a JavaScript value or object.
* @param {String} [attributePrefix=] - A Prefix, which is added to all properties generated by XML attributes.
* @returns {Object} - The converted result.
*/
XMLDocument.prototype.toObject = function(attributePrefix='') {
let obj = {};
obj[this.documentElement.nodeName] = this.documentElement.toObject(attributePrefix);
return obj;
};
/**
* The Node.toObject() method converts the Node into a JavaScript value or object.
* @param {String} [attributePrefix=] - A Prefix, which is added to all properties generated by XML attributes.
* @returns {Object|String|null} - The converted result.
*/
Node.prototype.toObject = function(attributePrefix='') {
let obj = null;
switch (this.nodeType) {
case Node.ELEMENT_NODE:
let hasAttributes = this.attributes.length > 0;
let hasChildNodes = this.childNodes.length > 0;
/* Stop conversion if the Node doesn't contain any attributes or child nodes */
if (!(hasAttributes || hasChildNodes)) {
break;
}
obj = {};
/* Convert attributes */
for (let attribute of this.attributes) {
obj[`attributePrefix${attribute.name}`] = attribute.value;
}
/* Convert child nodes */
for (let childNode of this.childNodes) {
switch (childNode.nodeType) {
case Node.ELEMENT_NODE:
break;
case Node.TEXT_NODE:
/* Check whether the child text node is the only child of the current node. */
if (!hasAttributes && this.childNodes.length === 1) {
obj = childNode.toObject(attributePrefix);
continue;
}
if (childNode.data.trim() === '') {continue;}
break;
default:
/* This recursion leads to a console message. */
childNode.toObject(attributePrefix);
continue;
}
/**
* If the child node is the first of its type in this childset,
* process it and add it directly as a property to the return object.
* If not add it to an array which is set as a property of the return object.
*/
if (childNode.nodeName in obj) {
if (!Array.isArray(obj[childNode.nodeName])) {
obj[childNode.nodeName] = [obj[childNode.nodeName]];
}
obj[childNode.nodeName].push(childNode.toObject(attributePrefix));
} else {
obj[childNode.nodeName] = childNode.toObject(attributePrefix);
}
}
break;
case Node.TEXT_NODE:
if (this.data.trim() !== '') {obj = this.data;}
break;
case Node.COMMENT_NODE:
console.log('Skipping comment node:');
console.log(node);
break;
case Node.DOCUMENT_NODE:
obj = {};
obj[this.documentElement.nodeName] = this.documentElement.toObject(attributePrefix);
break;
default:
/**
* The following node types are not processed because they don't offer data, which has to be stored in the object:
* Node.PROCESSING_INSTRUCTION_NODE, Node.DOCUMENT_TYPE_NODE, Node.DOCUMENT_FRAGMENT_NODE
* The following node types are deprecated and therefore not supported by this function:
* Node.ATTRIBUTE_NODE, Node.CDATA_SECTION_NODE, Node.ENTITY_REFERENCE_NODE, Node.ENTITY_NODE, Node.NOTATION_NODE
*/
console.log(`Node type: '${this.nodeType}' is not supported.`);
console.log(node);
break;
}
return obj;
}

View File

@ -24,10 +24,10 @@
'js/RessourceLists/JobList.js', 'js/RessourceLists/JobList.js',
'js/RessourceLists/JobInputList.js', 'js/RessourceLists/JobInputList.js',
'js/RessourceLists/JobResultList.js', 'js/RessourceLists/JobResultList.js',
'js/RessourceLists/PublicCorporaList.js',
'js/RessourceLists/SpacyNLPPipelineModelList.js', 'js/RessourceLists/SpacyNLPPipelineModelList.js',
'js/RessourceLists/TesseractOCRPipelineModelList.js', 'js/RessourceLists/TesseractOCRPipelineModelList.js',
'js/RessourceLists/UserList.js' 'js/RessourceLists/UserList.js',
'js/XMLtoObject.js'
%} %}
<script src="{{ ASSET_URL }}"></script> <script src="{{ ASSET_URL }}"></script>
{%- endassets %} {%- endassets %}
@ -53,7 +53,7 @@
// Initialize components // Initialize components
M.AutoInit(); M.AutoInit();
M.CharacterCounter.init(document.querySelectorAll('input[data-length][type="text"], input[data-length][type="email"], input[data-length][type="search"], input[data-length][type="password"], input[data-length][type="tel"], input[data-length][type="url"], textarea[data-length]')); M.CharacterCounter.init(document.querySelectorAll('input[data-length], textarea[data-length]'));
M.Dropdown.init( M.Dropdown.init(
document.querySelectorAll('#nav-more-dropdown-trigger'), document.querySelectorAll('#nav-more-dropdown-trigger'),
{alignment: 'right', constrainWidth: false, coverTrigger: false} {alignment: 'right', constrainWidth: false, coverTrigger: false}

View File

@ -1,30 +1,46 @@
{% extends "base.html.j2" %} {% extends "base.html.j2" %}
{% block main_attribs %} class="service-scheme" data-service="corpus-analysis"{% endblock main_attribs %}
{% block page_content %} {% block page_content %}
<div class="parallax-container"> <div class="corpus-list no-autoinit" id="corpus-list">
<div class="parallax"><img src="{{ url_for('static', filename='images/parallax_hq/canvas.png') }}"></div> <div class="parallax-container">
<div style="position: absolute; bottom: 0; width: 100%;"> <div class="parallax"><img src="{{ url_for('static', filename='images/parallax_hq/canvas.png') }}"></div>
<div class="container"> <div style="position: absolute; bottom: 0; width: 100%;">
<div class="white-text"> <div class="container">
<h1 id="title"><i class="nopaque-icons" style="font-size: inherit;">I</i>Corpora</h1> <div class="white-text">
</div> <h1 id="title"><i class="nopaque-icons" style="font-size: inherit;">I</i>Corpora</h1>
<div class="white" style="padding: 0 15px; border-radius: 20px;"> </div>
<div class="input-field"> <div class="white" style="padding: 1px 35px 0 10px; border-radius: 35px;">
<i class="material-icons prefix">search</i> <div class="input-field">
<input id="public-corpora-search" placeholder="Find public corpora" type="text"> <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> </div>
</div> </div>
</div>
<div class="container">
<div class="row"> <div class="row">
<div class="col s12" id="corpora"> <div class="col s12" id="corpora">
<div class="card"> <div class="card">
<div class="card-content"> <div class="card-content">
<div class="corpus-list"></div> <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> </div>
@ -36,9 +52,21 @@
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}
<script> <script>
let publicCorporaSearchElement = document.querySelector('#public-corpora-search'); let corpusListElement = document.querySelector('#corpus-list');
let corpusList = CorpusList.getInstance(document.querySelector('#corpora .corpus-list .list')); let corpusListOptions = {
publicCorporaSearchElement.addEventListener('keyup', function() {corpusList.listjs.search(this.value);}); 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 }}); corpusList._init({{ corpora|tojson }});
</script> </script>
{% endblock scripts %} {% endblock scripts %}

View File

@ -9,7 +9,7 @@
</div> </div>
<div class="col s12"> <div class="col s12">
<div id="mastodon"></div> <div id="aggregated-news"></div>
<div class="card" id="april-2022-update"> <div class="card" id="april-2022-update">
<div class="card-content"> <div class="card-content">
@ -132,29 +132,116 @@
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}
<script> <script>
let mastodonElement = document.querySelector('#mastodon'); function getMastodonStatuses() {
fetch(`https://fedihum.org/api/v1/accounts/109386364241901080/statuses`, {method: 'GET', headers: {Accept: 'application/json'}}) return new Promise((resolve, reject) => {
.then((response) => {return response.json();}) fetch(`https://fedihum.org/api/v1/accounts/109386364241901080/statuses`, {method: 'GET', headers: {Accept: 'application/json'}})
.then((statuses) => { .then((response) => {
for (let status of statuses) { if (!response.ok) {reject(response);}
console.log(status); return response.json();
let contentHtml = `<div>${status.content}</div>` })
let tagsHtml = '<p>'; .then((statuses) => {resolve(statuses);})
for (let tag of status.tags) { });
tagsHtml += `<a href="${tag.url}" class="chip">${tag.name}</a>`; }
} function getBisBlogsEntries() {
tagsHtml += '</p>'; return new Promise((resolve, reject) => {
let statusHtml = ` fetch(`https://blogs.uni-bielefeld.de/blog/uniintern/feed/entries/atom?cat=%2FAllgemein`, {method: 'GET', headers: {Accept: 'application/xml'}})
<div id="${status.id}" class="card"> .then((response) => {
if (!response.ok) {reject(response);}
return response.text();
})
.then((responseText) => {return new DOMParser().parseFromString(responseText, 'application/xml');})
.then((xmlDocument) => {return xmlDocument.toObject();})
.then((feed) => {resolve(feed);});
});
}
function sortAggregatedNews(a, b) {
let aDate;
let bDate;
switch (a.source) {
case 'mastodon':
aDate = new Date(a.created_at);
break;
case 'big-blogs':
aDate = new Date(a.published);
break;
default:
throw new Error('Unknown source');
}
switch (b.source) {
case 'mastodon':
bDate = new Date(b.created_at);
break;
case 'big-blogs':
bDate = new Date(b.published);
break;
default:
throw new Error('Unknown source');
}
return bDate - aDate;
}
function aggregateNews() {
return new Promise((resolve, reject) => {
Promise.all([getMastodonStatuses(), getBisBlogsEntries()])
.then(
(responses) => {
console.log(responses[1]);
let mastodonStatuses = responses[0].map((obj) => {return { ...obj, source: 'mastodon'}});
let bisBlogsEntries = responses[1].feed.entry.map((obj) => {return { ...obj, source: 'big-blogs'};});
let aggregatedNews = [...mastodonStatuses, ...bisBlogsEntries];
aggregatedNews.sort(sortAggregatedNews);
resolve(aggregatedNews);
},
(error) => {reject(error);}
);
});
}
function mastodonStatusToHtml(status) {
return htmlString = `
<div class="card white-text" style="background-color:#5D50E7;">
<div class="card-content">
<span class="card-title">New Actitvity on Mastodon</span>
${status.content}
</div>
</div>
`.trim();
}
function bisBlogsEntryToHtml(entry) {
return `
<div class="row">
<div class="col s1">
<img src="https://blogs.uni-bielefeld.de/blog/uniintern/resource/themabilder/unilogo-square.svg" alt="Bielefeld University Blogs" class="responsive-img">
</div>
<div class="col s11">
<div class="card" style="background-color: #14f5b4;">
<div class="card-content"> <div class="card-content">
<span class="card-title">Mastodon News</span> <span class="card-title">${entry.title['#text']}</span>
${contentHtml} ${entry.content['#text']}
${tagsHtml}
</div> </div>
</div> </div>
`; </div>
mastodonElement.insertAdjacentHTML('beforeend', statusHtml); </div>
`.trim();
}
let aggregatedNewsElement = document.querySelector('#aggregated-news');
aggregateNews().then((aggregatedNews) => {
for (let item of aggregatedNews) {
let itemHtmlString;
switch (item.source) {
case 'mastodon':
console.log(item);
itemHtmlString = mastodonStatusToHtml(item);
break;
case 'big-blogs':
itemHtmlString = bisBlogsEntryToHtml(item);
break;
default:
throw new Error('Unknown source');
} }
}); aggregatedNewsElement.insertAdjacentHTML('beforeend', itemHtmlString);
}
});
</script> </script>
{% endblock scripts %} {% endblock scripts %}