Nutzereingabe verarbeiten Flask

Django, Flask, Bottle, WSGI, CGI…
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Hallo,
mein Plan ist das der Nutzer der Webseite eine oder 2 Kategorien aus einer Liste auswählt welche dann hier als data_one und data_two gespeichert werden um dann den plot der Auswahlentsprechen anzuzeigen zu können. Wie mache ich das ganze am besten und wie kann ich die Kategorien in Klassen oder so setzen damit die Richtige Einheit am Plot angezeigt wird?

Code: Alles auswählen

from flask import Flask, render_template, url_for, request
import matplotlib 
matplotlib.use('agg') 
import matplotlib.pyplot as plt     #Csv Daten plotten
import datetime                     #Uhrzeit
import pytz                         #Zeitzone
import csv                          #Csv Datei schreiben
import io 



app = Flask(__name__)

@app.route('/data')
def daten():

    with open('data/data.csv') as lines:
        reader = csv.reader(lines, delimiter=',')
        tabelle = [
        row[:1] + [float(c) for c in row[1:]]
        for row in reader
    ]

    time = [zeile[0] for zeile in tabelle]
    luftfeuchtedrin = [zeile[1] for zeile in tabelle]
    tempraturdrin = [zeile[2] for zeile in tabelle]
    luftfeuchteausen = [zeile[3] for zeile in tabelle]
    temperaturausen = [zeile[4] for zeile in tabelle]
    erdfeuchte = [zeile[5] for zeile in tabelle]
    lux = [zeile[6] for zeile in tabelle]

    data_one = 
    data_two = 
    
    fig, ax = plt.subplots()
    ax.plot(time, data_one, label=data_one)
    ax.plot(time, data_two, label=data_two)
    ax.grid(True)
    ax.legend()
    ax.set_title('Luftfeuchtigkeit')
    ax.set_ylabel('Luftfeuchtigkeit in %')
    ax.set_xlabel('Uhrzeit')
    
    #Anzeige noch nicht fertig
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hypec: Die Kategorien müsstest Du ja anscheinend auf Spaltennummern abbilden. Also würde ich dem Benutzer ein <select> präsentieren das Namen auf Spaltennummern aus der CSV-Datei abbildet. Am einfachsten erlaubst Du eine Mehrfachauswahl, dann kann der Benutzer selbst entscheieden wie viele Kategorien er auswählt.

Man muss dann da auch nicht alle Spalten vorher extrahieren und an Namen binden. Mit denen kann man ja sowieso nichts anfangen wenn die eigentliche Auswahl als Argument herein kommen soll.

Die Einrückung beim Einlesen ist ein wenig verwirrend. Die schliessende Klammer der äusseren „list comprehension“ und die beiden Zeilen davor sollten wohl eine Ebene tiefer eingerückt sein.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Das Problem an der Mehrfachauswahl ist das sich verschiedene Punkte ausschließen. Es is zumindestens nicht sinvoll ein Plot zu generieren wo der Temperaturverlauf mit der Luftfeuchtigkeit verglichen wird.

Kannst du mir deinen 2 Block bitte ein bißchen näher erläutern?

Danke für den Hinweiß hab es jetzt richtig eingerückt.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hypec: Na dann musst Du halt noch irgendwie in eine Datenstruktur giessen welche Spalten zusammen geplottet werden und da noch eine Auswahl davor schalten. Wobei mir jetzt nicht so ganz klar ist welche Möglichkeiten Du alle erlauben möchtest und wie flexibel der Benutzer das auswählen können soll. Und wie flexibel die Datenlage am Ende sein soll, also ob diese Datei auch beliebig erweiterbar sein darf. Ob die Metadaten die man für diese CSV-Datei noch braucht hart im Programm stehen dürfen oder ob die aus einer Konfigurationsdatei kommen.

Zum zweiten Block/Absatz: Wenn der Benutzer da etwas auswählen kann, brauchst Du entweder statischen Code oder musst dynamisch einen Variablennamen ermitteln um mit den Namen etwas anfangen zu können. Beides ist keine sinnvolle Lösung. Also machen die Namen keinen Sinn.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Also es gibt die 6 Kategorien: Luftfeuchtigkeit innen, Luftfeuchtigkeit außen, Temperatur innen, Temperatur außen, Erdfeuchtigkeit, Lux(Lichtintensität) also es sind alles Messdaten von einem Gewächshaus. Man kann jede Kategorie einzeln Außwählen und in den beiden Kategorien wo es innen und außen gibt kann man das andere noch dazu auswählen. Also die Auswahl ist sehr beschränkt.

Welche Namen meinst du?
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hypec: Dann sind es ja nur vier Kategorien: Luftfeuchtigkeit, Temperatur, Erdfeuchtigkeit, und Lichtintensität.

Man könnte dem Benutzer alle Möglichkeiten in einem <select> anbieten wo die Werte dann jeweils eine ”Liste” von Spaltennummern sind. Die meisten <option>s haben dann nur ein ”Element” im Wert, und zwei haben zwei ”Elemente”.

Ich meine die Namen `luftfeuchtedrin`, `tempraturdrin`, `luftfeuchteausen`, `temperaturausen`, `erdfeuchte`, und `lux`.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Und wie soll ich die Namen ändern also in was?
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hypec: In gar nichts. Du machst in dem Code ja auch gar nichts mit den Namen. Die Parametrisierung wird später auch den Spaltennummern bestehen, oder irgend etwas anderem was die Spaltennummern enthält oder auf auf sie abgebildet wird.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Also die soll ich weg lassen und dann in dem <input> teil in HTML in die Spalte reinschreiben?
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hypec: Was willst Du „in die Spalten reinschreiben“? Du musst für das Plotten ja Spalten *auswählen* und das machst Du ja über die Nummer der Spalte, also den Index in jeder Zeile der 2D-Daten. Also könnten vom Browser die Spaltennummern kommen, die geplottet werden sollen. Entweder eine oder zwei. Und die müsste man dann über Metadaten auch noch irgendwie auf Beschriftungen abbilden und am besten auch gleich noch überprüfen ob die Spalten überhaupt zusammen auf einem Plot landen dürfen. Letzteres ist optional wenn einem egal ist, dass ein Benutzer bei direktem Aufruf der URL mit entsprechenden Daten auch unsinnige Plots bekommen kann.

Für diese Route gibt es auch kein direktes <form>. Zumindest nicht wenn Du das Bild in HTML eingebettet anzeigen möchtest.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Der Nummer der Spalte muss ich ja um Sie Auswählen zu können, damit man weiß was man auswählt einen Namen geben.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Nein, ich rede von Namen im Python-Programm, also das was für Python syntaktisch ein Name ist. Klar musst Du dem Benutzer einen “Namen“ präsentieren, aber das ist dann ja eine literale Zeichenkette im Python-Programm und kein Name im Quelltext. Oder noch nicht einmal eine Zeichenkette im Python-Programm wenn man die Metadaten zu der CSV-Datei ebenfalls in eine Datendatei auslagert.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Also du sagst ich soll die Namen in die CSV Datei mit reinschreiben?
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hypec: Das kannst Du auch machen, ist als Dokumentation in der Datendatei selbst auch nicht schlecht, aber Du wirst für die Spalten ja noch mehr Informationen brauchen. Kategorie und Einheit für die Beschriftung des Plots beispielsweise.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Also ich habe das ganze jetzt so gemacht:
.py:

Code: Alles auswählen

from flask import Flask, render_template, url_for, request, make_response
import matplotlib 
matplotlib.use('agg') 
import matplotlib.pyplot as plt     #Csv Daten plotten
import datetime                     #Uhrzeit
import pytz                         #Zeitzone
import csv                          #Csv Datei schreiben
import io 



app = Flask(__name__)


@app.route('/')
@app.route('/index')
def index():
    return render_template('index.html')

@app.route('/test')
def test():
    return render_template('test.html')

@app.route('/getdata', methods=['GET', 'POST'])
def getdata():
    if request.method == 'POST':
        file = open('test.txt', "a")
        data = request.data
        my_date = datetime.datetime.now(pytz.timezone('Europe/Berlin'))
        fmt = "%Y-%m-%d %H:%M:%S"
        for i in data:
                file.write(my_date.strftime(fmt) + "; ")
                file.write(str(data) + '\n')
                
        return data
    return render_template('getdata.html')

@app.route('/data', methods=['GET', 'POST'])
def daten():

    with open('data/data.csv') as lines:
        reader = csv.reader(lines, delimiter=',')
        tabelle = [
            row[:1] + [float(c) for c in row[1:]]
            for row in reader
        ]

    time = [zeile[0] for zeile in tabelle]
    luftfeuchtedrin = [zeile[1] for zeile in tabelle]
    tempraturdrin = [zeile[2] for zeile in tabelle]
    luftfeuchteausen = [zeile[3] for zeile in tabelle]
    temperaturausen = [zeile[4] for zeile in tabelle]
    erdfeuchte = [zeile[5] for zeile in tabelle]
    lux = [zeile[6] for zeile in tabelle]
    data_one = 'xy'

    if request.method == 'POST':
        data_one = request.form['data_one']

    if data_one == 'luftfeuchtigkeit':
        fig, ax = plt.subplots()
        ax.plot(time, luftfeuchtedrin, label="luftfeuchtedrin")
        ax.plot(time, luftfeuchteausen, label="luftfeuchteausen")
        ax.grid(True)
        ax.legend()
        ax.set_title('Luftfeuchtigkeit')
        ax.set_ylabel('Luftfeuchtigkeit in %')
        ax.set_xlabel('Uhrzeit')
    elif data_one == 'temperatur':
        fig, ax = plt.subplots()
        ax.plot(time, tempraturdrin, label="Temperatur drin")
        ax.plot(time, temperaturausen, label="Temperatur ausen")
        ax.grid(True)
        ax.legend()
        ax.set_title('Temperatur')
        ax.set_ylabel('Temperatur in Celsius')
        ax.set_xlabel('Uhrzeit')
    elif data_one == 'erdfeuchtigkeit':
        fig, ax = plt.subplots()
        ax.plot(time, erdfeuchte, label="Erdfeuchtigkeit")
        ax.grid(True)
        ax.legend()
        ax.set_title('Erdfeuchtigkeit')
        ax.set_ylabel('Erdfeuchtigkeit')
        ax.set_xlabel('Uhrzeit')
    elif data_one == 'lux':
        fig, ax = plt.subplots()
        ax.plot(time, lux, label="Lichtitensität(lux)")
        ax.grid(True)
        fig.legend()
        ax.set_title('Lichtitensität(lux)')
        ax.set_ylabel('Lux')
        ax.set_xlabel('Uhrzeit')
    else :
        return render_template('benutzer.html')

    buffer = io.BytesIO()
    plt.savefig(buffer, format = 'png')
    plot_data = buffer.getvalue()

    response = make_response(plot_data)
    response.headers['Content-type'] = 'image/png'
    return response

    return render_template('benutzer.html')

if __name__ == '__main__': app.run(debug=True)
.html:

Code: Alles auswählen

{% extends "base.html" %}
{% block content %}
    <h1>Upload new Data</h1>
    <br>
    <form method = "post">
            <div class="row">
                <div class="col-25">
                  <label for="data_one">Messdaten:</label>
                </div>
                <div class="col-75">
                  <select id="data_one" name="data_one">
                    <option value="luftfeuchtigkeit">Luftfeuchtigkeit</option>
                    <option value="temperatur">Temperatur</option>
                    <option value="erdfeuchtigkeit">Erdfeuchtigkeit</option>
                    <option value="lux">Lux</option>
                  </select>
                </div>
            </div>
        <input type = "submit" value = "submit" />
    </form>
{% endblock %}
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Bei `getdata` würde ich nicht erwarten, dass man da Daten schreiben kann. Dateien die geöffnet werden, sollten auch wieder geschlossen werden, am besten mit dem with-Statement arbeiten.
Statt mit einer Zeitzone mit Sommer- und Winterzeit eine nehmen, die das nicht hat, bei Bedarf kann man ja immer noch umrechnen. Du schreibst immer noch für jeden Buchstaben einmal die gesamten Daten. Benutze format:

Code: Alles auswählen

def store_data():
    if request.method == 'POST':
        data = request.data
        current_time = datetime.datetime.utcnow()
        with open('test.txt', "a") as output:
            output.write("{:%Y-%m-%d %H:%M:%S}; {}\n".format(current_dime, data))
    return render_template('getdata.html')
Die if-Blöcke in Daten enthalten zum Großteil identischen Code. Schau mal, was sich da wirklich ändert, speichere das in Variablen und plotte nur an einer Stelle. Dann kannst Du diese variablen Teile in ein Wörterbuch auslagern und sparst die das if komplett. Das return am Ende der Funktion wird nie erreicht. Eine Schnittstelle, die mal HTML und mal PNG liefert, ist schlecht. Trenne die beiden Aufgaben in zwei Funktionen. Im HTML fehlt dazu auch noch das passende <img>-Tag.
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Danke für die Tipps der Teil der Datei war noch der alte wo noch in die txt datei geschrieben wird. Deine Tipps werde ich befolgen wenn ich das ganze zu Csv änder, aber erstmal schreibe ich den Teil wo aus der Csv Datei gelesen wird fertig.
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Wichtiger für Dein aktuelles Problem ist eigentlich der zweite Teil meiner Antwort.
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Das ich das Bild mit <img> einbinden kann in das HTML Dokument hat Blackjack gesagt das es nicht geht.
Bei den if schleifen hab ich versucht data_one bei erdfeuchtigkeit und lux als variabel einzufügen das sie ja eigentlich "lux" als Inhalt haben sollte und somit die oben definierte Liste lux sein sollte aber das ging nicht.
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Eine Musterlösung sagt mehr als tausend Worte:

Code: Alles auswählen

PLOTS = {
    'luftfeuchtigkeit': {
        'title': 'Luftfeuchtigkeit',
        'ylabel': 'Luftfeuchtigkeit in %',
        'plots': [('luftfeuchtedrin', 1), ('luftfeuchteausen', 3)]
    },
    'temperatur': {
        'title': 'Temperatur',
        'ylabel': 'Temperatur in °C',
        'plots': [("Temperatur drin", 2), ("Temperatur ausen", 4)]
    },
    'erdfeuchtigkeit': {
        'title': 'Erdfeuchtigkeit',
        'ylabel': 'Erdfeuchtigkeit in %',
        'plots': [('Erdfeuchtigkeit', 5)]
    },
    'lux': {
        'title': 'Lichtitensität',
        'ylabel': 'Lichtitensität in Lux',
        'plots': [('Lichtitensität(lux)', 6)]
    },
}

app = Flask(__name__)

@app.route('/image/<filename>', methods=['GET'])
def daten(filename):
    data_one = filename.split('.')[0]
    with open('data/data.csv') as lines:
        reader = csv.reader(lines, delimiter=',')
        tabelle = [
            row[:1] + [float(c) for c in row[1:]]
            for row in reader
        ]

    time = [zeile[0] for zeile in tabelle]
    plot = PLOTS[data_one]
    fig, ax = plt.subplots()
    for label, idx in plot['plots']:
        ax.plot(time, [zeile[idx] for zeile in tabelle], label=label)
    ax.grid(True)
    fig.legend()
    ax.set_title(plot['title'])
    ax.set_ylabel(plot['ylabel'])
    ax.set_xlabel('Uhrzeit')

    buffer = io.BytesIO()
    plt.savefig(buffer, format = 'png')
    plot_data = buffer.getvalue()

    response = make_response(plot_data)
    response.headers['Content-type'] = 'image/png'
    return response

@app.route('/data', methods=['GET', 'POST'])
def daten():
    if request.method == 'POST':
        data_one = request.form['data_one']
    else:
        data_one = None
    return render_template('benutzer.html', data_one=data_one})

Code: Alles auswählen

{% extends "base.html" %}
{% block content %}
    <h1>Upload new Data</h1>
    <br>
    <form method = "post">
            <div class="row">
                <div class="col-25">
                  <label for="data_one">Messdaten:</label>
                </div>
                <div class="col-75">
                  <select id="data_one" name="data_one">
                    <option value="luftfeuchtigkeit">Luftfeuchtigkeit</option>
                    <option value="temperatur">Temperatur</option>
                    <option value="erdfeuchtigkeit">Erdfeuchtigkeit</option>
                    <option value="lux">Lux</option>
                  </select>
                </div>
            </div>
        <input type = "submit" value = "submit" />
    </form>
{%if data_one %}<img src="/image/{data_one}.png">{%endif%}
{% endblock %}
Antworten