コード例 #1
0
ファイル: dbutils.py プロジェクト: semtle/yokadi
def addTask(projectName, title, keywordDict=None, interactive=True):
    """Adds a task based on title and keywordDict.
    @param projectName: name of project as a string
    @param title: task title as a string
    @param keywordDict: dictionary of keywords (name : value)
    @param interactive: Ask user before creating project (this is the default)
    @type interactive: Bool
    @returns : Task instance on success, None if cancelled."""
    session = db.getSession()
    if keywordDict is None:
        keywordDict = {}

    # Create missing keywords
    if not createMissingKeywords(keywordDict.keys(), interactive=interactive):
        return None

    # Create missing project
    project = getOrCreateProject(projectName, interactive=interactive)
    if not project:
        return None

    # Create task
    task = Task(creationDate=datetime.now().replace(second=0, microsecond=0),
                project=project,
                title=title,
                description="",
                status="new")
    session.add(task)
    task.setKeywordDict(keywordDict)
    session.merge(task)

    return task
コード例 #2
0
ファイル: taskcmd.py プロジェクト: bport/yokadi
    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
コード例 #3
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))
コード例 #4
0
ファイル: dbutils.py プロジェクト: agateau/yokadi
def addTask(projectName, title, keywordDict=None, interactive=True):
    """Adds a task based on title and keywordDict.
    @param projectName: name of project as a string
    @param title: task title as a string
    @param keywordDict: dictionary of keywords (name : value)
    @param interactive: Ask user before creating project (this is the default)
    @type interactive: Bool
    @returns : Task instance on success, None if cancelled."""
    session = db.getSession()
    if keywordDict is None:
        keywordDict = {}

    # Create missing keywords
    if not createMissingKeywords(keywordDict.keys(), interactive=interactive):
        return None

    # Create missing project
    project = getOrCreateProject(projectName, interactive=interactive)
    if not project:
        return None

    # Create task
    task = Task(creationDate=datetime.now().replace(second=0, microsecond=0), project=project, title=title,
                description="", status="new")
    session.add(task)
    task.setKeywordDict(keywordDict)
    session.merge(task)

    return task
コード例 #5
0
ファイル: dbutils.py プロジェクト: bport/yokadi
def addTask(projectName, title, keywordDict=None, interactive=True):
    """Adds a task based on title and keywordDict.
    @param projectName: name of project as a string
    @param title: task title as a string
    @param keywordDict: dictionary of keywords (name : value)
    @param interactive: Ask user before creating project (this is the default)
    @type interactive: Bool
    @returns : Task instance on success, None if cancelled."""

    if keywordDict is None:
        keywordDict = {}

    # Create missing keywords
    if not createMissingKeywords(keywordDict.keys(), interactive=interactive):
        return None

    # Create missing project
    project = getOrCreateProject(projectName, interactive=interactive)
    if not project:
        return None

    # Create task
    try:
        task = Task(creationDate=datetime.now(), project=project, title=title, description="", status="new")
        task.setKeywordDict(keywordDict)
    except DuplicateEntryError:
        raise YokadiException("A task named %s already exists in this project. Please find another name" % title)

    return task
コード例 #6
0
    def testTApply(self):
        self.cmd.do_k_add("lala")
        for i in range(10):
            tui.addInputAnswers("y")
            self.cmd.do_t_add("x t%s" % i)
        ids = [1, 2, 4, 5, 6, 9]
        self.cmd.do_t_apply("1 2,4-6 9 t_add_keywords @lala")
        for taskId in range(1, 10):
            kwDict = Task.get(taskId).getKeywordDict()
            if taskId in ids:
                self.assertEqual(kwDict, dict(lala=None))
            else:
                self.assertNotEqual(kwDict, dict(lala=None))

        # raise error if t_list had not been called previously
        self.assertRaises(BadUsageException, self.cmd.do_t_apply, "__ t_add_keywords @toto")

        self.cmd.do_t_list("@lala")
        self.cmd.do_t_apply("__ t_add_keywords @toto")
        for taskId in range(1, 10):
            kwDict = Task.get(taskId).getKeywordDict()
            if taskId in ids:
                self.assertEqual(kwDict, dict(lala=None, toto=None))
            else:
                self.assertNotEqual(kwDict, dict(lala=None, toto=None))
コード例 #7
0
ファイル: taskcmd.py プロジェクト: bport/yokadi
    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
コード例 #8
0
 def testLastProjectName(self):
     # Using "_" with no prior project used should raise an exception
     self.assertRaises(YokadiException, self.cmd.do_t_add, "_ t1")
     tui.addInputAnswers("y")
     self.cmd.do_t_add("x t1")
     task1 = Task.get(1)
     self.cmd.do_t_add("_ t2")
     task2 = Task.get(2)
     self.assertEqual(task1.project, task2.project)
コード例 #9
0
    def testSetProject(self):
        tui.addInputAnswers("y")
        self.cmd.do_t_add("x t1")
        tui.addInputAnswers("y")
        self.cmd.do_t_project("1 y")
        task1 = Task.get(1)
        self.assertEqual(task1.project.name, "y")

        self.cmd.do_t_add("x t2")
        self.cmd.do_t_project("1 _")
        task1 = Task.get(1)
        self.assertEqual(task1.project.name, "x")
コード例 #10
0
ファイル: tasktestcase.py プロジェクト: kotenev/yokadi
 def testTlistUrgency0(self):
     # Given a project with two tasks, one with a negative urgency
     prj = Project(name="prj")
     self.session.add(prj)
     t1 = Task(project=prj, title="t1")
     self.session.add(t1)
     t2 = Task(project=prj, title="t2", urgency=-1)
     self.session.add(t2)
     self.session.flush()
     # When I list tasks with -u 0
     renderer = testutils.TestRenderer()
     self.cmd.do_t_list("-u 0", renderer=renderer)
     # Then the task with a negative urgency is not listed
     self.assertEqual(renderer.tasks, [t1])
コード例 #11
0
    def testLastTaskId(self):
        # Using "_" with no prior task activity should raise an exception
        self.assertRaises(YokadiException, self.cmd.getTaskFromId, "_")

        tui.addInputAnswers("y")
        self.cmd.do_t_add("x t1")
        task1 = Task.get(1)
        self.assertEqual(self.cmd.getTaskFromId("_"), task1)

        self.cmd.do_t_add("x t2")
        task2 = Task.get(2)
        self.assertEqual(self.cmd.getTaskFromId("_"), task2)

        self.cmd.do_t_mark_started("1")
        self.assertEqual(self.cmd.getTaskFromId("_"), task1)
コード例 #12
0
ファイル: taskcmd.py プロジェクト: bport/yokadi
    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))
コード例 #13
0
ファイル: board.py プロジェクト: kotenev/yokadi-postitwall
    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))
コード例 #14
0
ファイル: yokadid.py プロジェクト: bport/yokadi
    def run(self):
        filename = self.options.filename
        if not filename:
            filename = os.path.join(os.path.expandvars("$HOME"), ".yokadi.db")
            print "Using default database (%s)" % filename

        connectDatabase(filename, createIfNeeded=False)

        # Basic tests :
        if not (Task.tableExists() and Config.tableExists()):
            print "Your database seems broken or not initialised properly. Start yokadi command line tool to do it"
            sys.exit(1)

        # Start ical http handler
        if self.options.icalserver:
            yokadiIcalServer = YokadiIcalServer(self.options.tcpPort,
                                                self.options.tcpListen)
            yokadiIcalServer.start()

        # Start the main event Loop
        try:
            while event[1] != "SIGTERM":
                eventLoop()
                event[0] = True
        except KeyboardInterrupt:
            print "\nExiting..."
コード例 #15
0
ファイル: yokadid.py プロジェクト: bport/yokadi
    def run(self):
        filename = self.options.filename
        if not filename:
            filename = os.path.join(os.path.expandvars("$HOME"), ".yokadi.db")
            print "Using default database (%s)" % filename

        connectDatabase(filename, createIfNeeded=False)

        # Basic tests :
        if not (Task.tableExists() and Config.tableExists()):
            print "Your database seems broken or not initialised properly. Start yokadi command line tool to do it"
            sys.exit(1)

        # Start ical http handler
        if self.options.icalserver:
            yokadiIcalServer = YokadiIcalServer(self.options.tcpPort, self.options.tcpListen)
            yokadiIcalServer.start()

        # Start the main event Loop
        try:
            while event[1] != "SIGTERM":
                eventLoop()
                event[0] = True
        except KeyboardInterrupt:
            print "\nExiting..."
コード例 #16
0
ファイル: textlistrenderer.py プロジェクト: bport/yokadi
    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)
コード例 #17
0
    def testRecurs(self):
        tui.addInputAnswers("y")
        self.cmd.do_t_add("x t1")
        task = Task.get(1)
        self.cmd.do_t_recurs("1 daily 10:00")
        desc = str(task.recurrence)
        self.cmd.do_t_recurs("1 weekly FR 23:00")
        self.cmd.do_t_recurs("1 none")
        self.cmd.do_t_recurs("1 weekly fr 23:00")
        self.cmd.do_t_recurs("1 weekly Fr 23:00")
        self.cmd.do_t_recurs("1 weekly Friday 23:00")
        self.cmd.do_t_recurs("1 monthly 3 13:00")
        self.cmd.do_t_recurs("1 monthly second friday 13:00")
        self.cmd.do_t_recurs("1 yearly 3/07 11:20")
        self.cmd.do_t_recurs("1 quarterly 14 11:20")
        self.cmd.do_t_recurs("1 quarterly first monday 23:20")
        self.assertNotEqual(desc, str(task.recurrence))
        self.assertEqual(task.status, "new")
        self.cmd.do_t_mark_done("1")
        self.assertEqual(task.status, "new")

        for bad_input in ("",  # No task
                          "1",  # No recurence
                          "1 foo",  # Unknown recurrence
                          "1 daily",  # No time
                          "1 weekly",  # No day
                          "1 weekly monday",  # No time
                          "1 monthly",  # No day
                          "1 monthly 10",  # No time
                          "1 quarterly",  # No day
                          "1 quarterly 10",  # No time
                          "1 monthly foo 12:00",  # Bad date
                          ):
            self.assertRaises(YokadiException, self.cmd.do_t_recurs, bad_input)
コード例 #18
0
ファイル: taskcmd.py プロジェクト: bport/yokadi
 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"
コード例 #19
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)
コード例 #20
0
ファイル: completers.py プロジェクト: bport/yokadi
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]
コード例 #21
0
 def testMark(self):
     tui.addInputAnswers("y")
     self.cmd.do_t_add("x t1")
     task = Task.get(1)
     self.assertEqual(task.status, "new")
     self.cmd.do_t_mark_started("1")
     self.assertEqual(task.status, "started")
     self.cmd.do_t_mark_done("1")
     self.assertEqual(task.status, "done")
コード例 #22
0
ファイル: taskcmd.py プロジェクト: bport/yokadi
 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"
コード例 #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)
コード例 #24
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]
コード例 #25
0
ファイル: yokadid.py プロジェクト: bport/yokadi
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)
コード例 #26
0
ファイル: yokadid.py プロジェクト: bport/yokadi
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)
コード例 #27
0
ファイル: taskcmd.py プロジェクト: bport/yokadi
    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)
コード例 #28
0
ファイル: taskcmd.py プロジェクト: bport/yokadi
    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)
コード例 #29
0
ファイル: tasktestcase.py プロジェクト: semtle/yokadi
    def testRenderListSectionOrder(self):
        projectNames = "ccc", "aaa", "UPPER_CASE", "zzz", "mmm"
        projectList = []
        for name in projectNames:
            prj = Project(name=name)
            task = Task(project=prj, title="Hello")
            self.session.add(prj)
            self.session.add(task)
            projectList.append(prj)
        self.session.flush()

        renderer = testutils.TestRenderer()
        self.cmd._renderList(renderer, projectList, filters=[], order=[])

        self.assertEqual(list(renderer.taskDict.keys()), sorted(projectNames, key=lambda x: x.lower()))
コード例 #30
0
    def testAddKeywords(self):
        tui.addInputAnswers("y")
        self.cmd.do_t_add("x t1")
        task = Task.get(1)

        tui.addInputAnswers("y", "y")
        self.cmd.do_t_add_keywords("1 @kw1 @kw2=12")

        kwDict = task.getKeywordDict()
        self.assertEqual(kwDict, dict(kw1=None, kw2=12))

        for bad_input in ("",  # No task
                          "1",  # No keyword
                          "1 kw1"):  # No @ before kw1
            self.assertRaises(YokadiException, self.cmd.do_t_add_keywords, bad_input)
コード例 #31
0
def applyChanges(project, oldList, newList, interactive=True):
    """
    Modify a project so that its task list is newList

    @param project: the project name
    @param oldList: a list of Task instances
    @param newList: a list of MEditEntry
    @param interactive: whether to confirm creation of new keywords
    """
    session = db.getSession()

    # Sanity check: all ids in newList should be in oldList
    oldIds = set([x.id for x in oldList])
    newIds = set([x.id for x in newList if x.id is not None])
    unknownIds = newIds.difference(oldIds)
    if unknownIds:
        idString = ", ".join([str(x) for x in unknownIds])
        raise YokadiException("Unknown id(s): %s" % idString)

    # Check keywords
    for entry in newList:
        for name in entry.keywords:
            dbutils.getOrCreateKeyword(name, interactive=interactive)

    # Remove tasks whose lines have been deleted
    for id in oldIds.difference(newIds):
        task = dbutils.getTaskFromId(id)
        session.delete(task)

    # Update existing tasks, add new ones
    nbTasks = len(newList)
    for pos, newEntry in enumerate(newList):
        if newEntry.id:
            task = dbutils.getTaskFromId(newEntry.id)
        else:
            task = Task(creationDate=datetime.now().replace(second=0,
                                                            microsecond=0),
                        project=project)
        task.title = newEntry.title
        task.setKeywordDict(newEntry.keywords)
        task.setStatus(newEntry.status)
        task.urgency = nbTasks - pos
        if newEntry.id:
            session.merge(task)
        else:
            session.add(task)
コード例 #32
0
ファイル: projectcmd.py プロジェクト: bport/yokadi
 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"
コード例 #33
0
ファイル: tasktestcase.py プロジェクト: semtle/yokadi
    def testRenderListSectionOrderKeywords(self):
        prj = Project(name="prj")
        keywordNames = ["kw_" + x for x in ("ccc", "aaa", "UPPER_CASE", "zzz", "mmm")]
        keywordList = []
        for name in keywordNames:
            keyword = Keyword(name=name)
            task = Task(project=prj, title="Hello")
            TaskKeyword(task=task, keyword=keyword)
            self.session.add(task)
            keywordList.append(prj)
        self.session.flush()

        renderer = testutils.TestRenderer()
        self.cmd._renderList(renderer, [prj], filters=[], order=[], groupKeyword="kw_%")

        self.assertEqual(list(renderer.taskDict.keys()), sorted(keywordNames, key=lambda x: x.lower()))
コード例 #34
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"
コード例 #35
0
ファイル: yical.py プロジェクト: bport/yokadi
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
コード例 #36
0
ファイル: dbutils.py プロジェクト: bport/yokadi
def getTaskFromId(tid, parameterName="id"):
    """Returns a task given its id, or raise a YokadiException if it does not
    exist.
    @param tid: taskId string
    @param parameterName: name of the parameter to mention in exception
    @return: Task instance or None if existingTask is False"""

    # We do not use line.isdigit() because it returns True if line is '¹'!
    try:
        taskId = int(tid)
    except ValueError:
        raise YokadiException("<%s> should be a number" % parameterName)

    try:
        task = Task.get(taskId)
    except SQLObjectNotFound:
        raise YokadiException("Task %s does not exist. Use t_list to see all tasks" % taskId)
    return task
コード例 #37
0
ファイル: yical.py プロジェクト: digitalfox/yokadi
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
コード例 #38
0
def applyChanges(project, oldList, newList, interactive=True):
    """
    Modify a project so that its task list is newList

    @param project: the project name
    @param oldList: a list of Task instances
    @param newList: a list of MEditEntry
    @param interactive: whether to confirm creation of new keywords
    """
    session = db.getSession()

    # Sanity check: all ids in newList should be in oldList
    oldIds = set([x.id for x in oldList])
    newIds = set([x.id for x in newList if x.id is not None])
    unknownIds = newIds.difference(oldIds)
    if unknownIds:
        idString = ", ".join([str(x) for x in unknownIds])
        raise YokadiException("Unknown id(s): %s" % idString)

    # Check keywords
    for entry in newList:
        for name in entry.keywords:
            dbutils.getOrCreateKeyword(name, interactive=interactive)

    # Remove tasks whose lines have been deleted
    for id in oldIds.difference(newIds):
        task = dbutils.getTaskFromId(id)
        session.delete(task)

    # Update existing tasks, add new ones
    nbTasks = len(newList)
    for pos, newEntry in enumerate(newList):
        if newEntry.id:
            task = dbutils.getTaskFromId(newEntry.id)
        else:
            task = Task(creationDate=datetime.now().replace(second=0, microsecond=0), project=project)
        task.title = newEntry.title
        task.setKeywordDict(newEntry.keywords)
        task.setStatus(newEntry.status)
        task.urgency = nbTasks - pos
        if newEntry.id:
            session.merge(task)
        else:
            session.add(task)
コード例 #39
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)
コード例 #40
0
ファイル: taskcmd.py プロジェクト: bport/yokadi
    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))
コード例 #41
0
ファイル: projectcmd.py プロジェクト: bport/yokadi
 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)