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): ...@@ -45,8 +45,8 @@ def background_task(user_id, session_id):
with app.app_context(): with app.app_context():
user = db.session.query(User).filter_by(id=user_id).first() user = db.session.query(User).filter_by(id=user_id).first()
''' Get current values from the database. ''' ''' Get current values from the database. '''
corpora = list(map(lambda x: x.to_dict(), user.corpora)) corpora = user.corpora_as_dict()
jobs = list(map(lambda x: x.to_dict(), user.jobs)) jobs = user.jobs_as_dict()
''' Send initial values. ''' ''' Send initial values. '''
socketio.emit('init-corpora', socketio.emit('init-corpora',
json.dumps(corpora), json.dumps(corpora),
...@@ -59,8 +59,8 @@ def background_task(user_id, session_id): ...@@ -59,8 +59,8 @@ def background_task(user_id, session_id):
# print(session_id + ' running') # print(session_id + ' running')
# socketio.emit('message', 'heartbeat', room=session_id) # socketio.emit('message', 'heartbeat', room=session_id)
''' Get current values from the database ''' ''' Get current values from the database '''
new_corpora = list(map(lambda x: x.to_dict(), user.corpora)) new_corpora = user.corpora_as_dict()
new_jobs = list(map(lambda x: x.to_dict(), user.jobs)) new_jobs = user.jobs_as_dict()
''' Compute JSON patches. ''' ''' Compute JSON patches. '''
corpus_patch = jsonpatch.JsonPatch.from_diff(corpora, new_corpora) corpus_patch = jsonpatch.JsonPatch.from_diff(corpora, new_corpora)
jobs_patch = jsonpatch.JsonPatch.from_diff(jobs, new_jobs) jobs_patch = jsonpatch.JsonPatch.from_diff(jobs, new_jobs)
......
...@@ -204,6 +204,17 @@ class User(UserMixin, db.Model): ...@@ -204,6 +204,17 @@ class User(UserMixin, db.Model):
""" """
return self.can(Permission.ADMIN) 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): class AnonymousUser(AnonymousUserMixin):
""" """
......
...@@ -5,30 +5,59 @@ class CorpusList extends List { ...@@ -5,30 +5,59 @@ class CorpusList extends List {
} }
init() { _init() {
var corpus; for (let [id, corpus] of Object.entries(corpora)) {
this.addCorpus(corpus);
for (corpus of corpora) {
this.list.appendChild(this.createCorpusElement(corpus));
} }
this.reIndex();
this.update();
List.updatePagination(this); 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, var corpusDescriptionElement, corpusElement, corpusIconElement,
corpusTitleElement; corpusTitleElement;
corpusDescriptionElement = document.createElement("p"); corpusDescriptionElement = document.createElement("p");
corpusDescriptionElement.dataset.key = "description"; corpusDescriptionElement.classList.add("description");
corpusDescriptionElement.innerText = corpus.description; corpusDescriptionElement.innerText = corpus.description;
corpusElement = document.createElement("a"); corpusElement = document.createElement("a");
corpusElement.classList.add("avatar", "collection-item"); corpusElement.classList.add("avatar", "collection-item");
corpusElement.dataset.key = "id"; corpusElement.dataset.id = corpus.id;
corpusElement.dataset.value = corpus.id;
corpusElement.href = `/corpora/${corpus.id}`; corpusElement.href = `/corpora/${corpus.id}`;
corpusIconElement = document.createElement("i"); corpusIconElement = document.createElement("i");
corpusIconElement.classList.add("circle", "material-icons"); corpusIconElement.classList.add("circle", "material-icons");
...@@ -42,35 +71,9 @@ class CorpusList extends List { ...@@ -42,35 +71,9 @@ class CorpusList extends List {
corpusElement.appendChild(corpusTitleElement); corpusElement.appendChild(corpusTitleElement);
corpusElement.appendChild(corpusDescriptionElement); corpusElement.appendChild(corpusDescriptionElement);
return corpusElement; this.add(
} [{description: corpus.description, id: corpus.id, title: corpus.title}],
function(items) {items[0].elm = 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]});
}
}
}
} }
} }
...@@ -5,22 +5,69 @@ class JobList extends List { ...@@ -5,22 +5,69 @@ class JobList extends List {
} }
init() { _init() {
var job; for (let [id, job] of Object.entries(jobs)) {
this.addJob(job);
for (job of jobs) {
this.list.appendChild(this.createJobElement(job));
} }
this.reIndex();
this.update();
List.updatePagination(this); 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, var jobDescriptionElement, jobElement, jobServiceElement, jobStatusElement,
jobTitleElement; jobTitleElement, serviceColor, serviceIcon, statusColor;
jobDescriptionElement = document.createElement("p"); jobDescriptionElement = document.createElement("p");
jobDescriptionElement.classList.add("description"); jobDescriptionElement.classList.add("description");
...@@ -29,11 +76,26 @@ class JobList extends List { ...@@ -29,11 +76,26 @@ class JobList extends List {
jobElement.classList.add("avatar", "collection-item"); jobElement.classList.add("avatar", "collection-item");
jobElement.dataset.id = job.id; jobElement.dataset.id = job.id;
jobElement.href = `/jobs/${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 = document.createElement("i");
jobServiceElement.classList.add("circle", "material-icons", "service-icon", JobList.SERVICE_COLORS[job.service]); jobServiceElement.classList.add("circle", "material-icons", "service-icon", serviceColor);
jobServiceElement.innerText = JobList.SERVICE_ICONS[job.service]; 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 = 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.dataset.badgeCaption = "";
jobStatusElement.innerText = job.status; jobStatusElement.innerText = job.status;
jobTitleElement = document.createElement("span"); jobTitleElement = document.createElement("span");
...@@ -45,45 +107,12 @@ class JobList extends List { ...@@ -45,45 +107,12 @@ class JobList extends List {
jobElement.appendChild(jobTitleElement); jobElement.appendChild(jobTitleElement);
jobElement.appendChild(jobDescriptionElement); jobElement.appendChild(jobDescriptionElement);
return jobElement; this.add(
} [{description: job.description, id: job.id, title: job.title}],
function(items) {items[0].elm = 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]});
}
}
}
} }
} }
JobList.SERVICE_COLORS = {"nlp": "blue", JobList.SERVICE_COLORS = {"nlp": "blue",
"ocr": "green", "ocr": "green",
"default": "red"}; "default": "red"};
......
This diff is collapsed.
...@@ -11,9 +11,8 @@ ...@@ -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='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/materialize.min.css') }}" media="screen,projection"/>
<link type="text/css" rel="stylesheet" href="{{ url_for('static', filename='css/opaque.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/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.js') }}"></script>
<script src="{{ url_for('static', filename='js/list.utils.js') }}"></script> <script src="{{ url_for('static', filename='js/list.utils.js') }}"></script>
<script src="{{ url_for('static', filename='js/CorpusList.js') }}"></script> <script src="{{ url_for('static', filename='js/CorpusList.js') }}"></script>
...@@ -29,7 +28,7 @@ ...@@ -29,7 +28,7 @@
var subscriber; var subscriber;
corpora = JSON.parse(msg); corpora = JSON.parse(msg);
for (subscriber of corporaSubscribers) {subscriber.init();} for (subscriber of corporaSubscribers) {subscriber._init();}
}); });
...@@ -37,7 +36,7 @@ ...@@ -37,7 +36,7 @@
var subscriber; var subscriber;
jobs = JSON.parse(msg); jobs = JSON.parse(msg);
for (subscriber of jobsSubscribers) {subscriber.init();} for (subscriber of jobsSubscribers) {subscriber._init();}
}); });
...@@ -45,12 +44,8 @@ ...@@ -45,12 +44,8 @@
var patch, patchedCorpora, subscriber; var patch, patchedCorpora, subscriber;
patch = JSON.parse(msg); patch = JSON.parse(msg);
patchedCorpora = jsonpatch.apply_patch(corpora, patch); corpora = jsonpatch.apply_patch(corpora, patch);
delta = jsondiffpatch.diff(corpora, patchedCorpora); for (subscriber of corporaSubscribers) {subscriber._update(patch);}
corpora = patchedCorpora;
for (subscriber of corporaSubscribers) {
subscriber.updateWithPatch(delta);
}
}); });
...@@ -58,12 +53,8 @@ ...@@ -58,12 +53,8 @@
var patch, patchedJobs, subscriber; var patch, patchedJobs, subscriber;
patch = JSON.parse(msg); patch = JSON.parse(msg);
patchedJobs = jsonpatch.apply_patch(jobs, patch); jobs = jsonpatch.apply_patch(jobs, patch);
delta = jsondiffpatch.diff(jobs, patchedJobs); for (subscriber of jobsSubscribers) {subscriber._update(patch);}
jobs = patchedJobs;
for (subscriber of jobsSubscribers) {
subscriber.updateWithPatch(delta);
}
}); });
......
...@@ -31,11 +31,12 @@ ...@@ -31,11 +31,12 @@
</div> </div>
</div> </div>
<script> <script>
var corpusListOptions = {item: '<a><span class="title"></span><span class="description"></span></a>', var corpusList = new CorpusList("corpus-list", {
page: 4, item: '<div><span class="title"></span><span class="description"></span></div>',
pagination: true, page: 4,
valueNames: ["description", "title", {data: ["id"]}]} pagination: true,
var corpusList = new CorpusList("corpus-list", corpusListOptions); valueNames: ["description", "title", {data: ["id"]}]
});
corpusList.on("filterComplete", List.updatePagination); corpusList.on("filterComplete", List.updatePagination);
corpusList.on("searchComplete", List.updatePagination); corpusList.on("searchComplete", List.updatePagination);
</script> </script>
...@@ -74,13 +75,14 @@ ...@@ -74,13 +75,14 @@
</div> </div>
</div> </div>
<script> <script>
jobListOptions = {item: '<a><span class="title"></span><span class="description"></span></a>', var jobList = new JobList("job-list", {
page: 4, item: '<div><span class="title"></span><span class="description"></span></div>',
pagination: true, page: 4,
valueNames: ["description", "title", {data: ["id"]}]} pagination: true,
var jobList = new JobList("job-list", jobListOptions); valueNames: ["description", "title", {data: ["id"]}]
jobList.on("filterComplete", List.updatePagination); });
jobList.on("searchComplete", List.updatePagination); jobList.on("filterComplete", List.updatePagination);
jobList.on("searchComplete", List.updatePagination);
</script> </script>
<div id="new-corpus-modal" class="modal"> <div id="new-corpus-modal" class="modal">
......
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