diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c4dc76a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +env/* +pocwww.egg* diff --git a/development.ini b/development.ini index f1e5520..8c67182 100644 --- a/development.ini +++ b/development.ini @@ -18,7 +18,7 @@ pyramid.includes = # '127.0.0.1' and '::1'. # debugtoolbar.hosts = 127.0.0.1 ::1 -mongo_uri = mongodb://localhost:27017/poc +mongo_uri = mongodb://poc_www:HenryPump1903@localhost:27017/poc # mongo_uri = mongodb://10.20.155.202:27017/poc diff --git a/pocwww/__init__.py b/pocwww/__init__.py index d696977..b64b882 100644 --- a/pocwww/__init__.py +++ b/pocwww/__init__.py @@ -96,13 +96,12 @@ def main(global_config, **settings): jinja2_env.filters['datestring'] = format_dateString db_url = urlparse(settings['mongo_uri']) + config.registry.db = MongoClient( host=db_url.hostname, port=db_url.port, ) - config.registry.db.poc.users.update_one({"username": "admin"}, {"$set": {"username": "admin", "password": "l3tm31n"}}, upsert=True) - def add_db(request): db = config.registry.db[db_url.path[1:]] if db_url.username and db_url.password: @@ -112,6 +111,10 @@ def main(global_config, **settings): config.add_request_method(add_db, 'db', reify=True) config.add_static_view('static', 'static', cache_max_age=3600) + # Add login for admin user + config.registry.db.poc.authenticate(db_url.username, db_url.password) + config.registry.db.poc.users.update_one({"username": "admin"}, {"$set": {"username": "admin", "password": "l3tm31n"}}, upsert=True) + # CUSTOM JSON RENDERER prettyjson = JSON(indent=4) prettyjson.add_adapter(datetime, datetime_adapter) @@ -122,73 +125,72 @@ def main(global_config, **settings): # SHARED ROUTES config.add_route('home', '/') - config.add_route('json_snapshot', '/json') + config.add_route('home_json', '/json') + # Cards config.add_route('cards_page', '/cards/{cards_date}/{page_num}') - config.add_route('json_cards_page', '/json/cards/{cards_date}/{page_num}') + config.add_route('cards_page_json', '/json/cards/{cards_date}/{page_num}') config.add_route('cards_date', '/cards/{cards_date}') - config.add_route('json_cards_date', '/json/cards/{cards_date}') - + config.add_route('cards_date_json', '/json/cards/{cards_date}') config.add_route('cards', '/cards') - config.add_route('json_cards', '/json/cards') + config.add_route('cards_json', '/json/cards') + config.add_route('cards_singlecard', "/card/view/{stroke_number}") + config.add_route('cards_singlecard_json', "/json/card/view/{stroke_number}") + config.add_route('cards_lastcard_json', "/json/lastcard") - config.add_route('card_single', "/card/view/{stroke_number}") - config.add_route('json_card_single', "/json/card/view/{stroke_number}") - - config.add_route('values_all', "/values") - config.add_route('json_values_all', "/json/values") - - config.add_route('values_tag', "/values/tag/{tagname}") - - config.add_route('values_time', '/values/time/{time}') - config.add_route('json_values_time', '/json/values/time/{time}') + # Measurements + config.add_route('measurements_all', "/values") + config.add_route('measurements_all_json', "/json/values") + config.add_route('measurements_between_wparams_json', "/json/values/between/{startdt}/{enddt}") + config.add_route('measurements_between_json', "/json/values/between") + config.add_route("measurements_daterange_json", "/json/values/daterange") + config.add_route('measurements_singlebetween_wparams_json', "/json/values/tag/{tagname}/between/{startdt}/{enddt}") + config.add_route('measurements_singlebetween_json', "/json/values/tag/{tagname}") + config.add_route("measurements_singledaterange_json", "/json/values/tag/{tagname}/daterange") + config.add_route('measurements_single', "/values/tag/{tagname}") + # Gauge Off config.add_route('gaugeoff_all', '/gaugeoff') - config.add_route('json_gaugeoff_all', '/json/gaugeoff') + config.add_route('gaugeoff_all_json', '/json/gaugeoff') + # Fluid Shots config.add_route('fluidshots_all', '/fluidshots') - config.add_route('json_fluidshots_all', '/json/fluidshots') + config.add_route('fluidshots_all_json', '/json/fluidshots') + # Well Tests config.add_route('welltests_all', '/welltests') - config.add_route('json_welltests_all', '/json/welltests') + config.add_route('welltests_all_json', '/json/welltests') + # Run Status config.add_route('runstatus', '/runstatus') - config.add_route('json_runstatus_page', '/json/runstatus/{page_num}') - config.add_route('json_runstatus', '/json/runstatus') + config.add_route('runstatus_page_json', '/json/runstatus/{page_num}') + config.add_route('runstatus_json', '/json/runstatus') + config.add_route('runstatus_now_json', "/json/runstatusnow") - config.add_route('json_config', '/json/config', factory='pocwww.security.UserLoginFactory') + # Configuration + config.add_route('config_json', '/json/config', factory='pocwww.security.UserLoginFactory') config.add_route('config', '/config', factory='pocwww.security.UserLoginFactory') - config.add_route('json_setpoints', '/json/setpoints', factory='pocwww.security.UserLoginFactory') - config.add_route('json_mode', '/json/mode', factory='pocwww.security.UserLoginFactory') - config.add_route('setpoints', '/setpoints', factory='pocwww.security.UserLoginFactory') - + # Administration config.add_route('admin', '/admin', factory='pocwww.security.UserLoginFactory') - config.add_route('auth', '/sign/{action}') - config.add_route('register', '/register', factory='pocwww.security.UserLoginFactory') - # JSON-ONLY ROUTES - config.add_route('json_lastcard', "/json/lastcard") - config.add_route('json_runstatusnow', "/json/runstatusnow") + # Users + config.add_route('users_auth', '/sign/{action}') + config.add_route('users_register', '/register', factory='pocwww.security.UserLoginFactory') + config.add_route("users_json", "/json/users", factory='pocwww.security.UserLoginFactory') - config.add_route('json_valuesbetween_wparams', "/json/values/between/{startdt}/{enddt}") - config.add_route('json_valuesbetween', "/json/values/between") - config.add_route("json_valuesdaterange", "/json/values/daterange") - config.add_route('json_singlevaluebetween_wparams', "/json/values/tag/{tagname}/between/{startdt}/{enddt}") - config.add_route('json_singlevaluebetween', "/json/values/tag/{tagname}") - config.add_route("json_singlevaluedaterange", "/json/values/tag/{tagname}/daterange") + # POC Interface + config.add_route("poc_updateconfig_json", "/json/updateconfig", factory='pocwww.security.UserLoginFactory') + config.add_route("poc_shake_json", '/json/cmd/shake', factory='pocwww.security.UserLoginFactory') # Shake command is separate for allowing all access + config.add_route("poc_cmd_json", '/json/cmd/{action}', factory='pocwww.security.UserLoginFactory') + config.add_route("poc_updatepocaddress_json", "/json/updatepocaddress", factory='pocwww.security.UserLoginFactory') + config.add_route('poc_setpoints_json', '/json/setpoints', factory='pocwww.security.UserLoginFactory') + config.add_route('poc_mode_json', '/json/mode', factory='pocwww.security.UserLoginFactory') + config.add_route('poc_setpoints', '/setpoints', factory='pocwww.security.UserLoginFactory') - config.add_route("json_updateconfig", "/json/updateconfig", factory='pocwww.security.UserLoginFactory') - config.add_route("json_shake", '/json/cmd/shake', factory='pocwww.security.UserLoginFactory') - config.add_route("json_cmd", '/json/cmd/{action}', factory='pocwww.security.UserLoginFactory') - # config.add_route("json_cmd_start", "/json/cmd/start", factory='pocwww.security.UserLoginFactory') - # config.add_route("json_cmd_stop", "/json/cmd/stop", factory='pocwww.security.UserLoginFactory') - # config.add_route("json_cmd_shake", "/json/cmd/shake", factory='pocwww.security.UserLoginFactory') - config.add_route("json_update_poc_address", "/json/updatepocaddress", factory='pocwww.security.UserLoginFactory') - config.add_route("json_users", "/json/users", factory='pocwww.security.UserLoginFactory') config.scan() return config.make_wsgi_app() diff --git a/pocwww/admin.py b/pocwww/admin.py new file mode 100644 index 0000000..5d0a76d --- /dev/null +++ b/pocwww/admin.py @@ -0,0 +1,7 @@ +from pyramid.view import view_config + + +@view_config(route_name="admin", renderer="templates/admin.jinja2", permission="edit") +def admin_view(request): + address = get_poc_address(request) or 'localhost' + return {'navgroup': 'admin', 'pocIPAddress': address} diff --git a/pocwww/cards.py b/pocwww/cards.py new file mode 100644 index 0000000..e488917 --- /dev/null +++ b/pocwww/cards.py @@ -0,0 +1,87 @@ +from pyramid.view import view_config +from datetime import datetime +from datetime import timedelta +from math import ceil +from .pagination import Pagination + +def get_all_dates_with_cards(request): + datelist = [] + dateagg = request.db['cards'].aggregate([ + {"$group": { + "_id": {"$concat": [ + {"$substr": [{"$year": "$timestamp"}, 0, 4]}, + "-", + {"$substr": [{"$month": "$timestamp"}, 0, 2]}, + "-", + {"$substr": [{"$dayOfMonth": "$timestamp"}, 0, 2]}, + ]}, + "count": {"$sum": 1} + }}, + {"$sort": {"_id": -1}} + ]) + for d in dateagg: + datelist.append({'count': d['count'], 'date': datetime.strptime(d['_id'], "%Y-%m-%d").date()}) + return list(datelist) + +@view_config(route_name="cards_lastcard_json", renderer="prettyjson") +def json_lastcard(request): + return get_latest_card(request) + + +@view_config(route_name='cards', renderer='templates/datelist.jinja2') +@view_config(route_name='cards_json', renderer='prettyjson') +def cards(request): + return {'datelist': get_all_dates_with_cards(request), 'navgroup': 'cards'} + + +@view_config(route_name='cards_page', renderer='templates/cardlist.jinja2') +@view_config(route_name='cards_date', renderer='templates/cardlist.jinja2') +@view_config(route_name='cards_date_json', renderer='prettyjson') +@view_config(route_name='cards_page_json', renderer='prettyjson') +def cards_page(request): + page_num = 1 + try: + page_num = int(request.matchdict['page_num']) + except KeyError: + pass + + cards_date_start = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) + try: + cards_date_start = datetime.strptime(request.matchdict['cards_date'], "%Y-%m-%d") + except KeyError: + pass + + cards_date_end = cards_date_start + timedelta(days=1) + + num_per_page = 100 + num_cards = request.db['cards'].find({'timestamp': {'$lt': cards_date_end, '$gte': cards_date_start}}).count() + pages = ceil(num_cards / num_per_page) + + cards = request.db['cards'].find({'timestamp': {'$lt': cards_date_end, '$gte': cards_date_start}}).sort("timestamp", -1).skip(num_per_page * (page_num - 1)).limit(num_per_page) + + return { + 'cards': list(cards), + 'pagination': Pagination(page_num, num_per_page, num_cards), + 'cards_date': cards_date_start.strftime("%Y-%m-%d"), 'navgroup': 'cards', + 'datelist': get_all_dates_with_cards(request) + } + + +@view_config(route_name='cards_singlecard', renderer='templates/cardsingle.jinja2') +@view_config(route_name='cards_singlecard_json', renderer='prettyjson') +def card_single(request): + card = {} + try: + card = list(request.db['cards'].find({"strokeNumber": int(request.matchdict['stroke_number'])}))[0] + except IndexError: + pass + + datepage_url = request.referrer.split("/")[3:] + page_num = 1 + carddate = "" + if len(datepage_url) > 2: + page_num = int(datepage_url[2]) + if len(datepage_url) > 1: + carddate = datepage_url[1] + + return {"card": card, 'navgroup': 'cards', 'datelist': get_all_dates_with_cards(request), 'date': carddate, 'datepage': page_num} diff --git a/pocwww/config.py b/pocwww/config.py new file mode 100644 index 0000000..ff07722 --- /dev/null +++ b/pocwww/config.py @@ -0,0 +1,7 @@ +from pyramid.view import view_config + +@view_config(route_name="config", renderer="templates/config.jinja2", permission="edit") +@view_config(route_name="config_json", renderer="prettyjson", permission="edit") +def well_config(request): + current_configuration = list(request.db['wellConfiguration'].find().sort("timestamp", -1).limit(1))[0] + return {'navgroup': 'config', 'config': current_configuration} diff --git a/pocwww/fluid_shots.py b/pocwww/fluid_shots.py new file mode 100644 index 0000000..704dcd7 --- /dev/null +++ b/pocwww/fluid_shots.py @@ -0,0 +1,8 @@ +from pyramid.view import view_config + + +@view_config(route_name="fluidshots_all", renderer="templates/fluidshots.jinja2") +@view_config(route_name="fluidshots_all_json", renderer="prettyjson") +def fluidshots_all(request): + fluidshots = list(request.db['fluidShots'].find()) + return {'navgroup': 'fluidshots', 'data': fluidshots} diff --git a/pocwww/gauge_off.py b/pocwww/gauge_off.py new file mode 100644 index 0000000..b5d1633 --- /dev/null +++ b/pocwww/gauge_off.py @@ -0,0 +1,44 @@ +from pyramid.view import view_config + +@view_config(route_name="gaugeoff_all", renderer="templates/gaugeoff_all.jinja2") +@view_config(route_name="gaugeoff_all_json", renderer="prettyjson") +def gaugeoff_all(request): + gaugeoff_list = [] + dateagg = list(request.db['gaugeOff'].aggregate( + [ + { + '$project': { + 'yearMonthDay': {'$dateToString': {'format': "%Y-%m-%d", 'date': "$timestamp"}}, + 'tagname': 1, + 'dailyTotal': 1, + 'dailyAverage': 1, + 'maxDailyValue': 1, + 'minDailyValue': 1, + 'currentValue': 1 + } + }, + { + '$group': { + '_id': "$yearMonthDay", + 'tags': {'$push': {'name': "$tagname"}}, + 'totals': {'$push': {'total': "$dailyTotal"}}, + 'averages': {'$push': {'average': "$dailyAverage"}}, + 'max': {'$push': {'max': "$maxDailyValue"}}, + 'min': {'$push': {'min': "$minDailyValue"}}, + 'last': {'$push': {'last': "$currentValue"}} + } + } + ])) + for d in dateagg: + newGO = {'date': datetime.strptime(d["_id"], '%Y-%m-%d'), 'tags': {}} + for i in range(0, len(d['tags'])): + newTagObj = { + "max": d['max'][i]['max'], + "min": d['min'][i]['min'], + "last": d['last'][i]['last'], + "total": d['totals'][i]['total'], + "average": d['averages'][i]['average'], + } + newGO['tags'][d['tags'][i]['name']] = newTagObj + gaugeoff_list.append(newGO) + return {'navgroup': 'gaugeoff', 'data': gaugeoff_list} diff --git a/pocwww/home.py b/pocwww/home.py new file mode 100644 index 0000000..13affe7 --- /dev/null +++ b/pocwww/home.py @@ -0,0 +1,33 @@ +from pyramid.view import view_config + + +@view_config(route_name='home', renderer='templates/dashboard.jinja2') +@view_config(route_name="home_json", renderer="prettyjson") +def my_view(request): + latest_tag_values = [] + latesttag_agg = request.db['measurements'].aggregate([ + {'$sort': {'dateStored': 1}}, + { + '$group': { + '_id': '$tagname', + 'currentValue': {'$last': '$currentValue'}, + 'units': {'$last': '$units'}, + 'maxValue': {'$last': '$maxValue'}, + 'minValue': {'$last': '$minValue'}, + 'totalValue': {'$last': '$totalValue'}, + 'averageValue': {'$last': '$averageValue'}, + 'useTotal': {'$last': '$useTotal'}, + 'useAverage': {'$last': '$useAverage'} + } + } + ]) + for t in latesttag_agg: + latest_tag_values.append(t) + + latest_card = [] + try: + latest_card = list(request.db['cards'].find().sort("timestamp", -1).limit(1))[0] + except IndexError: + latest_card = [] + + return {'project': 'POC Web Interface', 'navgroup': 'dashboard', 'tag_values': latest_tag_values, 'card': latest_card} diff --git a/pocwww/json.py b/pocwww/json.py deleted file mode 100644 index d4b7f42..0000000 --- a/pocwww/json.py +++ /dev/null @@ -1,293 +0,0 @@ -from pyramid.view import view_config -from .view_helpers import * -from bson import json_util -import requests - - -# JSON -@view_config(route_name="json_lastcard", renderer="prettyjson") -def json_lastcard(request): - return get_latest_card(request) - - -@view_config(route_name="json_valuesbetween", renderer="prettyjson") -@view_config(route_name="json_valuesbetween_wparams", renderer="prettyjson") -def json_valuesbetween(request): - end = datetime.now() - try: # Attempt to get a value from the request. - end = request.matchdict['enddt'] - end = end.replace("T", " ") - end = datetime.strptime(end, "%Y-%m-%d %H:%M:%S.%fZ") - except KeyError: - pass - - start = end - timedelta(days=2) - try: # Attempt to get a value from the request. - start = request.matchdict['startdt'] - start = start.replace("T", " ") - start = datetime.strptime(start, "%Y-%m-%d %H:%M:%S.%fZ") - except KeyError: - pass - - tag_data = [] - grouped_tags = request.db['wellData'].aggregate([ - { - '$match': {"timestamp": {"$gt": start, "$lte": end}} - }, - { - '$sort': {"tagname": 1, "timestamp": 1} - }, - { - '$group': { - '_id': "$tagname", - 'timestamps': {'$push': "$timestamp"}, - 'currentValues': {'$push': "$currentValue"} - } - } - ]) - - for t in grouped_tags: - tag_data.append({"tagname": t['_id'], "timestamps": list(map(lambda a: a.strftime("%Y-%m-%d %H:%M:%S.%fZ"), t['timestamps'])), "currentValues": t['currentValues']}) - return {'values': tag_data, 'start': start, 'end': end} - - -@view_config(route_name="json_valuesdaterange", renderer="prettyjson") -def json_valuesdaterange(request): - date_limits = list(request.db['wellData'].aggregate([ - {"$group": { - "_id": 'null', - "last": {"$max": "$timestamp"}, - "first": {"$min": "$timestamp"} - }} - ]))[0] - return {'first_date': date_limits['first'], 'last_date': date_limits['last']} - - -@view_config(route_name="json_singlevaluebetween", renderer="prettyjson") -@view_config(route_name="json_singlevaluebetween_wparams", renderer="prettyjson") -def json_singlevaluebetween(request): - end = datetime.now() - try: # Attempt to get a value from the request. - end = request.matchdict['enddt'] - end = end.replace("T", " ") - end = datetime.strptime(end, "%Y-%m-%d %H:%M:%S.%fZ") - except KeyError: - pass - - start = end - timedelta(days=7) - try: # Attempt to get a value from the request. - start = request.matchdict['startdt'] - start = start.replace("T", " ") - start = datetime.strptime(start, "%Y-%m-%d %H:%M:%S.%fZ") - except KeyError: - pass - - tag_data = [] - grouped_tags = request.db['wellData'].aggregate([ - {"$match": {"tagname": request.matchdict['tagname'], 'timestamp': {'$gt': start, '$lte': end}}}, - { - '$sort': {"timestamp": 1} - } - ]) - return {'values': list(grouped_tags), 'start': start, 'end': end} - - -@view_config(route_name="json_singlevaluedaterange", renderer="prettyjson") -def json_singlevaluedaterange(request): - date_limits = list(request.db['wellData'].aggregate([ - {"$match": {"tagname": request.matchdict['tagname']}}, - {"$group": { - "_id": 'null', - "last": {"$max": "$timestamp"}, - "first": {"$min": "$timestamp"} - }} - ]))[0] - return {'first_date': date_limits['first'], 'last_date': date_limits['last']} - - -@view_config(route_name="json_runstatusnow", renderer="prettyjson") -def json_runstatusnow(request): - status = False - try: - status = list(request.db['runStatus'].find().sort("timestamp", -1).limit(1))[0] - except IndexError: - pass - - return {'runstatus': status} - - -@view_config(route_name="json_updateconfig", renderer="prettyjson", request_method='POST', permission="edit") -def json_updateconfig(request): - conv_to_float = [ - 'deltaT', - 'pumpDiameter', - 'fluidGradient', - 'tubingID', - 'tubingOD', - 'tubingAnchorDepth', - 'structuralRating', - 'stuffingBoxFriction', - 'tubingHeadPressure' - ] - t_conv_to_float = ['length', 'diameter', 'dampingFactor'] - jsb = request.json_body - new_config = {} - new_config['timestamp'] = datetime.utcnow() - new_config['storedBy'] = request.authenticated_userid - new_config['wellName'] = jsb['wellName'] - new_config['tapers'] = [] - for p in conv_to_float: - new_config[p] = float(jsb[p]) - - for t_i in range(0, len(jsb['tapers'])): - t = {} - for p in t_conv_to_float: - t[p] = float(jsb['tapers'][t_i][p]) - t['material'] = jsb['tapers'][t_i]['material'] - new_config['tapers'].append(t) - - result = request.db['wellConfiguration'].insert(new_config) - - addr_obj = list(request.db['pocConfiguration'].find({"_id": "pocIPAddress"})) - address = 'localhost' - if len(addr_obj) > 0: - address = addr_obj[0]['pocIPAddress'] - - update_url = "http://{}:8000/config?update=true".format(address) - r = requests.get(update_url) - pocCmdSts = "OK" if r.status_code == 200 else "failed" - - return {'new_config': request.json_body, 'stored_result': result, 'updated': pocCmdSts} - - -@view_config(route_name="json_cmd", renderer="prettyjson", permission="edit") -def json_cmd(request): - action = request.matchdict['action'] - address = get_poc_address(request) or 'localhost' - - java_url = {} - java_url['start'] = "http://{}:8000/command?cmd=start&user={}".format(address, request.authenticated_userid) - java_url['stop'] = "http://{}:8000/command?cmd=stop&user={}".format(address, request.authenticated_userid) - java_url['shake'] = "http://{}:8000/shake".format(address) - - r = requests.get(java_url[action]) - return r.text if r.status_code == 200 else {"status": "failure sending command"} - - -@view_config(route_name="json_shake", renderer="prettyjson", permission="view") -def json_shake(request): - address = get_poc_address(request) or 'localhost' - url = "http://{}:8000/shake".format(address) - - r = requests.get(url) - return r.text if r.status_code == 200 else {"status": "failure sending command"} - - -@view_config(route_name="json_update_poc_address", renderer="prettyjson", request_method='POST', permission="edit") -def json_update_poc_address(request): - try: - new_addr = request.json_body['pocIPAddress'] - upsert = request.db['pocConfiguration'].update_one({"_id": "pocIPAddress"}, {"$set": {'pocIPAddress': new_addr}}, upsert=True) - return {"status": "OK"} - except KeyError: - return {"status": "failure"} - - -@view_config(route_name="json_users", renderer="prettyjson", request_method='POST', permission="edit") -def json_newuser(request): - jsb = request.json_body - if request.db['users'].count({"username": jsb['username']}) > 0: - fail_reason = "There is already a user with this username" - return {"status": 'fail', "info": fail_reason} - - elif len(jsb['username']) < 5: - fail_reason = "The username must be at least 5 characters" - return {"status": 'fail', "info": fail_reason} - - elif len(jsb['password']) < 5: - fail_reason = "The password must be at least 5 characters" - return {"status": 'fail', "info": fail_reason} - - else: - set_return = set_password(request, jsb['username'], jsb['password']) - return {'status': "OK"} - - -@view_config(route_name="json_users", renderer="prettyjson", permission="edit", request_method='GET') -def json_getuser(request): - user_list = [] - users = list(request.db['users'].find()) - for user in users: - user_list.append(user['username']) - return {'users': user_list} - - -@view_config(route_name="json_users", renderer="prettyjson", permission="edit", request_method='DELETE') -def json_deleteuser(request): - request.db['users'].remove({'username': request.json_body['username']}) - user_list = [] - users = list(request.db['users'].find()) - for user in users: - user_list.append(user['username']) - return {'users': user_list} - - -@view_config(route_name="json_users", renderer="prettyjson", request_method='PUT', permission="edit") -def json_updateuser(request): - jsb = request.json_body - if len(jsb['username']) < 5: - fail_reason = "The username must be at least 5 characters" - return {"status": 'fail', "info": fail_reason} - - elif len(jsb['password']) < 5: - fail_reason = "The password must be at least 5 characters" - return {"status": 'fail', "info": fail_reason} - - else: - set_return = set_password(request, jsb['username'], jsb['password']) - return {'status': "OK"} - - -@view_config(route_name="json_setpoints", renderer="prettyjson", request_method='GET', permission='edit') -def json_setpoints(request): - return {'setpoints': list(request.db['setpoints'].find())} - - -@view_config(route_name="json_setpoints", renderer="prettyjson", request_method='POST', permission='edit') -def json_setpoints_post(request): - jsb = request.json_body - try: - name = jsb['name'] - value = jsb['value'] - upsert = request.db['setpoints'].update_one({"name": name}, {"$set": {'value': value, 'storedBy': request.authenticated_userid, 'lastStored': datetime.utcnow()}}, upsert=True) - - address = get_poc_address(request) or 'localhost' - url = "http://{}:8000/update?setpoint={}".format(address, name) - print(url) - r = requests.get(url) - update_status = r.text if r.status_code == 200 else {"status": "failure sending command"} - - return {"updated": list(request.db['setpoints'].find_one({"name": name})), 'status': update_status} - except KeyError: - return {"status": "bad reqest"} - - -runModes = {0: 'poc', 1: 'manual', 2: 'timer'} - - -@view_config(route_name="json_mode", renderer="prettyjson", request_method='POST', permission='edit') -def json_mode_post(request): - jsb = request.json_body - try: - mode = jsb['mode'] - upsert = request.db['setpoints'].update_one({"name": "runMode"}, {"$set": {'value': mode, 'storedBy': request.authenticated_userid, 'lastStored': datetime.utcnow()}}, upsert=True) - - address = get_poc_address(request) or 'localhost' - url = "http://{}:8000/mode?mode={}&user={}".format(address, runModes[mode], request.authenticated_userid) - print(url) - r = requests.get(url) - update_status = r.text if r.status_code == 200 else {"status": "failure sending command"} - - return {"updated": request.db['setpoints'].find_one({"name": "runMode"}), 'status': update_status} - except KeyError: - return {"status": "bad reqest"} diff --git a/pocwww/measurements.py b/pocwww/measurements.py new file mode 100644 index 0000000..c2778c7 --- /dev/null +++ b/pocwww/measurements.py @@ -0,0 +1,282 @@ +from pyramid.view import view_config +from pyramid.httpexceptions import HTTPFound +from pyramid.security import remember, forget +from datetime import datetime, timedelta +from functools import reduce +import pytz +from itertools import groupby + + +def get_date_measurements(request, d, tagname=None): + find_datetime = datetime(d.year, d.month, d.day, 0, 0, 0) + if tagname: + return list(request.db['measurements'].find({"dateStored": find_datetime, 'tagname': tagname})) + return list(request.db['measurements'].find({"dateStored": find_datetime})) + + +def expand_measurements_from_document(doc): + base_date = doc['dateStored'] + value_list = [] + values = doc['values'] + for hour in values: + for minute in values[hour]: + measurement_datetime = datetime(base_date.year, base_date.month, base_date.day, int(hour), int(minute), tzinfo=pytz.utc) + # print("{} = {}".format(measurement_datetime, values[hour][minute])) + value_list.append({'timestamp': measurement_datetime, 'tagvalue': values[hour][minute]}) + value_list.sort(key=lambda x: x['timestamp'], reverse=True) + doc['values'] = value_list + return doc + + +def get_measurements_between(request, start_datetime, end_datetime, tagname=None): + start_date = datetime(start_datetime.year, start_datetime.month, start_datetime.day, 0, 0, 0, tzinfo=pytz.utc) + end_date = datetime(end_datetime.year, end_datetime.month, end_datetime.day, 0, 0, 0, tzinfo=pytz.utc) + found_measurements = [] + if tagname: + found_measurements = list(map(expand_measurements_from_document, list(request.db['measurements'].find({"dateStored": {"$lte": end_date, "$gte": start_date}, 'tagname': tagname})))) + else: + found_measurements = list(map(expand_measurements_from_document, list(request.db['measurements'].find({"dateStored": {"$lte": end_date, "$gte": start_date}})))) + + for i in range(0, len(found_measurements)): + found_measurements[i]['values'] = list(filter(lambda x: x['timestamp'] >= start_datetime and x['timestamp'] <= end_datetime, found_measurements[i]['values'])) + return found_measurements + + +def combine_measurements(m1, m2): + new_measurement = { + "averages": [], + "totals": [], + "maxes": [], + "mins": [], + "units": m1["units"], + "values": sorted(m2['values'] + m1['values'], key=lambda x: x['timestamp'], reverse=True), + "tagname": m1["tagname"], + } + + # Average Value + try: + new_measurement['averages'] = m1['averages'] + new_measurement['averages'].append({"timestamp": m2['dateStored'], "averageValue": m2['averageValue']}) + except KeyError: + new_measurement['averages'].append({"timestamp": m1['dateStored'], "averageValue": m1['averageValue']}) + new_measurement['averages'].append({"timestamp": m2['dateStored'], "averageValue": m2['averageValue']}) + + # Total Value + try: + new_measurement['totals'] = m1['totals'] + new_measurement['totals'].append({"timestamp": m2['dateStored'], "totalValue": m2['totalValue']}) + except KeyError: + new_measurement['totals'].append({"timestamp": m1['dateStored'], "totalValue": m1['totalValue']}) + new_measurement['totals'].append({"timestamp": m2['dateStored'], "totalValue": m2['totalValue']}) + + # Max Value + try: + new_measurement['maxes'] = m1['maxes'] + new_measurement['maxes'].append({"timestamp": m2['dateStored'], "maxValue": m2['maxValue']}) + except KeyError: + new_measurement['maxes'].append({"timestamp": m1['dateStored'], "maxValue": m1['maxValue']}) + new_measurement['maxes'].append({"timestamp": m2['dateStored'], "maxValue": m2['maxValue']}) + + # Min Value + try: + new_measurement['mins'] = m1['mins'] + new_measurement['mins'].append({"timestamp": m2['dateStored'], "minValue": m2['minValue']}) + except KeyError: + new_measurement['mins'].append({"timestamp": m1['dateStored'], "minValue": m1['minValue']}) + new_measurement['mins'].append({"timestamp": m2['dateStored'], "minValue": m2['minValue']}) + + return new_measurement + + +def first_measurement(m1): + new_measurement = { + "averages": [], + "totals": [], + "maxes": [], + "mins": [], + "units": m1["units"], + "values": sorted(m1['values'], key=lambda x: x['timestamp'], reverse=True), + "tagname": m1["tagname"], + } + + # Average Value + try: + new_measurement['averages'] = m1['averages'] + except KeyError: + new_measurement['averages'].append({"timestamp": m1['dateStored'], "averageValue": m1['averageValue']}) + + # Total Value + try: + new_measurement['totals'] = m1['totals'] + except KeyError: + new_measurement['totals'].append({"timestamp": m1['dateStored'], "totalValue": m1['totalValue']}) + + # Max Value + try: + new_measurement['maxes'] = m1['maxes'] + except KeyError: + new_measurement['maxes'].append({"timestamp": m1['dateStored'], "maxValue": m1['maxValue']}) + + # Min Value + try: + new_measurement['mins'] = m1['mins'] + except KeyError: + new_measurement['mins'].append({"timestamp": m1['dateStored'], "minValue": m1['minValue']}) + + return new_measurement + + +def get_grouped_measurements_between(request, start_datetime, end_datetime, tagname=None): + data = sorted(get_measurements_between(request, start_datetime, end_datetime, tagname=tagname), key=lambda x: x['tagname']) + tag_data = {} + for k, g in groupby(data, lambda x: x['tagname']): + group = list(g) + if len(group) > 1: + tag_data[k] = reduce(combine_measurements, group) + else: + tag_data[k] = first_measurement(group[0]) + + return tag_data + + +@view_config(route_name='measurements_all', renderer="templates/valuesall.jinja2") +def values_page(request): + latest_tag_values = [] + latesttag_agg = request.db['measurements'].aggregate([ + {'$sort': {'dateStored': 1}}, + { + '$group': { + '_id': '$tagname', + 'currentValue': {'$last': '$currentValue'}, + 'units': {'$last': '$units'}, + 'maxValue': {'$last': '$maxValue'}, + 'minValue': {'$last': '$minValue'}, + 'totalValue': {'$last': '$totalValue'}, + 'averageValue': {'$last': '$averageValue'}, + 'useTotal': {'$last': '$useTotal'}, + 'useAverage': {'$last': '$useAverage'} + } + } + ]) + for t in latesttag_agg: + latest_tag_values.append(t) + + return {'navgroup': 'values', "current_values": latest_tag_values} + + +@view_config(route_name='measurements_all_json', renderer='prettyjson') +def values_all(request): + end = datetime.now(tz=pytz.utc) + try: # Attempt to get a value from the request. + end = request.matchdict['enddt'] + end = end.replace("T", " ") + end = pytz.utc.localize(datetime.strptime(end, "%Y-%m-%d %H:%M:%S.%fZ")) + except KeyError: + pass + + start = end - timedelta(days=2) + try: # Attempt to get a value from the request. + start = request.matchdict['startdt'] + start = start.replace("T", " ") + start = pytz.utc.localize(datetime.strptime(start, "%Y-%m-%d %H:%M:%S.%fZ")) + except KeyError: + pass + + latest_tag_values = [] + latesttag_agg = request.db['measurements'].aggregate([ + {'$sort': {'dateStored': 1}}, + { + '$group': { + '_id': '$tagname', + 'currentValue': {'$last': '$currentValue'}, + 'units': {'$last': '$units'}, + 'maxValue': {'$last': '$maxValue'}, + 'minValue': {'$last': '$minValue'}, + 'totalValue': {'$last': '$totalValue'}, + 'averageValue': {'$last': '$averageValue'}, + 'useTotal': {'$last': '$useTotal'}, + 'useAverage': {'$last': '$useAverage'} + } + } + ]) + for t in latesttag_agg: + latest_tag_values.append(t) + + all_values = get_grouped_measurements_between(request, start, end) + return {'navgroup': 'values', 'values': all_values, "current_values": latest_tag_values} + + +@view_config(route_name="measurements_singlebetween_json", renderer="prettyjson") +@view_config(route_name="measurements_singlebetween_wparams_json", renderer="prettyjson") +def json_singlevaluebetween(request): + end = datetime.now(tz=pytz.utc) + try: # Attempt to get a value from the request. + end = request.matchdict['enddt'] + end = end.replace("T", " ") + end = pytz.utc.localize(datetime.strptime(end, "%Y-%m-%d %H:%M:%S.%fZ")) + except KeyError: + pass + + start = end - timedelta(days=7) + try: # Attempt to get a value from the request. + start = request.matchdict['startdt'] + start = start.replace("T", " ") + start = pytz.utc.localize(datetime.strptime(start, "%Y-%m-%d %H:%M:%S.%fZ")) + except KeyError: + pass + + all_values = get_grouped_measurements_between(request, start, end, tagname=request.matchdict['tagname']) + return {'tag': all_values, 'start': start, 'end': end, 'tagname': request.matchdict['tagname']} + + +@view_config(route_name="measurements_between_json", renderer="prettyjson") +@view_config(route_name="measurements_between_wparams_json", renderer="prettyjson") +def json_valuesbetween(request): + end = datetime.now(tz=pytz.utc) + try: # Attempt to get a value from the request. + end = request.matchdict['enddt'] + end = end.replace("T", " ") + end = pytz.utc.localize(datetime.strptime(end, "%Y-%m-%d %H:%M:%S.%fZ")) + except KeyError: + pass + + start = end - timedelta(days=2) + try: # Attempt to get a value from the request. + start = request.matchdict['startdt'] + start = start.replace("T", " ") + start = pytz.utc.localize(datetime.strptime(start, "%Y-%m-%d %H:%M:%S.%fZ")) + except KeyError: + pass + + tag_data = get_grouped_measurements_between(request, start, end) + return {'values': tag_data, 'start': start, 'end': end} + + +@view_config(route_name="measurements_daterange_json", renderer="prettyjson") +def json_valuesdaterange(request): + date_limits = list(request.db['measurements'].aggregate([ + {"$group": { + "_id": 'null', + "last": {"$max": "$dateStored"}, + "first": {"$min": "$dateStored"} + }} + ]))[0] + return {'first_date': date_limits['first'], 'last_date': date_limits['last']} + + +@view_config(route_name="measurements_singledaterange_json", renderer="prettyjson") +def json_singlevaluedaterange(request): + date_limits = list(request.db['measurements'].aggregate([ + {"$match": {"tagname": request.matchdict['tagname']}}, + {"$group": { + "_id": 'null', + "last": {"$max": "$dateStored"}, + "first": {"$min": "$dateStored"} + }} + ]))[0] + return {'first_date': date_limits['first'], 'last_date': date_limits['last']} + +@view_config(route_name="measurements_single", renderer="templates/values_single.jinja2") +def measurements_single(request): + tagname = request.matchdict['tagname'] + return {'navgroup': 'values', 'tagname': tagname} diff --git a/pocwww/poc_interface.py b/pocwww/poc_interface.py new file mode 100644 index 0000000..d645870 --- /dev/null +++ b/pocwww/poc_interface.py @@ -0,0 +1,138 @@ +from pyramid.view import view_config +import requests +from datetime import datetime + +def get_poc_address(request): + addr_obj = list(request.db['pocConfiguration'].find({"_id": "pocIPAddress"})) + address = False + if len(addr_obj) > 0: + address = addr_obj[0]['pocIPAddress'] + return address + + +@view_config(route_name="poc_updateconfig_json", renderer="prettyjson", request_method='POST', permission="edit") +def json_updateconfig(request): + conv_to_float = [ + 'deltaT', + 'pumpDiameter', + 'fluidGradient', + 'tubingID', + 'tubingOD', + 'tubingAnchorDepth', + 'structuralRating', + 'stuffingBoxFriction', + 'tubingHeadPressure' + ] + t_conv_to_float = ['length', 'diameter', 'dampingFactor'] + jsb = request.json_body + new_config = {} + new_config['timestamp'] = datetime.utcnow() + new_config['storedBy'] = request.authenticated_userid + new_config['wellName'] = jsb['wellName'] + new_config['tapers'] = [] + for p in conv_to_float: + new_config[p] = float(jsb[p]) + + for t_i in range(0, len(jsb['tapers'])): + t = {} + for p in t_conv_to_float: + t[p] = float(jsb['tapers'][t_i][p]) + t['material'] = jsb['tapers'][t_i]['material'] + new_config['tapers'].append(t) + + result = request.db['wellConfiguration'].insert(new_config) + + addr_obj = list(request.db['pocConfiguration'].find({"_id": "pocIPAddress"})) + address = 'localhost' + if len(addr_obj) > 0: + address = addr_obj[0]['pocIPAddress'] + + update_url = "http://{}:8000/config?update=true".format(address) + r = requests.get(update_url) + pocCmdSts = "OK" if r.status_code == 200 else "failed" + + return {'new_config': request.json_body, 'stored_result': result, 'updated': pocCmdSts} + + +@view_config(route_name="poc_cmd_json", renderer="prettyjson", permission="edit") +def json_cmd(request): + print("got here") + action = request.matchdict['action'] + address = get_poc_address(request) or 'localhost' + + java_url = {} + java_url['start'] = "http://{}:8000/command?cmd=start&user={}".format(address, request.authenticated_userid) + java_url['stop'] = "http://{}:8000/command?cmd=stop&user={}".format(address, request.authenticated_userid) + # java_url['shake'] = "http://{}:8000/shake".format(address) + + r = requests.get(java_url[action]) + return r.text if r.status_code == 200 else {"status": "failure sending command"} + + +@view_config(route_name="poc_shake_json", renderer="prettyjson", permission="view") +def json_shake(request): + address = get_poc_address(request) or 'localhost' + url = "http://{}:8000/shake".format(address) + + r = requests.get(url) + return r.text if r.status_code == 200 else {"status": "failure sending command"} + + +@view_config(route_name="poc_updatepocaddress_json", renderer="prettyjson", request_method='POST', permission="edit") +def json_update_poc_address(request): + try: + new_addr = request.json_body['pocIPAddress'] + upsert = request.db['pocConfiguration'].update_one({"_id": "pocIPAddress"}, {"$set": {'pocIPAddress': new_addr}}, upsert=True) + return {"status": "OK"} + except KeyError: + return {"status": "failure"} + + +@view_config(route_name='poc_setpoints', renderer='templates/setpoints.jinja2', permission='edit') +def setpoints(request): + return {"navgroup": "setpoints"} + + +@view_config(route_name="poc_setpoints_json", renderer="prettyjson", request_method='GET', permission='edit') +def json_setpoints(request): + return {'setpoints': list(request.db['setpoints'].find())} + + +@view_config(route_name="poc_setpoints_json", renderer="prettyjson", request_method='POST', permission='edit') +def json_setpoints_post(request): + jsb = request.json_body + try: + name = jsb['name'] + value = jsb['value'] + upsert = request.db['setpoints'].update_one({"name": name}, {"$set": {'value': value, 'storedBy': request.authenticated_userid, 'lastStored': datetime.utcnow()}}, upsert=True) + + address = get_poc_address(request) or 'localhost' + url = "http://{}:8000/update?setpoint={}".format(address, name) + print(url) + r = requests.get(url) + update_status = r.text if r.status_code == 200 else {"status": "failure sending command"} + + return {"updated": list(request.db['setpoints'].find_one({"name": name})), 'status': update_status} + except KeyError: + return {"status": "bad reqest"} + + +runModes = {0: 'poc', 1: 'manual', 2: 'timer'} + + +@view_config(route_name="poc_mode_json", renderer="prettyjson", request_method='POST', permission='edit') +def json_mode_post(request): + jsb = request.json_body + try: + mode = jsb['mode'] + upsert = request.db['setpoints'].update_one({"name": "runMode"}, {"$set": {'value': mode, 'storedBy': request.authenticated_userid, 'lastStored': datetime.utcnow()}}, upsert=True) + + address = get_poc_address(request) or 'localhost' + url = "http://{}:8000/mode?mode={}&user={}".format(address, runModes[mode], request.authenticated_userid) + print(url) + r = requests.get(url) + update_status = r.text if r.status_code == 200 else {"status": "failure sending command"} + + return {"updated": request.db['setpoints'].find_one({"name": "runMode"}), 'status': update_status} + except KeyError: + return {"status": "bad reqest"} diff --git a/pocwww/run_status.py b/pocwww/run_status.py new file mode 100644 index 0000000..f6d0fb5 --- /dev/null +++ b/pocwww/run_status.py @@ -0,0 +1,30 @@ +from pyramid.view import view_config +from math import ceil +from .pagination import Pagination + +@view_config(route_name="runstatus", renderer="templates/runstatus.jinja2") +@view_config(route_name="runstatus_json", renderer="prettyjson") +@view_config(route_name="runstatus_page_json", renderer="prettyjson") +def run_status(request): + page_num = 1 + try: + page_num = int(request.matchdict['page_num']) + except KeyError: + pass + + num_per_page = 100 + num_cards = request.db['runStatus'].count() + pages = ceil(num_cards / num_per_page) + runStatuses = list(request.db['runStatus'].find().sort("timestamp", -1).skip(num_per_page * (page_num - 1)).limit(num_per_page)) + return {'navgroup': 'runstatus', 'data': runStatuses, 'pagination': Pagination(page_num, num_per_page, num_cards)} + + +@view_config(route_name="runstatus_now_json", renderer="prettyjson") +def json_runstatusnow(request): + status = False + try: + status = list(request.db['runStatus'].find().sort("timestamp", -1).limit(1))[0] + except IndexError: + pass + + return {'runstatus': status} diff --git a/pocwww/security.py b/pocwww/security.py index 3c2e501..c3d3434 100644 --- a/pocwww/security.py +++ b/pocwww/security.py @@ -1,4 +1,5 @@ from pyramid.security import Allow, Everyone, Authenticated +from passlib.apps import custom_app_context as poc_pwd_context class UserLoginFactory(object): @@ -8,3 +9,20 @@ class UserLoginFactory(object): def __init__(self, request): pass + +def set_password(request, username, password): + password_hash = poc_pwd_context.encrypt(password) + request.db['users'].update_one({'username': username}, {"$set":{"username": username, "password": password_hash}}, upsert=True) + + +def check_password(request, username, password): + users = list(request.db['users'].find({'username': username})) + if len(users) > 0: + this_user = users[0] + + # is it cleartext? + if password == this_user['password']: + set_password(request, username, password) + return check_password(request, username, password) + + return poc_pwd_context.verify(password, this_user['password']) diff --git a/pocwww/static/graphs.js b/pocwww/static/graphs.js index 9c3f9e7..9a47fe6 100644 --- a/pocwww/static/graphs.js +++ b/pocwww/static/graphs.js @@ -86,23 +86,30 @@ function drawChart(data){ var graph_data = [] ; var json_data = data.values; var ctx = document.getElementById("valueChart"); - for (var i = 0; i < json_data.length; i++){ + + var color_index = 0; + + for (x in data.values){ + var tagdata = data.values[x]; var newObj = { - label: json_data[i].tagname, + label: x, fill: false, data: [], lineTension: 0.05, - borderColor: color_scale[i % color_scale.length], + borderColor: color_scale[color_index % color_scale.length], pointRadius: 2 - } - for(var j = 0; j < json_data[i].timestamps.length; j++){ + }; + + for (var j = 0; j < tagdata.values.length; j++){ newObj.data.push({ - x: json_data[i].timestamps[j], - y: json_data[i].currentValues[j] + x: tagdata.values[j].timestamp, + y: tagdata.values[j].tagvalue }); } graph_data.push(newObj); + color_index++; } + scatterChart = new Chart(ctx, { type: 'line', responsive: true, @@ -130,33 +137,96 @@ function drawSingleGraph(data){ console.log("Destroying existing chart"); scatterChart.destroy(); } + + var tag = data.tag[data.tagname]; + console.log(tag); + var graph_data = []; - var values = data.values; var ctx = document.getElementById("myChart"); - var lines = ["currentValue", "maxDailyValue", "minDailyValue", "dailyAverage", "dailyTotal"]; - for (var i = 0; i < lines.length; i++){ - var baseObj = { - label: lines[i], - fill: false, - data: [], - lineTension: 0.05, - borderColor: color_scale[i % color_scale.length] - } - if (lines[i] == "dailyTotal"){ - baseObj['hidden'] = true; - } - graph_data.push(baseObj); + var color_index = 0; + + // Current Values + var currentValues = { + label: "Value", + fill: false, + data: [], + lineTension: 0.05, + borderColor: color_scale[color_index % color_scale.length] } - for(var i = 0; i < values.length; i++){ - for(var j = 0; j < lines.length; j++){ - var lineName = lines[j] - graph_data[lines.indexOf(lineName)].data.push({ - x: values[i].timestamp, - y: values[i][lineName] - }); - } + for (var i = 0; i < tag.values.length; i++){ + currentValues.data.push({x: tag.values[i].timestamp, y: tag.values[i].tagvalue}); } + + graph_data.push(currentValues) + color_index++; + + // Max Values + var maxValues = { + label: "Max", + fill: false, + data: [], + lineTension: 0.05, + borderColor: color_scale[color_index % color_scale.length] + } + + for (var i = 0; i < tag.maxes.length; i++){ + maxValues.data.push({x: tag.maxes[i].timestamp, y: tag.maxes[i].maxValue}); + } + + graph_data.push(maxValues) + color_index++; + + // Min Values + var minValues = { + label: "Min", + fill: false, + data: [], + lineTension: 0.05, + borderColor: color_scale[color_index % color_scale.length] + } + + for (var i = 0; i < tag.mins.length; i++){ + minValues.data.push({x: tag.mins[i].timestamp, y: tag.mins[i].maxValue}); + } + + graph_data.push(minValues) + color_index++; + + // Average Values + var averageValues = { + label: "Average", + fill: false, + data: [], + lineTension: 0.05, + borderColor: color_scale[color_index % color_scale.length] + } + + for (var i = 0; i < tag.averages.length; i++){ + averageValues.data.push({x: tag.averages[i].timestamp, y: tag.averages[i].maxValue}); + } + + graph_data.push(averageValues) + color_index++; + + // Total Values + var totalValues = { + label: "Total", + fill: false, + data: [], + lineTension: 0.05, + borderColor: color_scale[color_index % color_scale.length] + } + + for (var i = 0; i < tag.totals.length; i++){ + totalValues.data.push({x: tag.totals[i].timestamp, y: tag.totals[i].maxValue}); + } + + graph_data.push(totalValues) + color_index++; + + console.log(graph_data); + scatterChart = new Chart(ctx, { type: 'line', data: { diff --git a/pocwww/templates/dashboard.jinja2 b/pocwww/templates/dashboard.jinja2 index e5415d8..f98732e 100644 --- a/pocwww/templates/dashboard.jinja2 +++ b/pocwww/templates/dashboard.jinja2 @@ -23,11 +23,6 @@ Name Value - Timestamp - @@ -36,19 +31,18 @@ {{t._id}} - - {{t.value | round(3)}} - {{t.timestamp | datetime('short')}} - + {{t.currentValue | round(3)}} {{t.units}} {% endfor %} diff --git a/pocwww/templates/layout.jinja2 b/pocwww/templates/layout.jinja2 index c76c11b..a2d952d 100644 --- a/pocwww/templates/layout.jinja2 +++ b/pocwww/templates/layout.jinja2 @@ -67,9 +67,9 @@
  • Setpoints
  • Well Setup
  • {{request.authenticated_userid}}
  • -
  • +
  • {% else %} -