From 32dcb0c82960c7cff048e1875950465e1bfcd9da Mon Sep 17 00:00:00 2001
From: Fabio Manganiello <blacklight86@gmail.com>
Date: Sat, 6 Jan 2018 22:52:24 +0100
Subject: [PATCH] Added database plugin, #43

---
 platypush/plugins/db/__init__.py | 79 ++++++++++++++++++++++++++++++++
 requirements.txt                 |  3 ++
 setup.py                         |  3 +-
 3 files changed, 84 insertions(+), 1 deletion(-)
 create mode 100644 platypush/plugins/db/__init__.py

diff --git a/platypush/plugins/db/__init__.py b/platypush/plugins/db/__init__.py
new file mode 100644
index 000000000..f392d8c2f
--- /dev/null
+++ b/platypush/plugins/db/__init__.py
@@ -0,0 +1,79 @@
+import logging
+
+from sqlalchemy import create_engine
+
+from platypush.message.response import Response
+
+from .. import Plugin
+
+class DbPlugin(Plugin):
+    """ Database plugin. It allows you to programmatically select, insert,
+        update and delete records on a database backend through requests,
+        procedures and event hooks """
+
+    engine = None
+
+    def __init__(self, engine=None, *args, **kwargs):
+        """
+        Params:
+            engine -- Default SQLAlchemy connection engine string
+            (e.g. sqlite:///:memory: or mysql://user:pass@localhost/test)
+            that will be used. You can override this value in your statement actions
+
+            args, kwargs -- Extra arguments for sqlalchemy.create_engine
+        """
+
+        self.engine = self._get_engine(engine, *args, **kwargs)
+
+
+    def _get_engine(self, engine=None, *args, **kwargs):
+        if engine:
+            return create_engine(engine, *args, **kwargs)
+        else:
+            return self.engine
+
+    def execute(self, statement, engine=None, *args, **kwargs):
+        """ Executes a generic SQL statement """
+
+        engine = self._get_engine(engine, *args, **kwargs)
+
+        with engine.connect() as connection:
+            result = connection.execute(statement)
+
+        return Response()
+
+
+    def select(self, query, engine=None, *args, **kwargs):
+        """ Returns rows (as a list of dicts) given a query """
+
+        engine = self._get_engine(engine, *args, **kwargs)
+
+        with engine.connect() as connection:
+            result = connection.execute(query)
+            columns = result.keys()
+            rows = [
+                { columns[i]: row[i] for i in range(0, len(columns)) }
+                for row in result.fetchall()
+            ]
+
+        return Response(output=rows)
+
+
+    def insert(self, table, records, engine=None, *args, **kwargs):
+        """ Inserts records (as a list of dicts) into a table """
+
+        engine = self._get_engine(engine, *args, **kwargs)
+
+        for record in records:
+            statement = 'INSERT INTO {}({}) VALUES({})'.format \
+                (table, ','.join(record.keys()),
+                 ','.join([ ':' + key for key in record.keys() ]))
+
+            with engine.connect() as connection:
+                connection.execute(statement, **record)
+
+        return Response()
+
+
+# vim:sw=4:ts=4:et:
+
diff --git a/requirements.txt b/requirements.txt
index c65c0d08a..62753dd5c 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -11,6 +11,9 @@ websocket-client
 # HTTP backend support
 flask
 
+# Database plugin support
+sqlalchemy
+
 # Philips Hue plugin support
 phue
 
diff --git a/setup.py b/setup.py
index d5dfe5165..ea0b62ef6 100755
--- a/setup.py
+++ b/setup.py
@@ -64,6 +64,7 @@ setup(
         'Support for Apache Kafka backend': ['kafka-python'],
         'Support for Pushbullet backend': ['requests', 'websocket-client'],
         'Support for HTTP backend': ['flask'],
+        'Support for database plugin': ['sqlalchemy'],
         'Support for Philips Hue plugin': ['phue'],
         'Support for MPD/Mopidy music server plugin': ['python-mpd2'],
         'Support for Belkin WeMo Switch plugin': ['ouimeaux'],
@@ -71,7 +72,7 @@ setup(
         'Support for OMXPlayer plugin': ['omxplayer'],
         'Support for YouTube in the OMXPlayer plugin': ['youtube-dl'],
         'Support for Google Assistant': ['google-assistant-sdk[samples]'],
-        # 'Support for Flic buttons': ['-e git+https://github.com/50ButtonsEach/fliclib-linux-hci']
+        'Support for Flic buttons': ['git+ssh://git@github.com/50ButtonsEach/fliclib-linux-hci']
     },
 )