#!/usr/bin/python from bottle import Bottle, run, template, static_file, error, redirect, post, request import yaml import glob import re import time import random import string import os import shutil import subprocess app = Bottle() APP_ROOT = "/home/beieli" # Read Configuration from YAML-File with open("%s/bin/beielimon-config.yaml" % (APP_ROOT), 'r') as stream: try: config_data = yaml.load(stream) except yaml.YAMLError as exc: print(exc) def GetLastValues(scale_uuid): last_weight = "" last_weight_timestamp = "" last_swarmalarm = "" files = glob.glob("%s/data/weight-%s-????????.log" % (APP_ROOT,scale_uuid)) if len(files) > 0: with open(sorted(files)[-1], "r") as f: for line in f: pass result = line # Beispiel: 2017-11-07 08:23,0 m = re.match("(\d{4})-(\d{2})-(\d{2}) (\d\d:\d\d),(\d+)", result) if m: last_weight = "%sg" % (m.group(5)) last_weight_timestamp = "%s.%s.%s %s" % (m.group(3),m.group(2),m.group(1),m.group(4)) else: last_weight = "Fehler beim Parsen: %s" % (result) last_weight_timestamp = "" else: last_weight = "Keine Messwerte" last_weight_timestamp = "N/A" files = glob.glob("%s/data/swarmalarm-%s-????????.log" % (APP_ROOT,scale_uuid)) if len(files) > 0: with open(sorted(files)[-1], "r") as f: for line in f: pass result = line # Beispiel: 2017-11-07 08:23 m = re.match("(\d{4})-(\d{2})-(\d{2}) (\d\d:\d\d)", result) if m: last_swarmalarm = "%s.%s.%s %s" % (m.group(3),m.group(2),m.group(1),m.group(4)) else: last_swarmalarm = "Fehler beim Parsen: %s" % (result) else: last_swarmalarm = "keiner" return [last_weight, last_weight_timestamp, last_swarmalarm ] def RefreshData(): scales = config_data['scales'] hash_scales = {} for s in config_data['scales']: uuid = s['scale_uuid'] (last_weight, last_weight_timestamp, last_swarmalarm) = GetLastValues(uuid) hash_scales[uuid] = {} hash_scales[uuid]['alias'] = s['alias'] hash_scales[uuid]['last_weight'] = last_weight hash_scales[uuid]['last_weight_timestamp'] = last_weight_timestamp hash_scales[uuid]['last_swarmalarm'] = last_swarmalarm return hash_scales def CreateDatafile(scale_uuid,infotime): mycsvdir = "%s/web-csv" % (APP_ROOT) my_pattern = '%s%s' % (infotime,'?' * (8 - len(infotime))) files = glob.glob("%s/data/weight-%s-%s.log" % (APP_ROOT,scale_uuid,my_pattern)) filename = "%s/%s-%s.csv" % (mycsvdir,scale_uuid,infotime) with open(filename, 'w') as file: for ifile in sorted(files): if infotime in ifile: with_data = True with open(ifile, 'r') as ifile: for line in ifile: file.write(line) files = glob.glob("%s/data/temp-%s-%s.log" % (APP_ROOT,scale_uuid,my_pattern)) filename = "%s/temp-%s-%s.csv" % (mycsvdir,scale_uuid,infotime) with open(filename, 'w') as file: for ifile in sorted(files): if infotime in ifile: with_data = True with open(ifile, 'r') as ifile: for line in ifile: file.write(line) files = glob.glob("%s/data/humidity-%s-%s.log" % (APP_ROOT,scale_uuid,my_pattern)) filename = "%s/humidity-%s-%s.csv" % (mycsvdir,scale_uuid,infotime) with open(filename, 'w') as file: for ifile in sorted(files): if infotime in ifile: with_data = True with open(ifile, 'r') as ifile: for line in ifile: file.write(line) files = glob.glob("%s/data/accu-%s-%s.log" % (APP_ROOT,scale_uuid,my_pattern)) filename = "%s/accu-%s-%s.csv" % (mycsvdir,scale_uuid,infotime) with open(filename, 'w') as file: for ifile in sorted(files): if infotime in ifile: with_data = True with open(ifile, 'r') as ifile: for line in ifile: file.write(line) def GetInfoText(): with open('%s/bin/beielimon-config.yaml' % (APP_ROOT), 'r') as f: config_file_lines = f.read() # Wir maskieren das Passwort config_file_lines = re.sub(r"mailpwd:.*\n", "mailpwd: MASKED\n", config_file_lines) commands_output = "" for comm_arr in [ ['uptime','--pretty'], ['date'], ['uname', '-a'], ['df','-h','/boot','/root'], ['free','-m'], ['ip','a'] ]: commands_output = "%s\n# %s\n%s" % (commands_output,' '.join(comm_arr),subprocess.check_output(comm_arr)) res = """Konfigurationsfile:
%s
Diverse Befehle:
%s
""" % (config_file_lines, commands_output) return res def is_valid_yaml(yaml_string): res = True try: data_parsed = yaml.safe_load(yaml_string) except yaml.YAMLError: res = False return res @app.route('/') def index(): hash_scales = RefreshData() data = { 'scales': hash_scales, 'infotime': time.strftime("%Y", time.localtime()), 'beielipi_mobile_number': config_data['beielipi_mobile_number'], 'infotext': GetInfoText() } return template('%s/web-root/index.tpl' % (APP_ROOT), data) @app.get('/upload_config') def upload_config(): my_message = request.GET.get('message', '').strip() my_level = request.GET.get('level', '').strip() data = { 'message': my_message, 'level': my_level } return template('%s/web-root/upload_config.tpl' % (APP_ROOT), data) @app.post('/upload_config') def do_upload_config(): config_yaml = request.forms.get('config_yaml') if is_valid_yaml(config_yaml): # Wir machen noch ein Backup vor dem Ueberschreiben date_format_string = "+%Y%m%d-%H%M%S" os.system('cp %s/bin/beielimon-config.yaml %s/config-backups/beielimon-config.yaml.$(date %s)' % (APP_ROOT,APP_ROOT,date_format_string)) with open('%s/bin/beielimon-config.yaml' % (APP_ROOT), 'w') as file: file.write(string.replace(config_yaml,'\r','')) redirect("/upload_config?message=Konfiguration%20erfolgreich%20ersetzt.%20Bitte%20BeieliPi%20rebooten%20mit%20http%3A%2F%2Fbeielipi.local%2Freboot_beielipi&level=success") else: redirect("/upload_config?message=Fehler%20mit%20YAML-Syntax&level=danger") @app.route('/reboot_beielipi') def reboot_beielipi(): os.system('/sbin/init 6') @app.route('/static/') def server_static(filepath): return static_file(filepath, root='%s/web-root/static' % (APP_ROOT)) @app.route('/data/') def server_static(filepath): return static_file(filepath, root='%s/web-csv' % (APP_ROOT)) @app.route('/scale//') def scale_data(scale,infotime): scale_uuid = '' scale_alias = '' interface_type = '' for s in config_data['scales']: if s['alias'].replace(' ','_') == scale: scale_uuid = s['scale_uuid'] scale_alias = s['alias'] interface_type = s['interface_type'] CreateDatafile(scale_uuid, infotime) data = { 'scale_uuid': scale_uuid, 'scale_alias': scale_alias, 'interface_type': interface_type, 'infotime': infotime } return template('%s/web-root/scale.tpl' % (APP_ROOT), data) @app.route('/download/') def download(infotime): mypid = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(7)) mycsvdir = "%s/tmp/%s/csv" % (APP_ROOT,mypid) if not os.path.exists(mycsvdir): os.makedirs(mycsvdir) myzipdir = "%s/tmp/%s/zip" % (APP_ROOT,mypid) if not os.path.exists(myzipdir): os.makedirs(myzipdir) my_pattern = '%s%s' % (infotime,'?' * (8 - len(infotime))) files = glob.glob("%s/data/weight-*-%s.log" % (APP_ROOT,my_pattern)) for s in config_data['scales']: filename = "%s/%s-%s.csv" % (mycsvdir,s['alias'].replace(' ','_'),infotime) with open(filename, 'a') as file: for ifile in sorted(files): if (s['scale_uuid'] in ifile) and (infotime in ifile): with open(ifile, 'r') as ifile: for line in ifile: m = re.match("(\d{4})-(\d{2})-(\d{2}) (\d\d:\d\d),(\d+)", line) if m: file.write("%s.%s.%s %s,%s\n" % (m.group(3),m.group(2),m.group(1),m.group(4),m.group(5))) else: file.write("Fehler beim Parsen: %s" % (line)) zipfile = "%s/%s" % (myzipdir,infotime) shutil.make_archive(zipfile, 'zip', mycsvdir) zipfilename = "%s.zip" % (infotime) return static_file(zipfilename, root=myzipdir, mimetype='application/zip',download=zipfilename) @app.error(404) def error404(error): return index() run(app, host='0.0.0.0', port=80)