diff --git a/API/DataObjects.py b/API/DataObjects.py new file mode 100644 index 0000000..7855755 --- /dev/null +++ b/API/DataObjects.py @@ -0,0 +1,40 @@ +from sqlalchemy import create_engine +from sqlalchemy import Column, Integer, String, Binary, Float +from sqlalchemy.orm import relationship, sessionmaker, deferred +from sqlalchemy.orm.collections import attribute_mapped_collection +from sqlalchemy.ext.declarative import declarative_base + +from datetime import datetime + +Base = declarative_base() + +class LocationData(Base): + """ + Location Data for HuskyTrack + + Attributes: + uuid: The Location Data's universally unique identifier + uid: The user ID of the user this data belongs to + latitude: The latitude of the device when this measurement was taken + longitude: The longitude of the device when this measurement was taken + accuracy: The accuracy of the measurement as reported by the device + speed: The speed of the device when this measurement was taken + heading: The heading of the device when this measurement was taken + altitude: The altitude of the device when this measurement was taken + timestamp: The time at which this measurement was taken [ISO-8601 UTC] + """ + + __tablename__ = 'location_data' + + uuid = Column('uuid', String, primary_key=True) + uid = Column('uid', String, nullable=False) + latitude = Column('latitude', Float, nullable=False) + longitude = Column('longitude', Float, nullable=False) + accuracy = Column('accuracy', Float, nullable=False) + speed = Column('speed', Float, nullable=False) + heading = Column('heading', Float, nullable=False) + altitude = Column('altitude', Float, nullable=False) + timestamp = Column('timestamp', String, nullable=False) + + def __repr__(self): + return self.uuid \ No newline at end of file diff --git a/API/DataObjects.pyc b/API/DataObjects.pyc new file mode 100644 index 0000000..d2b98d6 Binary files /dev/null and b/API/DataObjects.pyc differ diff --git a/API/__pycache__/DataObjects.cpython-36.pyc b/API/__pycache__/DataObjects.cpython-36.pyc new file mode 100644 index 0000000..9c5ec1d Binary files /dev/null and b/API/__pycache__/DataObjects.cpython-36.pyc differ diff --git a/API/api.py b/API/api.py new file mode 100644 index 0000000..98fbba9 --- /dev/null +++ b/API/api.py @@ -0,0 +1,146 @@ +import configparser +import sys +import json +from datetime import datetime + +from flask import Flask, request + +from DataObjects import LocationData +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker + +from collections import defaultdict + +app = Flask(__name__) + +ISO8601 = "%Y%m%dT%H%M%S" +ISO8601_O = "%Y-%m-%d %H:%M:%S" + + +def get_config(): + """ + Get the configuration data for the application + """ + try: + config = configparser.SafeConfigParser() + config.read('config.ini') + return config + except Exception as e: + print("No configuration file found.", e) + sys.exit() + +def get_session(config): + """ + Attempt to initialize the database session, quit if the + program cannot find the database. + """ + try: + # Collect Configuration + dao_type = config.get('database', 'type').lower() + username = config.get('database', 'username') + password = config.get('database', 'pass') + host = config.get('database', 'host') + port = config.get('database', 'port') + database = config.get('database', 'database') + + # Build URL + if dao_type == 'sqlite': + url = dao_type + ":///" + host + else: + url = dao_type + "://" + username +\ + ":" + password + "@" + host + \ + ":" + port + "/" + database + + # Create session + engine = create_engine(url) + Session = sessionmaker(bind=engine) + session = Session() + return session + + except Exception as e: + print("Unable to configure database connection. Error:", e) + sys.exit() + +@app.route('/api') +def api(): + """ + Returns the version for compatibility/availability checks" + """ + return 'v0.1' + +@app.route('/api/locationdata', methods=('GET', 'POST')) +def location_data(): + """ + Request or post location data + """ + session = get_session(get_config()) + if request.method == 'POST': + if request.is_json: + try: + raw_location_data = request.get_json() + location = raw_location_data["location"] + coords = location["coords"] + lat = coords["latitude"] + lng = coords["longitude"] + acc = coords["accuracy"] + spd = coords["speed"] + hed = coords["heading"] + alt = coords["altitude"] + tim = location["timestamp"] + uuid = location["uuid"] + uid = "1" + locationData = LocationData(uuid = uuid, + uid = uid, + latitude = lat, + longitude = lng, + accuracy = acc, + speed = spd, + heading = hed, + altitude = alt, + timestamp = tim) + session.add(locationData) + session.commit() + return "Success" + except Exception as e: + session.rollback() + return ("Fail", str(e)) + else: + return "NOT JSON" + else: + try: + start = request.args.get('start') + end = request.args.get('end') + + start = datetime.strptime(start, ISO8601) + end = datetime.strptime(end, ISO8601) + + print(str([str(start), str(end)])) + query_results = session.query(LocationData).filter(LocationData.timestamp > start, LocationData.timestamp < end).all() + + dataPoints = defaultdict(list) + for result in query_results: + dataPoints[result.uid].append(result) + + result = [] + for user in dataPoints: + print(user) + json_entry = json.loads('{}') + json_entry['user_id'] = user + json_entry['start_time'] = min(dataPoints[user], key=lambda x:x.timestamp).timestamp + json_entry['end_time'] = max(dataPoints[user], key=lambda x:x.timestamp).timestamp + json_entry['locations'] = [] + for location in dataPoints[user]: + json_location = json.loads('{}') + json_location['latitude'] = location.latitude + json_location['longitude'] = location.longitude + json_location['timestamp'] = location.timestamp + json_entry['locations'].append(json_location) + result.append(json_entry) + + return json.dumps(result) + except Exception as e: + print(e) + return json.dumps([[l.uuid, l.latitude, l.longitude, l.timestamp] for l in session.query(LocationData).all()]) + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file diff --git a/API/config.ini b/API/config.ini new file mode 100644 index 0000000..94db58d --- /dev/null +++ b/API/config.ini @@ -0,0 +1,7 @@ +[database] +type = sqlite +host = C:\Users\demel\Documents\GitHub\GPS-Tracking-of-On-Campus-Walking-Project\API\db\test.db +username = mason +pass = mason +port = 3306 +database = mason \ No newline at end of file diff --git a/API/db/init_sqlite.sql b/API/db/init_sqlite.sql new file mode 100644 index 0000000..7e946da --- /dev/null +++ b/API/db/init_sqlite.sql @@ -0,0 +1,14 @@ +BEGIN TRANSACTION; +DROP TABLE IF EXISTS `location_data`; +CREATE TABLE IF NOT EXISTS `location_data` ( + `uuid` TEXT NOT NULL PRIMARY KEY UNIQUE, + `uid` TEXT NOT NULL, + `latitude` REAL NOT NULL, + `longitude` REAL NOT NULL, + `accuracy` REAL NOT NULL, + `speed` REAL NOT NULL, + `heading` REAL NOT NULL, + `altitude` REAL NOT NULL, + `timestamp` TEXT NOT NULL +); +COMMIT; \ No newline at end of file diff --git a/API/db/test.db b/API/db/test.db new file mode 100644 index 0000000..759bb0b Binary files /dev/null and b/API/db/test.db differ diff --git a/API/post_test.py b/API/post_test.py new file mode 100644 index 0000000..c2e3836 --- /dev/null +++ b/API/post_test.py @@ -0,0 +1,44 @@ +import json +import urllib2 +import uuid +import datetime + +data = { + "location": { + "coords": { + "latitude": 1.5, + "longitude": 1.5, + "accuracy": 1.5, + "speed": 1.5, + "heading": 1.5, + "altitude": 1.5 + }, + "extras": { + "foo": "bar" + }, + "activity": { + "type": "still", + "confidence": 100 + }, + "geofence": { + "identifier": "identifier", + "action": "ENTER" + }, + "battery": { + "level": 1.5, + "is_charging": True + }, + "timestamp": str(datetime.datetime.now().replace(microsecond=0).isoformat()), + "uuid": str(uuid.uuid4()), + "event": "EVENT", + "is_moving": 1.0, + "odometer": 1.0 + } + } + +req = urllib2.Request('http://127.0.0.1:5000/api/locationdata') +req.add_header('Content-Type', 'application/json') + +response = urllib2.urlopen(req, json.dumps(data)) + +print(response.read()) \ No newline at end of file