Results for category "Python"

5 Articles

Entwickeln mit Scrum/Agilo

Wegen eines privaten Projekts habe ich in den letzten paar Tagen eine kleine Entwicklungsumgebung aufgebaut. Dazu dient mir auf meinem Notebook eine VirtualBox (Ubuntu 10.04, Tomcat, MySQL 5.2, Apache 2, Trac + Agilo, SVN) als Deployment- und Entwicklungsplattform. Die Installation des ganzen Environments erledigte ich recht schnell anhand von http://wiki.ubuntuusers.de/Trac und http://www.agile42.com/cms/pages/download-install/

Nachdem ich mich mit Agilo ein bißchen auseinandersetzte, stellte ich fest, dass das Anlagen von Teams nicht funktionierte, die Fehlermeldung

 An error occurred while getting None from the database: no such table: agilo_sprint

war mehr als eindeutig: bei der Installation von Agilo wurden in meiner Version drei Tabellen schlichtweg nicht erstellt.
Nach kurzer Suche wurde ich fündig und legte mit sqlite3 trac.db die fehlenden Tabellen für die Benutzer manuell an:

CREATE TABLE agilo_team (
  name text,
  description text,
  UNIQUE (name)
);
CREATE TABLE agilo_team_member (
  name text,
  team text,
  description text,
  ts_mon real,
  ts_tue real,
  ts_wed real,
  ts_thu real,
  ts_fri real,
  ts_sat real,
  ts_sun real,
  UNIQUE (name)
);
CREATE TABLE agilo_calendar_entry (
  date integer,
  teammember text,
  hours real,
  UNIQUE (date,teammember)
);

agilo_calendar_entry wurde ebenfalls nicht angelegt, wie ich später feststellte.

Weiterhin stellte ich fest, dass beim Erstellen eines Sprints der Fehler

AttributeError: 'NoneType' object has no attribute 'toordinal'

auftrat. Google sei dank fixte ich das Problem, indem ich bei den Sprint-Daten Start- und Endzeitpunkt manuell eintrug. Anscheinend funktioniert das Feld Duration in days in der Sprint-Administration noch nicht so ganz.

Soweit dazu, eventuell gibt es ja die ein oder andere Person, die ebenfalls über diese beiden Fehler stolpert.

Für mich als Scrum-Neuling – produktiv hatte ich diese Entwicklungsmethode noch nicht eingesetzt – wurde ich beim näheren Betrachten der Optionen von Agilo erst einmal erschlagen. Deshalb folgt hier eine grobe Auflistung der Begrifflichkeiten und Funktionalitäten, wie sie in der Standard-Installation definiert sind.

  • Ticket-Typen
    • Requirement sind Tickets, die auf die Fragen Welches Problem zu lösen ist und warum es zum Problem wird. Requirements sollen SMART (Simple, Measurable, Achievable, Realistic and Traceable) sein. Nachdem ein Requirement angelegt wurde, kann über “Edit” 0..n User Stories zugewiesen werden.
    • User Story ist ein Ticket, das beschreibt was der Benutzer mit dem System erreichen will und warum (Nutzen dieser Funktionalität). Nachdem eine User-Story angelegt ist, kann über “Edit” auf einen neuen Task verwiesen werden. Eine User Story kann 0..n Tasks besitzen.
    • Tasks sind Tickets, die eine Aufgabe, die von einem Team Member zu erledigen ist, detailiert erklärt. Jeder Task soll ausführlich sein und sich auf eine Aufgabe beziehen.
  • Backlog
    • Das Product Backlog beinhaltet die User Stories und darunter jeweils die Requirements, die einer User Story zugeordnet sind.
    • Das Sprint Backlog beinhaltet die Requirements (und die User Story bzw. Tasks die dem Requirement zugewiesen worden sind) und offene Bugs. Es werden nur die Requirements/Tasks angezeigt, die dem Sprint zugewiesen worden sind.
    • Sprint bezeichnet den Zeitraum, in dem mehrere Tasks/Requirements gelöst werden. Im Administrations-Interface müssen jeweils neue Sprints mit Start- und End-Datum angelegt werden.
    • Milestone bezeichnet einen Zeitraum, der wiederum aus mehreren Sprints besteht

Die Frage nach dem “Wie gehe ich nun vor?” ist über oben die dargestellte Struktur eigentlich relativ klar:

  • Anlegen der User Stories (WELCHE Funktionalität wird benötigt um einen Business Value zu erreichen?)
  • Erstellen der Requirements (WELCHE Probleme einer User Story gilt es zu lösen?) und Zuweisen der Requirements an eine User Story
  • Erstellen der zugehörigen Tasks (WAS ist bei einer User Story technisch zu tun?), die einer User Story angehören
  • Erstellen eines Meilensteins. Requirements müssen dem Meilenstein zugewiesen werden
  • Erstellen von ein oder mehreren Sprints. Tasks, User Stories und Bugs müssen den Sprints zugewiesen werden

Eventuell werde ich die nächsten Tage noch den ein oder anderen Blog-Eintrag zu Agilo verfassen. Danke fürs Lesen,

Rautiges 2009-06-20

  • Die letzte Woche war davon geprägt, dass ich mich auf die Präsenzklausur von MLG vorbereitet habe. Nach einer Zusammenfassung von 100 Seiten bin ich gestern mit Marc um halb 11 nach Witten losgefahren, Klausurbeginn war um 15.30 Uhr. Wir waren aber bereits um 13.30 Uhr in Witten angekommen und bereiteten uns mental auf die bevorstehende Klausur vor.
    Die Themen der Prüfung waren

    • Bestimmen von Elementen dreier Mengen, die miteinander über Konjunktion, Disjunktion und Komplementär verknüpft worden sind. Die Aufgabe war relativ leicht. Allerdings bin ich mir nicht sicher, ob ich die Beschreibung, welche Elemente in der Wertemenge vorhanden sind, korrekt dargestellt habe.
      In der zweiten Teilaufgabe sollte die Verknüpfung eines Venn-Diagramms niedergeschrieben werden. War eine Sache von 30 Sekunden.
    • Der zweite Aufgabenblock behandelte Boolesche Operatoren: einmal sollte eine Verknüpfung nur mit Disjunktion, Konjunktion und Negation dargestellt werden; bei der zweiten ging es um einen Beweis. Die Aufgabe sollte ich korrekt gelöst haben.
    • In Aufgabenblock Nr. drei ging um vollständige Induktion. Hab mich da leider irgendwo verrechnet und somit fehlten mir am Ende 3n²:)
    • Im vierten Themenbereich konnte zwischen Äquivalenzrelationen und mehrstelligen Operatoren gewählt werden. Ich hab mich für ÄRs entschieden und sollte den Part auch größtenteils korrekt haben.
    • Im letzten Block konnte man sich für eine Aufgabe zu Graphen oder Boolesche Netze entscheiden. Ich habe ganz klar die BNs gewählt, da die Schreibarbeit in 5 Minuten erledigt war.

    Alles in allem waren die Aufgaben sehr fair gestellt und konnten in der vorgegebenen Zeit gelöst werden. Ich war 10 Minuten vor Schluss fertig (hatte keinen Nerv mehr, den Fehler in meiner Induktion zu suchen). Nach dem Kurs muss ich an dieser Stelle auch mal sagen, dass sich zuständige Prof meine Fragen sehr schnell beantwortet und auch die Aufgaben fix korrigiert hat. Lob!

  • Nachdem wir dann aus Witten wieder in Wolfsburg angekommen waren (so gegen 21 Uhr), legte ich mich für eine Stunde schlafen und machte mich dann für den THG Abiball fertig. War eine lustige Partie bis um halb drei Uhr morgens.
  • Während der letzten Woche habe als Ausgleich zur Vorbereitung von MLG zwei Scripts in Python programmiert:
    • Einmal ein Kommandozeilentool, dass über die Parameter einen RSS-Feed erzeugt. Klingt unspektakulär – ist es aber nicht. Hintergrund ist der, dass man mit diesem Tool z.B. automatisiert RSS-Einträge erstellen kann. Beispielsweise kann ein CRON-Job jede Stunde das syslog in den RSS-Feed schieben.
    • Das zweite Tool ist ein Frontend für pyinotify, was wiederum eine Python-Implementierung für inotify ist. Über eine Konfigurationsdatei lassen sich beliebig viele Hooks auf Verzeichnisse oder Dateien definieren, bei deren Aktivierung über die inotify-spezifischen Events beliebige Aktionen ausgeführt werden können.
      Das Frontend bietet die Option, dass die doppelte Ausführung eines Scripts verhindert wird. Beispielsweise wird auf den Ordner /media/mp3 ein Hook gelegt, der beim Erstellen einer neuen Datei automatisch ein Backup des Ordners erstellt. Wird eine Datei kopiert, ist alles schön: Nach Ende des Vorgangs wird das Backup erstellt. Werden nun zwei Dateien kopiert, sieht die Sache schon anders um: Das Backup-Script wird zweimal ausgeführt, da das Script bei JEDEM kopierten Element ausgeführt wird. Bei mehr als zwei Kopieraktionen werden die übrigen Aktionen verworfen, so dass das Backupscript so selten wie möglich aufgerufen wird.

Rautiges 2009-06-07

  • In der letzten Woche habe ich den Adobe AIR Twitter-Client “Spaz” so aufgemöbelt, dass ich damit meine Twicker-Ergebnisse direkt übermitteln kann. Die Sourcen dazu habe ich Funkatron, dem Maintainer von Spaz, zukommen lassen. Damit lässt sich recht einfach demonstrieren, wie sich der Twitter-Client erweitern lässt. Hat bis dato wohl noch keiner gemacht. Vielleicht werde ich in der nächsten Woche noch ein kleines Tutorial dazu schreiben.
  • Mittwoch habe ich im BibaBlog die ganzen alten Artikel überflogen und den vorhandenen Quellcode vernünftig formatiert. Durch verschiedene Syntax-Highlighting-Plugins gab es gewisse Divergenzen in der Darstellung 😉 Außerdem werden die youtube-Videos wieder korrekt dargestellt.
  • Florian hat im Rahmen eines kleines Projekts ein Installationsscript für EFA (Elektronisches Fahrtenbuch für Ruderer) gebaut. Damit wird – ausgehend von einem Ubuntu-Alternate-Install – ein System aufgesetzt, das nur die nötigsten Tools enthält. Besonders cool dabei: Flo hat ein Script geschrieben, dass per udev automatisch erkennt, ob ein USB-Stick angesteckt wurde und wenn dem so ist, wird die Datenbank von EFA über eine Python-GUI gesichert. Lob und Anerkennung, cooles Projekt!
  • Freitag waren die Jungs und ich in Termintor 4. Der Film war an sich ganz cool, auch wenn es einige logische Unstimmigkeiten gab 😉 Die visuellen Effekte waren aber top. Danach machten wir einen kurzen Abstecher im Sausalitos und nisteten uns dann auf der Geburtstagsfeier von Maike ein.
  • Samstag habe ich die Onlineklausur für Mathematisch-logische Grundlagen abgelegt. Die ersten 5 Minuten war ich etwas neben der Spur, konnte mich dann aber fangen und die Aufgaben relativ gut abarbeiten. Als Themen kamen vor: Bestimmung einer Gruppe/Abelsche Gruppe; Beweis des Absorptionsgesetz von Junktoren anhand Wahrheitstabelle (das war easy, ich hoffe, ich hab die Frage auch richtig verstanden 🙂 ); Bestimmen der Endmenge von Mengenoperationen; Entscheiden, ob eine Funktion surjektiv, injektiv oder total ist; Induktiver Beweis einer Formel. An dieser Stelle möchte ich mich noch einmal bei Timo bedanken, der mich in den letzten Wochen tatkräftig bei LaTeX- und Mathe-Fragen unterstützte.
  • Die letzten Tage über habe ich an zwei Songtexten für die Band gearbeitet. Einer der beiden Texte ist bereits fertig, der andere geht dem Ende entgegen. Bei einer der nächsten Bandproben wird es der Öffentlichkeit präsentiert.

HTC Touch Diamond mit Ubuntu nutzen

Mit Hilfe des USB-Kabels lässt sich der interne Speicher des Touch’ nutzen; Internetzugang sollte über Bluetooth mit Hilfe von PAN funktionieren. Ich bin aber noch nicht dazu gekommen, das zu testen.
Die Synchronisation zwischen PDA und Notebook dürfte mit SyncCE funktionieren. Auch dies habe ich noch nicht getestet.

PAN wird übrigens in einer der nächsten Versionen des NetworkManagers integriert werden. Es existiert zwar bereits ein Patch, der libbluez ansteuert. Der Patch ist allerdings gegen die Revision 2574 von NM programmiert worden.
Momentan ist aktuell die Revision 4xxx. Ich hab’ vorgestern versucht, den Patch auf den aktuellen NM-Trunk upzudaten – leider ohne Erfolg und zugegebenermaßen auch mit wenig Lust. War eine stupide Arbeit, beide Forks mit dem Patch abzugleichen.

Die Jungs von Launchpad diskutieren die Integration von PAN in NM hier. Im NetworkManager-Tracker ist der PAN-Request hier zu finden, inkl. des veralteten Patches.

Edith: Ich bin eeeUser-Forum über einen Thread gestolpert, in dem ein kleines Python-PAN-Script vorgestellt wird.

Hier der Code:

#!/usr/bin/env python
# (c) 2008 Kyle Reed
# PAN tethering with WM device w/o using deprecated pand
# I don't claim to know Python, use at your own risk 😉

import pygtk
pygtk.require('2.0')
import gtk
import dbus
import os

### For passing errors to the GUI ###
class BTException:
def __init__(self, msg):
self.error_msg = msg

### BT Helper handles all of the DBUS stuff ###
# usage flow:
# __init__
# getDevicesList
# selectNetworkDevice
# getDeviceProperties
# Connnect|Disconnect
class BTHelper:
# create the helper object and initialize dbus
def __init__(self):
# get the system bus object
self.bus = dbus.SystemBus()

# get the adapter interface to query paired devices
self.adapter = dbus.Interface(self.bus.get_object(
'org.bluez', '/org/bluez/hci0'), 'org.bluez.Adapter')

# get the devices list #
def getDevicesList(self):
try:
self.devices = self.adapter.ListDevices()
return self.devices
except dbus.DBusException:
raise BTException('Could not get adapter interface, is your BT adapter on?')

# get the name of a device from a device string
# doesn't require selectNetworkDevice to be called
def getDeviceName(self, devicePath):
try:
tempDev = dbus.Interface(self.bus.get_object(
'org.bluez', devicePath), 'org.bluez.Device')
tempProps = tempDev.GetProperties()
return tempProps['Alias']
except dbus.DBusException:
return '[Error Fetching Name]'

# select device to query by index #
def selectNetworkDevice(self, index):
try:
self.current_device = dbus.Interface(self.bus.get_object(
'org.bluez', self.devices[index]), 'org.bluez.Network')
except dbus.DBusException:
raise BTException('Could not get network interface for device, is your device active?')

# returns information about the device #
def getDeviceProperties(self):
try:
self.deviceProps = self.current_device.GetProperties()
return self.deviceProps
except dbus.DBusException:
raise BTException('Could not get properties for device, did you select a device?')

# connects and initializes the connection #
def connectDevice(self):
if self.deviceProps['Connected'] == 0:
print 'Connecting...'

try:
# connect and get interface
iface = self.current_device.Connect('NAP')
print 'Using network interface ' + iface

# bring up the interface and start DHCP
os.system('ifconfig up ' + iface)
os.system('dhclient ' + iface)
print 'Connected'
except dbus.DBusException, e:
raise BTException('Connect Failed: %s' % (e))
else:
print 'Already Connected'

def disconnectDevice(self):
if self.deviceProps['Connected']:
print 'Disconnecting...'

try:
# bring down interface
os.system('ifdown ' + self.deviceProps['Device'])

# disconnect
self.current_device.Disconnect()
print 'Disconnected'
except dbus.DBusException, e:
raise BTException('Disconnect Failed: %s' % (e))
else:
print 'Not Connected'
### End BTHelper ###

### Defines the class that draws the GUI ###
class BTHelperGUI:
titleText = 'BlueTooth PAN Helper'

# show an exception message
def showErrorMsg(self, msg):
errorDlg = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL,
gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, msg)
errorDlg.set_title(self.titleText)
errorDlg.run()
errorDlg.destroy()

### Signal Handler Methods
# exit button click
def handleExitClicked(self, widget, data=None):
print('Exiting')
self.exit()

# connect button clicked
def handleConnectClicked(self, widget, data=None):
try:
self.BTHelp.connectDevice()
self.getDeviceInformation()
except BTException, e:
self.showErrorMsg(e.error_msg)

# disconnect button clicked
def handleDisconnectClicked(self, widget, data=None):
try:
self.BTHelp.disconnectDevice()
self.getDeviceInformation()
except BTException, e:
self.showErrorMsg(e.error_msg)

# device selection changed
def handleDeviceSelectionChanged(self, widget, data=None):
selection = self.devicesListView.get_selection()
selectedIter = selection.get_selected()[1]

# nothing selected, return
if(selectedIter is None):
self.setButtonState(-1)
return

selectedIndex = self.devicesListStore.get_value(selectedIter, 2)
print('Selected %s' % (selectedIndex))
try:
self.BTHelp.selectNetworkDevice(selectedIndex)
self.getDeviceInformation()
except BTException, e:
self.showErrorMsg(e.error_msg)

# window manager is closing us (event)
def delete_event(self, widget, event, data=None):
self.exit()
return False
### End handler methods

### support methods ###
# process device names and add to devices list store
def processDevicesList(self):
# list devicees from the BT Adapter
try:
allDevices = self.BTHelp.getDevicesList()
count = 0;
for device in allDevices:
addrOnly = device.split('dev_')[-1]
addrOnly = addrOnly.replace('_', ':')
self.devicesListStore.append([addrOnly,
self.BTHelp.getDeviceName(device), count])
count += 1
except BTException, e:
self.showErrorMsg(e.error_msg)
# hard exit because we haven't stated the main loop
quit()

# get selected device information and set up controls
def getDeviceInformation(self):
try:
properties = self.BTHelp.getDeviceProperties()
self.statsListStore.clear()
self.statsListStore.append(['Connected',str(properties['Connected'])])
self.statsListStore.append(['Device',str(properties['Device'])])

self.setButtonState(int(properties['Connected']))
except BTException, e:
self.showErrorMsg(e.error_msg)

# enable/disable buttons are required
def setButtonState(self, state):
if(state == -1): # none selected
self.connectBtn.set_sensitive(False)
self.disconnectBtn.set_sensitive(False)
elif(state == 0): # disconnected
self.connectBtn.set_sensitive(True)
self.disconnectBtn.set_sensitive(False)
elif(state == 1): # connected
self.connectBtn.set_sensitive(False)
self.disconnectBtn.set_sensitive(True)

# common exit point for program
def exit(self):
gtk.main_quit()
### end support methods ###

def __init__(self):
self.BTHelp = BTHelper()

# create the main window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title(self.titleText)
self.window.set_default_size(400, 300)
self.window.set_border_width(10)

# set handlers for window events
self.window.connect('delete_event', self.delete_event)

# Devices list view
# set up data backend for devices list
# addr, name, index (hidden)
self.devicesListStore = gtk.ListStore(str, str, int)
self.processDevicesList()

# create devices list columns
devicesListCol = gtk.TreeViewColumn('Device Address')
devicesListCol2 = gtk.TreeViewColumn('Device Name')

# make text renderers and add to columns
addrCell = gtk.CellRendererText()
nameCell = gtk.CellRendererText()
devicesListCol.pack_start(addrCell, True)
devicesListCol2.pack_start(nameCell, True)

# set cell mappings to store data
devicesListCol.add_attribute(addrCell, 'text', 0)
devicesListCol2.add_attribute(nameCell, 'text', 1)

# create devices list and add columns
self.devicesListView = gtk.TreeView(self.devicesListStore)
self.devicesListView.append_column(devicesListCol)
self.devicesListView.append_column(devicesListCol2)

# set signal handlers for devices list
self.devicesListView.connect('cursor-changed', self.handleDeviceSelectionChanged)
# end devices list view

# stats list view
# create stats list data store
self.statsListStore = gtk.ListStore(str, str)
self.statsListStore.append(['Connected', ''])
self.statsListStore.append(['Device', ''])

# create stats columns
statsCol = gtk.TreeViewColumn('Property')
statsCol2 = gtk.TreeViewColumn('Value')

# create text renderers and add to columns
propCell = gtk.CellRendererText()
valueCell = gtk.CellRendererText()
statsCol.pack_start(propCell, True)
statsCol2.pack_start(valueCell, True)

# set data mappings with data store
statsCol.add_attribute(propCell, 'text', 0)
statsCol2.add_attribute(valueCell, 'text', 1)

# create stats list view
self.statsListView = gtk.TreeView(self.statsListStore)
self.statsListView.append_column(statsCol)
self.statsListView.append_column(statsCol2)
# end stats list view

# control buttons
self.connectBtn = gtk.Button('Connect')
self.disconnectBtn = gtk.Button('Disconnect')
self.exitBtn = gtk.Button('Exit')

# set handlers
self.exitBtn.connect('clicked', self.handleExitClicked)
self.connectBtn.connect('clicked', self.handleConnectClicked)
self.disconnectBtn.connect('clicked', self.handleDisconnectClicked)

# pack in a VBox
buttonBox = gtk.VBox(True, 2)
buttonBox.set_border_width(10)
buttonBox.pack_start(self.connectBtn)
buttonBox.pack_start(self.disconnectBtn)
buttonBox.pack_start(self.exitBtn)

# frames for decoration
statsFrame = gtk.Frame(' Status ')
statsFrame.add(self.statsListView)

buttonFrame = gtk.Frame(' Actions ')
buttonFrame.add(buttonBox)
# end control buttons

# add controls to layout
# +-----------------+
# | DEVLIST AREA    |
# +-----+-----------+
# |STATS|BUTTONS    |
# +-----+-----------+

tableLayout = gtk.Table(2, 2, False)
tableLayout.set_row_spacings(10)
tableLayout.set_col_spacings(10)
tableLayout.attach(self.devicesListView, 0, 2, 0, 1)
tableLayout.attach(statsFrame, 0, 1, 1, 2)
tableLayout.attach(buttonFrame, 1, 2, 1, 2)

# add layout to window
self.window.add(tableLayout)
self.window.show_all()

def main(self):
gtk.main()
### End BTHelperGUI ###

### Program Execution here ###
print('BlueTooth PAN Helper GUI')
btgui = BTHelperGUI()
btgui.main()

Good job!

Ich hab es auf meinem Touch ausprobiert und es funktioniert einwandfrei. Wichtig: Das Script als root starten. Auf dem Touch muss unter Programme -> Internet Freigabe bei PC-Verbindung “Bluetooth-PAN” ausgewählt und danach das Handy mit dem Notebook gepairt werden.
Ich bin mal wieder von Linux begeistert. Und von meinem neuen Handy auch 😉