├── .gitignore ├── siteconf.py ├── requirements.txt ├── siteconf.wsgi ├── json_handler.py ├── fabfile.py └── geologgerws.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.pyc 3 | virtpy 4 | .Python 5 | -------------------------------------------------------------------------------- /siteconf.py: -------------------------------------------------------------------------------- 1 | from geologgerws import Root as Root 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | CherryPy==3.2.4 2 | geojson==1.0.1 3 | psycopg2==2.5.1 4 | pymongo==2.5.2 5 | simplejson==3.3.0 6 | wsgiref==0.1.2 7 | -------------------------------------------------------------------------------- /siteconf.wsgi: -------------------------------------------------------------------------------- 1 | import os 2 | activate_this = '/var/www/apps/geologgerws/' + 'virtpy/bin/activate_this.py' 3 | execfile(activate_this, dict(__file__=activate_this)) 4 | 5 | import site 6 | site.addsitedir('/var/www/apps/geologgerws/') 7 | 8 | import cherrypy 9 | from siteconf import Root as Root 10 | 11 | application = cherrypy.Application(Root(), script_name=None, config=None) 12 | 13 | 14 | if __name__ == '__main__': 15 | 16 | cherrypy.engine.start() 17 | cherrypy.engine.block() 18 | -------------------------------------------------------------------------------- /json_handler.py: -------------------------------------------------------------------------------- 1 | import calendar 2 | import datetime 3 | import re 4 | try: 5 | import uuid 6 | _use_uuid = True 7 | except ImportError: 8 | _use_uuid = False 9 | 10 | from bson.dbref import DBRef 11 | from bson.max_key import MaxKey 12 | from bson.min_key import MinKey 13 | from bson.objectid import ObjectId 14 | from bson.timestamp import Timestamp 15 | from bson.tz_util import utc 16 | from geojson.mapping import Mapping 17 | from geojson import Feature, FeatureCollection 18 | 19 | _RE_TYPE = type(re.compile("foo")) 20 | 21 | def handler(obj): 22 | if isinstance(obj, ObjectId): 23 | return {"$oid": str(obj)} 24 | if isinstance(obj, DBRef): 25 | return obj.as_doc() 26 | if isinstance(obj, datetime.datetime): 27 | # TODO share this code w/ bson.py? 28 | return obj.isoformat() + 'Z' 29 | if isinstance(obj, _RE_TYPE): 30 | flags = "" 31 | if obj.flags & re.IGNORECASE: 32 | flags += "i" 33 | if obj.flags & re.MULTILINE: 34 | flags += "m" 35 | return {"$regex": obj.pattern, 36 | "$options": flags} 37 | if isinstance(obj, MinKey): 38 | return {"$minKey": 1} 39 | if isinstance(obj, MaxKey): 40 | return {"$maxKey": 1} 41 | if isinstance(obj, Timestamp): 42 | return {"t": obj.time, "i": obj.inc} 43 | if _use_uuid and isinstance(obj, uuid.UUID): 44 | return {"$uuid": obj.hex} 45 | if isinstance(obj, Mapping): 46 | return dict(obj) 47 | if isinstance(obj, Feature): 48 | return dict(obj) 49 | if isinstance(obj, FeatureCollection): 50 | return dict(obj) 51 | raise TypeError("%r is not JSON serializable" % obj) 52 | -------------------------------------------------------------------------------- /fabfile.py: -------------------------------------------------------------------------------- 1 | from fabric.api import * 2 | from fabric.contrib.files import exists, append, comment 3 | from fabric.colors import red 4 | import os 5 | 6 | env.sitename = os.path.basename(os.getcwd()) 7 | env.mongo_host = 'fire.rccc.ou.edu' 8 | env.psql_host = 'fire.rccc.ou.edu' 9 | env.apache_config = '/etc/httpd/conf.d/%(sitename)s.conf' % env 10 | env.python = '/usr/bin/python2.6' 11 | 12 | 13 | def testing(): 14 | """ 15 | Work on staging environment 16 | """ 17 | env.settings = 'testing' 18 | env.path = '/var/www/apps/%(sitename)s' % env 19 | env.virtpy = '%(path)s/virtpy' % env 20 | env.log_path = '%(path)s/log' % env 21 | env.hosts = ['test.cybercommons.org'] 22 | 23 | def fire(): 24 | """ 25 | Setup on fire.rccc.ou.edu 26 | """ 27 | env.settings = 'production' 28 | env.path = '/scratch/www/wsgi_sites/%(sitename)s' % env 29 | env.virtpy = '%(path)s/virtpy' % env 30 | env.log_path = '%(path)s/log' % env 31 | env.hosts = ['fire.rccc.ou.edu'] 32 | 33 | 34 | def production(): 35 | """ 36 | Work on production environment 37 | """ 38 | env.settings = 'production' 39 | env.path = '/var/www/apps/%(sitename)s' % env 40 | env.virtpy = '%(path)s/virtpy' % env 41 | env.log_path = '%(path)s/log' % env 42 | env.hosts = ['production.cybercommons.org'] 43 | 44 | def setup(): 45 | """ 46 | Setup directories and copy everything but virtual environment to server, 47 | then install virtual environment based on requirements.txt 48 | """ 49 | setup_directories() 50 | copy_working_dir() 51 | setup_virtualenv() 52 | install_requirements() 53 | apache_config() 54 | bounce_apache() 55 | 56 | def deploy(): 57 | """ 58 | Deploy changes which don't impact virtual environment 59 | """ 60 | copy_working_dir() 61 | bounce_apache() 62 | 63 | def setup_directories(): 64 | """ 65 | Setup directories on the remote system 66 | """ 67 | if not exists('%(path)s' % env): 68 | sudo('mkdir -p %(path)s' % env) 69 | sudo('mkdir -p %(log_path)s' % env) 70 | sudo('mkdir -p %(virtpy)s' % env) 71 | sudo('chgrp -R cybercom %(path)s' % env) 72 | sudo('chmod -R g+rw %(path)s' % env) 73 | 74 | 75 | def virtualenv(command): 76 | """ 77 | Wrapper to activate and run virtual environment 78 | """ 79 | with cd(env.virtpy): 80 | run('source %(virtpy)s/bin/activate' % env + '&&' + command) 81 | 82 | def setup_virtualenv(): 83 | """ 84 | Install the virtual environment 85 | """ 86 | run('virtualenv -p %(python)s --no-site-packages %(virtpy)s' % env) 87 | 88 | def bounce_apache(): 89 | """ Restart the apache web server """ 90 | sudo('/etc/init.d/httpd restart') 91 | 92 | def apache_config(secure=False): 93 | """ 94 | Set the apache config file to point to wsgi. Assumes app will be accessible at /sitename/ and 95 | .wsgi named sitename.wsgi 96 | """ 97 | # check if apache config lines exist in old wsgi_sites.conf and comment if found 98 | comment('/etc/httpd/conf.d/wsgi_sites.conf', r'^WSGIScriptAlias /%(sitename)s .*$' % env, use_sudo=True) 99 | confline = 'WSGIScriptAlias /%(sitename)s %(path)s/siteconfig.wsgi' %env 100 | append('%(apache_config)s' % env, confline, use_sudo=True) 101 | if secure: 102 | secure_app = """ 103 | 104 | AuthType Basic 105 | require valid-user 106 | TKTAuthLoginURL http://test.cybercommons.org/accounts/login/ 107 | TKTAuthTimeoutURL http://test.cybercommons.org/accounts/login/?timeout=1 108 | TKTAuthPostTimeoutURL http://test.cybercommons.org/accounts/login/?posttimeout=1 109 | TKTAuthUnauthURL http://test.cybercommons.org/accounts/login/?unauth=1 110 | TKTAuthIgnoreIP on 111 | TKTAuthBackArgName next 112 | 113 | """ % (env) 114 | append('%(apache_config)s' % env, secure_app, use_sudo=True) 115 | 116 | def copy_working_dir(): 117 | """ 118 | Shuttle application code from local to remote 119 | """ 120 | local('tar --exclude virtpy -czf /tmp/deploy_%(sitename)s.tgz .' % env) 121 | put('/tmp/deploy_%(sitename)s.tgz' % env, '%(path)s/deploy_%(sitename)s.tgz' % env) 122 | sudo('cd %(path)s; tar -xf deploy_%(sitename)s.tgz; rm deploy_%(sitename)s.tgz' % env) 123 | local('rm /tmp/deploy_%(sitename)s.tgz' % env) 124 | 125 | def install_requirements(): 126 | """ 127 | Install the contents of requirements.txt to the virtual environment 128 | """ 129 | check = exists('%(path)s/requirements.txt' % env) 130 | if check: 131 | virtualenv('pip install -E %(virtpy)s -r %(path)s/requirements.txt' % env) 132 | else: 133 | print red("Can't find requirements.txt!") 134 | 135 | def upgrade_requirements(): 136 | """ 137 | Install the contents of requirements.txt to the virtual environment 138 | """ 139 | check = exists('%(path)s/requirements.txt' % env) 140 | if check: 141 | virtualenv('pip install --upgrade -E %(virtpy)s -r %(path)s/requirements.txt' % env) 142 | else: 143 | print red("Can't find requirements.txt!") 144 | -------------------------------------------------------------------------------- /geologgerws.py: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # The MIT License (MIT) 3 | # 4 | # Copyright (c) 2013 The University of Oklahoma 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | ################################################################################ 24 | # 25 | # A web service to get data stored in mongodb for the geologger web application. 26 | # 27 | # Jonah Duckles - jduckles@ou.edu 28 | # 29 | 30 | ''' 31 | GET /lightlogs - list of available lightlog tagnames 32 | GET /lightlogs/tagname - a single tagname's most recent lightlog 33 | GET /twilights/ - a list of available twilight tagnames 34 | GET /twilights/tagname - a single tagnamees' most recent twilight data 35 | GET /coord/ - a list of available geospatial coordinates and their timestamp 36 | GET /coord/tagname - a single tagnames' most recent coordinate data 37 | ''' 38 | 39 | 40 | import cherrypy 41 | import pymongo 42 | import psycopg2 43 | import json 44 | from json_handler import handler 45 | 46 | import logging 47 | logging.basicConfig(level=logging.INFO) 48 | 49 | def distinct(seq): 50 | ''' returns distinct elements of a sequence ''' 51 | seen=set() 52 | seen_add = seen.add 53 | return [ x for x in seq if x not in seen and not seen_add(x) ] 54 | 55 | 56 | class Root(object): 57 | 58 | def __init__(self): 59 | ''' Intialize database connections ''' 60 | self.db = pymongo.Connection() 61 | self.auth = psycopg2.connect(dbname="auth", user='mstacy', host="fire.rccc.ou.edu") 62 | def uidtoname(self,user_id): 63 | ''' Convert uid to username ''' 64 | cur = self.auth.cursor() 65 | if user_id: 66 | cur.execute('select username from auth_user where id = %s' % user_id) 67 | return cur.fetchone()[0] 68 | else: 69 | return 'guest' 70 | def geologgercollection(self,collection,username,querytype,queryid=None): 71 | '''Get data from MongDB based on client's username''' 72 | try: 73 | col = self.db['geologger'][collection] 74 | except: 75 | logging.exception("Collection not found, either it doesnt exist or can't connect to Mongo") 76 | if collection == "coord": 77 | tagnamekey = 'properties.tagname' 78 | useridkey = 'properties.user_id' 79 | timestampkey = 'properties.timestamp' 80 | if querytype == "id": 81 | query = col.find(spec = {useridkey: username, "_id": queryid}, 82 | sort= [[timestampkey, -1]], limit=1, fields={'_id':False}) 83 | elif querytype == "tagname": 84 | query = col.find(spec = {useridkey: username, tagnamekey: queryid}, 85 | sort = [[timestampkey, -1]], limit =1, fields={'_id':False} ) 86 | else: 87 | tagnamekey = 'tagname' 88 | useridkey = 'user_id' 89 | timestampkey = 'timestamp' 90 | if querytype == "id": 91 | query = col.find(spec = {useridkey: username, "_id": queryid}, 92 | sort= [[timestampkey, -1]], limit=1, fields={'_id':False}) 93 | elif querytype == "tagname": 94 | query = col.find(spec={useridkey: username, tagnamekey: queryid}, 95 | sort = [['timestamp', -1]], limit = 1, fields={'_id': False}) 96 | if queryid: 97 | record = [ item for item in query] 98 | if len(record) > 0: 99 | return json.dumps(record, default=handler, indent=2) 100 | else: 101 | return "{error: Nothing found with that tagname}" 102 | else: 103 | tags = col.distinct(tagnamekey) 104 | tagsout = list() 105 | for tag in tags: 106 | query = col.find(spec={useridkey: username, tagnamekey: tag }, sort = [['timestamp', -1]], limit=1, fields={'_id': False, tagnamekey: True, timestampkey:True}) 107 | if query.count() > 0: 108 | tagsout.append([item for item in query ][0]) 109 | return json.dumps(tagsout, default=handler, indent=2) 110 | @cherrypy.expose 111 | def index(self): 112 | return ''' 114 | ''' 115 | @cherrypy.expose 116 | def lightlogs(self,querytype,queryid=None,callback=None, **kwargs): 117 | user_id = cherrypy.request.login 118 | if user_id != 'guest': 119 | username = self.uidtoname(user_id) 120 | else: 121 | username = 'guest' 122 | 123 | out = self.geologgercollection('lightlogs',username,querytype,queryid) 124 | if callback: 125 | cherrypy.response.headers['Content-Type'] = "application/javascript" 126 | return '%s(%s)'.encode('utf-8') % (str(callback),out) 127 | else: 128 | cherrypy.response.headers['Content-Type'] = "application/json" 129 | return out 130 | @cherrypy.expose 131 | def twilights(self,querytype,queryid=None,callback=None, **kwargs): 132 | user_id = cherrypy.request.login 133 | if user_id != 'guest': 134 | username = self.uidtoname(user_id) 135 | else: 136 | username = 'guest' 137 | out = self.geologgercollection('twilights',username,querytype,queryid, **kwargs) 138 | if callback: 139 | cherrypy.response.headers['Content-Type'] = "application/javascript" 140 | return u'%s(%s)' % (str(callback),out) 141 | else: 142 | cherrypy.response.headers['Content-Type'] = "application/json" 143 | return out 144 | @cherrypy.expose 145 | def coord(self, querytype, queryid=None,callback=None, **kwargs): 146 | user_id = cherrypy.request.login 147 | if user_id != 'guest': 148 | username = self.uidtoname(user_id) 149 | else: 150 | username = 'guest' 151 | out = self.geologgercollection('coord',username,querytype,queryid, **kwargs) 152 | if callback: 153 | cherrypy.response.headers['Content-Type'] = "application/javascript" 154 | return u'%s(%s)' % (str(callback),out) 155 | else: 156 | cherrypy.response.headers['Content-Type'] = "application/json" 157 | return out 158 | 159 | 160 | 161 | cherrypy.tree.mount(Root()) 162 | application = cherrypy.tree 163 | 164 | if __name__ == '__main__': 165 | cherrypy.engine.start() 166 | cherrypy.engine.block() 167 | --------------------------------------------------------------------------------