Пример #1
0
    def _renderList(self, renderer, projectList, filters, order,
                    limit=None, groupKeyword=None):
        """
        Render a list using renderer, according to the restrictions set by the
        other parameters
        @param renderer: renderer class (for example: TextListRenderer)
        @param projectList: list of project name (as unicode string)
        @param filters: filters in sqlobject format (example: Task.q.status == 'done')
        @param order: ordering in sqlobject format (example: -Task.q.urgency)
        @param limit: limit number tasks (int) or None for no limit
        @param groupKeyword: keyword used for grouping (as unicode string) or None
        """
        if groupKeyword:
            if groupKeyword.startswith("@"):
                groupKeyword = groupKeyword[1:]
            for keyword in Keyword.select(LIKE(Keyword.q.name, groupKeyword)):
                if unicode(keyword.name).startswith("_") and not groupKeyword.startswith("_"):
                    # BUG: cannot filter on db side because sqlobject does not understand ESCAPE needed whith _
                    continue
                taskList = Task.select(AND(TaskKeyword.q.keywordID == keyword.id,
                                           *filters),
                                       orderBy=order, limit=limit, distinct=True,
                                       join=LEFTJOINOn(Task, TaskKeyword, Task.q.id == TaskKeyword.q.taskID))
                taskList = list(taskList)
                if projectList:
                    taskList = [x for x in taskList if x.project in projectList]
                if len(taskList) > 0:
                    self.lastTaskIds.extend([t.id for t in taskList])  # Keep selected id for further use
                    renderer.addTaskList(unicode(keyword), taskList)
            renderer.end()
        else:
            hiddenProjectNames = []
            for project in projectList:
                if not project.active:
                    hiddenProjectNames.append(project.name)
                    continue
                taskList = Task.select(AND(Task.q.projectID == project.id, *filters),
                                       orderBy=order, limit=limit, distinct=True,
                                       join=LEFTJOINOn(Task, TaskKeyword, Task.q.id == TaskKeyword.q.taskID))
                taskList = list(taskList)

                if len(taskList) > 0:
                    self.lastTaskIds.extend([t.id for t in taskList])  # Keep selected id for further use
                    renderer.addTaskList(unicode(project), taskList)
            renderer.end()

            if len(hiddenProjectNames) > 0:
                tui.info("hidden projects: %s" % ", ".join(hiddenProjectNames))
Пример #2
0
    def __init__(self, name, filter_string):
        self.name = name

        project_name, keyword_filters = parseutils.extractKeywords(
            filter_string)

        q_filters = [x.filter() for x in keyword_filters]

        project_list = Project.select(LIKE(Project.q.name, project_name))
        q_filters.append(IN(Task.q.project, project_list))

        # Skip notes
        q_filters.append(
            parseutils.KeywordFilter("!@" + NOTE_KEYWORD).filter())

        # Only list done tasks if they were done after min_date
        min_date = compute_min_date()
        q_filters.append(
            OR(Task.q.status != 'done', Task.q.doneDate >= min_date))

        self.tasks = Task.select(AND(*q_filters),
                                 orderBy=Task.q.id,
                                 distinct=True,
                                 join=LEFTJOINOn(
                                     Task, TaskKeyword,
                                     Task.q.id == TaskKeyword.q.taskID))
Пример #3
0
    def do_t_reorder(self, line):
        """Reorder tasks of a project.
        It works by starting an editor with the task list: you can then change
        the order of the lines and save the list. The urgency field will be
        updated to match the order.
        t_reorder <project_name>"""
        try:
            project = Project.byName(line)
        except SQLObjectNotFound:
            raise BadUsageException("You must provide a valid project name")

        taskList = Task.select(AND(Task.q.projectID == project.id,
                                   Task.q.status != 'done'),
                               orderBy=-Task.q.urgency)
        lines = ["%d,%s" % (x.id, x.title) for x in taskList]
        text = tui.editText("\n".join(lines))

        ids = []
        for line in text.split("\n"):
            line = line.strip()
            if not "," in line:
                continue
            id = int(line.split(",")[0])
            ids.append(id)

        ids.reverse()
        for urgency, id in enumerate(ids):
            task = Task.get(id)
            task.urgency = urgency
Пример #4
0
    def do_t_reorder(self, line):
        """Reorder tasks of a project.
        It works by starting an editor with the task list: you can then change
        the order of the lines and save the list. The urgency field will be
        updated to match the order.
        t_reorder <project_name>"""
        try:
            project = Project.byName(line)
        except SQLObjectNotFound:
            raise BadUsageException("You must provide a valid project name")

        taskList = Task.select(AND(Task.q.projectID == project.id,
                                   Task.q.status != 'done'),
                               orderBy=-Task.q.urgency)
        lines = ["%d,%s" % (x.id, x.title) for x in taskList]
        text = tui.editText("\n".join(lines))

        ids = []
        for line in text.split("\n"):
            line = line.strip()
            if not "," in line:
                continue
            id = int(line.split(",")[0])
            ids.append(id)

        ids.reverse()
        for urgency, id in enumerate(ids):
            task = Task.get(id)
            task.urgency = urgency
Пример #5
0
    def end(self):
        today = datetime.now().replace(hour=0, minute=0)
        # Adjust idColumn
        maxId = Task.select().max(Task.q.id)
        self.idColumn.width = max(2, len(str(maxId)))

        # Adjust titleColumn
        self.titleColumn.width = self.maxTitleWidth
        totalWidth = sum([x.width for x in self.columns]) + len(self.columns)
        if totalWidth >= self.termWidth:
            self.titleColumn.width -= (totalWidth - self.termWidth) + len(self.columns)
        self.titleColumn.formater = TitleFormater(self.titleColumn.width, self.cryptoMgr)

        # Print table
        for sectionName, taskList in self.taskLists:
            dateSplitters = [(1, "day"), (7, "week"), (30, "month"), (30 * 4, "quarter"), (365, "year")]
            splitterRange, splitterName = dateSplitters.pop()
            splitterText = None
            self._renderTaskListHeader(sectionName)
            for task in taskList:
                while self.splitOnDate and task.creationDate > today - timedelta(splitterRange):
                    splitterText = "Last %s" % splitterName
                    if len(dateSplitters) > 0:
                        splitterRange, splitterName = dateSplitters.pop()
                    else:
                        self.splitOnDate = False

                if splitterText:
                    print >> self.out, C.GREEN + splitterText.center(totalWidth) + C.RESET
                    splitterText = None

                self._renderTaskListRow(task)
Пример #6
0
    def testAdd(self):
        tui.addInputAnswers("y")
        self.cmd.do_t_add("x t1")

        tui.addInputAnswers("y", "y")
        self.cmd.do_t_add("x @kw1 @kw2=12 t2")

        tui.addInputAnswers("n")
        self.cmd.do_t_add("notExistingProject newTask")

        tasks = list(Task.select())
        result = [x.title for x in tasks]
        expected = [u"t1", u"t2"]
        self.assertEqual(result, expected)

        kwDict = Task.get(2).getKeywordDict()
        self.assertEqual(kwDict, dict(kw1=None, kw2=12))

        for bad_input in ("",  # No project
                          "x"):  # No task name
            self.assertRaises(BadUsageException, self.cmd.do_t_add, bad_input)

        # Crypto stuff
        tui.addInputAnswers("a Secret passphrase")
        self.cmd.do_t_add("-c x encrypted t1")
        self.assertTrue(Task.get(3).title.startswith(cryptutils.CRYPTO_PREFIX))
Пример #7
0
def taskIdCompleter(cmd, text, line, begidx, endidx):
    # TODO: filter on parameter position
    # TODO: potential performance issue with lots of tasks, find a better way to do it
    tasks = [x for x in Task.select(Task.q.status != 'done') if str(x.id).startswith(text)]
    print
    for task in tasks:
        # Move that in a renderer class ?
        print "%s: %s / %s" % (task.id, task.project.name, task.title)
    return [str(x.id) for x in tasks]
Пример #8
0
def taskIdCompleter(cmd, text, line, begidx, endidx):
    # TODO: filter on parameter position
    # TODO: potential performance issue with lots of tasks, find a better way to do it
    tasks = [
        x for x in Task.select(Task.q.status != 'done')
        if str(x.id).startswith(text)
    ]
    print
    for task in tasks:
        # Move that in a renderer class ?
        print "%s: %s / %s" % (task.id, task.project.name, task.title)
    return [str(x.id) for x in tasks]
Пример #9
0
 def do_p_list(self, line):
     """List all projects."""
     for project in Project.select():
         if project.active:
             active = ""
         else:
             active = "(inactive)"
         print "%s %s %s %s" % (
             project.name.ljust(20),
             project.getKeywordsAsString().ljust(20),
             str(Task.select(Task.q.project == project).count()).rjust(4),
             active)
Пример #10
0
def eventLoop():
    """Main event loop"""
    delta = timedelta(hours=float(getConfigKey("ALARM_DELAY")))
    suspend = timedelta(hours=float(getConfigKey("ALARM_SUSPEND")))
    cmdDelayTemplate = getConfigKey("ALARM_DELAY_CMD")
    cmdDueTemplate = getConfigKey("ALARM_DUE_CMD")
    # For the two following dict, task id is key, and value is (duedate, triggerdate)
    triggeredDelayTasks = {}
    triggeredDueTasks = {}
    activeTaskFilter = [Task.q.status != "done",
                      Task.q.projectID == Project.q.id,
                      Project.q.active == True]
    while event[0]:
        now = datetime.today().replace(microsecond=0)
        delayTasks = Task.select(AND(Task.q.dueDate < now + delta,
                                   Task.q.dueDate > now,
                                   *activeTaskFilter))
        dueTasks = Task.select(AND(Task.q.dueDate < now,
                                 *activeTaskFilter))
        processTasks(delayTasks, triggeredDelayTasks, cmdDelayTemplate, suspend)
        processTasks(dueTasks, triggeredDueTasks, cmdDueTemplate, suspend)
        time.sleep(DELAY)
Пример #11
0
    def do_t_remove(self, line):
        parser = self.parser_t_remove()
        args = parser.parse_args(line)
        task = self.getTaskFromId(args.id)
        if not args.force:
            if not tui.confirm("Remove task '%s'" % task.title):
                return
        projectId = task.project.id
        task.destroySelf()
        print "Task '%s' removed" % (task.title)

        # Delete project with no associated tasks
        if Task.select(Task.q.projectID == projectId).count() == 0:
            Project.delete(projectId)
Пример #12
0
def eventLoop():
    """Main event loop"""
    delta = timedelta(hours=float(getConfigKey("ALARM_DELAY")))
    suspend = timedelta(hours=float(getConfigKey("ALARM_SUSPEND")))
    cmdDelayTemplate = getConfigKey("ALARM_DELAY_CMD")
    cmdDueTemplate = getConfigKey("ALARM_DUE_CMD")
    # For the two following dict, task id is key, and value is (duedate, triggerdate)
    triggeredDelayTasks = {}
    triggeredDueTasks = {}
    activeTaskFilter = [
        Task.q.status != "done", Task.q.projectID == Project.q.id,
        Project.q.active == True
    ]
    while event[0]:
        now = datetime.today().replace(microsecond=0)
        delayTasks = Task.select(
            AND(Task.q.dueDate < now + delta, Task.q.dueDate > now,
                *activeTaskFilter))
        dueTasks = Task.select(AND(Task.q.dueDate < now, *activeTaskFilter))
        processTasks(delayTasks, triggeredDelayTasks, cmdDelayTemplate,
                     suspend)
        processTasks(dueTasks, triggeredDueTasks, cmdDueTemplate, suspend)
        time.sleep(DELAY)
Пример #13
0
    def do_t_remove(self, line):
        parser = self.parser_t_remove()
        args = parser.parse_args(line)
        task = self.getTaskFromId(args.id)
        if not args.force:
            if not tui.confirm("Remove task '%s'" % task.title):
                return
        projectId = task.project.id
        task.destroySelf()
        print "Task '%s' removed" % (task.title)

        # Delete project with no associated tasks
        if Task.select(Task.q.projectID == projectId).count() == 0:
            Project.delete(projectId)
Пример #14
0
 def do_p_remove(self, line):
     parser = self.parser_p_remove()
     args = parser.parse_args(line)
     project = getProjectFromName(args.project)
     taskList = Task.select(Task.q.projectID == project.id)
     taskList = list(taskList)
     if not args.force:
         if not tui.confirm("Remove project '%s' and its %d tasks" % (project.name, len(taskList))):
             return
     print "Removing project tasks:"
     for task in taskList:
         task.delete(task.id)
         print "- task %(id)-3s: %(title)-30s" % dict(id=str(task.id), title=str(task.title))
     project.delete(project.id)
     print "Project removed"
Пример #15
0
 def do_t_purge(self, line):
     parser = self.parser_t_purge()
     args = parser.parse_args(line)
     filters = []
     filters.append(Task.q.status == "done")
     filters.append(Task.q.doneDate < (datetime.now() - timedelta(days=args.delay)))
     tasks = Task.select(AND(*filters))
     if tasks.count() == 0:
         print "No tasks need to be purged"
         return
     print "The following tasks will be removed:"
     print "\n".join(["%s: %s" % (task.id, task.title) for task in tasks])
     if args.force or tui.confirm("Do you really want to remove those tasks (this action cannot be undone)?"):
         Task.deleteMany(AND(*filters))
         print "Tasks deleted"
     else:
         print "Purge canceled"
Пример #16
0
 def do_p_remove(self, line):
     parser = self.parser_p_remove()
     args = parser.parse_args(line)
     project = getProjectFromName(args.project)
     taskList = Task.select(Task.q.projectID == project.id)
     taskList = list(taskList)
     if not args.force:
         if not tui.confirm("Remove project '%s' and its %d tasks" %
                            (project.name, len(taskList))):
             return
     print "Removing project tasks:"
     for task in taskList:
         task.delete(task.id)
         print "- task %(id)-3s: %(title)-30s" % dict(id=str(task.id),
                                                      title=str(task.title))
     project.delete(project.id)
     print "Project removed"
Пример #17
0
def generateCal():
    """Generate an ical calendar from yokadi database
    @return: icalendar.Calendar object"""
    cal = icalendar.Calendar()
    cal.add("prodid", "-//Yokadi calendar //yokadi.github.com//")
    cal.add("version", "2.0")
    # Add projects
    for project in Project.select(Project.q.active == True):
        vTodo = icalendar.Todo()
        vTodo.add("summary", project.name)
        vTodo["uid"] = PROJECT_UID % project.id
        cal.add_component(vTodo)
    # Add tasks
    for task in Task.select(Task.q.status != "done"):
        vTodo = createVTodoFromTask(task)
        cal.add_component(vTodo)

    return cal
Пример #18
0
    def testAdd(self):
        tui.addInputAnswers("y", "2", "4", "123")
        self.cmd.do_bug_add("x t1")

        tui.addInputAnswers("n")
        self.cmd.do_bug_add("notExistingProject newBug")

        tasks = list(Task.select())
        result = [x.title for x in tasks]
        expected = [u"t1"]
        self.assertEqual(result, expected)

        kwDict = Task.get(1).getKeywordDict()
        self.assertEqual(kwDict, dict(_severity=2, _likelihood=4, _bug=123))

        for bad_input in ("",  # No project
                          "x"):  # No task name
            self.assertRaises(YokadiException, self.cmd.do_bug_add, bad_input)
Пример #19
0
def generateCal():
    """Generate an ical calendar from yokadi database
    @return: icalendar.Calendar object"""
    cal = icalendar.Calendar()
    cal.add("prodid", '-//Yokadi calendar //yokadi.github.com//')
    cal.add("version", "2.0")
    # Add projects
    for project in Project.select(Project.q.active == True):
        vTodo = icalendar.Todo()
        vTodo.add("summary", project.name)
        vTodo["uid"] = PROJECT_UID % project.id
        cal.add_component(vTodo)
    # Add tasks
    for task in Task.select(Task.q.status != "done"):
        vTodo = createVTodoFromTask(task)
        cal.add_component(vTodo)

    return cal
Пример #20
0
 def do_t_purge(self, line):
     parser = self.parser_t_purge()
     args = parser.parse_args(line)
     filters = []
     filters.append(Task.q.status == "done")
     filters.append(
         Task.q.doneDate < (datetime.now() - timedelta(days=args.delay)))
     tasks = Task.select(AND(*filters))
     if tasks.count() == 0:
         print "No tasks need to be purged"
         return
     print "The following tasks will be removed:"
     print "\n".join(["%s: %s" % (task.id, task.title) for task in tasks])
     if args.force or tui.confirm(
             "Do you really want to remove those tasks (this action cannot be undone)?"
     ):
         Task.deleteMany(AND(*filters))
         print "Tasks deleted"
     else:
         print "Purge canceled"
Пример #21
0
    def end(self):
        today = datetime.now().replace(hour=0, minute=0)
        # Adjust idColumn
        maxId = Task.select().max(Task.q.id)
        self.idColumn.width = max(2, len(str(maxId)))

        # Adjust titleColumn
        self.titleColumn.width = self.maxTitleWidth
        totalWidth = sum([x.width for x in self.columns]) + len(self.columns)
        if totalWidth >= self.termWidth:
            self.titleColumn.width -= (totalWidth - self.termWidth) + len(
                self.columns)
        self.titleColumn.formater = TitleFormater(self.titleColumn.width,
                                                  self.cryptoMgr)

        # Print table
        for sectionName, taskList in self.taskLists:
            dateSplitters = [(1, "day"), (7, "week"), (30, "month"),
                             (30 * 4, "quarter"), (365, "year")]
            splitterRange, splitterName = dateSplitters.pop()
            splitterText = None
            self._renderTaskListHeader(sectionName)
            for task in taskList:
                while self.splitOnDate and task.creationDate > today - timedelta(
                        splitterRange):
                    splitterText = "Last %s" % splitterName
                    if len(dateSplitters) > 0:
                        splitterRange, splitterName = dateSplitters.pop()
                    else:
                        self.splitOnDate = False

                if splitterText:
                    print >> self.out, C.GREEN + splitterText.center(
                        totalWidth) + C.RESET
                    splitterText = None

                self._renderTaskListRow(task)
Пример #22
0
    def _renderList(self,
                    renderer,
                    projectList,
                    filters,
                    order,
                    limit=None,
                    groupKeyword=None):
        """
        Render a list using renderer, according to the restrictions set by the
        other parameters
        @param renderer: renderer class (for example: TextListRenderer)
        @param projectList: list of project name (as unicode string)
        @param filters: filters in sqlobject format (example: Task.q.status == 'done')
        @param order: ordering in sqlobject format (example: -Task.q.urgency)
        @param limit: limit number tasks (int) or None for no limit
        @param groupKeyword: keyword used for grouping (as unicode string) or None
        """
        if groupKeyword:
            if groupKeyword.startswith("@"):
                groupKeyword = groupKeyword[1:]
            for keyword in Keyword.select(LIKE(Keyword.q.name, groupKeyword)):
                if unicode(keyword.name).startswith(
                        "_") and not groupKeyword.startswith("_"):
                    # BUG: cannot filter on db side because sqlobject does not understand ESCAPE needed whith _
                    continue
                taskList = Task.select(
                    AND(TaskKeyword.q.keywordID == keyword.id, *filters),
                    orderBy=order,
                    limit=limit,
                    distinct=True,
                    join=LEFTJOINOn(Task, TaskKeyword,
                                    Task.q.id == TaskKeyword.q.taskID))
                taskList = list(taskList)
                if projectList:
                    taskList = [
                        x for x in taskList if x.project in projectList
                    ]
                if len(taskList) > 0:
                    self.lastTaskIds.extend([
                        t.id for t in taskList
                    ])  # Keep selected id for further use
                    renderer.addTaskList(unicode(keyword), taskList)
            renderer.end()
        else:
            hiddenProjectNames = []
            for project in projectList:
                if not project.active:
                    hiddenProjectNames.append(project.name)
                    continue
                taskList = Task.select(AND(Task.q.projectID == project.id,
                                           *filters),
                                       orderBy=order,
                                       limit=limit,
                                       distinct=True,
                                       join=LEFTJOINOn(
                                           Task, TaskKeyword,
                                           Task.q.id == TaskKeyword.q.taskID))
                taskList = list(taskList)

                if len(taskList) > 0:
                    self.lastTaskIds.extend([
                        t.id for t in taskList
                    ])  # Keep selected id for further use
                    renderer.addTaskList(unicode(project), taskList)
            renderer.end()

            if len(hiddenProjectNames) > 0:
                tui.info("hidden projects: %s" % ", ".join(hiddenProjectNames))
Пример #23
0
 def do_p_list(self, line):
     """List all projects."""
     for project in Project.select():
         if project.active:
             active = ""
         else:
             active = "(inactive)"
         print "%s %s %s %s" % (project.name.ljust(20), project.getKeywordsAsString().ljust(20), str(Task.select(Task.q.project == project).count()).rjust(4), active)