Flask Request.data

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

Hallo,
ich versuche von einem Arduino über http an meinen Flaskserver 5 Daten Stündlich zu schicken. Jetzt meine Frage wie kann ich die Daten am besten empfangen und in 5 Variabeln schreiben um sie dann auf dem Server zu nützen?

Beispiel Inhalt der 5 Daten:

Code: Alles auswählen

71.60
24.47
53.10
22.50
836
234.67

Code: Alles auswählen

@app.route('/getdata', methods=['GET', 'POST'])
def getdata():
    if request.method == 'POST':
        data = request.data      
        return data
    return render_template('getdata.html')
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hypec: Für den Arduino am einfachsten dürfte es sein wenn die POST-Anfrage als Content-Type 'text/plain' verwendet und du die Daten wie gezeigt durch Zeilenenden oder Leerzeichen getrennt als Text schickst. Das lässt sich in der Webanwendung dann ja auch einfach wieder mit `split()` und `float()` (ggf. auch `int()`) in 5 Werte zerlegen mit denen man dann weiter arbeiten kann.
“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

Was mache ich hier falsch, dass der String nicht in bytes umgewandelt wird?

Code: Alles auswählen

@app.route('/getdata', methods=['GET', 'POST'])
def getdata():
    global luftfeuchtigkeitdrin
    global temperaturdrin
    global luftfeuchtigkeitausen
    global temperaturausen
    global erdfeuchtigkeit
    global lux
    
    if request.method == 'POST':
        data = request.data
        bdata = bytes(data)
        bdata.split(',')
        a,b,c,d,e,f = bdata.split(',')

        luftfeuchtigkeitdrin = a
        temperaturdrin = b
        luftfeuchtigkeitausen = c
        temperaturausen = d
        erdfeuchtigkeit = e
        lux = f
        write_data()
        return data
    return render_template('getdata.html')

Code: Alles auswählen

2018-09-16T17:28:05.929988+00:00 app[web.1]:   File "/app/app.py", line 47, in getdata

2018-09-16T17:28:05.929990+00:00 app[web.1]:     bdata = bytes(data)

2018-09-16T17:28:05.935929+00:00 app[web.1]: TypeError: 'module' object is not callable
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das einzige was in der Zeile aufgerufen wird ist `bytes`, und laut Fehlermeldung ist das ein Modul. Also wo bindest Du den Namen an ein Modul?

Die einbuchstabigen Namen sind sehr unschön und auch völlig unnötig. Du kannst die Werte nach dem `split()` auch gleich den richtigen Namen zuweisen. Dann aber bitte lokalen Namen und keinen globalen. Das ist nicht nur unschön, sondern kann Dir bei Webanwendungen auch Fehler verursachen, denn man darf nicht davon ausgehen das es nur einen Thread gibt, der da gleichzeitig läuft. Im Zusammenhang damit sieht der `write_data()`-Aufruf so völlig Argumente suspekt aus. Was soll der denn schreiben wenn er nichts übergeben bekommt? Alles was eine Funktion ausser Konstanten benötigt, sollte die Funktion als Argument(e) übergeben bekommen.
“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

Ich hab das ganze als Globale Variabeln gespeichert um sie hier in write_data() weiter verwenden zu können.

Code: Alles auswählen

@app.route('/getdata', methods=['GET', 'POST'])
def getdata():
    global luftfeuchtigkeitdrin
    global temperaturdrin
    global luftfeuchtigkeitausen
    global temperaturausen
    global erdfeuchtigkeit
    global lux
    
    if request.method == 'POST':
        data = request.data
        bdata = bytes(data)
        bdata.split(',')
        a,b,c,d,e,f = bdata.split(',')

        luftfeuchtigkeitdrin = a
        temperaturdrin = b
        luftfeuchtigkeitausen = c
        temperaturausen = d
        erdfeuchtigkeit = e
        lux = f
        write_data()
        return data
    return render_template('getdata.html')

@app.route('/writedata')
def write_data():
    my_date = datetime.datetime.now(pytz.timezone('Europe/Berlin'))
    fmt = '%Y-%m-%d'
    tmt = '%H:%M'
    with open('data/data.csv', 'a', newline='') as csvfile:
        spamwriter = csv.writer(csvfile, delimiter=' ')
        spamwriter.writerow([my_date.strftime(fmt), my_date.strftime(tmt), luftfeuchtigkeitdrin, temperaturdrin, luftfeuchtigkeitausen, temperaturausen, erdfeuchtigkeit, lux])
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Also ich habe das ganze jetzt so umgeschrieben, allerdings wird damit mein problem das split nur bytes will und ich es nicht hinbekomme den str richtig in bytes zu formatieren leider immer noch nicht gelöst.

Code: Alles auswählen

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

    
    if request.method == 'POST':
        data = request.data
        bdata = bytes(data)
        bdata.split(',')
        luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux = bdata.split(',')

        write_data(luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux)
        return data
    return render_template('getdata.html')

@app.route('/writedata')
def write_data(luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux):
    my_date = datetime.datetime.now(pytz.timezone('Europe/Berlin'))
    fmt = '%Y-%m-%d'
    tmt = '%H:%M'
    with open('data/data.csv', 'a', newline='') as csvfile:
        spamwriter = csv.writer(csvfile, delimiter=' ')
        spamwriter.writerow([my_date.strftime(fmt), my_date.strftime(tmt), luftfeuchtigkeitdrin, temperaturdrin, luftfeuchtigkeitausen, temperaturausen, erdfeuchtigkeit, lux])

Code: Alles auswählen

2018-09-16T18:13:25.771042+00:00 app[web.1]:   File "/app/app.py", line 42, in getdata

2018-09-16T18:13:25.771044+00:00 app[web.1]:     bdata.split(',')

2018-09-16T18:13:25.771056+00:00 app[web.1]: TypeError: a bytes-like object is required, not 'str'
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hypec: Was hat denn `data` für einen Typ? Entweder das ist schon `str`, dann verstehe ich nicht warum Du es in Bytes umwandelst. Oder es ist schon `bytes`, dann macht der `bytes()`-Aufruf so gar keinen Sinn, denn dann hätte er ja keinen Effekt. Das Problem *jetzt* ist jedenfalls das `bdate` vom Typ `bytes` ist und Du versuchst das an einem Zeichenkettenkomma zu trennen. Das geht nicht, denn `bytes` und `str` passen so nicht zusammen. Entweder ruft man `split()` auf einem `bytes`-Objekt mit einem Argument auf das auch vom Typ `bytes` ist, oder man ruft `split()` auf einer Zeichenkette mit einer Zeichenkette als Argument auf. Mischen geht nicht.

Da es sich bei den Daten um Text handelt und nicht um Binärdaten, wäre es sinnvoll das ganze als Zeichenketten zu verarbeiten. Spätestens der Writer für die CSV-Datei will Zeichenketten haben und keine Bytes.
“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 data ist ein string allerdings wenn ich es als string lasse kommt die Fehlermeldung (genau kann ich es erst wieder heute nachmittag sagen schreibe gerade vom Handy): bytes-like Objekt erwartet, str bekommen. So vom Inhalt her kommt dann die Fehlermeldung was ich nicht verstehe da ich ein test Script habe, wo ich die d
Daten zwar nicht empfange sie aber trotzdem im gleichen Format als variabel habe und das funktioniert problemlos.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Hypec: warum ist `write_data` eine Route? Das macht doch keinen Sinn. `/getdata` zum Schreiben von Daten zu benutzen ist auch verwirrend.
Mal liefert `/getdata` ein HTML-Dokument, mal die Daten, die man per Post sowieso geschickt hat. Zweiteres ist so oder so unsinnig.
Datum und Uhrzeit sollten nicht in zwei getrennten Spalten stehen. Später muß man sie dann wieder mühsam in eine zusammenfassen.

Wo bekommst Du denn einen Fehler, wenn Du data als String beläßt?
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Oke habe ich jetzt geändert /getdata gibt die Daten zurück an den Sender, einfach nur damit ich weiß ob es geklappt hat, und das HTML Dokument da die Url ja auch im Browser existiert und dammit, falls sie jemand aufrufen sollte, nichts dummes rauskommt wird das HTML Dokument ausgeliefert. /getdata schreibt ja keine Daten es empfängt sie nur splitet sie und gibt sie dann an write_data weiter das sie dort in die csv Datei geschrieben werden.

Datum und Uhrzeit hab ich getrennt da ich später über ein input date zusammen mit einem input time, die Start und End Zeit abfragen will und damit ich diese nicht zusammen fügen muss habe ich es getrennt. Allerdings habe ich es noch nicht hinbekommen 2 Zeiten abzufragen und alles was dazwischen in der csv datei steht in eine list zu schreiben um es dann in einem Plot anzuzeigen.

Ich hab jetzt diesen code hier und der Server an den die Daten gesendet werden gibt mir in der Konsole diese Fehlermeldung zurück.

Code: Alles auswählen

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

    
    if request.method == 'POST':
        data = request.data
        data.split(',')
        luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux = data.split(',')

        write_data(luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux)
        return data
    return render_template('getdata.html')

def write_data(luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux):
    my_date = datetime.datetime.now(pytz.timezone('Europe/Berlin'))
    fmt = '%Y-%m-%d'
    tmt = '%H:%M'
    with open('data/data.csv', 'a', newline='') as csvfile:
        spamwriter = csv.writer(csvfile, delimiter=' ')
        spamwriter.writerow([my_date.strftime(fmt), my_date.strftime(tmt), luftfeuchtigkeitdrin, temperaturdrin, luftfeuchtigkeitausen, temperaturausen, erdfeuchtigkeit, lux])

Code: Alles auswählen

2018-09-17T13:26:46.606494+00:00 app[web.1]: [2018-09-17 13:26:46,604] ERROR in app: Exception on /getdata [POST]

2018-09-17T13:26:46.606517+00:00 app[web.1]: Traceback (most recent call last):

2018-09-17T13:26:46.606519+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 2292, in wsgi_app

2018-09-17T13:26:46.606520+00:00 app[web.1]:     response = self.full_dispatch_request()

2018-09-17T13:26:46.606522+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1815, in full_dispatch_request

2018-09-17T13:26:46.606523+00:00 app[web.1]:     rv = self.handle_user_exception(e)

2018-09-17T13:26:46.606525+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1718, in handle_user_exception

2018-09-17T13:26:46.606526+00:00 app[web.1]:     reraise(exc_type, exc_value, tb)

2018-09-17T13:26:46.606527+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/_compat.py", line 35, in reraise

2018-09-17T13:26:46.606529+00:00 app[web.1]:     raise value

2018-09-17T13:26:46.606530+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1813, in full_dispatch_request

2018-09-17T13:26:46.606532+00:00 app[web.1]:     rv = self.dispatch_request()

2018-09-17T13:26:46.606533+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.6/site-packages/flask/app.py", line 1799, in dispatch_request

2018-09-17T13:26:46.606535+00:00 app[web.1]:     return self.view_functions[rule.endpoint](**req.view_args)

2018-09-17T13:26:46.606536+00:00 app[web.1]:   File "/app/app.py", line 41, in getdata

2018-09-17T13:26:46.606538+00:00 app[web.1]:     data.split(',')

2018-09-17T13:26:46.606547+00:00 app[web.1]: TypeError: a bytes-like object is required, not 'str'

2018-09-17T13:26:46.608183+00:00 app[web.1]: 10.124.6.113 - - [17/Sep/2018:13:26:46 +0000] "POST /getdata HTTP/1.1" 500 291 "-" "SIMCOM_MODULE"

2018-09-17T13:26:46.609117+00:00 heroku[router]: at=info method=POST path="/getdata" host=pythonv1.herokuapp.com request_id=e3d0190c-cf26-4b89-b356-244614ed416a fwd="109.42.0.104" dyno=web.1 connect=0ms service=25ms status=500 bytes=456 protocol=http
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hypec: Ich denke wir haben schon verstanden was `getdata()` macht, und genau deswegen ist die URL ja so verwirrend. Aus Sicht des Aufrufers ist das falsch, denn wenn man etwas mit `get*` aufruft, erwartet jeder das der Aufrufer damit auf etwas zugreifen kann, nicht das er damit Daten senden kann.

Die Erklärung mit der Zeit ist auch komisch. Datum und Zeitangabe zusammen beschreiben den Zeitpunkt. Wenn Du am Ende filtern oder suchen möchtest, musst Du die dann ja doch immer zusammenfügen. Und auch die Eingabe, denn letztlich wird man da ja `datetime`-Objekte für die Vergleiche nehmen, und das nicht per Hand in Einzelteilen vergleichen wollen.

Die Problembeschreibung für die Ausnahme bleibt die gleiche: bei ``a.split(b)`` müssen entweder `a` und `b` beide `str` oder beide `bytes` sein. Mischen geht nicht.

Code: Alles auswählen

In [17]: 'a,b'.split(',')
Out[17]: ['a', 'b']

In [18]: b'a,b'.split(b',')
Out[18]: [b'a', b'b']

In [19]: b'a,b'.split(',')
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-19-484fae72ad9e> in <module>()
----> 1 b'a,b'.split(',')

TypeError: a bytes-like object is required, not 'str'
An der Stelle frage ich mich gerade ob `request.data` immer `bytes` sind, oder ob da der Sender einfach nicht sagt das es sich um Plaintext handelt was er da sendet.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie postest Du Daten an getdata? Was ist der Content-Type?
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Also den Contenten Type den hab ich bisher gar nicht definiert habe aber jetzt die letzten 2 Stunden mal rumprobiert hab aber ihn nicht so definiert bekommen das es als str gesendet wird.
Ich habe jetzt die Zeit zusammen gefasst und das mit dem als byte spliten gemacht hier in dem Code bekomme jetzt aber sowas hier zurück.

Code: Alles auswählen

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

    
    if request.method == 'POST':
        data = request.data
        #data.split(b',')
        luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux = data.split(b',')

        write_data(luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux)
        return data
    return render_template('getdata.html')

def write_data(luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux):
    my_date = datetime.datetime.now(pytz.timezone('Europe/Berlin'))
    fmt = '%Y-%m-%d %H:%M'
    with open('data/data.csv', 'a', newline='') as csvfile:
        spamwriter = csv.writer(csvfile, delimiter=' ')
        spamwriter.writerow([my_date.strftime(fmt), ',',luftfeuchtigkeitdrin, ',',temperaturdrin, ',',luftfeuchtigkeitausen, ',',temperaturausen, ',',erdfeuchtigkeit, ',',lux])
Bekomme ich:

Code: Alles auswählen

"2018-09-18 18:04" , b'71.60' , b'24.60' , b'69.80' , b'28.40' , b'978' , b'2456.96\n'
Gewünscht:

Code: Alles auswählen

2018-08-13 21:00,62.50,26.90,64.60,23.40,840,4342.92
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hypec: Na dann musst Du halt die Daten die ankommen selbst in eine Zeichenkette umwandeln. Am besten vor dem `split()`, dann muss das nicht für jedes Element gemacht werden.

Bei dem `csv`-Writer das Leerzeichen als Trenner anzugeben und dann in die Daten manuell ein Komma zwischen jedes Element zu setzen ist, äh, eher nicht sinnvoll.
“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

Danke das hab ich jetzt geschafft aber ich hab das problem das bei dem split ein "\n" eingebaut wird welches ich nicht mit replace weg bekomme, ich weiß das es bei dem Split mit eingebaut wird da es nicht nur in der Csv datei drin steht sondern auch bei dem return an den Absender mit dabei ist. Kann mir da jemand weiterhelfen?

Code: Alles auswählen

app = Flask(__name__)

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

    if request.method == 'POST':
        data = request.data
        data = str(data)
        data = data.replace("b'", "")
        data = data.replace("'", "")
        data = data.replace("\n", "")
        luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux = data.split(',')

        write_data(luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux)
        return data
    return render_template('getdata.html')
    
def write_data(luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux):
    my_date = datetime.datetime.now(pytz.timezone('Europe/Berlin'))
    fmt = '%Y-%m-%d %H:%M'
    with open('data/data.csv', 'a') as csvfile:
        spamwriter = csv.writer(csvfile, delimiter=',')
        spamwriter.writerow([my_date.strftime(fmt),luftfeuchtigkeitdrin,temperaturdrin,luftfeuchtigkeitausen,temperaturausen,erdfeuchtigkeit,lux])
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Hypec: Argh, das ist eklig und kaputt. Bitte nicht auf der Zeichenkettenrepräsentation eines `bytes`-Objekt rumwerkeln, sondern die Bytes ordentlich in eine Zeichenkette dekodieren. Dann musst Du da auch nichts entfernen was eigentlich gar nicht dazu gehört. Aus Bytes die Text enthalten eine Zeichenkette zu machen, nennt sich auch dekodieren und da gibt es eine Methode für.

Das '\\n' (also die *zwei* Zeichen (Backslash und kleines n), denn '\n' ist nur *ein* Zeichen (Zeilenendezeichen)) kommen auch nicht durch das `split()` sondern durch die falsche `str()`-Umwandlung.
“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

Oke wie muss ich den str dann richtig umwandeln und kann auch den Rest sauberer lösen?
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Hypec: ich verstehe nicht, warum Du nicht die offensichtlichsten Fehler korrigieren kannst. ›/getdata‹ ist nicht zum Hochladen von Daten da.

__blackjack__ hat Dir schon geschrieben, wie es gemacht werden muß (decode).
Hypec
User
Beiträge: 183
Registriert: Mittwoch 1. August 2018, 16:11

Kann ich machen aber da außer mir niemand an dem Server arbeitet oder Daten hin senden soll, habe ich es für mich halt aus serversicht geschrieben aber ich ändere es auch gerne in senddata.
Antworten