Pyramid Starter project
+Welcome to Henry Pump DataLogger Database, a Pyramid application generated by
Cookiecutter.
diff --git a/web_db/web_db/.coveragerc b/web_db/web_db/.coveragerc new file mode 100644 index 0000000..2104fd3 --- /dev/null +++ b/web_db/web_db/.coveragerc @@ -0,0 +1,3 @@ +[run] +source = web_db +omit = web_db/test* diff --git a/web_db/web_db/.gitignore b/web_db/web_db/.gitignore new file mode 100644 index 0000000..1853d98 --- /dev/null +++ b/web_db/web_db/.gitignore @@ -0,0 +1,21 @@ +*.egg +*.egg-info +*.pyc +*$py.class +*~ +.coverage +coverage.xml +build/ +dist/ +.tox/ +nosetests.xml +env*/ +tmp/ +Data.fs* +*.sublime-project +*.sublime-workspace +.*.sw? +.sw? +.DS_Store +coverage +test diff --git a/web_db/web_db/CHANGES.txt b/web_db/web_db/CHANGES.txt new file mode 100644 index 0000000..14b902f --- /dev/null +++ b/web_db/web_db/CHANGES.txt @@ -0,0 +1,4 @@ +0.0 +--- + +- Initial version. diff --git a/web_db/web_db/MANIFEST.in b/web_db/web_db/MANIFEST.in new file mode 100644 index 0000000..724586c --- /dev/null +++ b/web_db/web_db/MANIFEST.in @@ -0,0 +1,2 @@ +include *.txt *.ini *.cfg *.rst +recursive-include web_db *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml *.jinja2 diff --git a/web_db/web_db/README.txt b/web_db/web_db/README.txt new file mode 100644 index 0000000..7a6799e --- /dev/null +++ b/web_db/web_db/README.txt @@ -0,0 +1,29 @@ +Henry Pump DataLogger Database +============================== + +Getting Started +--------------- + +- Change directory into your newly created project. + + cd web_db + +- Create a Python virtual environment. + + python3 -m venv env + +- Upgrade packaging tools. + + env/bin/pip install --upgrade pip setuptools + +- Install the project in editable mode with its testing requirements. + + env/bin/pip install -e ".[testing]" + +- Run your project's tests. + + env/bin/pytest + +- Run your project. + + env/bin/pserve development.ini diff --git a/web_db/web_db/development.ini b/web_db/web_db/development.ini new file mode 100644 index 0000000..bc1f58d --- /dev/null +++ b/web_db/web_db/development.ini @@ -0,0 +1,61 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:web_db + +pyramid.reload_templates = true +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en +pyramid.includes = + pyramid_debugtoolbar + +# By default, the toolbar only appears for clients from IP addresses +# '127.0.0.1' and '::1'. +# debugtoolbar.hosts = 127.0.0.1 ::1 + +mongo_uri = mongodb://poc_www:HenryPump1903@localhost:27017/poc + +### +# wsgi server configuration +### + +[server:main] +use = egg:waitress#main +listen = *:6543 + +### +# logging configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### + +[loggers] +keys = root, web_db + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = INFO +handlers = console + +[logger_web_db] +level = DEBUG +handlers = +qualname = web_db + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/web_db/web_db/production.ini b/web_db/web_db/production.ini new file mode 100644 index 0000000..d08a725 --- /dev/null +++ b/web_db/web_db/production.ini @@ -0,0 +1,53 @@ +### +# app configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html +### + +[app:main] +use = egg:web_db + +pyramid.reload_templates = false +pyramid.debug_authorization = false +pyramid.debug_notfound = false +pyramid.debug_routematch = false +pyramid.default_locale_name = en + +### +# wsgi server configuration +### + +[server:main] +use = egg:waitress#main +listen = *:6543 + +### +# logging configuration +# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html +### + +[loggers] +keys = root, web_db + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console + +[logger_web_db] +level = WARN +handlers = +qualname = web_db + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s diff --git a/web_db/web_db/pytest.ini b/web_db/web_db/pytest.ini new file mode 100644 index 0000000..a067130 --- /dev/null +++ b/web_db/web_db/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +testpaths = web_db +python_files = *.py diff --git a/web_db/web_db/setup.py b/web_db/web_db/setup.py new file mode 100644 index 0000000..fa0f38b --- /dev/null +++ b/web_db/web_db/setup.py @@ -0,0 +1,53 @@ +"""Set up the package.""" +import os + +from setuptools import setup, find_packages + +here = os.path.abspath(os.path.dirname(__file__)) +with open(os.path.join(here, 'README.txt')) as f: + README = f.read() +with open(os.path.join(here, 'CHANGES.txt')) as f: + CHANGES = f.read() + +requires = [ + 'pyramid', + 'pyramid_jinja2', + 'pyramid_debugtoolbar', + 'waitress', + 'pymongo', +] + +tests_require = [ + 'WebTest >= 1.3.1', # py3 compat + 'pytest', + 'pytest-cov', +] + +setup( + name='web_db', + version='0.0', + description='Henry Pump DataLogger Database', + long_description=README + '\n\n' + CHANGES, + classifiers=[ + 'Programming Language :: Python', + 'Framework :: Pyramid', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', + ], + author='', + author_email='', + url='', + keywords='web pyramid pylons', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + extras_require={ + 'testing': tests_require, + }, + install_requires=requires, + entry_points={ + 'paste.app_factory': [ + 'main = web_db:main', + ], + }, +) diff --git a/web_db/web_db/web_db/__init__.py b/web_db/web_db/web_db/__init__.py new file mode 100644 index 0000000..e50cb25 --- /dev/null +++ b/web_db/web_db/web_db/__init__.py @@ -0,0 +1,78 @@ +"""Configure everything at once.""" +from pyramid.config import Configurator +from pymongo import MongoClient +from pyramid.renderers import JSON +from datetime import datetime, date +from bson.objectid import ObjectId + +try: + # for python 2 + from urlparse import urlparse +except ImportError: + # for python 3 + from urllib.parse import urlparse + +# JSON RENDERERS +def datetime_adapter(obj, request): + return obj.strftime("%Y-%m-%d %H:%M:%S.%fZ") + + +def objectId_adapter(obj, request): + return str(obj) + + +def date_adapter(obj, request): + return obj.strftime("%Y-%m-%d") + + +def pagination_adapter(obj, request): + p = { + 'page': obj.page, + 'per_page': obj.per_page, + 'total_count': obj.total_count, + } + return p + +def devices_include(config): + config.add_route("devices.collection", "/") + config.add_route("devices.single", "/{device_address}") + + +def main(global_config, **settings): + """Make a Pyramid WSGI application.""" + config = Configurator(settings=settings) + config.include('pyramid_jinja2') + + # Database Configuration + db_url = urlparse(settings['mongo_uri']) + config.registry.db = MongoClient( + host=db_url.hostname, + port=db_url.port, + ) + + def add_db(request): + db = config.registry.db[db_url.path[1:]] + if db_url.username and db_url.password: + db.authenticate(db_url.username, db_url.password) + return db + + config.add_request_method(add_db, 'db', reify=True) + + # CUSTOM JSON RENDERER + prettyjson = JSON(indent=4) + prettyjson.add_adapter(datetime, datetime_adapter) + prettyjson.add_adapter(date, date_adapter) + prettyjson.add_adapter(ObjectId, objectId_adapter) + # prettyjson.add_adapter(Pagination, pagination_adapter) + config.add_renderer('prettyjson', prettyjson) + + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('home', '/') + # config.include(devices_include, route_prefix="/devices") + config.add_route("devices.collection", "/devices") + config.add_route("devices.single", "/devices/{address}") + config.add_route("devices.single.tags.collection", "/devices/{address}/tags") + config.add_route("devices.single.tags.single", "/devices/{address}/tags/{tag_name}") + + config.scan() + return config.make_wsgi_app() diff --git a/web_db/web_db/web_db/devices.py b/web_db/web_db/web_db/devices.py new file mode 100644 index 0000000..b9694c1 --- /dev/null +++ b/web_db/web_db/web_db/devices.py @@ -0,0 +1,98 @@ +"""Handle all device-relation functions.""" +from pyramid.view import view_config + + +@view_config(route_name="devices.collection", renderer="prettyjson", request_method="GET") +def get_all(request): + """Retrieve all devices.""" + devices = request.db['data'].aggregate([ + {"$project": { + "address": 1, + "device_type": 1, + "tag_types": 1 + }} + ]) + return {"devices": list(devices)} + + +@view_config(route_name="devices.collection", renderer="prettyjson", request_method="POST") +def post_one(request): + """Insert one new device.""" + new_device = {"$set": {"address": request.json_body['address'], "device_type": request.json_body['device_type'], "tag_types": {"handshake": [], "normal": []}}} + print(new_device) + request.db['data'].update_one({'address': request.json_body['address']}, new_device, upsert=True) + devices = request.db['data'].find_one({'address': request.json_body['address']}, {"address": 1, "device_type": 1}) + return {"device": devices} + + +@view_config(route_name="devices.single", renderer="prettyjson", request_method="GET") +def get_single(request): + """Retrieve a single device by its address.""" + devices = request.db['data'].find({'address': request.matchdict['address']}, {"address": 1, "device_type": 1}) + return {"devices": list(devices)} + + +@view_config(route_name="devices.single", renderer="prettyjson", request_method="DELETE") +def delete_single(request): + """Delete a single device by its address.""" + result = request.db['data'].delete_one({'address': request.matchdict['address']}) + return {"deleted": result.deleted_count} + + +@view_config(route_name="devices.single", renderer="prettyjson", request_method="PUT") +def update_one(request): + """Update a single device by its address.""" + upd_device = {"address": request.json_body['address'], "device_type": request.matchdict['address']} + request.db['data'].update_one({'address': request.matchdict['address']}, {"$set": upd_device}, upsert=True) + devices = request.db['data'].find({'address': request.json_body['address']}, {"address": 1, "device_type": 1}) + return {"device": list(devices)[0]} + + +enable_tag = { + "tag_types.normal.name": 1, + "tag_types.normal.tag": 1, + "tag_types.normal.description": 1, + "tag_types.normal.data_type": 1, + "tag_types.normal.change_threshold": 1, + "tag_types.normal.guarantee_sec": 1, + "tag_types.normal.units": 1, + "tag_types.normal.min_expected": 1, + "tag_types.normal.max_expected": 1 +} + + +@view_config(route_name="devices.single.tags.collection", renderer="prettyjson", request_method="GET") +def get_devicetags(request): + """Retrieve a single device by its address.""" + devices = request.db['data'].find({'address': request.matchdict['address']}, {"address": 1, "device_type": 1, "tags": 1}) + return {"devices": list(devices)} + + +@view_config(route_name="devices.single.tags.collection", renderer="prettyjson", request_method="POST") +def add_normaltag(request): + """Retrieve a single device by its address.""" + jsb = request.json_body + new_tag = { + "name": jsb['name'], + "tag": jsb['tag'], + "description": jsb['description'], + "data_type": jsb['data_type'], + "change_threshold": jsb['change_threshold'], + "guarantee_sec": jsb['guarantee_sec'], + "units": jsb['units'], + "min_expected": jsb['min_expected'], + "max_expected": jsb['max_expected'] + } + + request.db['data'].update_one({'address': request.matchdict['address']}, {"$push": {"tag_types.normal": new_tag}}) + find_tag = request.db['data'].find_one({"$and": [{'address': request.matchdict['address']}, {"tag_types.normal.tag": new_tag['tag']}]}, enable_tag) + # find_tag = request.db['data'].find_one({"tag_types.normal.tag": new_tag['tag']}, enable_tag) + # find_tag = list(request.db['data'].find({'{}.tag_types.normal.tag'.format(request.matchdict['address']): new_tag['tag']}, enable_tag)) + return {"tag": find_tag} + + +@view_config(route_name="devices.single.tags.single", renderer="prettyjson", request_method="GET") +def get_devicesingletag(request): + """Retrieve a single tag by its address and tag_name.""" + find_tag = request.db['data'].find_one({"$and": [{'address': request.matchdict['address']}, {"tag_types.normal.tag": request.matchdict['tag_name']}]}, enable_tag) + return {"tag": find_tag} diff --git a/web_db/web_db/web_db/static/pyramid-16x16.png b/web_db/web_db/web_db/static/pyramid-16x16.png new file mode 100644 index 0000000..9792031 Binary files /dev/null and b/web_db/web_db/web_db/static/pyramid-16x16.png differ diff --git a/web_db/web_db/web_db/static/pyramid.png b/web_db/web_db/web_db/static/pyramid.png new file mode 100644 index 0000000..4ab837b Binary files /dev/null and b/web_db/web_db/web_db/static/pyramid.png differ diff --git a/web_db/web_db/web_db/static/theme.css b/web_db/web_db/web_db/static/theme.css new file mode 100644 index 0000000..0f4b1a4 --- /dev/null +++ b/web_db/web_db/web_db/static/theme.css @@ -0,0 +1,154 @@ +@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600,700); +body { + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; + color: #ffffff; + background: #bc2131; +} +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: 300; +} +p { + font-weight: 300; +} +.font-normal { + font-weight: 400; +} +.font-semi-bold { + font-weight: 600; +} +.font-bold { + font-weight: 700; +} +.starter-template { + margin-top: 250px; +} +.starter-template .content { + margin-left: 10px; +} +.starter-template .content h1 { + margin-top: 10px; + font-size: 60px; +} +.starter-template .content h1 .smaller { + font-size: 40px; + color: #f2b7bd; +} +.starter-template .content .lead { + font-size: 25px; + color: #f2b7bd; +} +.starter-template .content .lead .font-normal { + color: #ffffff; +} +.starter-template .links { + float: right; + right: 0; + margin-top: 125px; +} +.starter-template .links ul { + display: block; + padding: 0; + margin: 0; +} +.starter-template .links ul li { + list-style: none; + display: inline; + margin: 0 10px; +} +.starter-template .links ul li:first-child { + margin-left: 0; +} +.starter-template .links ul li:last-child { + margin-right: 0; +} +.starter-template .links ul li.current-version { + color: #f2b7bd; + font-weight: 400; +} +.starter-template .links ul li a, a { + color: #f2b7bd; + text-decoration: underline; +} +.starter-template .links ul li a:hover, a:hover { + color: #ffffff; + text-decoration: underline; +} +.starter-template .links ul li .icon-muted { + color: #eb8b95; + margin-right: 5px; +} +.starter-template .links ul li:hover .icon-muted { + color: #ffffff; +} +.starter-template .copyright { + margin-top: 10px; + font-size: 0.9em; + color: #f2b7bd; + text-transform: lowercase; + float: right; + right: 0; +} +@media (max-width: 1199px) { + .starter-template .content h1 { + font-size: 45px; + } + .starter-template .content h1 .smaller { + font-size: 30px; + } + .starter-template .content .lead { + font-size: 20px; + } +} +@media (max-width: 991px) { + .starter-template { + margin-top: 0; + } + .starter-template .logo { + margin: 40px auto; + } + .starter-template .content { + margin-left: 0; + text-align: center; + } + .starter-template .content h1 { + margin-bottom: 20px; + } + .starter-template .links { + float: none; + text-align: center; + margin-top: 60px; + } + .starter-template .copyright { + float: none; + text-align: center; + } +} +@media (max-width: 767px) { + .starter-template .content h1 .smaller { + font-size: 25px; + display: block; + } + .starter-template .content .lead { + font-size: 16px; + } + .starter-template .links { + margin-top: 40px; + } + .starter-template .links ul li { + display: block; + margin: 0; + } + .starter-template .links ul li .icon-muted { + display: none; + } + .starter-template .copyright { + margin-top: 20px; + } +} diff --git a/web_db/web_db/web_db/tags.py b/web_db/web_db/web_db/tags.py new file mode 100644 index 0000000..121fc58 --- /dev/null +++ b/web_db/web_db/web_db/tags.py @@ -0,0 +1,2 @@ +"""Handle all tag-relation functions.""" +from pyramid.view import view_config diff --git a/web_db/web_db/web_db/templates/layout.jinja2 b/web_db/web_db/web_db/templates/layout.jinja2 new file mode 100644 index 0000000..1701677 --- /dev/null +++ b/web_db/web_db/web_db/templates/layout.jinja2 @@ -0,0 +1,64 @@ + + +
+ + + + + + + +
+ No content
+ {% endblock content %} +Welcome to Henry Pump DataLogger Database, a Pyramid application generated by
Cookiecutter.