Example #1
0
    def do_t_filter(self, line):
        """Define permanent keyword filter used by t_list
        Ex.:
            - t_filter @work (filter all task that have the "work" keyword)
            - t_filter none (remove filter)"""
        # TODO: add completion

        if not line:
            raise YokadiException("You must give keyword as argument or 'none' to reset filter")

        if parseutils.simplifySpaces(line).lower() == "none":
            self.kFilters = []
            self.pFilter = ""
            self.prompt = "yokadi> "
        else:
            projectName, keywordFilters = parseutils.extractKeywords(line)
            self.kFilters = keywordFilters
            self.pFilter = projectName
            prompt = "y"
            if self.pFilter:
                prompt += " %s" % projectName
            if self.kFilters:
                parseutils.warnIfKeywordDoesNotExist(self.kFilters)
                prompt += " %s" % (" ".join([str(k) for k in keywordFilters]))
            self.prompt = "%s> " % prompt
Example #2
0
    def do_t_urgency(self, line):
        """Defines urgency of a task.
        t_urgency <id> <value>"""

        tokens = parseutils.simplifySpaces(line).split(" ")
        if len(tokens) != 2:
            raise BadUsageException("You must provide a taskId and an urgency value")
        task = self.getTaskFromId(tokens[0])
        try:
            # Do not use isdigit(), so that we can set negative urgency. This
            # make it possible to stick tasks to the bottom of the list.
            urgency = int(tokens[1])
        except ValueError:
            raise BadUsageException("Task urgency must be a digit")

        if urgency > 100:
            tui.warning("Max urgency is 100")
            urgency = 100
        elif urgency < -99:
            tui.warning("Min urgency is -99")
            urgency = -99

        task.urgency = urgency
        self.session.merge(task)
        self.session.commit()
Example #3
0
    def do_t_due(self, line):
        """Set task's due date
        t_due <id> <date>

        Date can be specified as a relative offset:
        - +5M: in 5 minutes
        - +3H: in 3 hours
        - +1D: in 1 day
        - +6W: in 6 weeks

        As a day in the week:
        - tomorrow:      tomorrow, same hour
        - tuesday 12:10: next tuesday, at 12:10
        - fr 15:30:      next friday, at 15:30

        Or as an absolute date or time:
        - 10:38:            at 10:38 today
        - 25/09/2010 12:10: on the 25th of September, 2010, at 12:10
        - 23/02/2010:       on the 23th of February, 2010
        - 01/04:            on the 1st of April
        - 12:               on the 12th of current month

        To reset a due date, use "none"."""
        line = parseutils.simplifySpaces(line)
        if len(line.split()) < 2:
            raise YokadiException("Give a task id and time, date or date & time")
        taskId, line = line.strip().split(" ", 1)
        task = self.getTaskFromId(taskId)

        if line.lower() == "none":
            task.dueDate = None
            print "Due date for task '%s' reset" % task.title
        else:
            task.dueDate = ydateutils.parseHumaneDateTime(line)
            print "Due date for task '%s' set to %s" % (task.title, task.dueDate.ctime())
Example #4
0
    def do_t_filter(self, line):
        """Define permanent keyword filter used by t_list
        Ex.:
            - t_filter @work (filter all task that have the "work" keyword)
            - t_filter none (remove filter)"""
        # TODO: add completion

        if not line:
            raise YokadiException(
                "You must give keyword as argument or 'none' to reset filter")

        if parseutils.simplifySpaces(line).lower() == "none":
            self.kFilters = []
            self.pFilter = ""
            self.prompt = "yokadi> "
        else:
            projectName, keywordFilters = parseutils.extractKeywords(line)
            self.kFilters = keywordFilters
            self.pFilter = projectName
            prompt = "y"
            if self.pFilter:
                prompt += " %s" % projectName
            if self.kFilters:
                parseutils.warnIfKeywordDoesNotExist(self.kFilters)
                prompt += " %s" % (" ".join([str(k) for k in keywordFilters]))
            self.prompt = "%s> " % prompt
Example #5
0
    def do_t_add_keywords(self, line):
        """Add keywords to an existing task
        t_add_keywords <id> <@keyword1> <@keyword2>[=<value>]...
        """
        tokens = parseutils.simplifySpaces(line).split(" ", 1)
        if len(tokens) < 2:
            raise YokadiException(
                "You should give at least two arguments: <task id> <keyword>")
        task = dbutils.getTaskFromId(tokens[0])
        garbage, keywordFilters = parseutils.extractKeywords(tokens[1])
        newKwDict = parseutils.keywordFiltersToDict(keywordFilters)
        if garbage:
            raise YokadiException(
                "Cannot parse line, got garbage (%s). Maybe you forgot to add @ before keyword ?"
                % garbage)

        if not dbutils.createMissingKeywords(list(newKwDict.keys())):
            # User cancel keyword creation
            return

        kwDict = task.getKeywordDict()
        kwDict.update(newKwDict)
        task.setKeywordDict(kwDict)
        self.session.merge(task)
        self.session.commit()
Example #6
0
    def do_t_urgency(self, line):
        """Defines urgency of a task.
        t_urgency <id> <value>"""

        tokens = parseutils.simplifySpaces(line).split(" ")
        if len(tokens) != 2:
            raise BadUsageException(
                "You must provide a taskId and an urgency value")
        task = self.getTaskFromId(tokens[0])
        try:
            # Do not use isdigit(), so that we can set negative urgency. This
            # make it possible to stick tasks to the bottom of the list.
            urgency = int(tokens[1])
        except ValueError:
            raise BadUsageException("Task urgency must be a digit")

        if urgency > 100:
            tui.warning("Max urgency is 100")
            urgency = 100
        elif urgency < -99:
            tui.warning("Min urgency is -99")
            urgency = -99

        task.urgency = urgency
        self.session.merge(task)
        self.session.commit()
Example #7
0
    def do_t_project(self, line):
        """Set task's project.
        t_project <id> <project>"""
        tokens = parseutils.simplifySpaces(line).split(" ")
        if len(tokens) != 2:
            raise YokadiException("You should give two arguments: <task id> <project>")
        task = self.getTaskFromId(tokens[0])
        projectName = tokens[1]
        projectName = self._realProjectName(projectName)

        task.project = dbutils.getOrCreateProject(projectName)
        if task.project:
            print "Moved task '%s' to project '%s'" % (task.title, projectName)
Example #8
0
    def do_t_project(self, line):
        """Set task's project.
        t_project <id> <project>"""
        tokens = parseutils.simplifySpaces(line).split(" ")
        if len(tokens) != 2:
            raise YokadiException(
                "You should give two arguments: <task id> <project>")
        task = self.getTaskFromId(tokens[0])
        projectName = tokens[1]
        projectName = self._realProjectName(projectName)

        task.project = dbutils.getOrCreateProject(projectName)
        if task.project:
            print "Moved task '%s' to project '%s'" % (task.title, projectName)
Example #9
0
 def do_t_recurs(self, line):
     """Make a task recurs
     t_recurs <id> yearly <dd/mm> <HH:MM>
     t_recurs <id> monthly <dd> <HH:MM>
     t_recurs <id> monthly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm>
     t_recurs <id> quarterly <dd> <HH:MM>
     t_recurs <id> quarterly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm>
     t_recurs <id> weekly <mo, tu, we, th, fr, sa, su> <hh:mm>
     t_recurs <id> daily <HH:MM>
     t_recurs <id> none (remove recurrence)"""
     tokens = parseutils.simplifySpaces(line).split(" ", 1)
     if len(tokens) < 2:
         raise YokadiException("You should give at least two arguments: <task id> <recurrence>")
     task = self.getTaskFromId(tokens[0])
     rule = RecurrenceRule.fromHumaneString(tokens[1])
     task.setRecurrenceRule(rule)
     self.session.commit()
Example #10
0
 def do_t_recurs(self, line):
     """Make a task recurs
     t_recurs <id> yearly <dd/mm> <HH:MM>
     t_recurs <id> monthly <dd> <HH:MM>
     t_recurs <id> monthly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm>
     t_recurs <id> quarterly <dd> <HH:MM>
     t_recurs <id> quarterly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm>
     t_recurs <id> weekly <mo, tu, we, th, fr, sa, su> <hh:mm>
     t_recurs <id> daily <HH:MM>
     t_recurs <id> none (remove recurrence)"""
     tokens = parseutils.simplifySpaces(line).split(" ", 1)
     if len(tokens) < 2:
         raise YokadiException(
             "You should give at least two arguments: <task id> <recurrence>"
         )
     task = self.getTaskFromId(tokens[0])
     rule = RecurrenceRule.fromHumaneString(tokens[1])
     task.setRecurrenceRule(rule)
     self.session.commit()
Example #11
0
    def do_t_add_keywords(self, line):
        """Add keywords to an existing task
        t_add_keywords <id> <@keyword1> <@keyword2>[=<value>]...
        """
        tokens = parseutils.simplifySpaces(line).split(" ", 1)
        if len(tokens) < 2:
            raise YokadiException("You should give at least two arguments: <task id> <keyword>")
        task = dbutils.getTaskFromId(tokens[0])
        garbage, keywordFilters = parseutils.extractKeywords(tokens[1])
        newKwDict = parseutils.keywordFiltersToDict(keywordFilters)
        if garbage:
            raise YokadiException("Cannot parse line, got garbage (%s). Maybe you forgot to add @ before keyword ?"
                                   % garbage)

        if not dbutils.createMissingKeywords(newKwDict.keys()):
            # User cancel keyword creation
            return

        kwDict = task.getKeywordDict()
        kwDict.update(newKwDict)
        task.setKeywordDict(kwDict)
Example #12
0
    def do_t_due(self, line):
        """Set task's due date
        t_due <id> <date>

        Date can be specified as a relative offset:
        - +5M: in 5 minutes
        - +3H: in 3 hours
        - +1D: in 1 day
        - +6W: in 6 weeks

        As a day in the week:
        - tomorrow:      tomorrow, same hour
        - tuesday 12:10: next tuesday, at 12:10
        - fr 15:30:      next friday, at 15:30

        Or as an absolute date or time:
        - 10:38:            at 10:38 today
        - 25/09/2010 12:10: on the 25th of September, 2010, at 12:10
        - 23/02/2010:       on the 23th of February, 2010
        - 01/04:            on the 1st of April
        - 12:               on the 12th of current month

        To reset a due date, use "none"."""
        line = parseutils.simplifySpaces(line)
        if len(line.split()) < 2:
            raise YokadiException(
                "Give a task id and time, date or date & time")
        taskId, line = line.strip().split(" ", 1)
        task = self.getTaskFromId(taskId)

        if line.lower() == "none":
            task.dueDate = None
            print("Due date for task '%s' reset" % task.title)
        else:
            task.dueDate = ydateutils.parseHumaneDateTime(line)
            print("Due date for task '%s' set to %s" %
                  (task.title, task.dueDate.ctime()))
        self.session.merge(task)
        self.session.commit()
Example #13
0
    def do_t_recurs(self, line):
        """Make a task recurs
        t_recurs <id> yearly <dd/mm> <HH:MM>
        t_recurs <id> monthly <dd> <HH:MM>
        t_recurs <id> monthly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm>
        t_recurs <id> quarterly <dd> <HH:MM>
        t_recurs <id> quarterly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm>
        t_recurs <id> weekly <mo, tu, we, th, fr, sa, su> <hh:mm>
        t_recurs <id> daily <HH:MM>
        t_recurs <id> none (remove recurrence)"""
        tokens = parseutils.simplifySpaces(line).split()
        if len(tokens) < 2:
            raise YokadiException("You should give at least two arguments: <task id> <recurrence>")
        task = self.getTaskFromId(tokens[0])

        # Define recurrence:
        freq = byminute = byhour = byweekday = bymonthday = bymonth = None

        tokens[1] = tokens[1].lower()

        if tokens[1] == "none":
            if task.recurrence:
                self.session.delete(task.recurrence)
                task.recurrence = None
            return
        elif tokens[1] == "daily":
            if len(tokens) != 3:
                raise YokadiException("You should give time for daily task")
            freq = rrule.DAILY
            byhour, byminute = ydateutils.getHourAndMinute(tokens[2])
        elif tokens[1] == "weekly":
            freq = rrule.WEEKLY
            if len(tokens) != 4:
                raise YokadiException("You should give day and time for weekly task")
            byweekday = ydateutils.getWeekDayNumberFromDay(tokens[2].lower())
            byhour, byminute = ydateutils.getHourAndMinute(tokens[3])
        elif tokens[1] in ("monthly", "quarterly"):
            if tokens[1] == "monthly":
                freq = rrule.MONTHLY
            else:
                # quarterly
                freq = rrule.YEARLY
                bymonth = [1, 4, 7, 10]
            if len(tokens) < 4:
                raise YokadiException("You should give day and time for %s task" % (tokens[1],))
            try:
                bymonthday = int(tokens[2])
                byhour, byminute = ydateutils.getHourAndMinute(tokens[3])
            except ValueError:
                POSITION = {"first": 1, "second": 2, "third": 3, "fourth": 4, "last":-1}
                if tokens[2].lower() in list(POSITION.keys()) and len(tokens) == 5:
                    byweekday = rrule.weekday(ydateutils.getWeekDayNumberFromDay(tokens[3].lower()),
                                              POSITION[tokens[2]])
                    byhour, byminute = ydateutils.getHourAndMinute(tokens[4])
                    bymonthday = None  # Default to current day number - need to be blanked
                else:
                    raise YokadiException("Unable to understand date. See help t_recurs for details")
        elif tokens[1] == "yearly":
            freq = rrule.YEARLY
            rDate = ydateutils.parseHumaneDateTime(" ".join(tokens[2:]))
            bymonth = rDate.month
            bymonthday = rDate.day
            byhour = rDate.hour
            byminute = rDate.minute
        else:
            raise YokadiException("Unknown frequency. Available: daily, weekly, monthly and yearly")

        if task.recurrence is None:
            task.recurrence = Recurrence()
        rr = rrule.rrule(freq, byhour=byhour, byminute=byminute, byweekday=byweekday,
                         bymonthday=bymonthday, bymonth=bymonth)
        task.recurrence.setRrule(rr)
        task.dueDate = task.recurrence.getNext()
        self.session.merge(task)
        self.session.commit()
Example #14
0
def computeCompleteParameterPosition(text, line, begidx, endidx):
    before = parseutils.simplifySpaces(line[:begidx].strip())
    return before.count(" ") + 1
Example #15
0
def parseHumaneDateTime(line, hint=None, today=None):
    """Parse human date and time and return structured datetime object
    Datetime  can be absolute (23/10/2008 10:38) or relative (+5M, +3H, +1D, +6W)
    @param line: human date / time
    @param hint: optional hint to tell whether time should be set to the
    beginning or the end of the day when not specified.
    @param today: optional parameter to define a fake today date. Useful for
    unit testing.
    @type line: str
    @return: datetime object"""
    def guessDate(text):
        out, fmt = testFormats(text, DATE_FORMATS)
        if not out:
            return None
        if not "%y" in fmt and not "%Y" in fmt:
            out = out.replace(year=today.year)
        return out.date()

    def applyTimeHint(date, hint):
        if not hint:
            return date
        if hint == TIME_HINT_BEGIN:
            return date.replace(hour=0, minute=0, second=0)
        elif hint == TIME_HINT_END:
            return date.replace(hour=23, minute=59, second=59)
        else:
            raise Exception("Unknown hint %s" % hint)

    line = parseutils.simplifySpaces(line).lower()
    if not line:
        raise YokadiException("Date is empty")

    if today is None:
        today = datetime.today().replace(microsecond=0)

    if line == "now":
        return today

    if line == "today":
        return applyTimeHint(today, hint)

    # Check for "+<delta>" format
    if line.startswith("+"):
        return today + parseDateTimeDelta(line[1:])
    if line.startswith("-"):
        return today - parseDateTimeDelta(line[1:])

    # Check for "<weekday> [<time>]" format
    firstWord = line.split()[0]

    weekdayDict = {
        "today": today.weekday(),
        "tomorrow": (today.weekday() + 1) % 7,
        }
    weekdayDict.update(WEEKDAYS)
    weekdayDict.update(SHORT_WEEKDAYS)
    weekday = weekdayDict.get(firstWord)
    if weekday is not None:
        date = today + timedelta(days=(weekday - today.weekday()) % 7)
        if " " in line:
            timeText = line.split(' ', 1)[1]
            tTime = guessTime(timeText)
            if tTime is None:
                raise YokadiException("Unable to understand time '%s'" % timeText)
            date = datetime.combine(date, tTime)
        else:
            date = applyTimeHint(date, hint)
        return date

    if " " in line:
        # Absolute date and time?
        dateText, timeText = line.split(' ', 1)
        tDate = guessDate(dateText)
        if tDate is not None:
            tTime = guessTime(timeText)
            if tTime is not None:
                return datetime.combine(tDate, tTime)

    # Only date?
    tDate = guessDate(line)
    if tDate is not None:
        dt = datetime.combine(tDate, today.time())
        return applyTimeHint(dt, hint)

    # Only time?
    tTime = guessTime(line)
    if tTime is not None:
        tDate = datetime.combine(today.date(), tTime)
        if tTime > today.time():
            return tDate
        else:
            return tDate + timedelta(days=1)

    raise YokadiException("Unable to understand date '%s'" % line)
Example #16
0
def parseHumaneDateTime(line, hint=None, today=None):
    """Parse human date and time and return structured datetime object
    Datetime  can be absolute (23/10/2008 10:38) or relative (+5M, +3H, +1D, +6W)
    @param line: human date / time
    @param hint: optional hint to tell whether time should be set to the
    beginning or the end of the day when not specified.
    @param today: optional parameter to define a fake today date. Useful for
    unit testing.
    @type line: str
    @return: datetime object"""
    def guessDate(text):
        out, fmt = testFormats(text, DATE_FORMATS)
        if not out:
            return None
        if not "%y" in fmt and not "%Y" in fmt:
            out = out.replace(year=today.year)
        return out.date()

    def applyTimeHint(date, hint):
        if not hint:
            return date
        if hint == TIME_HINT_BEGIN:
            return date.replace(hour=0, minute=0, second=0)
        elif hint == TIME_HINT_END:
            return date.replace(hour=23, minute=59, second=59)
        else:
            raise Exception("Unknown hint %s" % hint)

    line = parseutils.simplifySpaces(line).lower()
    if not line:
        raise YokadiException("Date is empty")

    if today is None:
        today = datetime.today().replace(microsecond=0)

    if line == "now":
        return today

    if line == "today":
        return applyTimeHint(today, hint)

    # Check for "+<delta>" format
    if line.startswith("+"):
        return today + parseDateTimeDelta(line[1:])
    if line.startswith("-"):
        return today - parseDateTimeDelta(line[1:])

    # Check for "<weekday> [<time>]" format
    firstWord = line.split()[0]

    weekdayDict = {
        "today": today.weekday(),
        "tomorrow": (today.weekday() + 1) % 7,
    }
    weekdayDict.update(WEEKDAYS)
    weekdayDict.update(SHORT_WEEKDAYS)
    weekday = weekdayDict.get(firstWord)
    if weekday is not None:
        date = today + timedelta(days=(weekday - today.weekday()) % 7)
        if " " in line:
            timeText = line.split(' ', 1)[1]
            tTime = guessTime(timeText)
            if tTime is None:
                raise YokadiException("Unable to understand time '%s'" %
                                      timeText)
            date = datetime.combine(date, tTime)
        else:
            date = applyTimeHint(date, hint)
        return date

    if " " in line:
        # Absolute date and time?
        dateText, timeText = line.split(' ', 1)
        tDate = guessDate(dateText)
        if tDate is not None:
            tTime = guessTime(timeText)
            if tTime is not None:
                return datetime.combine(tDate, tTime)

    # Only date?
    tDate = guessDate(line)
    if tDate is not None:
        dt = datetime.combine(tDate, today.time())
        return applyTimeHint(dt, hint)

    # Only time?
    tTime = guessTime(line)
    if tTime is not None:
        tDate = datetime.combine(today.date(), tTime)
        if tTime > today.time():
            return tDate
        else:
            return tDate + timedelta(days=1)

    raise YokadiException("Unable to understand date '%s'" % line)
Example #17
0
def computeCompleteParameterPosition(text, line, begidx, endidx):
    before = parseutils.simplifySpaces(line[:begidx].strip())
    return before.count(" ") + 1
Example #18
0
    def do_t_recurs(self, line):
        """Make a task recurs
        t_recurs <id> yearly <dd/mm> <HH:MM>
        t_recurs <id> monthly <dd> <HH:MM>
        t_recurs <id> monthly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm>
        t_recurs <id> quarterly <dd> <HH:MM>
        t_recurs <id> quarterly <first/second/third/last> <mo, tu, we, th, fr, sa, su> <hh:mm>
        t_recurs <id> weekly <mo, tu, we, th, fr, sa, su> <hh:mm>
        t_recurs <id> daily <HH:MM>
        t_recurs <id> none (remove recurrence)"""
        tokens = parseutils.simplifySpaces(line).split()
        if len(tokens) < 2:
            raise YokadiException(
                "You should give at least two arguments: <task id> <recurrence>"
            )
        task = self.getTaskFromId(tokens[0])

        # Define recurrence:
        freq = byminute = byhour = byweekday = bymonthday = bymonth = None

        tokens[1] = tokens[1].lower()

        if tokens[1] == "none":
            if task.recurrence:
                task.recurrence.destroySelf()
                task.recurrence = None
            return
        elif tokens[1] == "daily":
            if len(tokens) != 3:
                raise YokadiException("You should give time for daily task")
            freq = rrule.DAILY
            byhour, byminute = ydateutils.getHourAndMinute(tokens[2])
        elif tokens[1] == "weekly":
            freq = rrule.WEEKLY
            if len(tokens) != 4:
                raise YokadiException(
                    "You should give day and time for weekly task")
            byweekday = ydateutils.getWeekDayNumberFromDay(tokens[2].lower())
            byhour, byminute = ydateutils.getHourAndMinute(tokens[3])
        elif tokens[1] in ("monthly", "quarterly"):
            if tokens[1] == "monthly":
                freq = rrule.MONTHLY
            else:
                # quarterly
                freq = rrule.YEARLY
                bymonth = [1, 4, 7, 10]
            if len(tokens) < 4:
                raise YokadiException(
                    "You should give day and time for %s task" % (tokens[1], ))
            try:
                bymonthday = int(tokens[2])
                byhour, byminute = ydateutils.getHourAndMinute(tokens[3])
            except ValueError:
                POSITION = {
                    "first": 1,
                    "second": 2,
                    "third": 3,
                    "fourth": 4,
                    "last": -1
                }
                if tokens[2].lower() in POSITION.keys() and len(tokens) == 5:
                    byweekday = rrule.weekday(
                        ydateutils.getWeekDayNumberFromDay(tokens[3].lower()),
                        POSITION[tokens[2]])
                    byhour, byminute = ydateutils.getHourAndMinute(tokens[4])
                    bymonthday = None  # Default to current day number - need to be blanked
                else:
                    raise YokadiException(
                        "Unable to understand date. See help t_recurs for details"
                    )
        elif tokens[1] == "yearly":
            freq = rrule.YEARLY
            rDate = ydateutils.parseHumaneDateTime(" ".join(tokens[2:]))
            bymonth = rDate.month
            bymonthday = rDate.day
            byhour = rDate.hour
            byminute = rDate.minute
        else:
            raise YokadiException(
                "Unknown frequency. Available: daily, weekly, monthly and yearly"
            )

        if task.recurrence is None:
            task.recurrence = Recurrence()
        rr = rrule.rrule(freq,
                         byhour=byhour,
                         byminute=byminute,
                         byweekday=byweekday,
                         bymonthday=bymonthday,
                         bymonth=bymonth)
        task.recurrence.setRrule(rr)
        task.dueDate = task.recurrence.getNext()