Callmonitor für die FritzBox
-
- 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
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
-
- 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:
`_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):
...
Code: Alles auswählen
if hasattr(obj, 'match')
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.
-
- User
- Beiträge: 110
- Registriert: Freitag 25. Dezember 2009, 03:42
Danke für die schnelle Antwort.
Habe die Design-Bugs ausgebessert.
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.
Habe die Design-Bugs ausgebessert.
Das verstehe ich nicht so ganz.Z. 131 (`if not foo: return False`) lässt sich ausdrücken als `return foo`.
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.
-
- User
- Beiträge: 996
- Registriert: Mittwoch 9. Januar 2008, 13:48
Sorry, Denkfehler. Ich dachte da stünde sowas wiemaxi_king_333 hat geschrieben:Das verstehe ich nicht so ganz.Z. 131 (`if not foo: return False`) lässt sich ausdrücken als `return foo`.
Ich prüfe hier ja mit Regular Expression soll ich da nochmal neu prüfen für die Rückgabe?
Code: Alles auswählen
if not condition:
return False
else:
return True
Code: Alles auswählen
return not condition
-
- 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:
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.
`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
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.
-
- 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
Das ganze verhält sich so: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.
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).
Hm... Du hast recht, aber mir fällt da nichts ein.`convert_seconds` ist auch nicht so glücklich, weil man nicht weiß, zu was denn jetzt die Sekunden konvertiert werden.
Vielleicht s_to_hms?
-
- 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 :-)
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 :-)
-
- User
- Beiträge: 110
- Registriert: Freitag 25. Dezember 2009, 03:42
Ist halt so eine Sache, der Code beinhaltet ja nicht das vom Server empfangene.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.
Aber ich denke, ich sollte vielleicht ein paar Kommentare einbauen.
Und was ist, wenn ein Engländer das benutzen möchte, so ist es halt international.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
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.
*/
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)
-
- 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.
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.
-
- 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:
Was haltet ihr davon? (und von dem restlichen Code)
Freue mich auf Verbesserungs-Vorschläge.
Viele Grüße
Maxi
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)))
Freue mich auf Verbesserungs-Vorschläge.
Viele Grüße
Maxi
-
- 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 :-)
-
- User
- Beiträge: 110
- Registriert: Freitag 25. Dezember 2009, 03:42
Da ich info und connection in call umbenannt habe, habe ich hier das gleiche Problem wie vorher.Da sind immer noch magische Indices drin, `call[4]` zum Beispiel.
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
-
- User
- Beiträge: 996
- Registriert: Mittwoch 9. Januar 2008, 13:48
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 einfachmaxi_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.
Code: Alles auswählen
gutebeschreibung1, gutebeschreibung2, ... = foo.split(';')
Na dein Code braucht aber Dokumentation, weil man ja nicht versteht, was die Elemente für ne Bedeutung haben...aber ich möchte hier an den Spruch "guter Code braucht keine Dokumentation" erinnern
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 ^^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.
Das Leben ist wie ein Tennisball.
-
- User
- Beiträge: 110
- Registriert: Freitag 25. Dezember 2009, 03:42
Das ist aber so leider nicht möglich, da ich gutebeschreibung2 kennen muss um die restlichen Namen zu haben.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 einfachmachen 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 hoffnungslosCode: Alles auswählen
gutebeschreibung1, gutebeschreibung2, ... = foo.split(';')
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.
Wie gesagt, der Code mit den magischen Nummern ist über ein Jahr alt.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 ^^
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...
-
- 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.
Dann jeweils eine für ausgehende und ankommende.
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:
Nun geht das aber nicht in Python 2, deswegen hier der Workaround:
(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:
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:
(Ich habe grade keine gute Idee, wie man das ständige `_invoke_callbacks` loswerden kann, wegen dem `del` bei Disconnects.)
Hoffe, dass inspiriert. :-)
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
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
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(';')
Code: Alles auswählen
data = line.split(';')
timestamp = line.pop(0)
event = line.pop(0)
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)
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]
Hoffe, dass inspiriert. :-)
-
- 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
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
-
- 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.
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.
-
- User
- Beiträge: 110
- Registriert: Freitag 25. Dezember 2009, 03:42
Hi,
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.
Die anderen Dinge habe ich ausgebessert.
Vielen Dank für die Geduld und Hilfe,
Maxi
Hmm...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.
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.
Verstehe ich leider nicht ganz, soll ich also aus dem ganzen nur Funktionen auf Modulebene machen?Zur Kontakt-Datenbank: Die "späte" Initialisierung würde ich ganz rausnehmen und dieses bisschen Code auf Modulebene stattfinden lassen.
Die anderen Dinge habe ich ausgebessert.
Vielen Dank für die Geduld und Hilfe,
Maxi