| 1 |  |  | from __future__ import absolute_import | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  | import json | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  | import os | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | from ._connection import DatabaseConnection | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | from ._device import Device | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  | from ._user import User | 
            
                                                                                                            
                            
            
                                    
            
            
                | 9 |  |  | from ._stream import Stream, DATAPOINT_INSERT_LIMIT | 
            
                                                                                                            
                            
            
                                    
            
            
                | 10 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 11 |  |  | # By default assume that a ConnectorDB server is running on localhost with | 
            
                                                                                                            
                            
            
                                    
            
            
                | 12 |  |  | # default configuration | 
            
                                                                                                            
                            
            
                                    
            
            
                | 13 |  |  | CONNECTORDB_URL = "http://localhost:3124" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 14 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 15 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 16 |  |  | class ConnectorDB(Device): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 17 |  |  |     """ConnectorDB is the main entry point for any application that uses the python API. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 18 |  |  |     The class accepts both a username and password in order to log in as a user, and accepts an apikey | 
            
                                                                                                            
                            
            
                                    
            
            
                | 19 |  |  |     when logging in directly from a device:: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 20 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 21 |  |  |         import connectordb | 
            
                                                                                                            
                            
            
                                    
            
            
                | 22 |  |  |         cdb = connectordb.ConnectorDB("myusername","mypassword") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 23 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 24 |  |  |         #prints "myusername/user" - logging in by username/password combo | 
            
                                                                                                            
                            
            
                                    
            
            
                | 25 |  |  |         #logs in as the user device. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 26 |  |  |         print cdb.path | 
            
                                                                                                            
                            
            
                                    
            
            
                | 27 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 28 |  |  |     """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 29 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 30 |  |  |     def __init__(self, user_or_apikey=None, user_password=None, url=CONNECTORDB_URL): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 31 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 32 |  |  |         db = DatabaseConnection(user_or_apikey, user_password, url) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 33 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 34 |  |  |         # ConnectorDB uses bcrypt by default for password hashing. While great for security | 
            
                                                                                                            
                            
            
                                    
            
            
                | 35 |  |  |         # of passwords, it is extremely expensive, so it slows down queries. So, if we logged in | 
            
                                                                                                            
                            
            
                                    
            
            
                | 36 |  |  |         # as a user with password, attempt to get the user device apikey to use for future authentication | 
            
                                                                                                            
                            
            
                                    
            
            
                | 37 |  |  |         # so that queries are fast | 
            
                                                                                                            
                            
            
                                    
            
            
                | 38 |  |  |         if user_password is not None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 39 |  |  |             # Logins happen as a user device | 
            
                                                                                                            
                            
            
                                    
            
            
                | 40 |  |  |             Device.__init__(self, db, user_or_apikey + "/user") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 41 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 42 |  |  |             if self.apikey is not None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 43 |  |  |                 # Reset the auth to be apikey | 
            
                                                                                                            
                            
            
                                    
            
            
                | 44 |  |  |                 db.setauth(self.apikey) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 45 |  |  |         else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 46 |  |  |             # We logged in as a device - we have to ping the server to get our | 
            
                                                                                                            
                            
            
                                    
            
            
                | 47 |  |  |             # name | 
            
                                                                                                            
                            
            
                                    
            
            
                | 48 |  |  |             Device.__init__(self, db, db.path) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 49 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 50 |  |  |     def __call__(self, path): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 51 |  |  |         """Enables getting arbitrary users/devices/streams in a simple way. Just call the object | 
            
                                                                                                            
                            
            
                                    
            
            
                | 52 |  |  |         with the u/d/s uri | 
            
                                                                                                            
                            
            
                                    
            
            
                | 53 |  |  |             cdb = ConnectorDB("myapikey") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 54 |  |  |             cdb("user1") -> user1 object | 
            
                                                                                                            
                            
            
                                    
            
            
                | 55 |  |  |             cdb("user1/device1") -> user1/device1 object | 
            
                                                                                                            
                            
            
                                    
            
            
                | 56 |  |  |             cdb("user1/device1/stream1") -> user1/device1/stream1 object | 
            
                                                                                                            
                            
            
                                    
            
            
                | 57 |  |  |         """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 58 |  |  |         n = path.count("/") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 59 |  |  |         if n == 0: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 60 |  |  |             return User(self.db, path) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 61 |  |  |         elif n == 1: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 62 |  |  |             return Device(self.db, path) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 63 |  |  |         else: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 64 |  |  |             return Stream(self.db, path) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 65 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 66 |  |  |     def close(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 67 |  |  |         """shuts down all active connections to ConnectorDB""" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 68 |  |  |         self.db.close() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 69 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 70 |  |  |     def reset_apikey(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 71 |  |  |         """invalidates the device's current api key, and generates a new one. Resets current auth to use the new apikey, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 72 |  |  |         since the change would have future queries fail if they use the old api key.""" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 73 |  |  |         apikey = Device.reset_apikey(self) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 74 |  |  |         self.db.setauth(apikey) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 75 |  |  |         return apikey | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 76 |  |  |  | 
            
                                                                        
                            
            
                                    
            
            
                | 77 |  |  |     def count_users(self): | 
            
                                                                        
                            
            
                                    
            
            
                | 78 |  |  |         """Gets the total number of users registered with the database. Only available to administrator.""" | 
            
                                                                        
                            
            
                                    
            
            
                | 79 |  |  |         return int(self.db.get("", {"q": "countusers"}).text) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 80 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 81 |  |  |     def count_devices(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 82 |  |  |         """Gets the total number of devices registered with the database. Only available to administrator.""" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 83 |  |  |         return int(self.db.get("", {"q": "countdevices"}).text) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 84 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 85 |  |  |     def count_streams(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 86 |  |  |         """Gets the total number of streams registered with the database. Only available to administrator.""" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 87 |  |  |         return int(self.db.get("", {"q": "countstreams"}).text) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 88 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 89 |  |  |     def info(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 90 |  |  |         """returns a dictionary of information about the database, including the database version, the transforms | 
            
                                                                                                            
                            
            
                                    
            
            
                | 91 |  |  |         and the interpolators supported:: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 92 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 93 |  |  |             >>>cdb = connectordb.ConnectorDB(apikey) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 94 |  |  |             >>>cdb.info() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 95 |  |  |             { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 96 |  |  |                 "version": "0.3.0", | 
            
                                                                                                            
                            
            
                                    
            
            
                | 97 |  |  |                 "transforms": { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 98 |  |  |                     "sum": {"description": "Returns the sum of all the datapoints that go through the transform"} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 99 |  |  |                     ... | 
            
                                                                                                            
                            
            
                                    
            
            
                | 100 |  |  |                 }, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 101 |  |  |                 "interpolators": { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 102 |  |  |                     "closest": {"description": "Uses the datapoint closest to the interpolation timestamp"} | 
            
                                                                                                            
                            
            
                                    
            
            
                | 103 |  |  |                     ... | 
            
                                                                                                            
                            
            
                                    
            
            
                | 104 |  |  |                 } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 105 |  |  |             } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 106 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 107 |  |  |         """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 108 |  |  |         return { | 
            
                                                                                                            
                            
            
                                    
            
            
                | 109 |  |  |             "version": self.db.get("meta/version").text, | 
            
                                                                                                            
                            
            
                                    
            
            
                | 110 |  |  |             "transforms": self.db.get("meta/transforms").json(), | 
            
                                                                                                            
                            
            
                                    
            
            
                | 111 |  |  |             "interpolators": self.db.get("meta/interpolators").json() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 112 |  |  |         } | 
            
                                                                                                            
                            
            
                                    
            
            
                | 113 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 114 |  |  |     def __repr__(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 115 |  |  |         return "[ConnectorDB:%s]" % (self.path, ) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 116 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 117 |  |  |     def users(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 118 |  |  |         """Returns the list of users in the database""" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 119 |  |  |         result = self.db.read("", {"q": "ls"}) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 120 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 121 |  |  |         if result is None or result.json() is None: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 122 |  |  |             return [] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 123 |  |  |         users = [] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 124 |  |  |         for u in result.json(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 125 |  |  |             usr = self(u["name"]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 126 |  |  |             usr.metadata = u | 
            
                                                                                                            
                            
            
                                    
            
            
                | 127 |  |  |             users.append(usr) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 128 |  |  |         return users | 
            
                                                                                                            
                            
            
                                    
            
            
                | 129 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 130 |  |  |     def ping(self): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 131 |  |  |         """Pings the ConnectorDB server. Useful for checking if the connection is valid""" | 
            
                                                                                                            
                            
            
                                    
            
            
                | 132 |  |  |         return self.db.ping() | 
            
                                                                                                            
                            
            
                                    
            
            
                | 133 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 134 |  |  |     def import_users(self, directory): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 135 |  |  |         """Imports version 1 of ConnectorDB export. These exports can be generated | 
            
                                                                                                            
                            
            
                                    
            
            
                | 136 |  |  |         by running user.export(dir), possibly on multiple users. | 
            
                                                                                                            
                            
            
                                    
            
            
                | 137 |  |  |         """ | 
            
                                                                                                            
                            
            
                                    
            
            
                | 138 |  |  |         exportInfoFile = os.path.join(directory, "connectordb.json") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 139 |  |  |         with open(exportInfoFile) as f: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 140 |  |  |             exportInfo = json.load(f) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 141 |  |  |         if exportInfo["Version"] != 1: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 142 |  |  |             raise ValueError("Not able to read this import version") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 143 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 144 |  |  |         # Now we list all the user directories | 
            
                                                                                                            
                            
            
                                    
            
            
                | 145 |  |  |         for name in os.listdir(directory): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 146 |  |  |             udir = os.path.join(directory, name) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 147 |  |  |             if os.path.isdir(udir): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 148 |  |  |                 # Let's read in the user | 
            
                                                                                                            
                            
            
                                    
            
            
                | 149 |  |  |                 with open(os.path.join(udir, "user.json")) as f: | 
            
                                                                                                            
                            
            
                                    
            
            
                | 150 |  |  |                     usrdata = json.load(f) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 151 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 152 |  |  |                 u = self(usrdata["name"]) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 153 |  |  |                 if u.exists(): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 154 |  |  |                     raise ValueError("The user " + name + " already exists") | 
            
                                                                                                            
                            
            
                                    
            
            
                | 155 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 156 |  |  |                 del usrdata["name"] | 
            
                                                                                                            
                            
            
                                    
            
            
                | 157 |  |  |                 u.create(password=name, **usrdata) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 158 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 159 |  |  |                 # Now read all of the user's devices | 
            
                                                                                                            
                            
            
                                    
            
            
                | 160 |  |  |                 for dname in os.listdir(udir): | 
            
                                                                                                            
                            
            
                                    
            
            
                | 161 |  |  |                     ddir = os.path.join(udir, dname) | 
            
                                                                                                            
                            
            
                                    
            
            
                | 162 |  |  |                     if os.path.isdir(ddir): | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 163 |  |  |                         u.import_device(ddir) | 
            
                                                        
            
                                    
            
            
                | 164 |  |  |  |