Exemple #1
0
    def do_p_edit(self, line):
        """Edit a project.
        p_edit <project name>"""
        project = dbutils.getOrCreateProject(line, createIfNeeded=False)

        if not project:
            raise YokadiException("Project does not exist.")

        # Create project line
        projectLine = parseutils.createLine(project.name, "",
                                            project.getKeywordDict())

        # Edit
        line = tui.editLine(projectLine)

        # Update project
        projectName, garbage, keywordDict = parseutils.parseLine(line)
        if garbage:
            raise BadUsageException("Cannot parse line, got garbage (%s)" %
                                    garbage)
        if not dbutils.createMissingKeywords(keywordDict.keys()):
            return
        try:
            project.name = projectName
        except DuplicateEntryError:
            raise YokadiException(
                "A project named %s already exists. Please find another name" %
                projectName)
        project.setKeywordDict(keywordDict)
Exemple #2
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()
Exemple #3
0
def parseOneWordName(line):
    """Parse line, check it is a one word project name and return it
    @return: the name
    """
    line = line.strip()
    if " " in line:
        raise YokadiException("Name cannot contain spaces")
    if not line:
        raise YokadiException("Name cannot be empty")
    return line
Exemple #4
0
def getKeywordFromName(name):
    """Returns a keyword from its name, which may start with "@"
    raises a YokadiException if not found
    @param name: the keyword name
    @return: The keyword"""
    if not name:
        raise YokadiException("No keyword supplied")
    if name.startswith("@"):
        name = name[1:]
    lst = list(Keyword.selectBy(name=name))
    if len(lst) == 0:
        raise YokadiException("No keyword named '%s' found" % name)
    return lst[0]
Exemple #5
0
def getKeywordFromName(name):
    """Returns a keyword from its name, which may start with "@"
    raises a YokadiException if not found
    @param name: the keyword name
    @return: The keyword"""
    session = db.getSession()
    if not name:
        raise YokadiException("No keyword supplied")
    if name.startswith("@"):
        name = name[1:]
    lst = session.query(Keyword).filter_by(name=name).all()
    if len(lst) == 0:
        raise YokadiException("No keyword named '%s' found" % name)
    return lst[0]
Exemple #6
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
Exemple #7
0
    def do_t_describe(self, line):
        """Starts an editor to enter a longer description of a task.
        t_describe <id>"""
        def updateDescription(description):
            if self.cryptoMgr.isEncrypted(task.title):
                task.description = self.cryptoMgr.encrypt(description)
            else:
                task.description = description

        task = self.getTaskFromId(line)
        try:
            if self.cryptoMgr.isEncrypted(task.title):
                # As title is encrypted, we assume description will be encrypted as well
                self.cryptoMgr.force_decrypt = True  # Decryption must be turned on to edit

            description = tui.editText(
                self.cryptoMgr.decrypt(task.description),
                onChanged=updateDescription,
                lockManager=dbutils.TaskLockManager(task),
                prefix="yokadi-%s-%s-" % (task.project, task.title))
        except Exception as e:
            raise YokadiException(e)
        updateDescription(description)
        self.session.merge(task)
        self.session.commit()
Exemple #8
0
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
Exemple #9
0
    def _processVTodo(self, vTodo):
        if vTodo["UID"] in self.newTask:
            # This is a recent new task but remote ical calendar tool is not
            # aware of new Yokadi UID. Update it here to avoid duplicate new tasks
            print "update UID to avoid duplicate task"
            vTodo["UID"] = TASK_UID % self.newTask[vTodo["UID"]]

        if vTodo["UID"].startswith(UID_PREFIX):
            # This is a yokadi Task.
            if vTodo["LAST-MODIFIED"].dt > vTodo["CREATED"].dt:
                # Task has been modified
                print "Modified task: %s" % vTodo["UID"]
                result = TASK_RE.match(vTodo["UID"])
                if result:
                    id = result.group(1)
                    task = dbutils.getTaskFromId(id)
                    print "Task found in yokadi db: %s" % task.title
                    updateTaskFromVTodo(task, vTodo)
                else:
                    raise YokadiException("Task %s does exist in yokadi db " %
                                          id)
        else:
            # This is a new task
            print "New task %s (%s)" % (vTodo["summary"], vTodo["UID"])
            keywordDict = {}
            task = dbutils.addTask(INBOX_PROJECT,
                                   vTodo["summary"],
                                   keywordDict,
                                   interactive=False)
            # Keep record of new task origin UID to avoid duplicate
            # if user update it right after creation without reloading the
            # yokadi UID
            # TODO: add purge for old UID
            self.newTask[vTodo["UID"]] = task.id
Exemple #10
0
 def exit(self, status=0, msg=None):
     if msg:
         sys.stderr.write(msg)
     if status == 0:
         raise YokadiOptionParserNormalExitException()
     else:
         raise YokadiException(msg)
Exemple #11
0
 def _realProjectName(self, name):
     if name == '_':
         if self.lastProjectName is None:
             raise YokadiException("No previous project used")
     else:
         self.lastProjectName = name
     return self.lastProjectName
Exemple #12
0
    def _parseListLine(self, parser, line):
        """
        Parse line with parser, returns a tuple of the form
        (options, projectList, filters)
        """
        args = parser.parse_args(line)
        if len(args.filter) > 0:
            projectName, keywordFilters = parseutils.extractKeywords(u" ".join(
                args.filter))
        else:
            projectName = ""
            keywordFilters = []

        if self.kFilters:
            # Add keyword filter
            keywordFilters.extend(self.kFilters)

        if not projectName:
            if self.pFilter:
                # If a project filter is defined, use it as none was provided
                projectName = self.pFilter
            else:
                # Take all project if none provided
                projectName = "%"

        if projectName.startswith("!"):
            projectName = self._realProjectName(projectName[1:])
            projectList = Project.select(NOT(LIKE(Project.q.name,
                                                  projectName)))
        else:
            projectName = self._realProjectName(projectName)
            projectList = Project.select(LIKE(Project.q.name, projectName))

        if projectList.count() == 0:
            raise YokadiException("Found no project matching '%s'" %
                                  projectName)

        # Check keywords exist
        parseutils.warnIfKeywordDoesNotExist(keywordFilters)

        # Filtering and sorting according to parameters
        filters = []

        # Filter on keywords
        for keywordFilter in keywordFilters:
            filters.append(keywordFilter.filter())

        # Search
        if args.search:
            for word in args.search:
                if word.startswith("@"):
                    tui.warning(
                        "Maybe you want keyword search (without -s option) "
                        "instead of plain text search?")
                filters.append(
                    OR(LIKE(Task.q.title, "%" + word + "%"),
                       LIKE(Task.q.description, "%" + word + "%")))

        return args, projectList, filters
Exemple #13
0
 def do_c_set(self, line):
     """Set a configuration key to value : c_set <key> <value>"""
     line = line.split()
     if len(line) < 2:
         raise BadUsageException("You should provide two arguments : the parameter key and the value")
     name = line[0]
     value = " ".join(line[1:])
     session = db.getSession()
     p = session.query(Config).filter_by(name=name, system=False)
     if p.count() == 0:
         raise YokadiException("Sorry, no parameter match")
     else:
         if self.checkParameterValue(name, value):
             p[0].value = value
             tui.info("Parameter updated")
         else:
             raise YokadiException("Parameter value is incorrect")
Exemple #14
0
 def getTaskFromId(self, tid):
     if tid == '_':
         if self.lastTaskId is None:
             raise YokadiException("No previous task defined")
         tid = self.lastTaskId
     task = dbutils.getTaskFromId(tid)
     if tid != '_':
         self.lastTaskId = task.id
     return task
Exemple #15
0
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
Exemple #16
0
    def do_a_edit_name(self, line):
        """Edit the name of an alias.
        a_edit_name <alias name>"""
        session = db.getSession()
        name = line
        if not name in self.aliases:
            raise YokadiException("There is no alias named {}".format(name))

        newName = tui.editLine(name)
        newName = parseutils.parseOneWordName(newName)

        if newName in self.aliases:
            raise YokadiException("There is already an alias named {}.".format(newName))

        session = db.getSession()
        db.Alias.rename(session, name, newName)
        session.commit()
        self._updateAliasDict()
Exemple #17
0
def getTaskFromId(tid):
    """Returns a task given its id, or raise a YokadiException if it does not
    exist.
    @param tid: taskId string
    @return: Task instance or None if existingTask is False"""
    session = db.getSession()
    # We do not use line.isdigit() because it returns True if line is '¹'!
    try:
        taskId = int(tid)
    except ValueError:
        raise YokadiException("task id should be a number")

    try:
        task = session.query(Task).filter_by(id=taskId).one()
    except NoResultFound:
        raise YokadiException(
            "Task %s does not exist. Use t_list to see all tasks" % taskId)
    return task
Exemple #18
0
 def acquire(self):
     """Acquire a lock for that task and remove any previous stale lock"""
     lock = self._getLock()
     if lock:
         if lock.updateDate < datetime.now() - 2 * timedelta(seconds=tui.MTIME_POLL_INTERVAL):
             # Stale lock, removing
             TaskLock.delete(lock.id)
         else:
             raise YokadiException("Task %s is already locked by process %s" % (lock.task.id, lock.pid))
     TaskLock(task=self.task, pid=os.getpid(), updateDate=datetime.now())
Exemple #19
0
def parseDateTimeDelta(line):
    # FIXME: Do we really want to support float deltas?
    try:
        delta = float(line[:-1])
    except ValueError:
        raise YokadiException("Timeshift must be a float or an integer")

    suffix = line[-1].upper()
    if suffix == "W":
        return timedelta(days=delta * 7)
    elif suffix == "D":
        return timedelta(days=delta)
    elif suffix == "H":
        return timedelta(hours=delta)
    elif suffix == "M":
        return timedelta(minutes=delta)
    else:
        raise YokadiException(
            "Unable to understand time shift. See help t_set_due")
Exemple #20
0
def getVersion():
    if not Config.tableExists():
        # There was no Config table in v1
        return 1

    try:
        return int(Config.byName(DB_VERSION_KEY).value)
    except SQLObjectNotFound:
        raise YokadiException(
            "Configuration key '%s' does not exist. This should not happen!" %
            DB_VERSION_KEY)
Exemple #21
0
    def do_p_edit(self, line):
        """Edit a project.
        p_edit <project name>"""
        session = db.getSession()
        project = dbutils.getOrCreateProject(line, createIfNeeded=False)

        if not project:
            raise YokadiException("Project does not exist.")

        # Edit
        line = tui.editLine(project.name)

        # Update project
        projectName = parseutils.parseOneWordName(line)
        try:
            project.name = projectName
            session.commit()
        except IntegrityError:
            session.rollback()
            raise YokadiException("A project named %s already exists. Please find another name" % projectName)
Exemple #22
0
 def default(self, line):
     nline = resolveAlias(line, self.aliases)
     if nline != line:
         return self.onecmd(nline)
     elif nline.isdigit():
         self.do_t_show(nline)
     elif nline == "_":
         self.do_t_show(nline)
     else:
         raise YokadiException(
             "Unknown command. Use 'help' to see all available commands")
Exemple #23
0
    def _parseListLine(self, parser, line):
        """
        Parse line with parser, returns a tuple of the form
        (options, projectList, filters)
        """
        args = parser.parse_args(line)
        if len(args.filter) > 0:
            projectName, filters = parseutils.extractKeywords(" ".join(
                args.filter))
        else:
            projectName = ""
            filters = []

        if self.kFilters:
            # Add keyword filter
            filters.extend(self.kFilters)

        if not projectName:
            if self.pFilter:
                # If a project filter is defined, use it as none was provided
                projectName = self.pFilter
            else:
                # Take all project if none provided
                projectName = "%"

        if projectName.startswith("!"):
            projectName = self._realProjectName(projectName[1:])
            projectList = self.session.query(Project).filter(
                Project.name.notlike(projectName)).all()
        else:
            projectName = self._realProjectName(projectName)
            projectList = self.session.query(Project).filter(
                Project.name.like(projectName)).all()

        if len(projectList) == 0:
            raise YokadiException("Found no project matching '%s'" %
                                  projectName)

        # Check keywords exist
        parseutils.warnIfKeywordDoesNotExist(filters)

        # Search
        if args.search:
            for word in args.search:
                if word.startswith("@"):
                    tui.warning(
                        "Maybe you want keyword search (without -s option) "
                        "instead of plain text search?")
                condition = or_(Task.title.like("%" + word + "%"),
                                Task.description.like("%" + word + "%"))
                filters.append(DbFilter(condition))

        return args, projectList, filters
Exemple #24
0
def parseKeyword(line):
    """Parse given line to create a keyword filter
    @return: a KeywordFilter instance"""
    operators = ("!=", "=")
    if " " in line:
        raise YokadiException("Keyword filter should not contain spaces")

    name = None
    negative = False
    value = None
    valueOperator = None

    if line.startswith("!"):
        negative = True
        line = line[1:]

    if not line.startswith("@"):
        raise YokadiException("Keyword name must be prefixed with a @")

    line = line[1:]  # Squash @
    line = line.replace("==", "=")  # Tolerate == syntax
    for operator in operators:
        if operator in line:
            name, value = line.split(operator, 1)
            valueOperator = operator
            try:
                value = int(value)
            except ValueError:
                raise YokadiException(
                    "Value of %s keyword must be an integer (got %s)" %
                    (name, value))
            break
    else:
        # No operator found, only keyword name has been provided
        name = line

    return KeywordFilter(name,
                         negative=negative,
                         value=value,
                         valueOperator=valueOperator)
Exemple #25
0
 def do_c_get(self, line):
     parser = self.parser_c_get()
     args = parser.parse_args(line)
     key = args.key
     if not key:
         key = "%"
     k = Config.select(
         AND(LIKE(Config.q.name, key), Config.q.system == args.system))
     fields = [(x.name, "%s (%s)" % (x.value, x.desc)) for x in k]
     if fields:
         tui.renderFields(fields)
     else:
         raise YokadiException("Configuration key %s does not exist" % line)
Exemple #26
0
 def do_c_get(self, line):
     parser = self.parser_c_get()
     args = parser.parse_args(line)
     key = args.key
     if not key:
         key = "%"
     session = db.getSession()
     k = session.query(Config).filter(Config.name.like(key)).filter_by(system=args.system).all()
     fields = [(x.name, "%s (%s)" % (x.value, x.desc)) for x in k]
     if fields:
         tui.renderFields(fields)
     else:
         raise YokadiException("Configuration key %s does not exist" % line)
Exemple #27
0
    def getVersion(self):
        if not self.engine.has_table("config"):
            # There was no Config table in v1
            return 1

        try:
            return int(
                self.session.query(Config).filter_by(
                    name=DB_VERSION_KEY).one().value)
        except NoResultFound:
            raise YokadiException(
                "Configuration key '%s' does not exist. This should not happen!"
                % DB_VERSION_KEY)
Exemple #28
0
def getProjectFromName(name, parameterName="project_name"):
    """
    Helper function which returns a project given its name, or raise a
    YokadiException if it does not exist.
    """
    name = name.strip()
    if len(name) == 0:
        raise BadUsageException("Missing <%s> parameter" % parameterName)

    try:
        session = db.getSession()
        return session.query(Project).filter_by(name=name).one()
    except NoResultFound:
        raise YokadiException("Project '%s' not found. Use p_list to see all projects." % name)
Exemple #29
0
def getWeekDayNumberFromDay(day):
    """Return week day number (0-6) from week day name (short or long)
    @param day: week day as a string in short or long format (in english)
    @type day: str
    @return: week day number (int)"""
    if len(day) == 2 and day in SHORT_WEEKDAYS:
        dayNumber = SHORT_WEEKDAYS[day]
    elif day in WEEKDAYS:
        dayNumber = WEEKDAYS[day]
    else:
        raise YokadiException(
            "Day must be one of the following: [mo]nday, [tu]esday, [we]nesday, [th]ursday, [fr]iday, [sa]turday, [su]nday"
        )
    return dayNumber
Exemple #30
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)
Exemple #31
0
 def __init__(self, message, lineNumber, line):
     fullMessage = "Error line %d (\"%s\"): %s" % (lineNumber + 1, line, message)
     YokadiException.__init__(self, fullMessage)
     self.lineNumber = lineNumber
     self.message = message