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
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()
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())
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
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()
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()
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)
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)
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()
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()
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)
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()
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()
def computeCompleteParameterPosition(text, line, begidx, endidx): before = parseutils.simplifySpaces(line[:begidx].strip()) return before.count(" ") + 1
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)
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()