track hours spent working on projects
Persitent data is stored in a gozerbot Persist object, ‘projects’
projects.data[username][project] exists contrib.data[username][project] MUST NOT exist
projects.data[username][project] MUST NOT exist
add work hours to a project
add project to projects of username
share our project with another user make sure that 1) we own the project and 2) the other user has no project with this name and does not contribute to a project with the same name before calling this function
stop contributing to a project of someone else
delete a project
stop sharing our project with another user
get names of projects we contribute to
return description of project
get list of contributors and hours of work for a project
get owner of a project we contribute to
return projects owned by username
get users sharing a project with
add hours to a project
share a project with another user
stop sharing a project with another user
pt-add <projectname> <description> .. add project
delete a project or stop contributing to it
show available projects
display project report
get a list of users we share a project with
does this user contribute to a project with this name?
look if this user owns a project
alter project description
return number of projects
# gplugs/projecttracker.py # # """ track hours spent working on projects Persitent data is stored in a gozerbot Persist object, 'projects' user is owner of a project: projects.data[username][project] exists contrib.data[username][project] MUST NOT exist projects.data[username][project]['hours'] may exist dict: {user: total hours, ...} projects.data[username][project]['desc'] may exist string: project description projects.data[username][project]['share'] may exist list: other contributing users user contributes to a project of another user: contrib.data[username][project] exists string: project owner username projects.data[username][project] MUST NOT exist """ __gendocfirst__ = ['pt-add', ] __gendoclast__ = ['pt-del', ] __author__ = "Hans van Kranenburg <hans@knorrie.org>" __status__ = "seen"
from gozerbot.commands import cmnds from gozerbot.examples import examples from gozerbot.users import users from gozerbot.datadir import datadir from gozerbot.persist.persist import PlugPersist from gozerbot.plughelp import plughelp from gozerbot.tests import tests
import os
plughelp.add('projecttracker', 'track hours spent working on projects')
projects = PlugPersist('pt-projects') contrib = PlugPersist('pt-contrib') if not projects.data: projects.data = {} if not contrib.data: contrib.data = {}
def size(): """ return number of projects """ size = 0 for userprojects in projects.data: size += len(userprojects) return size
def addproject(username, project, desc): """ add project to projects of username """ if not projects.data.has_key(username): projects.data[username] = {} if not projects.data[username].has_key(project): projects.data[username][project] = {} if desc: projects.data[username][project]['desc'] = desc projects.save()
def hasproject(username, project): """ look if this user owns a project """ if projects.data.has_key(username) and \ projects.data[username].has_key(project): return True return False
def delproject(username, project): """ delete a project """ try: if projects.data[username].has_key(project): if projects.data[username][project].has_key('share'): for otheruser in \ projects.data[username][project]['share']: try: del contrib.data[otheruser][project] except KeyError: pass del projects.data[username][project] except KeyError: pass projects.save() contrib.save()
def getprojects(username): """ return projects owned by username """ try: return projects.data[username].keys() except KeyError: return []
def setdesc(username, project, desc): """ alter project description """ try: projects.data[username][project]['desc'] = desc projects.save() except KeyError: pass
def getdesc(username, project): """ return description of project """ try: return projects.data[username][project]['desc'] except KeyError: return None
def hascontrib(username, project): """ does this user contribute to a project with this name? """ try: return contrib.data[username].has_key(project) except KeyError: return False
def getowner(username, project): """ get owner of a project we contribute to """ if hascontrib(username, project): return contrib.data[username][project] return None
def getcontrib(username): """ get names of projects we contribute to """ try: return contrib.data[username].keys() except KeyError: return []
def delcontrib(username, project): """ stop contributing to a project of someone else """ if hascontrib(username, project): owner = getowner(username, project) delshare(owner, project, username)
def addhours(owner, project, username, hours): """ add work hours to a project """ if not projects.data[owner][project].has_key('hours'): projects.data[owner][project]['hours'] = {} if not projects.data[owner][project]['hours'].has_key(username): projects.data[owner][project]['hours'][username] = hours else: projects.data[owner][project]['hours'][username] += hours projects.save() return projects.data[owner][project]['hours'][username]
def gethourslist(username, project): """ get list of contributors and hours of work for a project """ result = [] if hasproject(username, project): owner = username elif hascontrib(username, project): owner = getowner(username, project) else: result.append("unknown project %s" % project) return result if projects.data[owner][project].has_key('hours'): # (h)ours(l)ist hl = projects.data[owner][project]['hours'] if hl: # (c)ontributor to the project for c in hl: if hl[c] != 0: result.append('%s (%s)' % (c, hl[c])) if not result: result.append("no work done yet") return result
def handle_projectadd(bot, ievent): """ pt-add <projectname> <description> .. add project """ if not ievent.rest: ievent.missing('<projectname> [<description>]') return username = users.getname(ievent.userhost) try: project, desc = ievent.rest.split(' ', 1) except ValueError: project = ievent.rest desc = None project = project.strip().lower() if hasproject(username, project) or hascontrib(username, project): ievent.reply('project %s already exists' % project) return if desc: desc = desc.strip() addproject(username, project, desc) ievent.reply('project %s added' % (project)) cmnds.add('pt-add', handle_projectadd, 'USER') examples.add('pt-add', 'pt-add <projectname> [<description>] .. \ add a project to the project tracker', 'pt-add gc Gozerbot coden ;]') tests.add('pt-add gozerbot coden', ' added|already')
def handle_projectlist(bot, ievent): """ show available projects """ username = users.getname(ievent.userhost) result = [] l = getprojects(username) for project in l: desc = getdesc(username, project) if desc: result.append('%s (%s)' % (desc, project)) else: result.append(project) l = getcontrib(username) for project in l: owner = getowner(username, project) desc = getdesc(owner, project) if desc: result.append('%s (%s, owner=%s)' % (desc, project, \ owner)) else: result.append('%s, owner=%s' % (project, owner)) if result: ievent.reply('', result, dot=True) else: ievent.reply('no projects found') cmnds.add('pt-list', handle_projectlist, 'USER') examples.add('pt-list', 'list available projects', 'pt-list') tests.add('pt-list', 'gozerbot')
def handle_projectdel(bot, ievent): """ delete a project or stop contributing to it """ try: project = ievent.rest.strip().lower() except ValueError: ievent.missing('<projectname>') return username = users.getname(ievent.userhost) if hasproject(username, project): delproject(username, project) ievent.reply('project %s deleted' % (project)) elif hascontrib(username, project): delcontrib(username, project) ievent.reply('no longer contributing to %s' % (project)) else: ievent.reply('no project %s' % project) cmnds.add('pt-del', handle_projectdel, 'USER') examples.add('pt-del', 'delete a project from the project tracker or \ stop contibuting to it', 'pt-del mekker') tests.add('pt-del mekker')
def handle_addhours(bot, ievent): """ add hours to a project """ username = users.getname(ievent.userhost) try: project, rest = ievent.rest.split(' ', 1) except ValueError: ievent.missing('<projectname> <hours> [<comment>]') return project = project.strip().lower() if hasproject(username, project): owner = username elif hascontrib(username, project): owner = getowner(username, project) else: ievent.reply('no project %s' % project) return try: hours, comment = rest.split(' ', 1) except ValueError: hours = rest comment = '' try: hours = float(hours) except ValueError: ievent.reply('%s is not a number' % hours) return total = addhours(owner, project, username, hours) desc = getdesc(owner, project) or project # h = 'hours' # if abs(hours) == 1: # h = 'hour' # if hours < 0: # ievent.reply('removed %s %s from %s' % (hours, h, project)) # return # ievent.reply('added %s %s to %s' % (hours, h, project)) ievent.reply('hours spent on %s is now: %s' % (desc, total)) cmnds.add('pt', handle_addhours, 'USER') examples.add('pt', 'track time spent on a project', 'pt gc 4') tests.add('pt gozerbot coden 4')
def handle_report(bot, ievent): """ display project report """ username = users.getname(ievent.userhost) if not ievent.rest: result = [] l = getprojects(username) for project in l: desc = getdesc(username, project) or project report = gethourslist(username, project) result.append('%s: %s' % (desc, ' '.join(report))) l = getcontrib(username) for project in l: owner = getowner(username, project) desc = getdesc(owner, project) or project report = gethourslist(owner, project) result.append('%s: %s' % (desc, ' '.join(report))) if result: ievent.reply('', result, dot=True) else: ievent.reply('no projects found') else: project = ievent.rest.strip().lower() if hasproject(username, project): owner = username elif hascontrib(username, project): owner = getowner(username, project) else: ievent.reply('no project %s' % project) return desc = getdesc(owner, project) or project report = gethourslist(owner, project) ievent.reply('%s: ' % desc, report, dot=True) cmnds.add('pt-report', handle_report, 'USER') examples.add('pt-report', 'report hours of work on a project', \ '1) pt-report 2) pt-report gc') tests.add('pt-report gozerbot coden')