Callmonitor für die FritzBox

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,

was man am Wochenende, wenn man eigentlich lernen sollte, so alles bastelt.
Ein kleines, wirklich kleines, Projekt von mir, das ich schonmal angefangen hatte, aber irgendwie immer zu unflexibel war.
Bin jetzt auf die Idee gekommen, einfach eine Art Framework zu basteln, mit dem man, sofern man den Python kann, alles beliebig anpassen kann.
Das mitgelieferte Beispiel kann man verwenden, wenn man das ganze einfach nur benutzen möchte.
Es zeigt über libnotify eine kleine Meldung an, wenn jemand anruft, wenn angerufen wird, wenn angenommen wird und wenn wieder aufgelegt wird.
Vielleicht kann es ja jemand gebrauchen.
In meinem erst kürzlich eingerichteten Blog habe ich eine Seite für angelegt:
http://www.linuxmaxi.de.tc/projekte/fbcallnotify/
Der Code liegt auf GitHub:
https://github.com/koehlma/fbcallnotify

Was sagt ihr dazu?

Viele Grüße
Maxi
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Code: Alles auswählen

def parse(string, connection):
    for parser in _PARSER:
        if _PARSER[parser] in connection:
Schau dir mal `dict.iteritems` an.

`_PARSER` ist kein besonders guter Name für die Information, die da drin gespeichert wird. Man sollte nur durch angucken des Namens wissen, welche Informaton in einer Variable stehen. Und an `_PARSER` ist sicherlich kein Parser gebunden, sondern irgendwelche Informationen für einen Parser, deren Sinn aufgrund der nutzlosen Benennung allerdings nicht erkennbar ist :-)

`duration` sollte man vielleicht auch einen besseren Namen geben, sodass erkenntlich ist, dass das Teil Sekunden nimmt und ein (Stunden, Minuten, Sekunden)-Tupel zurückgibt.

Genauso mit `libnotify`, das könnte man vielleicht einfach `notify` nennen. Übrigens sollte `titel` wohl eher `title` heißen.

Bei Default-Argumentwerten macht man keine Leerzeichen zwischen das = und den Wert, also nicht `icon = None` sondern `icon=None`.

`shell` könnte man vielleicht auch eindeutiger nenen, wobei ich vorschlagen würde, da komplett drauf zu verzichten und direkt `subprocess.Popen` zu nutzen. `shlex` würde ich da auch aus dem Spiel lassen. Gib den Befehl doch einfach als Liste an.

In `fbcallnotify/monitor.py` sind die Dictionary-Elemente (ab Z. 42) ein Leerzeichen zu weit eingerückt.

Zeile 47 (`if condition == a or condition == b`) lässt sich schreiben als`if condition in {a, b}`.

Nichtkonforme Einrückung z.B. auch ab Z. 58, 67, 87, 92, 97, 102.

Der Typcheck in Z. 130 ist ein wenig seltsam, allerdings gibt es da auch keine schöne Lösung für. Ich sehe zwei Möglichkeiten, ein Objekt darauf zu prüfen, ob es eine kompilierte Regular Expression ist:

Code: Alles auswählen

import re
RE_TYPE = type(re.compile(''))
...
if isinstance(obj, RE_TYPE):
   ...
oder

Code: Alles auswählen

if hasattr(obj, 'match')
in der Annahme, dass nur Strings oder Regular Expressions verwendet werden.

Z. 131 (`if not foo: return False`) lässt sich ausdrücken als `return foo`.

143: Bei Keyword-Argumenten verwendet man auch keine Leerzeichen zwischen Name, = und Wert.

Sieht ansonsten ziemlich gut strukturiert aus, wobei mir noch nicht klar ist, wozu man da jetzt GObject braucht.
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Danke für die schnelle Antwort.
Habe die Design-Bugs ausgebessert.
Z. 131 (`if not foo: return False`) lässt sich ausdrücken als `return foo`.
Das verstehe ich nicht so ganz.
Ich prüfe hier ja mit Regular Expression soll ich da nochmal neu prüfen für die Rückgabe?

GObject habe ich auch entfernt. Jetzt kommt asyncore zum Einsatz.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

maxi_king_333 hat geschrieben:
Z. 131 (`if not foo: return False`) lässt sich ausdrücken als `return foo`.
Das verstehe ich nicht so ganz.
Ich prüfe hier ja mit Regular Expression soll ich da nochmal neu prüfen für die Rückgabe?
Sorry, Denkfehler. Ich dachte da stünde sowas wie

Code: Alles auswählen

if not condition:
    return False
else:
    return True
was sich als

Code: Alles auswählen

return not condition
ausdrücken ließe.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

`parse`: Es ist nicht klar, was `connection` sein soll. Aus dem Code der Funktion könnte man erahnen, dass das ein String(-ähnliches) Objekt ist. Aber warum heißt es dann "connection"? Da vielleicht einen hilfreicheren Namen ausdenken.

`convert_seconds` ist auch nicht so glücklich, weil man nicht weiß, zu was denn jetzt die Sekunden konvertiert werden.

Was passiert, wenn der `gi`-Import fehlschlägt? Dann hat man kein `notify`? Bisschen doof :-)

Diese `info`-Liste ist auch nicht so schön, weil man (ohne den ganzen Code gelesen zu haben) nicht versteht, was in den einzelnen Stellen der Liste für Informationen stehen (`info[1]`, `info[2]`) usf. Ich sehe da folgende Möglichkeiten:
  • Vielleicht ist es möglich, immer nur eine Information weiterzugeben, dann brauchst du keinen Container
  • Du verwendest ein Dictionary für die einzelnen Elemente (damit bekommen sie Namen)
  • Du verwendest ein `collections.namedtuple`, falls die Daten nicht veränderbar sein müssen
monitor.py: `if hours != 0` kann man auch schreiben als `if hours`, weil `0` auf booleanisch `False` ist.
fbcallnotify/monitor.py, 106: `type(foo) == bar` sollte man schreiben als `isinstance(foo, bar)`

Der Code von `_ring`, `_call`, `_disconnect`, `_connect` ist genau das selbe, nur mit verändertem Index in dieser `info`-Struktur. Da könntest du im Rahmen der Einführung einer neuen Datenstruktur für diese `info` (was auch immer für ne Info das ist, ein besserer Name wäre auch da angebracht) versuchen, den Code auf eine einzige Methode zu reduzieren, die du dann mit anderen Parametern aufrufst.
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

`parse`: Es ist nicht klar, was `connection` sein soll. Aus dem Code der Funktion könnte man erahnen, dass das ein String(-ähnliches) Objekt ist. Aber warum heißt es dann "connection"? Da vielleicht einen hilfreicheren Namen ausdenken.
Diese `info`-Liste ist auch nicht so schön, weil man (ohne den ganzen Code gelesen zu haben) nicht versteht, was in den einzelnen Stellen der Liste für Informationen stehen (`info[1]`, `info[2]`) usf. Ich sehe da folgende Möglichkeiten:

Vielleicht ist es möglich, immer nur eine Information weiterzugeben, dann brauchst du keinen Container
Du verwendest ein Dictionary für die einzelnen Elemente (damit bekommen sie Namen)
Du verwendest ein `collections.namedtuple`, falls die Daten nicht veränderbar sein müssen
Der Code von `_ring`, `_call`, `_disconnect`, `_connect` ist genau das selbe, nur mit verändertem Index in dieser `info`-Struktur. Da könntest du im Rahmen der Einführung einer neuen Datenstruktur für diese `info` (was auch immer für ne Info das ist, ein besserer Name wäre auch da angebracht) versuchen, den Code auf eine einzige Methode zu reduzieren, die du dann mit anderen Parametern aufrufst.
Das ganze verhält sich so:
Ist ja ein Callmonitor, welcher sich mit der FritzBox verbindet.
Diese sendet Zeilen-Weise einen Semikolon separierten String, welcher mit split(';') getrennt und dann info genannt wird.
Dieser unterscheidet sich jedoch zwischen ring, call, connect und disconnect, daher die Separation.
Mit diesen Informationen wird dann ein vereinheitliches Dictionary gebildet, das ich connection nenne.
Dieser Name kommt daher, da es ja eine Telefon-Verbindung ist.
Jemand ruft an bzw. wird angerufen - Verbindungsanfrage.
Angenommen - Verbindung wird angenommen.
Abgelehnt/Aufgelegt - Verbindung wird beendet.
Ich könnte vielleicht für connection einen eigenen Datentyp schaffen (Daten müssen veränderbar sein).
`convert_seconds` ist auch nicht so glücklich, weil man nicht weiß, zu was denn jetzt die Sekunden konvertiert werden.
Hm... Du hast recht, aber mir fällt da nichts ein.
Vielleicht s_to_hms?
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Danke für die Erklärung, aber ich möchte hier an den Spruch "guter Code braucht keine Dokumentation" erinnern -- dein Code muss so selbsterklärend sein, dass du genau solche Erklärungen nicht brauchst.

Das Dictionary ist denke ich mal schon in Ordnung, nur solltest du `connection` durch etwas eindeutigeres ersetzen.

Für das Sekunden-zu-Stundenminutensekunden fällt mir ehrlich gesagt auch nichts ein. Du könntest aber folgenden Trick verwenden: Lass' die Funktion einfach direkt die menschenlesbare Zeitdauer-Angabe zurückgeben, dann kannst du es `human_readable_duration` nennen :-)
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Danke für die Erklärung, aber ich möchte hier an den Spruch "guter Code braucht keine Dokumentation" erinnern -- dein Code muss so selbsterklärend sein, dass du genau solche Erklärungen nicht brauchst.
Ist halt so eine Sache, der Code beinhaltet ja nicht das vom Server empfangene.
Aber ich denke, ich sollte vielleicht ein paar Kommentare einbauen.
Für das Sekunden-zu-Stundenminutensekunden fällt mir ehrlich gesagt auch nichts ein. Du könntest aber folgenden Trick verwenden: Lass' die Funktion einfach direkt die menschenlesbare Zeitdauer-Angabe zurückgeben, dann kannst du es `human_readable_duration` nennen :-)
Und was ist, wenn ein Engländer das benutzen möchte, so ist es halt international.
Ich könnte es auch int_seconds_to_tuple_hours_minutes_seconds nennen ;).

Werde mal eine Nach drüber schlafen, vielleicht fallen mir dann bessere Namen ein.
Vielen Dank auf alle Fälle schonmal für die Hilfe und Aufklärung.
/* Edit:
Habe jetzt doch noch was verändert: nun gibt es die überflüssigen Funktionen, die im Grunde das gleiche machen, nicht mehr.
*/
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Folgendes (aus ``monitor.py``) ist ja wohl auch ziemlich redundant:

Code: Alles auswählen

def ring(connection):
    string = fbcallnotify.extensions.parse('von %FROMNUMBER% zu %TONUMBER%', connection)
    fbcallnotify.extensions.notify('Callmonitor', 'Eingehender Anruf', string)

def call(connection):
    string = fbcallnotify.extensions.parse('von %FROMNUMBER% zu %TONUMBER%', connection)
    fbcallnotify.extensions.notify('Callmonitor', 'Ausgehender Anruf', string)

def connect(connection):
    string = fbcallnotify.extensions.parse('von %FROMNUMBER% zu %TONUMBER%', connection)
    fbcallnotify.extensions.notify('Callmonitor', 'Angenommen', string)
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

So, habe nun die Struktur überarbeitet, die redundanten Funktionen entfernt und die Namen korrigiert.
Dabei ist mir wieder aufgefallen wir geil Linux, einige Systemdienste und Python doch sind.
Über dbus wird meine Musik, sofern mich jemand anruft und diese läuft, angehalten und wieder wiedergeben.
Für dbus musste ich jedoch von Python 3 wieder zurück auf Python 2, aber das macht ja nichts, solange es funktioniert.
Und da eh schon Python 2 habe ich mir überlegt, das die Notifications auch über pynotify angezeigt werden können.
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,

nun kann auch ein Telefonbuch verwendet werden, was eine sehr komplizierte Funktion nötig macht.
Ich habe leider überhaupt keine Ahnung, ob das so der richtige Weg ist...
Da es für eine ortsinterne Nummer ja soviele Möglichkeiten gibt:
+49 6547 98765
0049 6547 98765
0 6547 98765
98765
Nun möchte ich aber in meinem Telefonbuch nicht jede Möglichkeit anlegen.
Daher gebe ich eine der ersten beiden ein und dem Skript die Vorwahl und die Landes-Vorwahl.
Dieses verarbeitet dann die Einträge und baut für jedes ein Regular Expression, dass dann alle Möglichkeiten abdeckt.
Wenn nun ein Anruf reinkommt oder rausgeht, wird geprüft, ob er zu einer der Nummern passt.
Hier mal der Code:

Code: Alles auswählen

import re

_country_prefix = re.compile('^(00([0-9]{2})|\+([0-9]{2}))([0-9]*)$')

def init_numbers(area_code, country_code='49'):
    global _area_number
    global _country_code
    global _area_code
    _country_code = country_code
    _area_code = area_code
    _area_number = re.compile('^%s?([0-9]*)$' % (area_code))

def _convert_number(number):
    country_prefix = _country_prefix.match(number)
    if country_prefix.group(2):
        country = country_prefix.group(2)
    else:
        country = country_prefix.group(3)
    if country == _country_code:
        area_number = _area_number.match(country_prefix.group(4))
        if area_number:
            return re.compile('^((\+%s|00%s|0)%s)?%s$' % (country,
                                                          country,
                                                          _area_code,
                                                          area_number.group(1)))
        else:
            return re.compile('^(\+%s|00%s|0)%s$' % (country,
                                                     country,
                                                     country_prefix.group(4)))            
    else:
        return re.compile('^(\+%s|00%s)%s$' % (country,
                                               country,
                                               country_prefix.group(4)))
Was haltet ihr davon? (und von dem restlichen Code)
Freue mich auf Verbesserungs-Vorschläge.

Viele Grüße
Maxi
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Da sind immer noch magische Indices drin, `call[4]` zum Beispiel. Das weiß kein Mensch, was das zu bedeuten hat, und du in zwei Wochen auch nicht mehr :-)
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Da sind immer noch magische Indices drin, `call[4]` zum Beispiel.
Da ich info und connection in call umbenannt habe, habe ich hier das gleiche Problem wie vorher.
Ist eben ein Resultat von split(';') und da die Werte dann im Call-Dictionary Schlüsseln zugeordnet werden, denke ich, dass ich schon noch weiß, was was ist.
Der restliche Code arbeitet dann mit den Schlüsseln, daher sollte das kein Problem sein.
(Diese Projekt lag über ein Jahr auf meiner Festplatte rum, bis ich es zu neuem Leben erweckt habe.
Hatte da gerade mit Python angefangen und habe nun direkt wieder gewusst, was da was ist, eben wegen der Schlüssel.
Damals wurde es extrem unflexiebel über grausige Konfigurations-Dateien konfiguriert.
Und ich wollte ein GUI drüberbauen, http://www.python-forum.de/viewtopic.ph ... 79#p158979)
Da mach ich mir eher sorgen, dass ich in 2 Wochen keine Ahnung mehr habe, was _conver_number da genau macht.
Daher hatte ich ja extra nochmal den Qellcode hier hin kopiert, damit Ihr Euer Voting dazu abgeben könnt und ich ihn perfektionieren kann.
Du hast ja selber gesagt,
aber ich möchte hier an den Spruch "guter Code braucht keine Dokumentation" erinnern
.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

maxi_king_333 hat geschrieben:Ist eben ein Resultat von split(';') und da die Werte dann im Call-Dictionary Schlüsseln zugeordnet werden, denke ich, dass ich schon noch weiß, was was ist.
Ich aber nicht! Und ich möchte den Code vielleicht auch lesen und verstehen. Das Problem mit der Liste, von der keiner weiß, was die Elemente bedeuten, wäre ja gar nicht so gravierend, wenn die Liste nicht an verschiedenen Stellen verwendet würde, weil man dann einfach

Code: Alles auswählen

gutebeschreibung1, gutebeschreibung2, ... = foo.split(';')
machen könnte. Deine seltsame Liste wird aber rumgereicht und man muss für das Verstehen einer Codestelle, in der `call[n]` auftritt, genau verstehen, wie `call` zustande kommt. Und wenn es dann auch noch im Laufe des Programms umbenannt wird, wirds ziemlich hoffnungslos :-)
aber ich möchte hier an den Spruch "guter Code braucht keine Dokumentation" erinnern
Na dein Code braucht aber Dokumentation, weil man ja nicht versteht, was die Elemente für ne Bedeutung haben...
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

maxi_king_333 hat geschrieben:Ist eben ein Resultat von split(';') und da die Werte dann im Call-Dictionary Schlüsseln zugeordnet werden, denke ich, dass ich schon noch weiß, was was ist.
Ach ja, da werden Erinnerungen wach. Die Erfahrung von magischen Zahlen, deren Bedeutung man mit absuluter Sicherheit niemals vergisst, haben hier sicher alle schon gemacht. Du in wenigen Wochen wahrscheinlich auch ^^
Das Leben ist wie ein Tennisball.
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Dauerbaustelle hat geschrieben:Ich aber nicht! Und ich möchte den Code vielleicht auch lesen und verstehen. Das Problem mit der Liste, von der keiner weiß, was die Elemente bedeuten, wäre ja gar nicht so gravierend, wenn die Liste nicht an verschiedenen Stellen verwendet würde, weil man dann einfach

Code: Alles auswählen

gutebeschreibung1, gutebeschreibung2, ... = foo.split(';')
machen könnte. Deine seltsame Liste wird aber rumgereicht und man muss für das Verstehen einer Codestelle, in der `call[n]` auftritt, genau verstehen, wie `call` zustande kommt. Und wenn es dann auch noch im Laufe des Programms umbenannt wird, wirds ziemlich hoffnungslos :-)
Das ist aber so leider nicht möglich, da ich gutebeschreibung2 kennen muss um die restlichen Namen zu haben.
Daher brauche ich wohl oder übel die "magischen Zahlen".
Außer es gäbe eine magische Python-Funktion. (vielleicht gibt es die ja sogar)
Rumgereicht wird die Liste auch nicht, sie wird nur an ein Objekt von Call übergeben, das die Liste in ein Dictionary(sich selbst) umwandelt, welches dann rumgeht.
Und das es umbenannt wird, stimmt so nicht.
Es heißt überall call, wobei call ein Objekt der Klasse Call ist bzw. temporär das Ergebnis von split(';').
Damit man sieht, das das Objekt von Call und der gesplittete String zusammengehören.
EyDu hat geschrieben:Ach ja, da werden Erinnerungen wach. Die Erfahrung von magischen Zahlen, deren Bedeutung man mit absuluter Sicherheit niemals vergisst, haben hier sicher alle schon gemacht. Du in wenigen Wochen wahrscheinlich auch ^^
Wie gesagt, der Code mit den magischen Nummern ist über ein Jahr alt.
Die Nummern sind ja auch nicht irgendwie willkürlich nur um irgendwelche Programm-Internen-Daten zu speichern, sondern sie kommen von Außen.
Wäre es Programm-Intern und ich könnte sie beliebig verändern, dan würde ich natürlich keine Nummern nehmen.

Nichts nichtsdestotrotz habe ich mal 2 Kommentare, die die Struktur erklären eingefügt.
Würde das gerne auch ohne die "magischen Nummern" lösen. - Weiß aber nicht wie...
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Okay das Protokoll ist echt ein wenig... alternativ.

Hier mal ein Vorschlag. Die definierst erstmal eine abstrakte Datenstruktur für Anrufe.

Code: Alles auswählen

class BaseCall(object):
  def __init__(self, from_, to, contacts=contacts.Contacts()):
    self.from_ = from_
    self.to = to
    self.active = False
    self.contacts = contacts

  def get_from_name(self):
    return self.contacts.resolve(self.from_)

  def get_to_name(self):
    return self.contacts.resolve(self.to)

  def on_connect(self):
    self.active = True

  def on_disconnect(self, duration):
    self.active = False
    self.duration = duration
Dann jeweils eine für ausgehende und ankommende.

Code: Alles auswählen

class IncomingCall(BaseCall):
  def __init__(self, from_, to, over_intern):
    super(IncomingCall, self).__init__(from_, to)
    self.over_intern = over_intern

class OutgoingCall(BaseCall):
  def __init__(self, from_, to, over_extern):
    super(OutgoingCall, self).__init__(from_, to)
    self.over_extern = over_extern
Mehr brauchst du in der Datenstruktur erstmal nicht. Die Parsing-Sachen einfach in Funktionen reinstopfen, die haben nicht wirklich was mit der Datenstruktur eines Anrufs zu tun (bin mir da aber nicht 100% sicher :-).

Jetzt zum richtigen Spaß. Dieses Protokoll. Das Auspacken des `split`-Ergebnisses wäre in Python 3 schöner als in Python 2:

Code: Alles auswählen

timestamp, event, *data = line.split(';')
Nun geht das aber nicht in Python 2, deswegen hier der Workaround:

Code: Alles auswählen

data = line.split(';')
timestamp = line.pop(0)
event = line.pop(0)
(Wer hier ne schönere Idee hat, immer her damit!)

Jetzt weiß man schonmal, welche Bedeutung das erste und das zweite Element der Liste haben. Jetzt das muss man das ja auch noch dispatchen:

Code: Alles auswählen

handler = getattr(self, 'handle_%s' % event)
handler(*data)
Das ruft z.B. bei einem "connect" die Methode `handle_connect` mit den Parametern in `data` auf.

Und dann braucht man auch noch Implementation für die ganzen Events:

Code: Alles auswählen

def handle_call(self, id, over_intern, from_, to):
  self._calls[id] = IncomingCall(from_, to, over_intern)
  self._invokel_callbacks('call', id)

def handle_ring(self, id, from_, to, over_extern):
  self._calls[id] = OutgoingCall(from_, to, over_extern)
  self._invoke_callbacks('ring', id)

def handle_connect(self, id, over_intern):
  self._calls[id].on_connect()
  self._invoke_callbacks('connect', id)

def handle_disconnect(self, id, duration):
  self._calls[id].on_disconnect(duration)
  self._invoke_callbacks('disconnect', id)
  del self._calls[id]
(Ich habe grade keine gute Idee, wie man das ständige `_invoke_callbacks` loswerden kann, wegen dem `del` bei Disconnects.)

Hoffe, dass inspiriert. :-)
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,

sorry, dass ich mich jetzt erst melde, hatte mir durch ein Update auf Gnome 3 das System zerschossen.
In der Tat, war das eine Inspiration und es ist auch etwas bei rumgekommen:
https://github.com/koehlma/fbcallnotify ... monitor.py
Nur ist mit das irgendwie zu kompliziert, mit den Vererbungen, hasattr und getattr, jedoch weiß man jetzt, was was ist.
Da kam mir die Idee das ganze mit Regular Expression zu lösen, mich würde mal interressieren, was Ihr (Du) davon haltet.
Kann das ja durch das Versions-Management jederzeit rückgängig machen.
Ich finde diese Lösung sehr gut:
Man weiß was was ist, durch ein Dictionary.
Keine eigenen Daten-Typen mehr.
Updaten der Informationen durch die Dictionary eigene update()-Funktion.

Viele Grüße
Maxi
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Das mit den Regular Expressions finde ich ziemlich dämlich. Du gewinnst dadurch gar nix außer Unleserlichkeit. Das Datenformat ist so simpel, dass ein manueller "Parser" (der ja im Wesentlichen aus einem `.split(';')` besteht) viel verständlicher ist. Die Regular Expressions sind so vollgestopft von "Rauschen", dass man nicht erkennen kann, welches Format die Eingangsdaten überhaupt haben.

Welche Bedeutung hat eigentlich `Callmonitor._functions`? Konnte das nirgendwo sonst finden, und die Bedeutung wird dank nichtssagendem Namen auch nicht klar.

Übrigens ist durch das geregexe die `handle_read`-Methode wieder total unübersichtlich geworden.

`add_callbacks` und `add_actions` sehen mir verdächtig ähnlich, vielleicht kann man davon eines eliminieren oder alternativ `add_action(filter, action)` definieren durch `add_callbacks(filter, action.ring, ... action.disconnect)`.

Methoden würde ich übrigens wie folgt anordnen: In "Einheiten" bündeln und innerhalb dieser Einheit nach logischer Abfolge/Abhängigkeit. Also z.B. `main` und dann `main_quit` direkt nach `__init__`, und `handle_close` nach `handle_read`.

Zur Kontakt-Datenbank: Die "späte" Initialisierung würde ich ganz rausnehmen und dieses bisschen Code auf Modulebene stattfinden lassen. Das `__init__` einer Klasse sollte nichts tun als den Zustand des neu erstellen Objektes initialisieren, also Attribute setzen und so fort. Das heißt aber, dass dort _keine_ Logik stattfinden soll, und schon gar nicht solche, die Seiteneffekte hat (Dateien lesen). Das würde ich in eine Methode auslagern und die dann von mir aus direkt nach Erstellen des Objektes aufrufen.
maxi_king_333
User
Beiträge: 110
Registriert: Freitag 25. Dezember 2009, 03:42

Hi,
Das mit den Regular Expressions finde ich ziemlich dämlich. Du gewinnst dadurch gar nix außer Unleserlichkeit. Das Datenformat ist so simpel, dass ein manueller "Parser" (der ja im Wesentlichen aus einem `.split(';')` besteht) viel verständlicher ist. Die Regular Expressions sind so vollgestopft von "Rauschen", dass man nicht erkennen kann, welches Format die Eingangsdaten überhaupt haben.
Hmm...
Ich finde das mit den Funktionen und den Parametern irgendwie unleserlich (mag daran liegen, dass ich noch nicht soviel Ahnung habe).
Wobei ich sagen muss, das mit den Regular Expressions ist auch nicht das gelbe vom Ei.
Es gab schonmal ein ähnliches Projekt, das allerdings die Notifications für alle Nummern anzeigt.
Die haben das so gelöst: http://pynogio.cpegel.de/trac/browser/pynogio/event.py
Dieses Event Objekt wird jedesmal erzeugt und dann hier von evaluate verarbeitet: http://pynogio.cpegel.de/trac/browser/p ... manager.py
Dort wird auch mit den magischen Nummern gearbeitet, wobei das Zuordnen in der gleichen Methode wie das Splitten passiert.
Was hällst Du davon?
Ansonsten werde ich mich wohl an die Parameter gewöhnen müssen. ;)
Zur Kontakt-Datenbank: Die "späte" Initialisierung würde ich ganz rausnehmen und dieses bisschen Code auf Modulebene stattfinden lassen.
Verstehe ich leider nicht ganz, soll ich also aus dem ganzen nur Funktionen auf Modulebene machen?

Die anderen Dinge habe ich ausgebessert.

Vielen Dank für die Geduld und Hilfe,
Maxi
Antworten