def connectDatabase(dbFileName, createIfNeeded=True): """Connect to database and create it if needed @param dbFileName: path to database file @type dbFileName: str @param createIfNeeded: Indicate if database must be created if it does not exists (default True) @type createIfNeeded: bool""" dbFileName=os.path.abspath(dbFileName) if sys.platform == 'win32': connectionString = 'sqlite:/'+ dbFileName[0] +'|' + dbFileName[2:] else: connectionString = 'sqlite:' + dbFileName connection = connectionForURI(connectionString) sqlhub.processConnection = connection if not os.path.exists(dbFileName): if createIfNeeded: print "Creating database" createTables() # Set database version according to current yokadi release Config(name=DB_VERSION_KEY, value=str(DB_VERSION), system=True, desc="Database schema release number") else: print "Database file (%s) does not exist or is not readable. Exiting" % dbFileName sys.exit(1) # Check version version = getVersion() if version != DB_VERSION: tui.error("Your database version is %d but Yokadi wants version %d." \ % (version, DB_VERSION)) print "Please, run the %s/update.py script to migrate your database prior to running Yokadi" % \ os.path.abspath(utils.shareDirPath()) sys.exit(1)
def do_a_remove(self, line): """Remove an alias""" if line in self.aliases: del self.aliases[line] aliases = Config.selectBy(name="ALIASES")[0] aliases.value = repr(self.aliases) else: tui.error("No alias with that name. Use a_list to display all aliases")
def __init__(self): try: self.aliases = eval(Config.byName("ALIASES").value) except SQLObjectNotFound: self.aliases = {} except Exception, e: tui.error("Aliases syntax error. Ignored") self.aliases = {}
def warnIfKeywordDoesNotExist(keywordFilters): """Warn user is keyword does not exist @return: True if at least one keyword does not exist, else False""" doesNotExist = False for keyword in [k.name for k in keywordFilters]: if Keyword.select(LIKE(Keyword.q.name, keyword)).count() == 0: tui.error("Keyword %s is unknown." % keyword) doesNotExist = True return doesNotExist
def onecmd(self, line): """This method is subclassed just to be able to encapsulate it with a try/except bloc""" try: # Decode user input line=line.decode(tui.ENCODING) return Cmd.onecmd(self, line) except YokadiOptionParserNormalExitException: pass except YokadiException, e: tui.error("*** Yokadi error ***\n\t%s" % e)
def onecmd(self, line): """This method is subclassed just to be able to encapsulate it with a try/except bloc""" try: # Decode user input line = line.decode(tui.ENCODING) return Cmd.onecmd(self, line) except YokadiOptionParserNormalExitException: pass except UnicodeDecodeError, e: tui.error("Unicode decoding error. Please check you locale and terminal settings (%s)." % e)
def run(): athlete_data = read_data("athlete_events.csv") # keeps repeating unless break while True: selection = tui.menu() if selection == "years": process.list_years(athlete_data) elif selection == "tally": process.tally_medals(athlete_data) elif selection == "team": process.tally_team_medals(athlete_data) elif selection == "exit": break else: tui.error("Invalid Selection!")
def run(): data = read_data("athlete_events.csv") # Data variable is the result of the call to function 'read_data' while True: # Menu selection selection = tui.menu() if selection == "years": process.list_years(data) elif selection == "tally": process.tally_medals(data) elif selection == "ctally": process.tally_team_medals(data) elif selection == "exit": break # Exits (or breaks) the run of the program else: tui.error("Invalid Selection!")
def do_c_set(self, line): """Set a configuration key to value : c_set <key> <value>""" line = line.split() if len(line) < 2: raise YokadiException("You should provide two arguments : the parameter key and the value") name = line[0] value = " ".join(line[1:]) p = Config.select(AND(Config.q.name == name, Config.q.system == False)) if p.count() == 0: tui.error("Sorry, no parameter match") else: if self.checkParameterValue(name, value): p[0].value = value tui.info("Parameter updated") else: tui.error("Parameter value is incorrect")
def run(): athlete_data = read_data("athlete_events.csv") # The run function also contains the keywords pass. # These should be replaced with appropriate calls to the functions in the module process once this has been implemented while True: selection = tui.menu() if selection == "years": process.list_years(athlete_data) elif selection == "tally": process.tally_medals(athlete_data) elif selection == "team": process.tally_team_medals(athlete_data) elif selection == "exit": break else: tui.error("Invalid Selection!")
def _t_add(self, cmd, line): """Code shared by t_add and bug_add.""" line = line.strip() if not line: tui.error("Missing parameters") self.do_help(cmd) return None projectName, title, keywordDict = parseutils.parseLine(line) if not title: tui.error("Missing title") self.do_help(cmd) return None task = dbutils.addTask(projectName, title, keywordDict) if not task: tui.reinjectInRawInput(u"%s %s" % (cmd, line)) return None self.lastTaskId = task.id return task
def main(): if len(sys.argv) > 1: term = sys.argv[1] else: term = showPrompt(hasTopic=False) if term is "": return while True: questions = [] matches = [] index = 1 for source in SOURCES: lst = findMatches(source, term) for keyword in lst: matches.append((source, keyword)) questions.append((index, "%s (%s)" % (keyword, source))) index += 1 if len(matches) > 0: print print "# Matching topics:" for pos, txt in questions: print "%2d: %s" % (pos, txt) else: tui.error("No match found") print answer = showPrompt(hasTopic=len(matches) > 0) if answer.isdigit(): index = int(answer) - 1 if index < 0 or index >= len(matches): tui.error("Wrong topic number") continue source, keyword = matches[index] showDoc(source, keyword) elif answer == "": return else: term = answer
def parse(self, line): """Parse given line to create a keyword filter""" operators = ("=<", ">=", "!=", "<", ">", "=") if " " in line: tui.error("No space in keyword filter !") return if line.startswith("!"): self.negative = True line = line[1:] if not line.startswith("@"): tui.error("Keyword name must be be prefixed with a @") return line = line[1:] # Squash @ line = line.replace("==", "=") # Tolerate == syntax for operator in operators: if operator in line: self.name, self.value = line.split(operator, 1) self.valueOperator = operator try: self.value = int(self.value) except ValueError: tui.error("Keyword value must be an integer (got %s)" % (self.value, self.name)) return break # Exit operator loop else: # No operator found, only keyword name has been provided self.name, self.value = line, None
def extractKeywords(line): """Extract keywords (@k1 @k2=n..) from line @param line: line from which keywords are extracted @returns: (remaining_text, {keywordDict})""" keywordDict = {} remainingText=[] for token in line.split(): if token.startswith("@"): token=token[1:] if "=" in token: keyword, value = token.split("=", 1) try: value = int(value) except ValueError: tui.error("Keyword value must be an integer (got %s). Removing value for %s keyword" % (value, keyword)) value = None else: keyword, value = token, None keywordDict[keyword] = value else: remainingText.append(token) return (u" ".join(remainingText), keywordDict)
def do_t_list(self, line): def selectRendererClass(): if options.format != "auto": return gRendererClassDict[options.format] defaultRendererClass = TextListRenderer if not options.output: return defaultRendererClass ext = os.path.splitext(options.output)[1] if not ext: return defaultRendererClass return gRendererClassDict.get(ext[1:], defaultRendererClass) #BUG: completion based on parameter position is broken when parameter is given parser = self.parser_t_list() options, args = parser.parse_args(line) if len(args) > 0: projectName, keywordFilters = parseutils.extractKeywords(u" ".join(args)) 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 = projectName[1:] projectList = Project.select(NOT(LIKE(Project.q.name, projectName))) else: projectList = Project.select(LIKE(Project.q.name, projectName)) if projectList.count() == 0: tui.error("Found no project matching '%s'" % projectName) return # Check keywords exist parseutils.warnIfKeywordDoesNotExist(keywordFilters) # Filtering and sorting according to parameters filters = [] # Filter on keywords for keywordFilter in keywordFilters: filters.append(keywordFilter.filter()) order = -Task.q.urgency, Task.q.creationDate limit = None if options.done: filters.append(Task.q.status == 'done') if options.done != "all": filters.append(parseutils.createFilterFromRange(options.done)) elif not options.all: filters.append(Task.q.status != 'done') if options.topUrgent: order = -Task.q.urgency limit = 5 if options.topDue: filters.append(Task.q.dueDate != None) order = Task.q.dueDate limit = 5 if options.overdue: filters.append(Task.q.dueDate < datetime.now()) order = Task.q.dueDate if options.search: for word in options.search: filters.append(OR(LIKE(Task.q.title, "%" + word + "%"), LIKE(Task.q.description, "%" + word + "%"))) # Define output if options.output: out = open(options.output, "w") else: out = tui.stdout # Instantiate renderer rendererClass = selectRendererClass() renderer = rendererClass(out) # Fill the renderer if options.keyword: if options.keyword.startswith("@"): options.keyword = options.keyword[1:] for keyword in Keyword.select(LIKE(Keyword.q.name, options.keyword)): if unicode(keyword.name).startswith("_") and not options.keyword.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: continue renderer.addTaskList(unicode(keyword), taskList) # Call renderer 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: continue renderer.addTaskList(unicode(project), taskList) renderer.end() if len(hiddenProjectNames) > 0: tui.info("hidden projects: %s" % ", ".join(hiddenProjectNames))
try: # Decode user input line=line.decode(tui.ENCODING) return Cmd.onecmd(self, line) except YokadiOptionParserNormalExitException: pass except YokadiException, e: tui.error("*** Yokadi error ***\n\t%s" % e) except IOError, e: # We can get I/O errors when yokadi is piped onto another shell commands # that breaks. print >>sys.stderr, "*** I/O error ***\n\t%s" % e except KeyboardInterrupt: print "*** Break ***" except Exception, e: tui.error("Unhandled exception (oups)\n\t%s" % e) print "This is a bug of Yokadi, sorry." print "Send the above message by email to Yokadi developers ([email protected]) to help them make Yokadi better." cut="---------------------8<----------------------------------------------" print cut traceback.print_exc() print "--" print "Python: %s" % sys.version.replace("\n", " ") print "SQLObject: %s" % sqlobjectVersion.replace("\n", " ") print "OS: %s (%s)" % os.uname()[0:3:2] print "Yokadi: %s" % utils.currentVersion() print cut print def loadHistory(self): """Tries to load previous history list from disk"""
def do_t_list(self, line): doneRangeList= ["today", "thisweek", "thismonth"] def keywordDictIsSubsetOf(taskKeywordDict, wantedKeywordDict): # Returns true if taskKeywordDict is a subset of wantedKeywordDict # taskKeywordDict is considered a subset of wantedKeywordDict if: # 1. All wantedKeywordDict keys are in taskKeywordDict # 2. All wantedKeywordDict valued keywords have the same value # in taskKeywordDict for wantedKeyword, wantedValue in wantedKeywordDict.items(): if not wantedKeyword in taskKeywordDict: return False if wantedValue and taskKeywordDict[wantedKeyword] != wantedValue: return False return True def taskHasWantedKeywordDict(task, wantedKeywordDict): """ @param task: task object @param wantedKeywordDict: dict of name/value of wanted keyword # a task is considered a subset of wantedKeywordDict if: # 1. All wantedKeywordDict keys are in task or project keywords # 2. All wantedKeywordDict valued keywords have the same value # in task or project keyword""" for wantedKeyword, wantedValue in wantedKeywordDict.items(): taskFilters=[Task.q.id==task.id, TaskKeyword.q.taskID==task.id, TaskKeyword.q.keywordID==Keyword.q.id, LIKE(Keyword.q.name, wantedKeyword)] projectFilters=[Project.q.id==task.projectID, ProjectKeyword.q.projectID==Project.q.id, ProjectKeyword.q.keyword==Keyword.q.id, LIKE(Keyword.q.name, wantedKeyword)] if wantedValue: taskFilters.append(TaskKeyword.q.value==wantedValue) projectFilters.append(ProjectKeyword.q.value==wantedValue) if Task.select(AND(*taskFilters)).count()==0 and Task.select(AND(*projectFilters)).count()==0: return False # All critera were met, return ok return True def createFilterFromRange(_range): # Parse the _range string and return an SQLObject filter minDate = date.today() if _range == "today": pass elif _range == "thisweek": minDate -= timedelta(minDate.weekday()) elif _range == "thismonth": minDate = minDate.replace(day = 1) else: raise YokadiException("Invalid range value '%s'" % _range) return Task.q.doneDate>=minDate def selectRendererClass(): if options.format != "auto": return gRendererClassDict[options.format] defaultRendererClass = TextListRenderer if not options.output: return defaultRendererClass ext = os.path.splitext(options.output)[1] if not ext: return defaultRendererClass return gRendererClassDict.get(ext[1:], defaultRendererClass) #BUG: completion based on parameter position is broken when parameter is given parser = self.parser_t_list() options, args = parser.parse_args(line) if len(args) > 0: projectName, keywordDict = parseutils.extractKeywords(u" ".join(args)) else: projectName = "" keywordDict = {} if not projectName: # Take all project if none provided projectName="%" projectList = Project.select(LIKE(Project.q.name, projectName)) if projectList.count()==0: tui.error("Found no project matching '%s'" % projectName) return # Check keywords exist for keyword in keywordDict.keys(): if Keyword.select(LIKE(Keyword.q.name, keyword)).count()==0: tui.error("Keyword %s is unknown." % keyword) # Filtering and sorting according to parameters filters=[] order=-Task.q.urgency, Task.q.creationDate limit=None if options.done: filters.append(Task.q.status=='done') if options.done != "all": filters.append(createFilterFromRange(options.done)) elif not options.all: filters.append(Task.q.status!='done') if options.topUrgent: order=-Task.q.urgency limit=5 if options.topDue: filters.append(Task.q.dueDate!=None) order=Task.q.dueDate limit=5 if options.search: for word in options.search: filters.append(OR(LIKE(Task.q.title, "%"+word+"%"), LIKE(Task.q.description, "%"+word+"%"))) # Define output if options.output: out = open(options.output, "w") else: out = tui.stdout # Instantiate renderer rendererClass = selectRendererClass() renderer = rendererClass(out) # Fill the renderer if options.keyword: if options.keyword.startswith("@"): options.keyword = options.keyword[1:] for keyword in Keyword.select(LIKE(Keyword.q.name, options.keyword)): if unicode(keyword.name).startswith("_") and not options.keyword.startswith("_"): #BUG: cannot filter on db side because sqlobject does not understand ESCAPE needed whith _ continue taskList = Task.select(AND(TaskKeyword.q.taskID == Task.q.id, TaskKeyword.q.keywordID == keyword.id, *filters), orderBy=order, limit=limit) taskList = list(taskList) if keywordDict: # FIXME: factorize (see project oriented rendering below) taskList = [x for x in taskList if taskHasWantedKeywordDict(x, keywordDict)] if projectList: taskList = [x for x in taskList if x.project in projectList] if len(taskList) == 0: continue renderer.addTaskList(unicode(keyword), taskList) # Call renderer 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) if keywordDict: taskList = [x for x in taskList if taskHasWantedKeywordDict(x, keywordDict)] else: taskList = list(taskList) if len(taskList) == 0: continue renderer.addTaskList(unicode(project), taskList) renderer.end() if len(hiddenProjectNames) > 0: tui.info("hidden projects: %s" % ", ".join(hiddenProjectNames))