Simple Zeiterfassung

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
leinad
User
Beiträge: 9
Registriert: Freitag 14. September 2018, 20:08

Hallo liebe Leute,

ich bin ein absoluter Python-Anfänger, weiß aber genau, dass diese Sprache mein aktuelles Problem lösen kann.

Ich brauche ein Skript, dass mir zu der Eingabe einer 10-stelligen Zahl und Enter die Systemzeit und das Datum in einem Dokument abspeichert. Wenn diese Zahl noch einmal kommt, soll erneut die Zahl + Zeit + Datum in dem gleichen Dokument hinzugefügt werden. Wenn eine andere Zahl kommt ebenso...

Hintergrund ist eine einfach Zeiterfassung. Ich möchte ein RFID-Modul an einen Raspberry Pi anschließen um so die Arbeitszeiten von Mitarbeitern zu erfassen.

Ich bin für jede Hilfe dankbar.

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

Was hast Du versucht? Wo hast Du konkret ein Problem?
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@leinad: So ganz allgemein, ohne das Du ein konkretes Problem hast, kann man da eigentlich nur sagen: Zerlege das Problem in Teilprobleme, und die wieder in Teilprobleme, solange bis die Teilprobleme so klein sind, das sie sich mit einer Funktion mit wenigen Zeilen Code lösen lassen. Wenn Du so eine Funktion geschrieben hast, testest Du sie. Falls sie nicht funktioniert: Fehler suchen und beheben. Wenn sie dann am Ende funktioniert, kann man aus den kleinen Teillösungen grössere Teilllösungen zusammensetzen. Wieder mit Funktionen mit wenigen Zeilen Code. Und wieder testen, bevor man sie in anderen Teillösungen verwendet. Am Ende hat man dann ein Lösung für das Gesamtproblem.

Inhaltlich würde ich einen Blick auf das `csv`-Modul in der Standardbibliothek empfehlen für das Dateiformat. Das `datetime`-Modul für das ermitteln von den Zeitstempeln. Und dann solltest Du die Zeiten als UTC speichern und nicht als lokale Zeiten, denn sonst kann man bei der Umstellung von Sommer- und Winterzeit unangenehme Überraschungen erleben. Oder man dokumentiert deutlich dass das Programm damit nicht klar kommt. :-)

Um die UTC-Zeitstempel dann wieder in lokale Zeit umzurechnen, gibt es das (externe) `pytz`-Modul.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
leinad
User
Beiträge: 9
Registriert: Freitag 14. September 2018, 20:08

Hallo,
danke erst mal für die Antworten.

Dann beginne ich mal mit einem Problem eines Teilproblems:
Der Code

Code: Alles auswählen

import time
a = input()
if a == "0000229698":
	file = open("september18.txt","w")
	file.write("Manfred\n")
	file.write(time.strftime("%d.%m.%Y %H:%M:%S"))
	file.close()
	
elif a == "000400285":
	file = open("september18.txt","w")
	file.write("Benjamin\n")
	file.write(time.strftime("%d.%m.%Y %H:%M:%S"))
	file.close()
lässt nun zwar endlich zu, dass ich bei return keinen Fehler bekomme aber eine Ausgabe kriege ich dennoch nicht.
Was ist an der Syntax falsch? Wenn ich 0000229698 eintippe bekomme ich einen Syntax-Error.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@leinad: Du verwendest dann offenbar Python 2. Die `input()`-Funktion erwartet dort einen beliebigen gültigen Python-Ausdruck und wertet den aus. Und 0000229698 ist kein gültiges Zahlliteral in Python 2.7. Das wäre davor irgendwann mal gewesen, weil man da mit einer führenden 0 und nur Ziffern Oktalzahlen eingeben konnte. Das wurde durch das Präfix ``0o`` abgelöst (Null und kleines O).

Du willst aber gar keine Python-Ausdrücke sondern das der Benutzer (oder die Hardware die die RFID-Tokens erkennt) eine Zeichenkette eingibt. Dafür ist `raw_input()` da. Oder Du verwendest Python 3. Da ist `input()` das was bei Python 2 `raw_input()` war.

Fang am besten *gleich* an richtige Namen zu verwenden. Namen sollen dem Leser vermitteln was der Wert bedeutet. `a` ist absolut nichtssagend.

Die beiden Verzweigungen machen fast das gleiche. Weder Code noch Daten sollte man wiederholen. Das macht es nur schwierig da hinterher etwas anzupassen ohne das man etwas vergisst, oder bei einer der Kopien etwas ganz leicht anders macht. Der Dateiname sollte nur einmal im Quelltext stehen. Da kann man sich eine Konstante für definieren. Und der Code der in die Datei schreibt, unterscheidet sich nur durch den Benutzernamen. Den Benutzernamen mit einem Zeitstempel in eine Datei zu schreiben wäre ein Teilproblem das man mit einer Funktion lösen kann die den Benutzernamen als Argument übergeben bekommt.

Man möchte sicher nicht jedes mal wenn sich die Benutzer ändern, den Code ändern. Die Zuordnung von RFID zu Benutzername sind Daten. Hier würde sich ein Wörterbuch (`dict`) anbieten. Das kann man auch erst einmal als Konstante definieren. Später dann aber besser als Datei die am Anfang des Programmlaufs geladen wird.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
leinad
User
Beiträge: 9
Registriert: Freitag 14. September 2018, 20:08

Danke für Deinen Hinweis mit der Python-Version!!! Wenn ich den Code unverändert mit python3 laufen lasse, kommt genau das, was ich erwartet habe..

Wenn ich "a" gleich durch die richtigen Namen ersetze ist es schon übersichtlicher. Ich schreibe jetzt auch in unterschiedliche Dateien damit ich am Ende des Monats für jeden Mitarbeiter eine gesonderte Datei habe.

Der Code

Code: Alles auswählen

import time

if input() == "0000229698":
	file = open("ManfredSeptember18.txt","w")
	file.write("Manfred\n")
	file.write(time.strftime("%d.%m.%Y %H:%M:%S"))
	file.close()
	
elif input() == "000400285":
	file = open("BenjaminSeptember18.txt","w")
	file.write("Benjamin\n")
	file.write(time.strftime("%d.%m.%Y %H:%M:%S"))
	file.close()

ist also erst mal zu gebrauchen.

Benutzer und Zeitstempel in eine Funktion zu packen gelingt mir nicht und es kommt ein Syntax Fehler wenn ich schreibe:

Code: Alles auswählen

import time

if input() == "0000229698":
	file = open("ManfredSeptember18.txt","w")
	file.write("Manfred\n" time.strftime("%d.%m.%Y %H:%M:%S"))
	file.close()
	
elif input() == "000400285":
	file = open("BenjaminSeptember18.txt","w")
	file.write("Benjamin\n" time.strftime("%d.%m.%Y %H:%M:%S"))
	file.close()

Ich lasse den Code ja in meinem Terminal durch python3 /Phad/zur/Datei.txt laufen und Python wartet auf meine Eingabe. Wenn ich die 10-stellige Zahl und Enter eintippe, ist ja die Eingabe beendet. Kann ich irgendwie in meinem Programm schreiben, dass die Eingabe wieder geöffnet werden soll (wie "springe an den Anfang des Programms") oder bedarf es dafür eines extra Programmes?

Ein weiteres Problem ist, dass die Zeilen in den Dokumenten einfach überschrieben werden. Das ist natürlich nicht Sinn der Sache.

Im Prinzip kann ich ja file.write("BENUTZER\n") weg lassen, da die Dateien ja eh schon entsprechend benannt sind.


Ich denke mal es ist erkennbar, dass ich null Programmiererfahrung habe. Danke für die Hilfe!
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@leinad: Das wird so nicht funktionieren weil das `input()` als Teil jeder Bedingung neu aufgerufen wird. Das darf aber nur einmal vor den prüfungen passieren, denn man will ja *eine* Eingabe gegen mehrere Bedingungen prüfen, und nicht die erste Eingabe nur gegen die erste Bedingung, und falls die falsch ist, die zweite Eingabe gegen die zweite Bedingung. Genau so steht das aber jetzt im Code.

Du hast da nichts in eine Funktion gepackt. Damit war gemeint das Du eine Funktion definierst, der man den Namen übergibt, und die dann alles macht was da im Moment zweimal im Code steht. Also die Datei mit dem übergebenen Benutzernamen im Dateinamen öffnen, den Zeitstempel schreiben, und dann die Datei schliessen. Damit dieser Code nur *einmal* im Programm steht.

Wenn man Code wiederholt ausführen möchte nimmt man eine Schleife. Wenn das ”endlos” wiederholt werden soll, bietet sich eine ``while True:``-Schleife an.

Die Dateien werden überschrieben weil Du den Dateimodus mit 'w' so gewählt hast. Da gibt's auch noch andere. Zum Beispiel einen um Daten an eine vorhandene Datei anzuhängen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
leinad
User
Beiträge: 9
Registriert: Freitag 14. September 2018, 20:08

Schade, dass ich gerade keinen Zugang zu meinem Laptop habe sonst könnte ich weiter probieren. Es ist trotzdem in meinem Sinne, dass ich das "Problem" zeitnah lösen kann.

Könnte die while-Schleife so aussehen?

Code: Alles auswählen

while input() == benutzer:
	file = open(benutzer".txt","a")
	file.write(time.strftime("%d.%m.%Y %H:%M:%S"))
	file.close()
Dann stehe ich jetzt vor einem anderen Problem. Da das input ja nun nur einmal geprüft wird, muss ich vorher die ganzen Benutzer den einzelnen Ziffernfolgen zuordnen und dem Programm mitteilen. Wie kann ich das umsetzen?
Danke für Deine Geduld.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@leinad: Was ist denn `benutzer` hier? Von `input()` kommt ja eine RFID als Rückgabewert, das heisst `benutzer` müsste sinnvollerweise an eine Zeichenkette mit einer RFID gebunden sein. Dann wird `benutzer` aber (syntaktisch falsch) verwendet um den Dateinamen zu bestimmen. Soll das jetzt eine RFID im Dateinamen sein, oder der Benutzername?

Zudem hast Du mit dem ``while`` so wie es da steht, das Problem, dass man nur einen Benutzer erkennen kann. Du versuchst hier glaube ich zu viel auf einmal zu machen. Wie gesagt, zerlege das in Teilproblem die Du mit *Funktionen* löst, und teste die. Du hattest ja schon mal Code der deutlich näher daran war einen Benutzer zu erkennen. Wenn der in einer Funktion stehen würde, könntest Du diese Funktion verwenden um wiederholt Benutzer zu erkennen.

Zuordnungen von Daten kann man wie schon gesagt mit Wörterbüchern (`dict`) machen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
leinad
User
Beiträge: 9
Registriert: Freitag 14. September 2018, 20:08

Okay, dann will ich die RFID-Nummern den Namen zuordnen mit:

Code: Alles auswählen

benutzer = {"Manfred" : "0000229698", "Benjamin" : "000400285"}
Soweit sogut..

Was ist denn syntaktisch falsch? "benutzer" ist ja jetzt eine Variable, oder? Die einzelnen Dokumente enden aber alle mit ".txt" so dass ich mir gedacht haben, diesen Teil in Anführungszeichen zu setzen. Ich muss ja Python irgendwie mitteilen, dass es das "Manfred-Dokument" suchen und öffnen soll wenn die passende Nummer durch RFID eingegeben wird.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@leinad: Mit dem Wörterbuch machst Du es genau umgekehrt: Es werden Namen RFID-Nummern zugeordnet.

`benutzer` ist eine Variable. Man kann aber nicht einen Variablennamen und eine literale Zeichenkette einfach so nebeneinander schreiben:

Code: Alles auswählen

In [10]: benutzername = 'Manfred'

In [11]: benutzername'.txt'
  File "<ipython-input-11-a5a227897860>", line 1
    benutzername'.txt'
                     ^
SyntaxError: invalid syntax
Das ist kein gültiges Python, da weiss der Compiler nicht was er machen soll. Wenn Du die beiden Zeichenketten zu einer verbinden möchtest, musst Du das schon explizit sagen, in diesem Fall zum Beispiel mit dem ``+``-Operator (``benutzername + '.txt'``), oder der `format()`-Methode (``'{}.txt'.format(benutzername)``), oder ab Python 3.6 durch einen f-string (``f'{benutzername}.txt'``).
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
leinad
User
Beiträge: 9
Registriert: Freitag 14. September 2018, 20:08

Ich denke, es macht keinen Sinn, ohne dass ich es jetzt und hier probieren kann ob Codes laufen, weiterzumachen. Muss ich heute Abend weiter probieren.

Was würde mir denn erspart werden, wenn ich mit "def" eine Funktion definiere?

Code: Alles auswählen

benutzer = {"0000229698" : "Manfred", "000400285" : "Ben"}
Ist das für python nicht das gleiche wie die o.g. Zuordnung?
leinad
User
Beiträge: 9
Registriert: Freitag 14. September 2018, 20:08

Ok dank Ubuntu Phone konnte ich weiter probieren.

Code: Alles auswählen

import time
benutzer = {"0000229698" : "Manfred", "000400285" : "Ben"}

while input() == benutzer:
	file = open("benutzer + '.txt'","a")
	file.write(time.strftime("%d.%m.%Y %H:%M:%S"))
	file.close()

Dieser Code ergibt keine Fehler aber auch kein Dokument als Output. Kann aber auch sein, dass Ubuntu Touch die Fehler irgend wo anders abspeichert.

Ich denke aber, dass der o.g. Code immer noch nicht richtig ist. Mit dem dict gebe ich doch nur Synonyme für die einzelnen Nummern an. Jetzt weiß das Programm doch immer noch nicht, dass es die Nummer als input kriegt und dann das Synonym aus dem Wörterbuch in den Dateinamen schreiben soll. Ich weiß aber leider auch nicht, wie ich es dazu bringe..

Oh man, das ganze ist schwerer als gedacht. Mein Turbo pascal-Schul-Wissen ist nun auch schon 12 Jahre alt...
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@leinad: Das vergleicht jetzt eine Zeichenkette mit einem Wörterbuch auf Gleichheit. Das ergibt grundsätzlich `False` weil es keine Zeichenkette gibt die gleich einem Wörterbuch ist.

Den Dateinamen bildest Du auch falsch. Die Datei hätte jetzt den Namen benutzer + '.txt'. Also wirklich genau diese Zeichenfolge.

Die fehlt Grundlagenwissen das man nicht durch raten bekommt, sondern in dem man ein Tutorial durcharbeitet. Zum Beispiel das in der Python-Dokumentation. Und wirklich durcharbeiten. Also nicht einfach nur lesen, sondern das was da steht auch ausprobieren, verändern, in der Dokumentation Details nachlesen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
leinad
User
Beiträge: 9
Registriert: Freitag 14. September 2018, 20:08

Okay, danke für die Hinweise.

Wenn ich mal ein zufriedenstellenden Code habe, schreibe ich ihn selbstverständlich hier rein.
leinad
User
Beiträge: 9
Registriert: Freitag 14. September 2018, 20:08

Guten Abend!

Code: Alles auswählen

import time

while 1 == 1:
	file = open(input(), "a")
	file.write(time.strftime("%d.%m.%Y %H:%M:%S") + '\n')
	file.close()
Das ist der Code, der genau das hervorbringt, was ich ursprünglich wollte. Es werden nun zwar keine Namen abgespeichert aber ich weiß ja, wer welche Kartennummer hat...

"while 1 == 1" ist bestimmt nicht sehr elegant und man könnte dem auch noch einen Sinn geben aber soweit durchschau ichs noch nicht...
Und einen Vorteil hat die Sache: 1 ist und bleibt 1 sodass die Schleife wohl nie abbricht.
Danke für die konstruktiven Hinweise!
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@leinad: Die Bedingung ist in der Tat nicht elegant. Man scheibt ja auch nicht ``x = 1 + 1`` wenn man ``x = 2`` schreiben könnte. ``1 == 1`` hat ein Ergebnis: `True`. Das Zeilenende kannst Du auch gleich mit in das Argument von `strftime()` schreiben.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten