Warenkorb Funktion

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
TheOnlyOne
User
Beiträge: 5
Registriert: Dienstag 24. November 2020, 08:15

Hallo zusammen,

ich möchte gerne für unseren Verein ein kleines Kassensystem bauen für die Getränke auf Basis dieses Beitrages:
https://forum-raspberrypi.de/forum/thre ... ngssystem/

Soweit ist das Backend für den Admin schon fertig mit Datenbank und allen Zeug.
Nachdem das Projekt schon etwas älter ist habe ich es alles auf Python3 umgeschrieben und es funktioniert soweit.

Folgender Ablauf:
Nutzer legt seinen RfID Chip auf und wird über Chip ID "angemeldet"
Nutzer scannt einen EAN Code über einen USB Handscanner und bucht somit das entsprechende Produkt was in der Datenbank hinterlegt ist.

Jetzt stehe ich vor der Herausforderung das der Nutzer auch mehrere Getränke buchen kann.
Diese sollen erst in der GUI in einer Tabelle dargestellt werden bevor man auf "buchen" klickt.
Erst dann soll das ganze in die Datenbank geschrieben werden.

Das Projekt auf deren Basis ich arbeite nimmt aber sofort die Buchung entgegen und schreibt es in die Datenbank.
Heisst wenn man 2x Limo/ Bier getrunken hat, muss man für das zweite Getränk wieder den Chip auflegen.

Ich bin mir nicht sicher wie ich das anstellen soll das die gescannten Getränke erst einmal zwischen gespeichert werden.

Code: Alles auswählen

def who_am_i(cardid):
    GPIO.output(15, GPIO.HIGH)
    time.sleep(0.1)
    GPIO.output(15, GPIO.LOW)
    cursor.execute("SELECT * FROM customers where tagid = " +cardid)
    if not cursor.rowcount:
            GPIO.output(18, GPIO.HIGH)
            cprint('  Sorry aber den Benutzer gibt es nicht! ', 'white', 'on_red', attrs=['bold'])
            print time.strftime("%H:%M:%S") 
            time.sleep(2)
            GPIO.output(18, GPIO.LOW)
            db.close
            hello()
    else:
        os.system('cls' if os.name=='nt' else 'clear')
        result = cursor.fetchall() 
        for data in result:
            userid = data[0]
            name = (str(data[2]) +" "+str(data[3]))
            if str(data[4]) == "1":
                admin()
            else:
                termios.tcflush(sys.stdin, termios.TCIOFLUSH)
                sql =("SELECT CONCAT_WS( '', c.firstName, c.lastName ) AS 'Name', SUM( p.price ) AS 'Offener Betrag' FROM customers c LEFT JOIN orders o ON o.customerId = c.id LEFT JOIN products p ON p.id = o.productId WHERE c.id = %s AND o.isPaid =0 GROUP BY c.id")
                cursor.execute(sql, (userid))
                result = cursor.fetchall()
                txt = "0.0 €"
                for data in result:
                    txt = (str(data[1]) +" €")
                #colored('Your account: ' ,'blue', 'on_white' ), colored(txt, 'white', 'on_red') 
                text= ("Hallo, " +name)
                while len(text)< 41:
                    text = text + " "
                cprint (text, 'blue', 'on_white')
                while len(txt)< 19:
                    txt = txt + " "
                print colored('Auf deiner Karte sind: ' ,'blue', 'on_white' ), colored(txt ,'white', 'on_red', attrs=['bold'])
                cprint (41*'-','green', 'on_grey')
                cprint('        Bitte scanne ein Produkt!        ', 'white', 'on_green', attrs=['bold'])
                cprint (41*'-','green', 'on_grey')
                signal.alarm(TIMEOUT)
                ean = input()
                signal.alarm(0)
                os.system('cls' if os.name=='nt' else 'clear')
                cursor.execute("SELECT * FROM products where ean = '" +ean+"'")
                if not cursor.rowcount:
                    GPIO.output(18, GPIO.HIGH)
                    cprint('Sorry aber das Produkt kenne ich nicht!', 'white', 'on_red', attrs=['bold'])
                    time.sleep(3)
                    db.close
                    hello()
                else:
                    result = cursor.fetchall() 
                    for data in result:
                        drinkid = data[0]
                    txt = (str(data[2]) +" ")
                    platzhalter = ""
                    txt2 = ("-> " +str(data[3])+" €")
                    while len(txt)+len(txt2)+len(platzhalter)< 42:
                            platzhalter = platzhalter + " "
                    print colored (txt+platzhalter, 'blue', 'on_white'), colored(txt2, 'red', 'on_white')           
                    sql =("INSERT INTO orders (customerId, productId) VALUES(%s, %s)")
                    cursor.execute(sql, (userid,drinkid))
                    db.commit()
                    sql =("SELECT CONCAT_WS( '', c.firstName, c.lastName ) AS 'Name', SUM( p.price ) AS 'Offener Betrag' FROM customers c LEFT JOIN orders o ON o.customerId = c.id LEFT JOIN products p ON p.id = o.productId WHERE c.id = %s AND o.isPaid =0 GROUP BY c.id")
                    cursor.execute(sql, (userid))
                    result = cursor.fetchall()
                    for data in result:
                        txt = (str(data[1]) +" €")
                        platzhalter =""
                        while len(txt)+len(platzhalter)< 21:
                            platzhalter = platzhalter + " "
                        print colored('Jetzt auf der Karte: '+ platzhalter ,'blue', 'on_white' ), colored(txt, 'white', 'on_red') 
                    sql = ("SELECT COUNT(customerId) AS OrdersFromCustomerID FROM orders WHERE customerId=%s")
                    cursor.execute(sql, (userid))
                    result = cursor.fetchall()
                    for data in result:
                        txt = (" " +str(data[0])) 
                        platzhalter = ""
                        while len(txt)+len(platzhalter)< 18:
                            platzhalter = platzhalter + " " 
                        print colored('Anzahl von Getränken: '+platzhalter ,'blue', 'on_white' ), colored(txt, 'yellow', 'on_white') 
                    
                    time.sleep(6.5)
                    db.close
                    hello()
Das ganze beginnt ab etwas hier:

Code: Alles auswählen

else:
                    result = cursor.fetchall() 
                    for data in result:
                        drinkid = data[0]
                    txt = (str(data[2]) +" ")
                    platzhalter = ""
                    txt2 = ("-> " +str(data[3])+" €")
                    while len(txt)+len(txt2)+len(platzhalter)< 42:
                            platzhalter = platzhalter + " "
                    print colored (txt+platzhalter, 'blue', 'on_white'), colored(txt2, 'red', 'on_white')           
                    sql =("INSERT INTO orders (customerId, productId) VALUES(%s, %s)")
                    cursor.execute(sql, (userid,drinkid))
                    db.commit()
                    sql =("SELECT CONCAT_WS( '', c.firstName, c.lastName ) AS 'Name', SUM( p.price ) AS 'Offener Betrag' FROM customers c LEFT JOIN orders o ON o.customerId = c.id LEFT JOIN products p ON p.id = o.productId WHERE c.id = %s AND o.isPaid =0 GROUP BY c.id")
                    cursor.execute(sql, (userid))
                    result = cursor.fetchall()
                    for data in result:
                        txt = (str(data[1]) +" €")
                        platzhalter =""
                        while len(txt)+len(platzhalter)< 21:
                            platzhalter = platzhalter + " "
                        print colored('Jetzt auf der Karte: '+ platzhalter ,'blue', 'on_white' ), colored(txt, 'white', 'on_red') 
                    sql = ("SELECT COUNT(customerId) AS OrdersFromCustomerID FROM orders WHERE customerId=%s")
                    cursor.execute(sql, (userid))
                    result = cursor.fetchall()
                    for data in result:
                        txt = (" " +str(data[0])) 
                        platzhalter = ""
                        while len(txt)+len(platzhalter)< 18:
                            platzhalter = platzhalter + " " 
                        print colored('Anzahl von Getränken: '+platzhalter ,'blue', 'on_white' ), colored(txt, 'yellow', 'on_white') 
                    
                    time.sleep(6.5)
                    db.close
                    hello()
Er stellt mir den Namen des gescannten Produktes da sowie den Preis da. Ich meine dann greift das "time.sleep(6.5)" und er beendet danach die Verbindung und geht wieder zurück auf hello().
Diesen Teil des Scriptes (hello) habe ich nicht kopiert da er nicht notwendig ist.

Danke für eure Ideen
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Funktion heißt komisch, who_am_i macht nicht das, was der Name verrät. Außerdem ist sie mit 86 Zeilen zu lange, das sieht man auch daran, dass 6 Einrücktiefen zu viel sind.

Benutze keine magischen Zahlenwerte, sondern definiere für die Pin-Nummern Konstanten, dadurch wird der Code viel lesbarer.
cursor kommt aus dem nichts, alles was eine Funktion braucht, muß sie über ihre Argumente bekommen. Zu dem ist ein cursor etwas kurzlebiges, der nur für eine Transaktion existieren sollte.

Nun der wichtige Satz: NIEMALS Werte in SQL-Statements hineinformatieren, oder per + zusammenstückeln. dafür gibt es Platzhalter. An anderer Stelle benutzt Du die ja auch.
Keine *-Selects sondern die Felder immer explizit angeben.
Der if-Block ist zu tief eingerückt.
Das SELECT scheint nur einen Wert zurückzuliefern. Dafür benutzt man fetchone und nicht fetchall und eine for-Schleife die exakt nur einmal durchlaufen wird.
Du schreibst, Du hättest auf Python3 umgestellt, benutzt aber print falsch.
db kommt aus dem nichts und das close wird nicht aufgerufen, hat hier auch nichts verloren. Der Aufruf von hello läßt befürchten, dass Du auch gar keine richtigen Funktionen hast, sondern Funktionen als Sprungmarken mißverstehst.
os.system sollte man nicht verwenden.
`result` wird in der selben Schleife mehrfach verwendet, das ist sehr verwirrend.
Strings stückelt man nicht mit + zusammen sondern benutzt Formatstrings. Damit kann man auch Strings mit Leerzeichen aufffüllen.
Input und Datenbankoperationen sollte nicht so eng durchmischt werden, das gehört in mehrere Funktionen.
Damit wird Dein Problem auch einfacher lösbar.

Insgesamt vermute ich, dass Du das gesamte Programm nochmal neu strukturieren mußt. Eine Funktion sollte genau eine Aufgabe erfüllen.
Datenbankabfragen abkapseln.
TheOnlyOne
User
Beiträge: 5
Registriert: Dienstag 24. November 2020, 08:15

Hallo Srius3,

sorry ich hätte es dazu schreiben sollen, das ist das Projekt wie ich es heruntergeladen habe.
Auf dieser Basis möchte ich diesen Warenkorb erstellen :)

Das ist nicht der Code von mir :)
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Es ist eigentlich egal, von wem der Code ist. Er ist so schlecht, dass man ihn nicht einfach erweitern kann. Wenn Du ihn also nutzen möchtest, wäre der erste Schritt, den Code aufzuräumen.
TheOnlyOne
User
Beiträge: 5
Registriert: Dienstag 24. November 2020, 08:15

ja ok das verstehe ich sogar, allerdings bin ich Anfänger und lerne auf Basis diesen Codes wie das ganze funktioniert.
Könntest du mir vielleicht ein paar Begriffe an die Hand geben nach denen ich googlen kann wie man mehrere Artikel in einer Liste/Tabelle anzeigt und diese
dann später per "INSERT INTO" in die Datenbank speichert?

Danke Dir
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@TheOnlyOne: Auf Basis von *dem* Code solltest Du nichts lernen. Der Teil mit der Card-ID ist auch kaputt. Es kann passieren das die IDs von verschiedenen Karten auf den gleichen `cardid`-Wert abgebildet werden.

Ich habe mir den verlinkten Beitrag für das Projekt gerade mal angeschaut. Auf dem „Webinterface für alle“ steht für jeden sichtbar wer wie viele Getränke hatte und wie viel Geld auf noch auf der Karte ist. Ähm, das ist wohl nicht so ganz Datenschutzkonform, oder?
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
TheOnlyOne
User
Beiträge: 5
Registriert: Dienstag 24. November 2020, 08:15

Ne Ne das Webinterface habe ich selbst neu gemacht und basiert nicht auf den Projekt.
Lediglich das Frontend interessiert mich da ich im Gegensatz zu PHP keine Ahnung von Python habe.

Da Python für mich die einzige Variante ist (neben Java) so ein Frontend auf dem Raspberry zu bauen muss ich mich wohl oder übel damit auseinandersetzen.
Python kann nativ auf GPIO´s und somit auf die RFID Reader zugreifen.
Bei PHP geht das nicht.

Für mein einfaches Verständnis muss ich es schaffen die RFID ID mit einem SQL Query abzufragen.

Code: Alles auswählen

SELECT * FROM customers where tagid = 5662526
In der Tabelle "customers" ist gespeichert der Name und die tagid sowie eine vortlaufende k_id, sprich die Kunden ID
Somit kann ich ja schon mal den User begrüßen mit seinem namen.
Mit der Kunden ID kann ich jetzt weiter arbeiten das ich auch später weiß auf welches Benutzerkonto ich das buchen soll.

Der nächste Schritt ist das Scannen der Produkte.
Der Scanner wird als Tastatur erkannt somit funktioniert das ganz gut.
Gescannt werden die EAN Nummern wie z.B. diese. 4005906003625 (Adelholzener Wasser)
Mit dieser EAN mache ich wieder eine SQL Abfrage um den User nicht die Nummer anzuzeigen sondern den Namen sowie den Preis.
befindet sich alles in der Tabelle "products (wer hätte es gedacht :) )

Jetzt trink der User aber 1x Wasser und 1x Bier, also muss ich es ja schon fast in einem array speichern um es dann später bei
den klick auf das Feld "buchen" in die Datenbank zu schreiben.

BTW. Wie würdet ihr eine GUi mit Python bauen die in etwa das kann:

Bild
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@TheOnlyOne: Die 5662526 als `tagid` sieht falsch aus. Wie sehen denn die Bytes dazu aus? 56 62 52 6? 56 62 5 26? 56 6 25 26? Oder eine der weiteren Möglichkeiten mit verschiedenen Karten auf die gleiche `tagid` zu kommen? Die Abbildung der vier Bytewerte auf den Wert der als ID in die Datenbank eingetragen wird muss *eindeutig* sein.

Wie soll denn die GUI umgesetzt werden? GUI-Rahmenwerke haben dafür ja üblicherweise ein Widget und eventuell auch eine Model-Klasse für so etwas wie eine Tabelle. Und bei einer Webanwendung wird es interessant wie man da die Daten zwischen Server und Client hin und her schiebt. Ob man die auch bei der Erfassung schon in die Datenbank schreibt, oder zwischen Server und Client immer hin und her überträgt, oder mit JavaScript arbeitet und das erst einmal alles im Client sammelt, oder…
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
TheOnlyOne
User
Beiträge: 5
Registriert: Dienstag 24. November 2020, 08:15

@TheOnlyOne: Die 5662526 als `tagid` sieht falsch aus. Wie sehen denn die Bytes dazu aus? 56 62 52 6? 56 62 5 26? 56 6 25 26? Oder eine der weiteren Möglichkeiten mit verschiedenen Karten auf die gleiche `tagid` zu kommen? Die Abbildung der vier Bytewerte auf den Wert der als ID in die Datenbank eingetragen wird muss *eindeutig* sein.
Ich verwende MFRC522 um den RFID Reader anzusteuern auf dem Raspberry. Damit erhalte ich direkt die Zahlenfolge der Rfid Karte zurück.
Diese ist immer eindeutig. Im Admin Portal wenn man einen User anlegt überprüft er ob es diese tagid schon mal gibt in der Datenbank.
Wie soll denn die GUI umgesetzt werden? GUI-Rahmenwerke haben dafür ja üblicherweise ein Widget und eventuell auch eine Model-Klasse für so etwas wie eine Tabelle. Und bei einer Webanwendung wird es interessant wie man da die Daten zwischen Server und Client hin und her schiebt.
Das ist genau die Frage, es gibt diverse Applikationen um eine GUI zu erstellen. Habt ihr Tipps für einen Anfänger mit was ich arbeiten soll?
Ob man die auch bei der Erfassung schon in die Datenbank schreibt, oder zwischen Server und Client immer hin und her überträgt, oder mit JavaScript arbeitet und das erst einmal alles im Client sammelt, oder…
Da gebe ich Dir recht, allerdings ist das ein Thema was ich technisch noch nicht mal ansatzweise händeln könnte.
Nachdem die Kasse direkt per LAN verbunden ist, muss ich im ersten Schritt davon ausgehen das die Datenbank immer zur Verfügung steht.

Theoretisch wäre die Idee irgendwann vor dem buchen zu schauen ob die Datenbank erreichbar ist, wenn nicht soll er die Buchungen nehmen und lokal speichern, sozusagen im offline Betrieb.
Wenn die Datenbank wieder erreichbar ist kann er es ja schreiben.
Wie gesagt, ein Thema das zu komplex ist.

Daher noch mal die Frage, habt ihr Ideen oder Stichwörter nach denen ich googlen kann um mehrere Buchungen zu speichern um diese dann gesammelt zu buchen?

Danke
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@TheOnlyOne: Was ist „MFRC522“ in diesem Kontext? Ich bezweifle sehr stark das Du die Tag-ID da richtig als Zahl vorliegen hast. In dem verlinkten Projekt wird es jedenfalls falsch gemacht und Deine Beispiel-ID sieht aus als wäre sie genau *so* zustande gekommen. Und auf diese ”ID” würden sechs Karten/RF-IDs passen:

Code: Alles auswählen

Possible tags for "5662526"
   1. ["5", "66", "25", "26"]
   2. ["5", "66", "252", "6"]
   3. ["56", "6", "25", "26"]
   4. ["56", "6", "252", "6"]
   5. ["56", "62", "5", "26"]
   6. ["56", "62", "52", "6"]
Bei der Datenbank bin ich bis jetzt immer davon ausgegangen das die erreichbar ist. Das wäre dann ja noch eine andere, weitere Baustelle, wie man mit Nichterreichbarkeit der Datenbank umgeht.

Ich glaube wir reden aneinander vorbei. Ganz allgemein ist das sammeln von irgendwelchen Daten kein Problem wenn man die Python-Grundlagen drauf hat. Da sind die offensichtlichen Stichworte das was in jedem Grundlagentutorial vorkommen sollte. Das musst Du ja sowieso können.

Dann ist das nächste wie Du denn die GUI realisieren willst. Webanwendung hast Du ja jetzt offenbar ausgeschlossen. Also wie dann? GUI-Rahmenwerk? Welches? GUI-Rahmenwerke stellen Anzeigeelemente zur Verfügung. Und je nach dem welches Rahmenwerk Du verwendest, musst Du Dich in das Rahmenwerk einarbeiten und schauen was das so bietet und wie man das konkret verwendet.

Vielleicht noch als Hinweis: objektorientierte Programmierung (OOP), also wie man sinnvoll eigene Klassen schreibt, ist Voraussetzung für GUIs. Wenn Du das noch nicht kannst, solltest Du überlegen das vorher mal ohne GUI zu üben, sonst musst Du die Konzepte OOP und ereignisbasierte Programmierung zusammen lernen statt einzeln nacheinander.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten