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

Use JSON patch the correct way!

parent 7702de87
No related branches found
No related tags found
No related merge requests found
......@@ -45,8 +45,8 @@ def background_task(user_id, session_id):
with app.app_context():
user = db.session.query(User).filter_by(id=user_id).first()
''' Get current values from the database. '''
corpora = list(map(lambda x: x.to_dict(), user.corpora))
jobs = list(map(lambda x: x.to_dict(), user.jobs))
corpora = user.corpora_as_dict()
jobs = user.jobs_as_dict()
''' Send initial values. '''
socketio.emit('init-corpora',
json.dumps(corpora),
......@@ -59,8 +59,8 @@ def background_task(user_id, session_id):
# print(session_id + ' running')
# socketio.emit('message', 'heartbeat', room=session_id)
''' Get current values from the database '''
new_corpora = list(map(lambda x: x.to_dict(), user.corpora))
new_jobs = list(map(lambda x: x.to_dict(), user.jobs))
new_corpora = user.corpora_as_dict()
new_jobs = user.jobs_as_dict()
''' Compute JSON patches. '''
corpus_patch = jsonpatch.JsonPatch.from_diff(corpora, new_corpora)
jobs_patch = jsonpatch.JsonPatch.from_diff(jobs, new_jobs)
......
......@@ -204,6 +204,17 @@ class User(UserMixin, db.Model):
"""
return self.can(Permission.ADMIN)
def corpora_as_dict(self):
corpora = {}
for corpus in self.corpora:
corpora[str(corpus.id)] = corpus.to_dict()
return corpora
def jobs_as_dict(self):
jobs = {}
for job in self.jobs:
jobs[str(job.id)] = job.to_dict()
return jobs
class AnonymousUser(AnonymousUserMixin):
"""
......
......@@ -5,30 +5,59 @@ class CorpusList extends List {
}
init() {
var corpus;
for (corpus of corpora) {
this.list.appendChild(this.createCorpusElement(corpus));
_init() {
for (let [id, corpus] of Object.entries(corpora)) {
this.addCorpus(corpus);
}
this.reIndex();
this.update();
List.updatePagination(this);
}
createCorpusElement(corpus) {
_update(patch) {
var item, operation, pathArray, valueName;
for (operation of patch) {
pathArray = operation.path.split("/").slice(1);
switch(operation.op) {
case "add":
this.addCorpus(operation.value);
break;
case "remove":
if (pathArray.length != 1) {break;}
this.remove("id", pathArray[0]);
break;
case "replace":
if (pathArray.length != 2) {break;}
item = this.get("id", pathArray[0])[0];
valueName = pathArray[1];
switch(valueName) {
case "description":
item.values({"description": operation.value});
break;
case "title":
item.values({"title": operation.value});
break;
default:
break;
}
default:
break;
}
}
}
addCorpus(corpus) {
var corpusDescriptionElement, corpusElement, corpusIconElement,
corpusTitleElement;
corpusDescriptionElement = document.createElement("p");
corpusDescriptionElement.dataset.key = "description";
corpusDescriptionElement.classList.add("description");
corpusDescriptionElement.innerText = corpus.description;
corpusElement = document.createElement("a");
corpusElement.classList.add("avatar", "collection-item");
corpusElement.dataset.key = "id";
corpusElement.dataset.value = corpus.id;
corpusElement.dataset.id = corpus.id;
corpusElement.href = `/corpora/${corpus.id}`;
corpusIconElement = document.createElement("i");
corpusIconElement.classList.add("circle", "material-icons");
......@@ -42,35 +71,9 @@ class CorpusList extends List {
corpusElement.appendChild(corpusTitleElement);
corpusElement.appendChild(corpusDescriptionElement);
return corpusElement;
}
updateWithPatch(delta) {
var corpusElement, key, listItem;
for (key in delta) {
if (key === "_t") {continue;}
if (key.startsWith("_")) {
this.remove("id", delta[key][0].id);
} else if (Array.isArray(delta[key])) {
corpusElement = this.createCorpusElement(delta[key][0]);
listItem = this.add({"description": delta[key][0].description,
"title": delta[key][0].title,
"id": delta[key][0].id})[0];
if (listItem.elm) {
listItem.elm.replaceWith(corpusElement);
}
listItem.elm = corpusElement;
} else {
listItem = this.get("id", corpora[parseInt(key)].id)[0];
if (delta[key]["description"]) {
listItem.values({"description": delta[key]["description"][1]});
}
if (delta[key]["title"]) {
listItem.values({"title": delta[key]["title"][1]});
}
}
}
this.add(
[{description: corpus.description, id: corpus.id, title: corpus.title}],
function(items) {items[0].elm = corpusElement;}
);
}
}
......@@ -5,22 +5,69 @@ class JobList extends List {
}
init() {
var job;
for (job of jobs) {
this.list.appendChild(this.createJobElement(job));
_init() {
for (let [id, job] of Object.entries(jobs)) {
this.addJob(job);
}
this.reIndex();
this.update();
List.updatePagination(this);
}
createJobElement(job) {
_update(patch) {
var item, jobStatusElement, newStatusColor, operation, pathArray, status,
statusColor, valueName;
for (operation of patch) {
pathArray = operation.path.split("/").slice(1);
switch(operation.op) {
case "add":
this.addJob(operation.value);
break;
case "remove":
if (pathArray.length != 1) {break;}
this.remove("id", pathArray[0]);
break;
case "replace":
if (pathArray.length != 2) {break;}
item = this.get("id", pathArray[0])[0];
valueName = pathArray[1];
switch(valueName) {
case "description":
item.values({"description": operation.value});
break;
case "status":
jobStatusElement = item.elm.querySelector(".status");
status = jobStatusElement.innerHTML;
if (JobList.STATUS_COLORS.hasOwnProperty(status)) {
statusColor = JobList.STATUS_COLORS[status];
} else {
statusColor = JobList.STATUS_COLORS['default'];
}
if (JobList.STATUS_COLORS.hasOwnProperty(operation.value)) {
newStatusColor = JobList.STATUS_COLORS[operation.value];
} else {
newStatusColor = JobList.STATUS_COLORS['default'];
}
jobStatusElement.classList.remove(statusColor);
jobStatusElement.classList.add(newStatusColor);
jobStatusElement.innerHTML = operation.value;
case "title":
item.values({"title": operation.value});
break;
default:
break;
}
default:
break;
}
}
}
addJob(job) {
var jobDescriptionElement, jobElement, jobServiceElement, jobStatusElement,
jobTitleElement;
jobTitleElement, serviceColor, serviceIcon, statusColor;
jobDescriptionElement = document.createElement("p");
jobDescriptionElement.classList.add("description");
......@@ -29,11 +76,26 @@ class JobList extends List {
jobElement.classList.add("avatar", "collection-item");
jobElement.dataset.id = job.id;
jobElement.href = `/jobs/${job.id}`;
if (JobList.SERVICE_COLORS.hasOwnProperty(job.service)) {
serviceColor = JobList.SERVICE_COLORS[job.service];
} else {
serviceColor = JobList.SERVICE_COLORS['default'];
}
if (JobList.SERVICE_ICONS.hasOwnProperty(job.service)) {
serviceIcon = JobList.SERVICE_ICONS[job.service];
} else {
serviceIcon = JobList.SERVICE_ICONS['default'];
}
jobServiceElement = document.createElement("i");
jobServiceElement.classList.add("circle", "material-icons", "service-icon", JobList.SERVICE_COLORS[job.service]);
jobServiceElement.innerText = JobList.SERVICE_ICONS[job.service];
jobServiceElement.classList.add("circle", "material-icons", "service-icon", serviceColor);
jobServiceElement.innerText = serviceIcon;
if (JobList.STATUS_COLORS.hasOwnProperty(job.status)) {
statusColor = JobList.STATUS_COLORS[job.status];
} else {
statusColor = JobList.STATUS_COLORS['default'];
}
jobStatusElement = document.createElement("span");
jobStatusElement.classList.add("badge", "new", "status", JobList.STATUS_COLORS[job.status]);
jobStatusElement.classList.add("badge", "new", "status", statusColor);
jobStatusElement.dataset.badgeCaption = "";
jobStatusElement.innerText = job.status;
jobTitleElement = document.createElement("span");
......@@ -45,45 +107,12 @@ class JobList extends List {
jobElement.appendChild(jobTitleElement);
jobElement.appendChild(jobDescriptionElement);
return jobElement;
}
updateWithPatch(delta) {
var jobElement, jobStatusElement, key, listItem;
for (key in delta) {
if (key === "_t") {continue;}
if (key.startsWith("_")) {
this.remove("id", delta[key][0].id);
} else if (Array.isArray(delta[key])) {
jobElement = this.createJobElement(delta[key][0]);
listItem = this.add({"description": delta[key][0].description,
"title": delta[key][0].title,
"id": delta[key][0].id})[0];
if (listItem.elm) {
listItem.elm.replaceWith(jobElement);
}
listItem.elm = jobElement;
} else {
listItem = this.get("id", jobs[parseInt(key)].id)[0];
if (delta[key]["status"]) {
jobStatusElement = listItem.elm.querySelector(".status");
jobStatusElement.classList.remove(JobList.STATUS_COLORS[delta[key]["status"][0]]);
jobStatusElement.classList.add(JobList.STATUS_COLORS[delta[key]["status"][1]]);
jobStatusElement.innerHTML = delta[key]["status"][1];
}
if (delta[key]["description"]) {
listItem.values({"description": delta[key]["description"][1]});
}
if (delta[key]["title"]) {
listItem.values({"title": delta[key]["title"][1]});
}
}
}
this.add(
[{description: job.description, id: job.id, title: job.title}],
function(items) {items[0].elm = jobElement;}
);
}
}
JobList.SERVICE_COLORS = {"nlp": "blue",
"ocr": "green",
"default": "red"};
......
This diff is collapsed.
......@@ -11,9 +11,8 @@
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='fonts/material-icons/material-icons.css') }}">
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/materialize.min.css') }}" media="screen,projection"/>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/opaque.css') }}" media="screen,projection"/>
<script src="{{ url_for('static', filename='js/socket.io.js') }}"></script>
<script src="{{ url_for('static', filename='js/jsonpatch.min.js') }}"></script>
<script src="{{ url_for('static', filename='js/jsondiffpatch.umd.js') }}"></script>
<script src="{{ url_for('static', filename='js/socket.io.js') }}"></script>
<script src="{{ url_for('static', filename='js/list.js') }}"></script>
<script src="{{ url_for('static', filename='js/list.utils.js') }}"></script>
<script src="{{ url_for('static', filename='js/CorpusList.js') }}"></script>
......@@ -29,7 +28,7 @@
var subscriber;
corpora = JSON.parse(msg);
for (subscriber of corporaSubscribers) {subscriber.init();}
for (subscriber of corporaSubscribers) {subscriber._init();}
});
......@@ -37,7 +36,7 @@
var subscriber;
jobs = JSON.parse(msg);
for (subscriber of jobsSubscribers) {subscriber.init();}
for (subscriber of jobsSubscribers) {subscriber._init();}
});
......@@ -45,12 +44,8 @@
var patch, patchedCorpora, subscriber;
patch = JSON.parse(msg);
patchedCorpora = jsonpatch.apply_patch(corpora, patch);
delta = jsondiffpatch.diff(corpora, patchedCorpora);
corpora = patchedCorpora;
for (subscriber of corporaSubscribers) {
subscriber.updateWithPatch(delta);
}
corpora = jsonpatch.apply_patch(corpora, patch);
for (subscriber of corporaSubscribers) {subscriber._update(patch);}
});
......@@ -58,12 +53,8 @@
var patch, patchedJobs, subscriber;
patch = JSON.parse(msg);
patchedJobs = jsonpatch.apply_patch(jobs, patch);
delta = jsondiffpatch.diff(jobs, patchedJobs);
jobs = patchedJobs;
for (subscriber of jobsSubscribers) {
subscriber.updateWithPatch(delta);
}
jobs = jsonpatch.apply_patch(jobs, patch);
for (subscriber of jobsSubscribers) {subscriber._update(patch);}
});
......
......@@ -31,11 +31,12 @@
</div>
</div>
<script>
var corpusListOptions = {item: '<a><span class="title"></span><span class="description"></span></a>',
page: 4,
pagination: true,
valueNames: ["description", "title", {data: ["id"]}]}
var corpusList = new CorpusList("corpus-list", corpusListOptions);
var corpusList = new CorpusList("corpus-list", {
item: '<div><span class="title"></span><span class="description"></span></div>',
page: 4,
pagination: true,
valueNames: ["description", "title", {data: ["id"]}]
});
corpusList.on("filterComplete", List.updatePagination);
corpusList.on("searchComplete", List.updatePagination);
</script>
......@@ -74,13 +75,14 @@
</div>
</div>
<script>
jobListOptions = {item: '<a><span class="title"></span><span class="description"></span></a>',
page: 4,
pagination: true,
valueNames: ["description", "title", {data: ["id"]}]}
var jobList = new JobList("job-list", jobListOptions);
jobList.on("filterComplete", List.updatePagination);
jobList.on("searchComplete", List.updatePagination);
var jobList = new JobList("job-list", {
item: '<div><span class="title"></span><span class="description"></span></div>',
page: 4,
pagination: true,
valueNames: ["description", "title", {data: ["id"]}]
});
jobList.on("filterComplete", List.updatePagination);
jobList.on("searchComplete", List.updatePagination);
</script>
<div id="new-corpus-modal" class="modal">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment