Neustart Projekt

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

Aus den vorherigen Thread ist ersichtlich, dass ich Spatti code und falsche Namensgebungen hab.
D.h. ich fange nochmals neu an. :) und hoffe auf einwenig Hilfe wie die Strucktur sein könnte. Klar, nicht nur eine Lösungn ist die korrekte, aber mit einer guten Basis sollte man schon beginnen. :P

Log.txt sieht neu so aus:
Do;17.05.2018;07:02:59;Proj G
Do;17.05.2018;07:58:33;Proj C
Do;17.05.2018;11:58:58;MITTAG
Do;17.05.2018;12:26:01;Proj G
Do;17.05.2018;14:01:51;Proj I
Do;17.05.2018;15:40:05;Proj G
Do;17.05.2018;16:12:27;FEIERABEND
Fr;18.05.2018;07:00:20;Seminar A @Tag
Di;22.05.2018;07:00:01;Proj C
Di;22.05.2018;08:00:02;Proj G
Di;22.05.2018;09:29:00;Proj J
Di;22.05.2018;11:00:00;Proj I
Di;22.05.2018;12:00:00;MITTAG
Di;22.05.2018;12:30:00;Proj G
Di;22.05.2018;13:00:00;Proj B
Di;22.05.2018;15:00:04;Proj G
Di;22.05.2018;16:00:05;FEIERABEND
Mi;23.05.2018;07:02:08;Proj G
Mi;23.05.2018;07:20:20;Proj C
Mi;23.05.2018;07:23:46;Proj K
Mi;23.05.2018;07:49:29;Proj C
Mi;23.05.2018;07:49:38;Proj H
Mi;23.05.2018;07:49:47;Proj J
Mi;23.05.2018;07:49:50;Proj K
Mi;23.05.2018;11:22:06;Proj G
Mi;23.05.2018;11:51:40;MITTAG
Mi;23.05.2018;12:14:47;Proj G
Mi;23.05.2018;15:15:48;Proj I
Mi;23.05.2018;15:47:27;Proj C
Mi;23.05.2018;16:04:52;FEIERABEND
Do;14.06.2018;17:29:57;Seminar B @Tag

wypython als Gui sollte passen.
- Eintrag einer Zeile bassiert mit Button klick im MainFrame
- Die Buttons im MainFrame kommen aus der Log.txt datei. (Die letzen z.b. 20Eintrage, natürlich nur einmal aufgeführt.) Plus z.b. 3 Fixe wie Mittag, Feierabend und AddProject
Die Einträge in "log.txt" können analysiert werden. Kalender Start-Ende in einem z.b Grid. mit Angaben wie %Verteilung und h der Projekte über den Zeitraum, so wie die jeden Tages. D.h. es wird ein ChildFrame dafür benötigt.

Ich würde das so machen:
4 py-Dateien (MainFrame, ChildFrame, Filehaendler, Datahaender.)
jeder der Dateien hat eine Klasse.
Die Klasse MainFrame greift auf die Dataheander, die wiederum auf Filehaendler.

Idee dahinter ist. Filehandler liest die Anforderung welche aus Dataheander kommen aus. Dataheander rechnet sachen aus wie deltaTime oder Datum usw und übergibt das ganze an MainFrame. Mainframe spielt das dann ins Childframe bevor es dargestellt wird.

Einen Eintrag aus dem MainFrame(KlichButton) geht zum Datenhaender und dann zum Filehander. Grund: Feierabend ist z.b. time() +6min.

Dies hat den Vorteil, wenn ich mal von der txt Datei weggehe, muss ich den Rest nicht zu arg anpassen.

Ist das für das kleine Tool übertrieben? Die Namen der Klassen oder Dateien sind sicherlich falsch.


Ich habe mal angefange:
MainFramw.py

Code: Alles auswählen


class MainFrame(wx.Frame):
    def __init__(self, title):
        wx.Frame.__init__(self, None, title=title, pos=(7, 7),
                          size=(BREITE + 16, 12 * 27 + 330),
                          style=wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.RESIZE_BORDER)

        self.CL_Datamanager = Datamanager
def fix_button(self):
def filebutton(self):
def menu_bar(self):

def main():
Dataheander.py

Code: Alles auswählen

class Datamanager():
    def __init__(self):
        self.CL_Logfilemanager = Logfilemanager
def get_data_filter_log(self, startDate ,endDate ):
def get_last_jobs(self):
def get_info(self):
def parse_logfile(self, logfile):
def write_in_file(self, event, name_fix=None):
...
Logfilehaender.py

Code: Alles auswählen

class Logfilemanager():
def get_log_file(self):
def filter_log(self, startDate, endDate):
def get_last_entry(self):
def get_last_jobs(self,how_many_jobs):
def write_in_file(self, eintrag):
def open_log_datei(self, event):
ChildFrame.py

Code: Alles auswählen



class ChildFrameDatenliste(wx.Frame):
def grid_vormatieren(self, zeilenzahl):
def input_to_grids_frame_zusammenfassung(self, the_key, the_value, zeile, prozentualer_anteil, details_listen):

das ganz ist nicht komplett.

Jezt die Frage an euch, binn ich total daneben? :oops:
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Omm: Die erste Spalte der `Log.txt` sieht redundant aus. Der Wochentag ergibt sich ja aus dem Datum. Ich würde auch überlegen ob Datum und Uhrzeit auf zwei Spalten aufgeteilt werden sollten, denn beide zusammen beschreiben den Zeitpunkt.

Eine Datei pro Klasse ist eigentlich nicht was man in Python macht, denn eine Datei beschreibt ein Modul und wenn man in jedem Modul nur eine Klasse hat, verwendet man die Ebene der Module nicht zum organisieren des Projekts.

Die Klassen `Filehaendler` und `Datahaender` sollten den Zusatz Handler nicht haben, der bringt keinen Mehrwert.  Das `*manager` welches dann in den Codebeispielen steht ist da nicht besser. Das ist eine Nullaussage. Jede Klasse ist ein Manager, also könnte man das an jede dran pappen. Oder eben überall weg lassen. `File` und `Data` könnten dann vielleicht etwas präziser sein. `MainFrame` geht noch, aber `ChildFrame` ist ebenfalls viel zu generisch. Der Leser möchte ja nicht nur wissen, das es ein Kindfenster ist, sondern was die *Bedeutung* von diesem Fenster ist.

Mir sind die Zuständigkeiten von `Datahaender` und `Filehaendler` nicht ganz klar, beziehungsweise vermisse ich Datentypen für einzelne Einträge und eventuell einen Container-Datentyp der die dann enthält. Die beiden Handler-Klassen sind dann vielleicht auch nur eine Sammlung von Funktionen.

Wo kommt in der `MainFrame.__init__()` der `Datamanager` her? Und was soll das `CL_` beim Attributnamen? Und wenn Du es neu schreibst kannst Du die Namen nach dem Style Guide for Python Code gestalten.

Die `MainFrame`-Methodennamen würden eher zu Werten passen. Funktions- und Methodennamen haben in der Regel Tätigkeiten als Namen, die beschreiben was die Funktion oder Methode tut. `fix_button()` ginge noch wenn das eine Methode wäre die eine Schaltfläche irgendwie ”fixt”. `filebutton()` wäre dann aber shon sehr komisch. Beides wären gute Namen für `Button`-Objekte.

Position und Grösse eines Fensters sollte man nicht vorgeben. Für eine passende Platzierung ist die Fensterverwaltung des Systems zuständig. Wenn ich ein Fenster immer an der gleichen Stelle und in der gleichen Grösse haben möchte, dann stelle ich das auf Systemebene ein.

Beim `Datamanager` ist wieder die Frage wo da auf magische Weise `Logfilemanager` her kommt. Wobei mir jetzt langsam ein Licht aufgeht was das CL vielleicht bedeuten soll: `class`? Das hat da nichts zu suchen. Ich würde da einfach irgen ein aufrufbares Objekt erwarten, also zum Beispiel auch eine Funktion, denn an der Stelle ist es ja völlig egal ob das eine Funktion, Methode, oder Klasse ist, hauptsache beim Aufruf wird ein `Logfilemanager`-Objekt erstellt.

`Datamanager` und `Logfilemanager` scheinen mir momentan zu komplex. Ich würde einfach erst einmal mit einer Klasse anfangen. Wenn man später mehr als ein Datenformat unterstützen möchte, braucht man vielleicht auch gar nicht zwingend eine Klasse sondern Funktionen zum lesen/schreiben der verschiedenen Formate. KISS-Prinzip.

Ich würde bei der Umsetzung mit den einzelnen Datensätzen anfangen, und dann als nächstes eine Container-Klasse, welche die nötigen Operationen zur Verfügung stellen.

Mal so ungetestet als Ansatz:

Code: Alles auswählen

class LogEntry(object):
    
    TIMESTAMP_FORMAT = '%d.%m.%Y %H:%M:%S'
    
    def __init__(self, timestamp, text):
        self.timestamp = timestamp
        self.text = text
    
    @property
    def weekday(self):
        return format(self.timestamp, '%a')
    
    def as_row(self):
        return [format(self.timestamp, self.TIMESTAMP_FORMAT), self.text]

    @classmethod
    def from_row(cls, row):
        timestamp, text = row
        return cls(DateTime.strptime(self.TIMESTAMP_FORMAT), text)


class Log(object):

    DELIMITER = ';'
    
    def __init__(self, entries=()):
        self.entries = list(entries)
    
    def __iter__(self):
        return iter(self.entries)
    
    def save(self, filename):
        with open(filename, 'w') as csv_file:
            writer = csv.writer(csv_file, delimiter=self.DELIMITER)
            writer.writerows(e.as_row() for e in self)
    
    @classmethod
    def load(cls, filename):
        with open(filename) as csv_file:
            reader = csv.reader(csv_file, delimiter=self.DELIMITER)
            return cls(map(LogEntry.from_row, reader))
Falls so etwas wie `LastEntry` aus Deinem anderen Thema tatsächlich der letzte Eintrag sein sollte, kann man auch überlegen ob man dafür eine Methode hat, oder ob man da nicht einfach Indexzugriff implementiert, so dass man über ``log[-1]`` auf das letzte Element zugreifen kann. Man kann auch Datumsslices unterstützen und dann die Einträge zwischen zwei Zeitpunkten über ``log[start_date:end_date]``.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

Hallo blackjack

ertmal vielen Danke, dass Du mir hilfst.

Wenn ich Dich richtig verstehe, dann mache ich aus Datamanager und Logfilemanager eine Datei.
In diese sind dann die beiden Klassen (LogEntry und Log). Diese Datei nenne ich am besten neu log.py
Die trennung der Klassen ist für mich wichtig, denn am liebsten möchte ich auf eine sql zugreiffen. Jedoch momentan wäre ich damit komplett überfordert.
Die beiden Frame nehme ich ebenfals zusammen und mache daraus framegui.py. Da sind es dann die Klassen MainFrame und DetailsFrame.

Die Idee war, Logfilemanager nur für den Eintrag und das Auslesen der Datei zu benutzen.

Code: Alles auswählen

class Logfilemanager():
    def get_log_file(self):
        log = []
        with open(os.path.join('C:\\', 'Users', os.getenv('username'), 'log2.txt')) as logFile:
            for line in logFile:
                x = line.split(';')
                LDatum = datetime.date(datetime.strptime(x[1], "%d.%m.%Y"))
                LTime = datetime.strptime(x[2], "%H:%M:%S")
                logEntry = {'Weekday': x[0], "DateStr": x[1], "TimeStr": x[2], "ProjectName": x[3].strip('\n'), "DateNum": LDatum, "TimeNum": LTime}
                log.append(logEntry)
            return log

    def filter_log(self, startDate, endDate):
        log = self.get_log_file()
        logFiltered = []
        for entry in log:
            if (entry["DateNum"] >= startDate) and (entry["DateNum"] <= endDate):
                logFiltered.append(entry)
        return logFiltered

    def get_last_entry(self):
        return self.get_log_file()[-1]

    def get_last_jobs(self,how_many_jobs):
        return self.get_log_file()[:how_many_jobs]


    def write_in_file(self, eintrag):
        self.log_datei = open(os.path.join('C:\\', 'Users', os.getenv('username'), 'log2.txt'), 'a')
        self.log_datei.write(eintrag)
        self.log_datei.close()

    def open_log_datei(self, event):
        os.startfile(os.path.join('C:\\', 'Users', os.getenv('username'), 'log2.txt'))

Datamanager dann nur für Umformatierung oder Auswertungen/Berechnung usw. :

Code: Alles auswählen

how_many_jobs = 40
class Datamanager():
    def __init__(self):
        self.CL_Logfilemanager = Logfilemanager

    def get_data_filter_log(self, startDate ,endDate ):
        #startDate = datetime.date(datetime.strptime('01.05.2018', "%d.%m.%Y"))
        #endDate = datetime.date(datetime.strptime('12.05.2018', "%d.%m.%Y"))
        test2 = self.CL_Logfilemanager().filter_log(startDate, endDate)
        print(test2)

    def get_last_jobs(self):
        jobs = Logfilemanager().get_last_jobs(how_many_jobs)
        neueListe = []
        for item in jobs:
            x = item.get('ProjectName')
            if not x in neueListe and x != 'MITTAG' and x != 'FEIERABEND':
                neueListe.append(x)
        return neueListe

    def get_info(self):
        entry = self.CL_Logfilemanager().get_last_entry()
        return '{} {} {}\n{}'.format(*map(entry.get, ['Weekday', 'DateStr', 'DateStr', 'ProjectName']))

    def auswertung(self, event):
        print('a')

    def check_log_datei(self, logdatei):
        print('a')

    def parse_logfile_day(self, logfile):
        print('a')
    def parse_logfile(self, logfile):
        print('a')
    def blink(self):
        print('a')
    def show_log_datei(self, event):
        os.startfile(os.path.join('C:\\', 'Users', os.getenv('username'), 'log.txt'))

    def write_in_file(self, event, name_fix=None):
        locale.setlocale(locale.LC_ALL, 'de')

        entry_new_ProjectName = datetime.today().strftime("%a;%d.%m.%Y;%H:%M:%S;") + name_fix + "\n"
        Logfilemanager().write_in_file(entry_new_ProjectName)
Datahaender Loghaender sind Datamanager.py und Logfilemanager.py habe mich verschrieben. sorry
Aber eben, die gibt es bald nicht mehr. :lol:
Die Funktionen dann muss ich mir separat zu gemühte führen. Was hat es mit den @ aufsich? :oops:
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Omm: `Logfilemanager` ist keine Klasse. Du hast da Funktionen in eine Klasse gesteckt die einfach nur Funktionen auf Modulebene sein sollten.

Und `Datamanager` ist auch nicht wirklich sinnvoll eine Klasse.

Wie gesagt würde ich da noch nicht zwischen Unterscheiden. Ich würde das erst einmal auf der CSV-Datei machen und wenn eine Datenbank dazu kommen soll, kann man immer noch sehen wie man das am besten trennt. Oder ob man da am Ende nicht einfach für CSV und Datenbank jeweils eine unterschiedliche Klasse hat die eben die gleiche Schnittstelle bietet.

Das Wörterbuch mit den redundanten Daten pro Eintrag ist nicht schön. Da würde ich wie gesagt einen eigenen Datentyp für einführen. Und eventuell solltest Du auch überlegen ob die Aktivitäten wirklich 1:1 einem Logeintrag entsprechen sollten, oder ob die nicht besser eine Anfangs- und Endzeit haben sollten. Da kann man dann zum Beispiel auch ein `property()` schreiben das die Dauer berechnet und was man sonst noch so braucht.

Ich würde noch eine Spalte/ein Attribut für die Art eines Eintrags einführen statt das über spezielle Projektnamen zu regeln. Kann ja auch sein das man an einem Tag zwei grössere Lücken hat, die man dann ja schlecht beide MITTAG nennen kann. Oder generell sowohl Start als auch Ende eines Projekts protokollieren, statt das in den ”inneren” Protokolleinträgen implizit zu haben.

Es gibt Methoden auf `Datamanager` die ein Argument `event` bekommen – das klingt so nach GUI und hat da nichts zu suchen. Was soll `blink()` machen?

`@` ist an der Stelle Dekoratorsyntax. Schau Dir am besten mal die Dokumentationen zu den Funktionen `property()` und `classmethod()` an. Letzlich ist das `@` nur syntaktischer Zucker:

Code: Alles auswählen

@expression
def spam():
    pass

# <=>

def spam():
    pass

spam = expression(spam)
Du solltest das `csv`-Modul für CSV-Daten verwenden. Sonst kommt irgendwann einmal einer auf die Idee ein Semikolon in einem Projektnamen zu verwenden und das fällt auf die Nase.

`locale.setlocale()` sollte nur einmal im Programm aufgerufen werden und das auch sehr weit oben in der Aufrufhierarchie. Und dann auch eher nicht mit einem konkreten Wert sondern mit einer leeren Zeichenkette, damit die Systemeinstellung verwendet wird. Andere Werte sind nicht portabel!

``os.path.join('C:\\', 'Users', os.getenv('username'), 'log.txt')`` steht mehrfach im Programm. Ach nee das war ``os.path.join('C:\\', 'Users', os.getenv('username'), 'log2.txt')``. Subtiler Unterschied. Würde nicht passieren wenn man den Code der diesen Wert erstellen soll nicht mehrfach im Code stehen hätte. Und es macht auch nicht so viel Sinn `os.path.join()` zu verwenden wenn dann dort hart kodiert ein systemabhängiger Pfad drin steht.

`framegui` ist ein komischer Name. Ich würde es einfach nur `gui` nennen. Oder erst einmal gar nicht trennen. Wenn trennen, dann aber beide Module in ein Package stecken. Auf oberster Ebene sollte nur ein Modul- oder Packagename stehen.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

Hallo blackjack

das ist ja ein ganz anderer Ansatz. Keine txt sonder eine csv. mit Start und End eines Projektes.
Deines Argument mit der Lücke, macht Sinn. (Arztbesuche oder sonnst was)
Na gut. nochmals :lol: back to Start.

log.csv hätte dann nachstehende Spalten:
Nr (vortlaufende Zahl)
datum (Datum)
start_projekt (startzeit vom Projekt)
stop_projekt (stopzeit vom Projekt)
zeit_projekt (stop - start)
projekt_name (Projektname)

Tag fällt weg, dafür habe ich ja das Datum.
Wenn ich länger darüber nachdenke, merke ich, dass ich das schon lange hätte machen sollen.
blick() bringt den Mittagbutten zum blinken bringt. So als erinnerung. :)
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich denke das mit der Datei hast Du missverstanden: Du hast ja bereits eine CSV statt einer unstrukturierten Textdatei. Und die sollte auch weiterhin Logcharakter haben, sonst kann man nur abgeschlossene Zeiträume speichern statt Anfang und Ende dann wenn es gerade passiert zu speichern. Oder man muss immer wieder die komplette Datei neu schreiben. Nur die Datenstrukturen innerhalb des Programms müssen ja nicht 1:1 der Struktur in der Datei entsprechen.

Wozu ist die Nr-Spalte gut? Wen Datum und Zeiten getrennt sind, kann man kein Projekt über Mitternacht hinaus haben. Und man müsste mehr prüfen was an den Daten inkonsistent sein/werden kann. Ich würde da für beide Zeitpunkte wirklich den kompletten Zeitpunkt, also mit Datum speichern. Die Zeitspalte ist redundant, weil man die aus Anfang und Ende berechnen kann. Das ist auch wieder ein Punkt den man beim Einlesen prüfen müsste, oder aber ignorieren und neu berechnen, dann muss man es aber gar nicht erst speichern.

`blink()` hat doch gar keinen Zugriff auf die GUI‽ Das hat dort nichts zu suchen. Man kann da höchstens eine Methode anbieten mit der man prüfen könnte ob oder in wie viel Zeit ein Alarm fällig wäre.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Omm
User
Beiträge: 90
Registriert: Samstag 7. April 2018, 14:05

Danke für deine Geduld.
Ich bin mir sicher, manchmal kribelt es unter den Nägeln. Zumal du es sicher schon längstend erledigt hättest.

mir war nicht bewusst, dass das so auch eine csv Datei ist.
wenn ich das richtig verstehe, würde die Datei (log.txt) dann so aussehen:
(Datum mit StartZeit);Datum mit StopZeit);(ProjektName)
15.05.2018 11:46:00;15.05.2018 12:46:00;Projekt A

log.py mit der classe LogEntry und classe Log
gui.py mit classe MainFrame, deailauflistung in der selben classe.
Antworten