├── .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 |
--------------------------------------------------------------------------------