Send data model changes to Watchers via redis

Change-Id: I1b210dab6e63fa1f151ab6884e7f634919311bbb
diff --git a/containers/xos/pip_requirements.txt b/containers/xos/pip_requirements.txt
index 4d67ceb..ae66fbe 100644
--- a/containers/xos/pip_requirements.txt
+++ b/containers/xos/pip_requirements.txt
@@ -132,3 +132,4 @@
 wheel==0.24.0
 wrapt==1.10.8
 wsgiref==0.1.2
+redis==2.10.5
diff --git a/xos/core/models/plcorebase.py b/xos/core/models/plcorebase.py
index 891356d..f671cde 100644
--- a/xos/core/models/plcorebase.py
+++ b/xos/core/models/plcorebase.py
@@ -13,6 +13,9 @@
 from model_autodeletion import ephemeral_models
 from cgi import escape as html_escape
 
+import redis
+from redis import ConnectionError
+
 try:
     # This is a no-op if observer_disabled is set to 1 in the config file
     from synchronizers.base import *
@@ -273,6 +276,30 @@
         if 'synchronizer' not in threading.current_thread().name:
             self.updated = timezone.now()
 
+        # Transmit update via Redis
+        changed_fields = []
+
+        if self.pk is not None:
+            my_model = type(self)
+            try:
+                orig = my_model.objects.get(pk=self.pk)
+
+                for f in my_model._meta.fields:
+                    oval = getattr(orig, f.name)
+                    nval = getattr(self, f.name)
+                    if oval != nval:
+                        changed_fields.append(f.name)
+            except:
+                changed_fields.append('__lookup_error')
+
+        try:
+            r = redis.Redis("redis")
+            payload = json.dumps({'pk':self.pk,'changed_fields':changed_fields})
+            r.publish(self.__class__.__name__, payload)
+        except ConnectionError:
+            # Redis not running.
+            pass
+
         super(PlCoreBase, self).save(*args, **kwargs)
 
         # This is a no-op if observer_disabled is set