Datenbankeintrag mit Auslesen eines RFID Tags(SQL Abfrage und Eintrag)

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Sc0uty
User
Beiträge: 9
Registriert: Dienstag 15. Januar 2019, 09:40

Guten Tag Zusammen,

für ein Schulprojekt wollen wir ein Automatisches Anwesenheitssystem Programmieren, dieses Läuft über ein Raspberry PI der Verbunden ist mit einem RFID Scan gerät.
Da ich aber relativ Neu in Python bin, stoße ich nun vor ein paar Problemen, wo ich mit Google keine passende Erklärung oder Beispiel gefunden habe das es gut erklärt.

Wie folgt, wir wollen die RFID Karte, einem Schüler zuweisen, durch das Scannen der ID, wird diese Erkannt ( ist in einer Datenbank dem Schüler(DBName: Schueler) Eindeutig zugewiesen ) soweit kein Problem, das war jetzt noch recht einfach, jetzt zu dem Schwierigen Teil für mich.

Schüler haben eindeutige IDs, RFID Karte ist einer ID zugewiesen ( in der Tabelle "Schueler" ) -> das Python Script soll beim Erfolgreichen Scannen die ID des Schülers (zb. RFID = 1 SchuelerID = 1 ) mit Hilfe der eindeutigen RFID auslesen, diese in der Tabelle Anwesenheiten wieder finden und den Entsprechenden Wert 1 in der ersten Stunde (Std1,Std2 etc.. ) eintragen.

Bei den Stunden ist das Problem das diese noch verglichen werden müssen mit der Zeit, wenn 7:45 dann Eintrag in der ersten Stunde --> bis 9 Uhr kommt man zu spät soll die zuspät kommende Zeit eingetragen werde, wenn Std2 ab 9:15 - 11 Uhr immer so weiter.

Meine Frage ist jetzt, wie Vergleiche ich jetzt die RFID in der Tabelle "Schueler" lese somit die ID des Schülers aus, vergleiche diese mit der ID des Schülers in der Tabelle "Anwesenheiten".
Ich habe die Datenbank nicht gemacht hätte es Sinnvoller und Logischer gelöst gerade weil man als Endnutzer diese Datenbank nicht sieht weil am Ende eine Weboberfläche hat, wo man alles auslesen kann, ich hätte direkt die RFID in die Anwesenheitstabelle eingefügt, dann wäre das nicht so schwer wie es jetzt ist.

Ich erwarte keine Lösungen, sei denn ich komme komplett nicht weiter, aber ich würde mich über Lösungsansätze und Tipps freuen, weil ich Python mit SQL Anbindung gerne Lernen würde.


Code: Alles auswählen

import sys
import tty
import termios
import time
import thread
import datetime

import RPi.GPIO as GPIO
import logging
import db
import MFRC522

import MySQLdb


def connect():
    
    return MySQLdb.connect(host="Mir bekannt", user="mir bekannt", passwd="mir bekannt", db="mir bekannt")


def identity():
    reading = True
    while reading:
        MIFAREReader = MFRC522.MFRC522()

        (status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)

        if status == MIFAREReader.MI_OK:
            print("Karte wurde Erkannt")

        (status,backData) = MIFAREReader.MFRC522_Anticoll()
        if status == MIFAREReader.MI_OK:
            print ("Die Karten ID ist: "+str(backData[0])+""+str(backData[1])+""+str(backData[2])+""+str(backData[3])+""+str(backData[4]))
            MIFAREReader.AntennaOff()
            reading=False
            return str(backData[0])+str(backData[1])+str(backData[2])+str(backData[3])+str(backData[4])


def setGpio():
    # Show no warnings
    GPIO.setwarnings(False)
    # Use GPIO pin numbers
    GPIO.setmode(GPIO.BOARD)        

def read():
    cardId=identity()
    return cardId
    
cardId=read()   
db2 = connect()
c = db2.cursor()
check = c.execute("SELECT Vorname FROM Schueler WHERE KartenID = " + cardId)  ## Hier das Auslesen des Schülers mit der ID...
result = c.fetchall()

for data in result:  

    print "Name:" + str(data[0])

Code: Alles auswählen

 Std1 = 1 
SchuelerID = 1 
check2 = c.execute("UPDATE Anwesenheiten SET Std1 = %s WHERE SchuelerID = %s" % (Std1,SchuelerID)) ## Hier habe ich Versucht erstmal Generell ein Eintrag zusetzten.
    
in der Tabelle Anwesenheiten gibt es zwei PKeys, mit SchuelerID und dem aktuellen Datum.
Sc0uty
User
Beiträge: 9
Registriert: Dienstag 15. Januar 2019, 09:40

Update für den neusten Code :

Code: Alles auswählen

import sys
import tty
import termios
import time
import thread
import datetime

import RPi.GPIO as GPIO
import logging
import db
import MFRC522

import MySQLdb


def connect():
    
    
    return MySQLdb.connect(host="mir bekannt", user="mir bekannt", passwd="mir bekannt", db="mir bekannt")


def identity():
    reading = True
    while reading:
        MIFAREReader = MFRC522.MFRC522()

        (status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)

        if status == MIFAREReader.MI_OK:
            print("Karte wurde Erkannt")

        (status,backData) = MIFAREReader.MFRC522_Anticoll()
        if status == MIFAREReader.MI_OK:
            print ("Die Karten ID ist: "+str(backData[0])+""+str(backData[1])+""+str(backData[2])+""+str(backData[3])+""+str(backData[4]))
            MIFAREReader.AntennaOff()
            reading=False
            return str(backData[0])+str(backData[1])+str(backData[2])+str(backData[3])+str(backData[4])



def setGpio():
    # Show no warnings
    GPIO.setwarnings(False)
    # Use GPIO pin numbers
    GPIO.setmode(GPIO.BOARD)        

def read():
    cardId=identity()
    return cardId




cardId=read()   
db2 = connect()
c = db2.cursor()
check = c.execute("SELECT Vorname,Nachname FROM Schueler WHERE KartenID = " + cardId)
result = c.fetchall()

for data in result:
    
    Std1 = 1 
    SchuelerID = 1
    db = connect()
    c = db.cursor()
    check = c.execute("UPDATE Anwesenheiten SET Std1 = %s WHERE SchuelerID = %s" % (Std1,SchuelerID))
    db.commit()
    db.close()
    print "Hallo" + str(data[0]) + str(data[1]) + "du wurdest erfolgreicht als Anwesend eingetragen."
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

tty, termio, thread, werden importiert aber nicht benutzt. Weg damit. (Es gibt auch selten Fälle, wo man diese Bibliotheken direkt benutzt, es gibt für alle schönere Alternativen).
In `identity` sollte die while-Schleife eine while-True sein, zumal die Bedingung nie erfüllt ist. Ansonsten würde man solche Schleifen per break verlassen (bei Dir aber schon per return).
Statt Strings mit + zusammenzustückeln nimmt man String-Formatierung, hier vielleicht besser das ganze nach Hexadezimal codieren.
GPIO-Warnings sind dazu da, sie zu beheben, und nicht sie zu ignorieren, GPIO.cleanup fehlt daher auch. `setGPIO` ist dann auch ein etwas irreführender Namen.

Niemals Werte in SQL-Statements hineinformatieren! Dafür gibt es Platzhalter:

Code: Alles auswählen

c.execute("SELECT Vorname FROM Schueler WHERE KartenID = %s", (cardId,))
`check` wird nie benutzt und execute hat auch keinen sinnvollen Rückgabewert. Bei einer Abfrage, die exakt eine Lösung hat, ist eine for-Schleife die falsche Methode, fetchone wäre geeigneter.
Ein Datenbankdesign, das Variablen in Feldnamen kodiert ist schlecht. Bitte mit demjenigen der das ausgedacht hat, nochmal reden und ihn gerne auch auf eine Diskussion hier verweisen.

Tabellen sollten so generisch sein, wie es geht. Hier braucht also die Anwesenheitstabelle, neben SchuelerID, noch die Felder Stunden-Nummer, und Zeit, bzw. noch weitere Flags, falls man das zu-Spätkommen oder andere Information direkt kodieren will.
Wenn man Statistik über die Fehlzeiten eines ganzen Jahres machen will, ist das Datenbankdesign sowieso zu knapp.

Generell gilt, Anwesenheit ist ein personenbezogenes Datum und unterliegt im Datenschutz daher besonderen Regeln. Der Zugriff muß dokumentiert und von Unbefugten unterbunden werden. Alle Teilnehmer müssen der Datenaufnahme schriftlich zustimmen. Das hier ist ausdrücklich keine Rechtsbelehrung und hat keinen Anspruch auf Richtigkeit oder Vollständigkeit. Am besten befragst Du zu diesem Thema einen Experten.
Sc0uty
User
Beiträge: 9
Registriert: Dienstag 15. Januar 2019, 09:40

Hallo, erstmal vielen Dank für deine Antwort.
GPIO-Warnings sind dazu da, sie zu beheben, und nicht sie zu ignorieren, GPIO.cleanup fehlt daher auch. `setGPIO` ist dann auch ein etwas irreführender Namen.
Ich will die Warnings ausschalten, da mir gesagt wird das dieser schon in Benutzung ist, aber dennoch Funktioniert, ich weis nicht ob das zu Problemen führen kann.
Niemals Werte in SQL-Statements hineinformatieren! Dafür gibt es Platzhalter:
Das habe ich gestern ebenfalls Gelesen, und direkt verbessert außer an diesem Punkt.

`check` wird nie benutzt und execute hat auch keinen sinnvollen Rückgabewert. Bei einer Abfrage, die exakt eine Lösung hat, ist eine for-Schleife die falsche Methode, fetchone wäre geeigneter.
Also kann ich check ohne Probleme raus Löschen ? Ich habe mich mit fetchone noch nie Beschäftigt, werde es mir aber mal durchlesen und versuchen zu verwirklichen, momentan benutze ich für alles for-schleifen, weil der Code momentan dann das erfüllt was er soll, ich bin mir sicher man kann es jetzt schon einfacher gestalten.

Um den Thread nicht mit Code zu zu Ballern, werde ich einfach immer mein Zweiten Poste, als Code Update und mit Datum versehen außer es wird explizit über etwas geredet.

Tabellen sollten so generisch sein, wie es geht. Hier braucht also die Anwesenheitstabelle, neben SchuelerID, noch die Felder Stunden-Nummer, und Zeit, bzw. noch weitere Flags, falls man das zu-Spätkommen oder andere Information direkt kodieren will.
Wenn man Statistik über die Fehlzeiten eines ganzen Jahres machen will, ist das Datenbankdesign sowieso zu knapp.
Die Anwesenheitstabelle besteht zur Zeit aus dem Datum, der SchuelerID und den Stunden, wo zur Zeit nur eine 1 und 0 eingetragen wird ob jemand in diesen Tabellen Anwesend oder Abwesend war.
Die Stunden in der Anwesenheitstabelle sollen in der Tabelle Stunden zeit ausgelesen werden werden sprich die erste Stunde beginnt bei uns um 7:45, also wenn ich den Chip um 7:45 vor den Scanner halte soll er das erkennen, die Stundennummer auslesen und diese dann in der Anwesenheitstabelle eintragen, sollte man dann zwischen 7:45 und 9 Uhr erscheinen soll die Differenz von der Zeit von 7:45 eingetragen werden, um die Verspätungen zu messen.

Ich hatte für die Fehlzeiten zum Beispiel vorgeschlagen, da ja nicht 2000 Schüler zu Spät kommen am Tag einfach ein Doc automatisch mitschreiben zu lassen, was die Zeit dann mit Trackt für jeden Tag, diese dann mit der SchuelerID versehen sind, und diese dann in einer Extra Tabelle immer zusammengefasst wird.

Wobei ich es als Schwierig ertrachte, da jeden Tag das neue Datum für jeden einzelnen Schüler eingetragen wird sprich unsere Schule hat als Beispiel 2000 Schüler, also würden in der Anwesenheitstabelle per Schedule von myphpadmin, jeden Tag 2000 neue Einträge erstellt.

Ein Datenbankdesign, das Variablen in Feldnamen kodiert ist schlecht. Bitte mit demjenigen der das ausgedacht hat, nochmal reden und ihn gerne auch auf eine Diskussion hier verweisen[...]
Wie meinst du das, dass Variablen in Feldnamen schlecht sind ?
Ich habe schon oftmals Kritik an der Datenbank ausgeübt weil vieles für mich als Coder, leichter gewesen wäre.
Wir sind eine Gruppe aus 4 Personen, wobei 2 für die Datenbank zu ständig sind, die sind für Kritik offen, also immer her damit
Ich werde später noch Bilder der Datenbank anhängen, mit man sich ein genaueres Bild machen kann von den Tabellen die für den Code wichtig sind.

Generell gilt, Anwesenheit ist ein personenbezogenes Datum und unterliegt im Datenschutz daher besonderen Regeln. Der Zugriff muß dokumentiert und von Unbefugten unterbunden werden. Alle Teilnehmer müssen der Datenaufnahme schriftlich zustimmen. Das hier ist ausdrücklich keine Rechtsbelehrung und hat keinen Anspruch auf Richtigkeit oder Vollständigkeit. Am besten befragst Du zu diesem Thema einen Experten.
Es ist nur ein Stufenprojekt für unseren Abschluss, dort werden nur unsere Namen eingetragen, außer die Schule will das haben dann sieht das natürlich anders aus



Nun suche ich noch nach einer Möglichkeiten zwei Zeiten zu vergleichen, mit das Script weis wo er was ein tragen muss und die Differenz zu berechnen um die Zeit des zu Zuspätkommens einzutragen.
Sollte ich die Zeit einfach in Hexa umrechnen, falls das möglich ist, und das dann wieder zurück ?
Zuletzt geändert von Sc0uty am Mittwoch 16. Januar 2019, 08:57, insgesamt 1-mal geändert.
Sc0uty
User
Beiträge: 9
Registriert: Dienstag 15. Januar 2019, 09:40

Code Update da ich Beiträge nicht ändern kann ^^
Update vom 16.01

Code: Alles auswählen

import sys

import time

from datetime import datetime

import RPi.GPIO as GPIO
import logging
import db
import MFRC522

import MySQLdb







def connect():
    
    
    return MySQLdb.connect(host="Bekannt", user="Bekannt", passwd="Bekannt", db="Bekannt")


def identity():
    reading = True
    while reading:
        MIFAREReader = MFRC522.MFRC522()

        (status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)

        if status == MIFAREReader.MI_OK:
            print("Karte wurde Erkannt")

        (status,backData) = MIFAREReader.MFRC522_Anticoll()
        if status == MIFAREReader.MI_OK:
            print ("Die Karten ID ist: "+str(backData[0])+""+str(backData[1])+""+str(backData[2])+""+str(backData[3])+""+str(backData[4]))
            MIFAREReader.AntennaOff()
            reading=False
            return str(backData[0])+str(backData[1])+str(backData[2])+str(backData[3])+str(backData[4])



def Gpio():
    # Show no warnings
    GPIO.setwarnings(False)
    # Use GPIO pin numbers
    GPIO.setmode(GPIO.BOARD)        

def read():
    cardId=identity()
    return cardId


cardId=read()   
db2 = connect()
c = db2.cursor()
check = c.execute("SELECT Vorname FROM Schueler WHERE KartenID = %s", (cardId,))
result = c.fetchall()




for data in result :
    

    db2 = connect()
    c = db2.cursor()
    check = c.execute("SELECT Vorname, Nachname FROM Schueler WHERE SchuelerID = " + str(result[0][0]))
    result1 = c.fetchall()


    for data1 in result1:
    
           
            
            Std1 = 1 
            SchuelerID = str(result[0][0])
            db = connect()
            c = db.cursor()
            check = c.execute("UPDATE Anwesenheiten SET Std1 = %s WHERE SchuelerID = %s AND Datum = %s " % (Std1,SchuelerID,str((datetime.now().strftime('%Y%m%d')))))
            db.commit()
            db.close()
           
        
            print(datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
            
            
            print "Hallo" + str(data1[0]) + str(data1[1]) + "du wurdest erfolgreicht als Anwesend eingetragen."




GPIO.cleanup()  
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sc0uty: Du willst die Warnung nicht ausschalten. Dir wird ja nur gesagt das die Pins schon benutzt werden, weil *Du* in Deinem Code nicht ordentlich hinter Dir aufgeräumt hast. *Das* willst Du machen und nicht die Warnungen ignorieren. Die sind ja dazu da Dich darauf aufmerksam zu machen, dass Du was falsch machst. Also mach es richtig, dann kommen auch keine Warnungen mehr.

Du bastelst immer noch selber Werte in Zeichenketten mit SQL-Anweisungen. Das ist wirklich ganz Böse™.

Die Zeichenkette mit der ID wird falsch erzeugt. So wie Du das machst ist das Ergebnis nicht eindeutig, dass heisst es kann verschiedene Karten/Chips geben bei denen hier die gleiche ID heraus kommt. Mal als ganz simples Beispiel die Karten [1, 1, 1, 11] und [11, 1, 1, 1] führen beide zur ID-Zeichenkette '11111'. Du musst entweder die einzelnen Komponenten in der Zeichenkette eindeutig trennen, beispielsweise '1-1-1-11' und '11-1-1-1' oder dafür sorgen das jede Komponente in der Zeichenkette die gleiche Länge hat, beispielsweise '001001001011' und '011001001001'. Das geht mit Zeichenkettenformatierung recht einfach und wäre auch ”pythonischer” als `str()` und ``+``. Letzteres ist eher BASIC denn Python. Bei Zeichenkettenformatierung wäre es dann auch einfach die Komponenten in Hexadezimaldarstellung zu formatieren, dann braucht es nur zwei Stellen für jede Komponente: '0101010b' und '0b010101'.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Die `read()`-Funktion ist überflüssig. Die ruft ja nur `identity()` auf und gibt deren Ergebnis zurück. Warum?

Funktionen bennent man üblicherweise nach der Tätigkeit die sie ausführen. `identity` ist keine Tätigkeit. Ebensowenig `Gpio`. Letzteres entspricht zudem nicht den Namenskonventionen in Python. Alles `klein_mit_unterstrichen` ausser Konstanten (KOMPLETT_GROSS) und Klassen (`MixedCase`). Siehe Style Guide for Python Code.

Zudem sind Abkürzungen, und noch schlimmer Einzelbuchstaben, in 99,9% der Fälle keine guten, verständlichen Namen. Wenn etwas ein Cursor-Objekt ist, dann nenn das `cursor` und nicht einfach nur `c`. Die Zeiten das Arbeitsspeicher so knapp war, dass Programmiersprachen die Länge von Bezeichnern beschränkt haben, sind schon eine Weile vorbei. :-)

Das `reading` überflüssig ist und einfach durch ``while True:`` ersetzt werden sollte, wurde glaube ich schon gesagt.

Muss bzw. sollte man wirklich in jedem Schleifendurchlauf ein neues `MFRC522`-Exemplar erzeugen? Das erscheint mir unnötig. Wahrscheinlich würde es sogar reichen das nur ein einziges mal zu erzeugen und als Argument an die Funktion zu übergeben.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sc0uty
User
Beiträge: 9
Registriert: Dienstag 15. Januar 2019, 09:40

Danke für die Kritik die werde ich mir zu herzen nehmen und dementsprechend in der Zukunft handeln.
Mal als ganz simples Beispiel die Karten [1, 1, 1, 11] und [11, 1, 1, 1] führen beide zur ID-Zeichenkette '11111'. Du musst entweder die einzelnen Komponenten in der Zeichenkette eindeutig trennen, beispielsweise '1-1-1-11' und '11-1-1-1' oder dafür sorgen das jede Komponente in der Zeichenkette die gleiche Länge hat, beispielsweise '001001001011' und '011001001001'.
Wenn ich das richtig Verstehe, müsste ich dann die Hex Zahl auch in der Datenbank ein Tragen, für die Karten ID mit er diese auch wieder findet ?
Sonst müsste ich die ID ja erst in Hex umrechnen und dann wieder zurück, dann könnte der Fehler ja immer noch auftreten.
Die `read()`-Funktion ist überflüssig. Die ruft ja nur `identity()` auf und gibt deren Ergebnis zurück. Warum?
Dort lesen wir einfach momentan einfach nur mit die ID aus, das fällt weg sobald das Programm komplett Fertig ist.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Die Karten-ID rechnest Du einmal in einen eindeutigen String oder eine eindeutige Zahl um, je nach dem, was Dir besser paßt, und diesen Wert benutzt Du überall sonst, da muß man also nichts nachträglich wieder zurückrechnen.

Statt die Stunde in `Anwesenheit` explizit zu speichern, reicht es, wenn Du prüfst, ob der Schüler einen Eintrag in der Datenbank für Anwesend hat, dessen Zeit kleiner als der Start der entsprechenden Stunde ist und dessen zugehöriger Eintrag für Abwesend entweder nicht existiert (aktuelle Stunde) oder größer ist, als die betrachtete Stunde.

Was sind denn die Anwendungsfälle für die Datenbank? Welche Fragen sollen damit beantwortet werden?
Sc0uty
User
Beiträge: 9
Registriert: Dienstag 15. Januar 2019, 09:40

Die Karten-ID rechnest Du einmal in einen eindeutigen String oder eine eindeutige Zahl um, je nach dem, was Dir besser paßt, und diesen Wert benutzt Du überall sonst, da muß man also nichts nachträglich wieder zurückrechnen.
Das haben wir gestern genau durch besprochen und werden es so durchführen, diesen Sicherheitsaspekt haben wir nicht bedacht.
Statt die Stunde in `Anwesenheit` explizit zu speichern, reicht es, wenn Du prüfst, ob der Schüler einen Eintrag in der Datenbank für Anwesend hat, dessen Zeit kleiner als der Start der entsprechenden Stunde ist und dessen zugehöriger Eintrag für Abwesend entweder nicht existiert (aktuelle Stunde) oder größer ist, als die betrachtete Stunde.
Wir haben das so verstanden wir gucken ob der Eintrag z.B Größer oder kleiner als 7:45 ist und tragen dementsprechend etwas ein, eigentlich sollte noch die Zeit, die er zuspät gekommen ist Eingetragen werden sprich um 7:45 ist Unterrichtsbeginn und der Schüler komm z.B um 8:00 Uhr, dann soll diese Differenz eingetragen werden, die einzige Lösung die mir vorschwebt ist das ich mir die Zeit des Unterrichtsbeginn aus der Datenbank ziehe, diese minus der aktuellen Zeit des RaspPi, ich habe dies versucht aber die Formate sind scheinbar so unterschiedlich, das mir auch nichts in Sinn kommen würde ich in die gleich Formatieren könnte, mit dies gelingen könnte.
Was sind denn die Anwendungsfälle für die Datenbank? Welche Fragen sollen damit beantwortet werden?
Explizit auf das RaspPi Programm bezogen ?

In der Schueler Tabelle wir die RFID Karte eingetragen, einem Schueler ist eine eindeutige ID zugewiesen, diese ID wird ausgelesen, und wird mit der eindeutigen ID in der Tabelle Anwesenheiten für Schueler verglichen.
Dann soll aus der Tabelle Stundenzeit, die aktuelle Stunde gezogen werden, dort gibt es zwei Spalten jeh Stunde, einmal UntBeginn und UntEnde nun soll verglichen werden ob der Schüler zu Spät da ist oder Rechtzeitig da ist, in dem die Aktuelle Zeit damit verglichen wird.

Simpel gesagt, soll einfach nur die Anwesenheit getrackt werden mit der RFID wie oben erklärt, die Fehlstunden bzw die Gesamtzeit der zu spät kommenden Zeit zusammen gerechnet werden und später in einer Webanwendung zusammen gerechnet werden.

Bild

Bild

Bild

Bild

Bild







Beste Grüße.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Sc0uty hat geschrieben: Donnerstag 17. Januar 2019, 08:28[..., ich habe dies versucht aber die Formate sind scheinbar so unterschiedlich, das mir auch nichts in Sinn kommen würde ich in die gleich Formatieren könnte
Es handelt sich doch um Zeiten, die als datetime-Objekte vorliegen sollten. Aktuelle Zeit sowieso und aus der Datenbank werden TIMESTAMP-Felder automatisch als datetime gelesen.

Wie schon geschrieben, solltest Du die Anwesenheit pro Stunde gar nicht in der Datenbank speichern, sondern bei Bedarf aus der Kombination der Tabellen Anwesenheit und Stundenzeit berechnen. Das kann auch als View direkt in der Datenbank modeliert werden, so dass man wie auf eine normale Tabelle darauf zugreifen kann.

Also eine Abfrage dieser Form, wenn man wissen will, ob ein Schüler mit einer bestimmten ID pünktlich in der ersten Stunde war:

Code: Alles auswählen

SELECT (time(a.anwesend_ab) <= s.untericht_beginn) as "puenktlich", (time(a.anwesend_ab) - s.untericht_beginn) as "verspaetung" FROM Anwesenheit a, Stundenzeit s
    WHERE a.schueler_id = 123
    AND date(a.anwesend_ab) == current_date()
    AND time(a.anwesend_ab) <= s.unterricht_ende
    AND (a.anwesend_bis IS NULL OR time(a.anwesen_bis) >=s.unterricht_beginn)
    AND s.nummer = 1
unter der Voraussetzung, dass die Tabelle Anwesenheit zu jeder Schüler-ID die Anwesenheit als Zeitstempel inklusive Datum speichert. Dann kann man natürlich auch statt des heutigen Tages einen beliebig anderen Tag nehmen. Hier wird nur berücksichtigt, dass der Schüler zu Beginn der Stunde anwesend war, kann aber nach 5 Minuten auch wieder gegangen sein. Liefert die Abfrage keinen Eintrag, hat der Schüler komplett gefehlt.

Das muß man jetzt natürlich noch mit dem individuellen Stundenplan des Schülers kombinieren, und kann dann Statistik über Pünktlichkeit und Fehlstunden machen.

Zum Datenbankdesign: die Verknüpfung Schüler, Anwesenheit sollte n:m sein, weil ja ein Schüler an mehreren Tagen anwesend ist. Dagegen ist die n:m-Verknüpfung von Schüler zu Schulklasse etwas komisch? Ein Schüler ist doch in maximal einer Klasse.
Was ist der Sinn der Stundenplan-Tabelle? Die Verknüpfung Schulklasse <-> Lehrer enthält doch schon alles, was den Stundenplan betrifft, sollte also wirklich eine eigenständige Tabelle "Stunde" sein, wo dann noch der Tag fehlt. Die Planungstabelle an sich sieht dann fast identisch zu dieser Stunde-Tabelle aus, nur dass statt eines tatsächlichen Tages ein generischer Wochentag steht (oder falls es auch Zweiwochenrhythmus gibt was komplizierteres).

Abkürzungen sollte man in Tabellen wie in Variablennamen vermeiden. UntBeginn ist eigentlich unterricht_beginn.
Sc0uty
User
Beiträge: 9
Registriert: Dienstag 15. Januar 2019, 09:40

Erstmal soll ich mich bei euch, gerade bei Sirius in Namen der Gruppe bedanken Ihr seid echt eine große Hilfe, auf vieles wären wir wahrscheinlich gar nicht gekommen.
Wie schon geschrieben, solltest Du die Anwesenheit pro Stunde gar nicht in der Datenbank speichern, sondern bei Bedarf aus der Kombination der Tabellen Anwesenheit und Stundenzeit berechnen. Das kann auch als View direkt in der Datenbank modeliert werden, so dass man wie auf eine normale Tabelle darauf zugreifen kann.
Wenn ich das Richtig verstehe, sollen wir in der Anwesenheitstabelle nur noch ein Eintrag machen der jede Stunde dann überschrieben wird, weil du sagtest wir sollten nicht die Anwesenheit pro Stunde gar nicht speichern ?
Wird der Alte dann Gelöscht ist und ist dieser dann im View nicht mehr Abrufbar ? Weil sonst stellt sich die Zusammenfassung der Gesamtzeit als schwierig da wenn der Eintrag immer wieder Überschrieben wird.

Weil bei uns ist das Problem, das die Anwesenheit jede Stunde überprüft wird, deshalb auch die mehreren Stunden.

Dort stellt sich halt die Frage, wie ich im Python Programm, schreiben kann das er in die Std2 weiter springen soll, um den Eintrag dann dort zu setzten.

Ich hätte da halt so Vorstellungen, wie du es Angeben hast, nur das ich versuche die s.nummer variabel zu machen.
Sprich sowas wie ist current_date() zwischen AktuellesDatum 7:45 - 8:30, trage in der Ersten Stunde ein, vorbei ich dann ja für jede UStunde, einen extra Code schreiben müsste in einer Schleife oder nicht ?
[....]dass die Tabelle Anwesenheit zu jeder Schüler-ID die Anwesenheit als Zeitstempel inklusive Datum speichert. Dann kann man natürlich auch statt des heutigen Tages einen beliebig anderen Tag nehmen.
Wir haben die Idee mit dem Zeitstempel in die Anwesenheit übernommen.
Und wollen dies dann auch über die View lösen, so das wir gar nicht mehr die Zeit, irgendwie groß was Berechnen müssen sondern wie du schon sagtest einfach nur die TimeStamp hinzufügen müssen in die Anwesenheit und der Rest macht ja dein gezeigtes Beispiel was die Zeit des zu Spätkommens angeht.

Über ein Schedule Job, soll täglich eine neue Zeile mit dem Aktuellen Datum hinzugefügt werden, für jeden Schüler, mit dort die Anwesenheit für den Aktuellen Tag mit geschrieben werde kann ( Sei denn es gibt eine Sinnvollere Lösung).
Dagegen ist die n:m-Verknüpfung von Schüler zu Schulklasse etwas komisch? Ein Schüler ist doch in maximal einer Klasse.
Es wurde wohl so Eingetragen, weil es Differenzierungsunterricht gibt, sprich wo die Klasse aufgeteilt wird und mit theoretischen Parallelklassen zusammen gesteckt wird.
Sc0uty
User
Beiträge: 9
Registriert: Dienstag 15. Januar 2019, 09:40

Die Idee die ich habe zu dem Problem mit der Zweiten Dritten Stunde, und dem Eintrag ist folgende :

Was ist wenn man eine Extra Tabelle macht, wo es keine Rolle spielt ob man diesen Eintrag überschreibt weil dieser Automatisch mit der Anwesenheitstabelle der Stunden Nummer, und die Stundenzeit, und dann dementsprechend in die passende Stunde eingetragen wird.

In der neuen Tabelle müsste ja dann nur nochmal die Schüler ID stehen und ein aktueller Timestamp, und dann wird halt geguckt ist die Zeit <= 8:30 dann trage diesen TimeStamp bei der Schüler ID in Anwesenheitstabelle in der 2 Stunde ein, wenn = NULL.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Ich verstehe einiges, von dem was Du geschrieben hast nicht.
- In der Anwesenheitstabelle soll nichts überschrieben werden. Die Idee, etwas zu überschreiben wurde in einer Zeit geboren, als Rechenzeit teuer und Speicherplatz kostbar war. Ich hatte Dich so verstanden, dass sich der Schüler einmal am Morgen anmeldet und wenn er geht, wieder abmeldet. Jetzt lese ich es so, dass sich der Schüler für jede Stunde einmal anmeldet. Dann kann man ja gleich eine Verknüpfung zwischen Anwesenheit- und Stunde-Tabelle machen.
- Dann stellt sich mir aber auch nicht die Frage nach dem Springen. Die Suche ist einfach die Stunde die als nächstes aufhört: minimum(untericht_ende) von allen Stunden mit current_time < unterricht_ende.

Nochmal: eine Tabelle Anwesenheit, wo jede Stunde ihr eigenes Feld hat, ist nicht sinnvoll. Abfragen darauf sind zu kompliziert. Solche Tabellen überführt man am besten in eine Form, wo statt einzelner Felder einzelne Einträge benutzt werden.
Sc0uty
User
Beiträge: 9
Registriert: Dienstag 15. Januar 2019, 09:40

In der Anwesenheitstabelle soll nichts überschrieben werden. Die Idee, etwas zu überschreiben wurde in einer Zeit geboren, als Rechenzeit teuer und Speicherplatz kostbar war. Ich hatte Dich so verstanden, dass sich der Schüler einmal am Morgen anmeldet und wenn er geht, wieder abmeldet. Jetzt lese ich es so, dass sich der Schüler für jede Stunde einmal anmeldet. Dann kann man ja gleich eine Verknüpfung zwischen Anwesenheit- und Stunde-Tabelle machen.
Tut mir leid, dann hab ich mich nicht richtig Ausgedrückt, da die Anwesenheit aktuell jede Stunde genommen wird, soll das in dem Fall auch so sein.

Dann stellt sich mir aber auch nicht die Frage nach dem Springen. Die Suche ist einfach die Stunde die als nächstes aufhört: minimum(untericht_ende) von allen Stunden mit current_time < unterricht_ende.
Das mit dem Springen ist so gemeint, das dass Gerät beim Scannen weis in welche Stunde es eintragen muss. Wobei du oben ja schon einen Lösungsansatz geliefert hast.


Nochmal: eine Tabelle Anwesenheit, wo jede Stunde ihr eigenes Feld hat, ist nicht sinnvoll. Abfragen darauf sind zu kompliziert. Solche Tabellen überführt man am besten in eine Form, wo statt einzelner Felder einzelne Einträge benutzt werden.
Das haben wir soweit verstanden, wir suchen gerade nur nach einer passenden Lösung(für alle ich darf das leider nicht alleine Entscheiden).


Tut mir leid wenn ich Dinge nicht Eindeutig rüber gebracht habe.
Zuletzt geändert von Sc0uty am Donnerstag 17. Januar 2019, 14:14, insgesamt 3-mal geändert.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sc0uty: Noch ein Hinweis nebenbei: Was man auf jeden Fall machen sollte ist das speichern der An-/Abmeldevorgänge in einer Logdatei, also mindestens ID und Zeitstempel, wahrscheinlich noch wer für den Eintrag verantwortlich ist. Also zum Beispiel Scanner oder Person die diesen Eintrag gemacht hat, damit am Ende a) alles über diese unveränderbaren Daten nachvollziehbar ist wenn es Streitfälle gibt (Scanner defekt oder Uhr falsch eingestellt, Lehrer behauptet er habe da nichts manuell geändert, etc.) und b) man die Datenbank auch aus diesen Daten wieder aufbauen könnte in dem man diese Ereignisse sozusagen noch einmal neu ”abspielt”. Letzteres ist praktisch wenn die Datenbank kaputt gegangen ist, oder man das Schema ändert, und zum Testen von vorgegebenen Konstellationen, ob die die erwarteten Ergebnisse liefern.

Edit: Bezüglich DSGVO: Zu so einem Projekt gehören auch die Informations- und Dokumentationspflichten. Man muss Dokumentieren welche persönlichen Daten wie lange gespeichert und verarbeitet werden, wie die geschützt sind, und den Fall das jemand Auskunfts-, Lösch-, oder Korrektur-Anfragen stellt, berücksichtigen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten