From d699fd09e59023eec7d09bc064e332a396aaca99 Mon Sep 17 00:00:00 2001
From: Patrick Jentsch <p.jentsch@uni-bielefeld.de>
Date: Tue, 21 Feb 2023 13:59:11 +0100
Subject: [PATCH] Add live data updates for corpus follower lists

---
 app/models.py                                 | 45 +++++++++++++++----
 .../js/ResourceLists/CorpusFollowerList.js    | 33 +++++++++++++-
 2 files changed, 69 insertions(+), 9 deletions(-)

diff --git a/app/models.py b/app/models.py
index 5e2bdcdf..4e40793b 100644
--- a/app/models.py
+++ b/app/models.py
@@ -653,7 +653,7 @@ class User(HashidMixin, UserMixin, db.Model):
                 self.role.to_json_serializeable(backrefs=True)
         if relationships:
             json_serializeable['corpus_follower_associations'] = {
-                x.hashid: x.to_json_serializeable()
+                x.hashid: x.to_json_serializeable(relationships=True)
                 for x in self.corpus_follower_associations
             }
             json_serializeable['corpora'] = {
@@ -672,10 +672,6 @@ class User(HashidMixin, UserMixin, db.Model):
                 x.hashid: x.to_json_serializeable(relationships=True)
                 for x in self.spacy_nlp_pipeline_models
             }
-            json_serializeable['followed_corpora'] = {
-                x.hashid: x.to_json_serializeable(relationships=True)
-                for x in self.followed_corpora
-            }
 
         if filter_by_privacy_settings:
             if not self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_EMAIL):
@@ -1430,7 +1426,7 @@ class Corpus(HashidMixin, db.Model):
                 self.user.to_json_serializeable(backrefs=True)
         if relationships:
             json_serializeable['corpus_follower_associations'] = {
-                x.hashid: x.to_json_serializeable()
+                x.hashid: x.to_json_serializeable(relationships=True)
                 for x in self.corpus_follower_associations
             }
             json_serializeable['files'] = {
@@ -1454,12 +1450,27 @@ class Corpus(HashidMixin, db.Model):
 @db.event.listens_for(TesseractOCRPipelineModel, 'after_delete')
 def ressource_after_delete(mapper, connection, ressource):
     jsonpatch = [{'op': 'remove', 'path': ressource.jsonpatch_path}]
-    room = f'users.{ressource.user_hashid}'
-    socketio.emit('users.patch', jsonpatch, room=room)
     room = f'/users/{ressource.user_hashid}'
     socketio.emit('PATCH', jsonpatch, room=room)
 
 
+@db.event.listens_for(CorpusFollowerAssociation, 'after_delete')
+def corpus_follower_association_after_delete_handler(mapper, connection, ressource):
+    corpus_owner_hashid = ressource.corpus.user.hashid
+    corpus_hashid = hashids.encode(ressource.corpus_id)
+    follower_hashid = hashids.encode(ressource.follower_id)
+    # Send a PATCH to the corpus owner
+    jsonpatch_path = f'/users/{corpus_owner_hashid}/corpora/{corpus_hashid}/corpus_follower_associations/{ressource.hashid}'
+    jsonpatch = [{'op': 'remove', 'path': jsonpatch_path}]
+    room = f'/users/{corpus_owner_hashid}'
+    socketio.emit('PATCH', jsonpatch, room=room)
+    # Send a PATCH to the follower
+    jsonpatch_path = f'/users/{follower_hashid}/corpus_follower_associations/{ressource.hashid}'
+    jsonpatch = [{'op': 'remove', 'path': jsonpatch_path}]
+    room = f'/users/{follower_hashid}'
+    socketio.emit('PATCH', jsonpatch, room=room)
+
+
 @db.event.listens_for(Corpus, 'after_insert')
 @db.event.listens_for(CorpusFile, 'after_insert')
 @db.event.listens_for(Job, 'after_insert')
@@ -1478,6 +1489,24 @@ def ressource_after_insert_handler(mapper, connection, ressource):
     socketio.emit('PATCH', jsonpatch, room=room)
 
 
+# @db.event.listens_for(CorpusFollowerAssociation, 'after_insert')
+# def corpus_follower_association_after_insert_handler(mapper, connection, ressource):
+#     corpus_owner_hashid = ressource.corpus.user.hashid
+#     corpus_hashid = hashids.encode(ressource.corpus_id)
+#     follower_hashid = hashids.encode(ressource.follower_id)
+#     value = ressource.to_json_serializeable()
+#     # Send a PATCH to the corpus owner
+#     jsonpatch_path = f'/users/{corpus_owner_hashid}/corpora/{corpus_hashid}/corpus_follower_associations/{ressource.hashid}'
+#     jsonpatch = [{'op': 'add', 'path': jsonpatch_path, 'value': value}]
+#     room = f'/users/{corpus_owner_hashid}'
+#     socketio.emit('PATCH', jsonpatch, room=room)
+#     # Send a PATCH to the follower
+#     jsonpatch_path = f'/users/{follower_hashid}/corpus_follower_associations/{ressource.hashid}'
+#     jsonpatch = [{'op': 'add', 'path': jsonpatch_path, 'value': value}]
+#     room = f'/users/{follower_hashid}'
+#     socketio.emit('PATCH', jsonpatch, room=room)
+
+
 @db.event.listens_for(Corpus, 'after_update')
 @db.event.listens_for(CorpusFile, 'after_update')
 @db.event.listens_for(Job, 'after_update')
diff --git a/app/static/js/ResourceLists/CorpusFollowerList.js b/app/static/js/ResourceLists/CorpusFollowerList.js
index d83e4a39..616f3793 100644
--- a/app/static/js/ResourceLists/CorpusFollowerList.js
+++ b/app/static/js/ResourceLists/CorpusFollowerList.js
@@ -169,5 +169,36 @@ class CorpusFollowerList extends ResourceList {
     }
   }
 
-  onPatch(patch) {}
+  onPatch(patch) {
+    let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/corpus_follower_associations/([A-Za-z0-9]*)`);
+    let filteredPatch = patch.filter(operation => re.test(operation.path));
+    for (let operation of filteredPatch) {
+      switch(operation.op) {
+        case 'add': {
+          // let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/corpus_follower_associations/([A-Za-z0-9]*)$`);
+          // if (re.test(operation.path)) {this.add(operation.value);}
+          break;
+        }
+        case 'remove': {
+          let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/corpus_follower_associations/([A-Za-z0-9]*)$`);
+          if (re.test(operation.path)) {
+            let [match, jobId] = operation.path.match(re);
+            this.remove(jobId);
+          }
+          break;
+        }
+        case 'replace': {
+          // let re = new RegExp(`^/users/${this.userId}/corpora/${this.corpusId}/corpus_follower_associations/([A-Za-z0-9]*)/(service|status|description|title)$`);
+          // if (re.test(operation.path)) {
+          //   let [match, jobId, valueName] = operation.path.match(re);
+          //   this.replace(jobId, valueName, operation.value);
+          // }
+          break;
+        }
+        default: {
+          break;
+        }
+      }
+    }
+  }
 }
-- 
GitLab