def main(task_id, action): w = TaskWarrior() # First, make sure that the task id is valid. if action == 'start': task = w.get_task(id=task_id) if task[0] is None: print "Invalid task ID." return task = task[1] print task name = task['description'].replace(",","") # get rid of commas name = "%s %s" % (task_id, name) # store task_id in the name so we can find it again later when we want to mark a task as 'done'... category = "" if 'tags' in task: # preserve tags from TaskWarrior name = "%s, %s " % (name, " ".join(task['tags'])) if 'project' in task: # if there is a project assigned in TW, make it a Project Hamster category name = "%s@%s" % (name, task['project']) subprocess.check_output(['hamster', action, name]) return if action == 'done': if task_id == 0: cur_hamster = subprocess.check_output(['hamster', 'current']).strip().split() task_id = int(cur_hamster[2]) subprocess.check_output(['hamster', 'stop']) w.task_done(id=task_id) return
def get_activities(self, query = None): if not self.source: return [] if self.source == "evo": return [activity for activity in get_eds_tasks() if query is None or activity['name'].startswith(query)] elif self.source == "gtg": conn = self.__get_gtg_connection() if not conn: return [] activities = [] tasks = [] try: tasks = conn.GetTasks() except dbus.exceptions.DBusException: #TODO too lame to figure out how to connect to the disconnect signal self.__gtg_connection = None return self.get_activities(query) # reconnect for task in tasks: if query is None or task['title'].lower().startswith(query): name = task['title'] if len(task['tags']): name = "%s, %s" % (name, " ".join([tag.replace("@", "#") for tag in task['tags']])) activities.append({"name": name, "category": ""}) return activities elif self.source == "task": conn = TaskWarrior () if not conn: return [] activities = [] tasks = [] task_filter = {'status':'pending'} tasks = conn.filter_tasks(task_filter) for task in tasks: name = task['description'].replace(",","") # replace comma category = "" if 'tags' in task: name = "%s, %s " % (name, " ".join(task['tags'])) if 'project' in task: category = task['project'] activities.append({"name":name,"category":category}) return activities
def __init__(self): locale.setlocale(locale.LC_CTYPE, "") self.window = curses.initscr() curses.noecho() curses.cbreak() self.window.keypad(1) self.window.clear() self.window.border() self.lines, self.cols = self.window.getmaxyx() self.day_width = self.cols // 7 self.day_height = (self.lines - 2) // 6 self.task_warrior = TaskWarrior(marshal=True) pending_tasks = self.task_warrior.load_tasks()['pending'] self.tasks = {} now = datetime.now() timezone = LocalTimezone() self.offset = timezone.utcoffset(now) for task in pending_tasks: if 'due' not in task: continue due = task['due'] + self.offset current_date = date(due.year, due.month, due.day) if current_date not in self.tasks: self.tasks[current_date] = list() self.tasks[current_date].append(task)
def synchronize(issues): tw = TaskWarrior() # Load info about the task database tasks = tw.load_tasks() is_bugwarrior_task = lambda task: task.get('description', '').startswith(MARKUP) # Prune down to only tasks managed by bugwarrior for key in tasks.keys(): tasks[key] = filter(is_bugwarrior_task, tasks[key]) # Build a list of only the descriptions of those local bugwarrior tasks local_descs = [t['description'] for t in sum(tasks.values(), []) \ if t['status'] not in ('deleted')] # Now for the remote data. # Build a list of only the descriptions of those remote issues remote_descs = [i['description'] for i in issues] # Build the list of tasks that need to be added is_new = lambda issue: issue['description'] not in local_descs new_issues = filter(is_new, issues) old_issues = filter(lambda i: not is_new(i), issues) # Build the list of local tasks that need to be completed is_done = lambda task: task['description'] not in remote_descs done_tasks = filter(is_done, tasks['pending']) log.struct(new=len(new_issues), completed=len(done_tasks)) # Add new issues for issue in new_issues: log.info("Adding task {0}", issue['description'].encode("utf-8")) tw.task_add(**issue) # Update any issues that may have had new properties added. These are # usually annotations that come from comments in the issue thread. pending_descriptions = [t['description'] for t in tasks['pending']] for upstream_issue in old_issues: if upstream_issue['description'] not in pending_descriptions: continue id, task = tw.get_task(description=upstream_issue['description']) for key in upstream_issue: if key not in task: log.info("Updating {0} on {1}", key, upstream_issue['description'].encode("utf-8")) task[key] = upstream_issue[key] id, task = tw.task_update(task) # Delete old issues for task in done_tasks: log.info("Completing task {0}", task['description'].encode("utf-8")) tw.task_done(uuid=task['uuid'])
def synchronize(issues): tw = TaskWarrior() # Load info about the task database tasks = tw.load_tasks() is_bugwarrior_task = lambda task: task['description'].startswith(MARKUP) # Prune down to only tasks managed by bugwarrior for key in tasks.keys(): tasks[key] = filter(is_bugwarrior_task, tasks[key]) # Build a list of only the descriptions of those local bugwarrior tasks local_descs = [t['description'] for t in sum(tasks.values(), [])] # Now for the remote data. # Build a list of only the descriptions of those remote issues remote_descs = [i['description'] for i in issues] # Build the list of tasks that need to be added is_new = lambda issue: issue['description'] not in local_descs new_issues = filter(is_new, issues) # Build the list of local tasks that need to be completed is_done = lambda task: task['description'] not in remote_descs done_tasks = filter(is_done, tasks['pending']) log.struct(new=len(new_issues), completed=len(done_tasks)) for issue in new_issues: log.info("Adding task {0}", issue['description']) tw.task_add(**issue) for task in done_tasks: log.info("Completing task {0}", task['description']) tw.task_done(id=None, uuid=task['uuid'])
def setup(self): # Create some temporary config stuff fd, fname = tempfile.mkstemp(prefix='taskw-testsrc') dname = tempfile.mkdtemp(prefix='taskw-tests-data') with open(fname, 'w') as f: f.writelines(['data.location=%s' % dname]) # Create empty .data files for piece in ['completed', 'pending', 'undo']: with open(os.path.sep.join([dname, piece + '.data']), 'w'): pass # Save names for .tearDown() self.fname, self.dname = fname, dname # Create the taskwarrior db object that each test will use. self.tw = TaskWarrior(config_filename=fname)
def add_taskwarrior_task(current_balance, tags, priority='M'): ''' Add or update the taskwarrior task. If a task with the description given in TASK_DESC and the given tags already exists. The task description will be updated to the value of the current_balance if changed. Otherwise a new task will be added. ''' task_filter = {'tags': tags, 'description': TASK_DESC} desc = " ".join([TASK_DESC, str(current_balance)]) tw = TaskWarrior() task = tw.filter_tasks(task_filter) if task: old_desc = task['description'] old_balance = float(old_desc.split()[-1]) if current_balance < old_balance: task.update({'description': desc, 'priority': priority}) tw.task_update(task) else: tw.task_add(desc, tags=tags, priority=priority)
#! /usr/bin/python3 from taskw import TaskWarrior from myschool_student import * import re if __name__ == "__main__": tw = TaskWarrior() assignment_list = get_assignment_list() tasks = tw.load_tasks()["pending"] for asg in assignment_list: if asg.handin: continue courseid = asg.courseid[6:] taskdescr = "{}: {}".format(courseid, asg.name) if not any([taskdescr in t.values() for t in tasks]): tw.task_add(taskdescr, project="homework:" + courseid, due=asg.due_datetime) print("Task due: {}".format(int(asg.due_datetime.timestamp()))) print('taskw: Adding task "{}"'.format(taskdescr)) elif asg.handin: print('taskw: Moving task "{}" completed.'.format(taskdescr)) tw.task_done(task)
def complete_task(tw: TaskWarrior, task: Dict[str, Any]) -> None: r = tw.task_done(webdesk_key=task['webdesk_key']) logger.log(logging.INFO + 5, 'Completed task: %s', r['description'])
from taskw import TaskWarrior w = TaskWarrior() tasks = w.load_tasks() # replace_tags = { # 'trello_prio:_high': 'trello_high_priority', # 'trello_prio:_med': 'trello_medium_priority', # 'trello_prio:_low': 'trello_low_priority', # 'github_awaiting____': 'github_awaiting', # } done_trello_lists = [ "PR Sent", "Merged in Develop", "Merged in Candidate", "Merged in Master", "Merged in Release", "Done", ] replace_projects = { "Project Cinnamon": "cinnamon", "Development: Backend Services": "services", "Merchant Widget": "widget", "Development: Adgo App": "monolith", } priority_map = { "trello_high_priority": "H", "trello_medium_priority": "M",
class TaskWarriorSide(GenericSide): """Handles interaction with the TaskWarrior client.""" def __init__(self, **kargs): super(TaskWarriorSide, self).__init__() # Tags are used to filter the tasks for both *push* and *pull*. self.config = { "tags": [], "config_filename": "~/.taskrc", "enable_caching": True } self.config.update(**kargs) assert isinstance(self.config["tags"], list), "Expected a list of tags" # TaskWarrior instance as a class memeber - initialize only once self.tw = TaskWarrior(marshal=True, config_filename=self.config["config_filename"]) # All TW tasks self.items: Dict[str, List[dict]] = [] # Whether to refresh the cached list of items self.reload_items = True def _load_all_items(self): """Load all tasks to memory. May return already loaded list of items, depending on the validity of the cache. """ if not self.config["enable_caching"]: self.items = self.tw.load_tasks() return if self.reload_items: self.items = self.tw.load_tasks() self.reload_items = False @overrides def get_all_items(self, **kargs): """Fetch the tasks off the local taskw db. :param kargs: Extra options for the call. * Use the `order_by` arg to specify the order by which to return the items. * Use the `use_ascending_order` boolean flag to specify ascending/descending order * `include_completed` to also include completed tasks [Default: True] :return: list of tasks that exist locally :raises: ValueError in case the order_by key is invalid """ self._load_all_items() tasks = [] if kargs.get("include_completed", True): tasks.extend(self.items["completed"]) tasks.extend(self.items["pending"]) tags = set(self.config["tags"]) tasks = [t for t in tasks if tags.issubset(t.get("tags", []))] if "order_by" in kargs and kargs["order_by"] is not None: if "use_ascending_order" in kargs: assert isinstance(kargs["use_ascending_order"], bool) use_ascending_order = kargs["use_ascending_order"] else: use_ascending_order = True assert (kargs["order_by"] in [ "description", "end", "entry", "id", "modified", "status", "urgency" ] and "Invalid 'order_by' value") tasks.sort(key=lambda t: t[kargs["order_by"]], reverse=not use_ascending_order) return tasks @overrides def get_single_item(self, item_id: str) -> Union[dict, None]: t = self.tw.get_task(id=item_id)[-1] or None assert "status" in t.keys() # type: ignore return t if t["status"] != "deleted" else None # type: ignore @overrides def update_item(self, item_id: str, **changes): """Update an already added item. :raises ValaueError: In case the item is not present in the db """ changes.pop("id", False) t = self.tw.get_task(uuid=UUID(item_id))[-1] # task CLI doesn't allow `imask` unwanted_keys = ["imask", "recur", "rtype", "parent"] for i in unwanted_keys: t.pop(i, False) # taskwarrior doesn't let you explicitly set the update time. # even if you set it it will revert to the time that you call # `tw.task_update` d = dict(t) d.update(changes) self.tw.task_update(d) @overrides def add_item(self, item) -> dict: """Add a new Item as a TW task. :param item: This should contain only keys that exist in standard TW tasks (e.g., proj, tag, due). It is mandatory that it contains the 'description' key for the task title """ assert "description" in item.keys(), "Item doesn't have a description." assert ("uuid" not in item.keys( )), "Item already has a UUID, try updating it instead of adding it" curr_status = item.get("status", None) if curr_status not in ["pending", "done"]: self.logger.info( 'Invalid status of task: "%s", setting it to pending', item["status"]) item["status"] = "pending" item.setdefault("tags", []) item["tags"] += self.config["tags"] description = item.pop("description") new_item = self.tw.task_add(description=description, **item) len_print = min(20, len(description)) self.logger.info('Task "{}" created - "{}"...'.format( new_item["id"], description[0:len_print])) return new_item @overrides def delete_single_item(self, item_id) -> None: self.tw.task_delete(uuid=item_id) @staticmethod def items_are_identical(item1, item2, ignore_keys=[]) -> bool: keys = [ k for k in [ "annotations", "description", "due", "modified", "status", "uuid" ] if k not in ignore_keys ] # special care for the annotations key if "annotations" in item1 and "annotations" in item2: if item1["annotations"] != item2["annotations"]: return False item1.pop("annotations") item2.pop("annotations") # one may contain empty list elif "annotations" in item1 and "annotations" not in item2: if item1["annotations"] != []: return False item1.pop("annotations") # one may contain empty list elif "annotations" in item2 and "annotations" not in item1: if item2["annotations"] != []: return False item2.pop("annotations") else: pass return GenericSide._items_are_identical(item1, item2, keys) @staticmethod def get_task_id(item: dict) -> str: """Get the ID of a task in string form""" return str(item["uuid"])
def find_taskwarrior_from_azure_devops_id(work_item_id, task_list): return [t for t in task_list if t['description'].startswith(str(work_item_id))] def format_description(desc, tag_list): for keyword, tag in keyword_tag_replace_map.items(): desc, count = re.subn(r'[/\s[]*\b' + keyword + r'\b[]/\s]*', '', desc, flags=re.IGNORECASE) if count and tag: tag_list.append(tag) return desc if len(desc) <= max_description_length else f"{desc[:max_description_length]}..." # Task Warrior tasks load. task_warrior = TaskWarrior() print(f'Loading "{project}" Task warriors pending tasks ...') tw_tasks = get_taskwarrior_tasks(project.lower()) print(f'Found {len(tw_tasks)} tasks.') # Azure Devops current sprint's tasks load. print(f'Loading "{project}" Azure Devops work items ...') work_items = get_azure_devops_work_items(project, current_sprint, assignee) print(f'Found {len(work_items)} work items.') for work_item in work_items: # Checks if there is already a time warrior task for this Azure Devops item. existing_tw = find_taskwarrior_from_azure_devops_id(work_item['id'], tw_tasks) if existing_tw:
class TestDB(object): def setup(self): # Create some temporary config stuff fd, fname = tempfile.mkstemp(prefix='taskw-testsrc') dname = tempfile.mkdtemp(prefix='taskw-tests-data') with open(fname, 'w') as f: f.writelines(['data.location=%s' % dname]) # Create empty .data files for piece in ['completed', 'pending', 'undo']: with open(os.path.sep.join([dname, piece + '.data']), 'w'): pass # Save names for .tearDown() self.fname, self.dname = fname, dname # Create the taskwarrior db object that each test will use. self.tw = TaskWarrior(config_filename=fname) def tearDown(self): os.remove(self.fname) shutil.rmtree(self.dname) def test_has_two_categories(self): tasks = self.tw.load_tasks() eq_(len(tasks), 2) def test_empty_db(self): tasks = self.tw.load_tasks() eq_(len(sum(tasks.values(), [])), 0) def test_add(self): self.tw.task_add("foobar") tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 1) def test_unchanging_load_tasks(self): tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) @raises(KeyError) def test_completion_raising_unspecified(self): self.tw.task_done() def test_completing_task_by_id_unspecified(self): self.tw.task_add("foobar") self.tw.task_done(id=1) tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) eq_(len(tasks['completed']), 1) eq_(len(sum(tasks.values(), [])), 1) ok_(tasks['completed'][0]['end'] != None) def test_completing_task_with_date(self): self.tw.task_add("foobar") uuid = self.tw.load_tasks()['pending'][0]['uuid'] self.tw.task_done(uuid=uuid, end="1234567890") tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) eq_(len(tasks['completed']), 1) eq_(tasks['completed'][0]['end'], '1234567890') def test_completing_task_by_id_specified(self): self.tw.task_add("foobar") self.tw.task_done(id=1) tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) eq_(len(tasks['completed']), 1) eq_(len(sum(tasks.values(), [])), 1) def test_completing_task_by_id_retrieved(self): task = self.tw.task_add("foobar") self.tw.task_done(id=task['id']) tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) eq_(len(tasks['completed']), 1) eq_(len(sum(tasks.values(), [])), 1) def test_completing_task_by_uuid(self): self.tw.task_add("foobar") uuid = self.tw.load_tasks()['pending'][0]['uuid'] self.tw.task_done(uuid=uuid) tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) eq_(len(tasks['completed']), 1) eq_(len(sum(tasks.values(), [])), 1) @raises(KeyError) def test_get_task_mismatch(self): self.tw.task_add("foobar") self.tw.task_add("bazbar") uuid = self.tw.load_tasks()['pending'][0]['uuid'] self.tw.get_task(id=2, uuid=uuid) # which one? def test_updating_task(self): self.tw.task_add("foobar") tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 1) task = tasks['pending'][0] task["priority"] = "L" self.tw.task_update(task) tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 1) eq_(tasks['pending'][0], task) @raises(KeyError) def test_update_exc(self): task = dict(description="lol") self.tw.task_update(task)
def get_taskwarrior_instance(): return TaskWarrior(marshal=True)
task.get("urgency"), task.get("entry").date(), task.get("due").date() if task.get("due") else "", task.get("end").date() if task.get("end") else "", task.get("project", " ").strip(), ",".join(task.get("tags", [])), task.get("description").strip(), task.get("jira", ""), ]) print(table) if __name__ == "__main__": # TODO: Wrap this in a main() method. task_client = TaskWarrior(marshal=True) today = date.today() yesterday = date.today() + relativedelta(days=-1) print(f"Daily Standup Report for {today}") print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") print("") print("Yesterday's Completed Tasks") print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~") print("") completed_tasks = get_completed_tasks(task_client=task_client, start_date=yesterday, end_date=today) print_task_table(completed_tasks) overdue_tasks = filter_tasks(
class TW_Loader: symbol = "*" def __init__(self, config=None): if config is not None: self.w = TaskWarrior(config_filename=config) else: self.w = TaskWarrior() self.refresh_tasks() def refresh_tasks(self): self.tasks = self.w.load_tasks() pending = self.tasks['pending'] # newlist = sorted(pending, key=itemgetter('project', 'urgency'), reverse=True) #reverse=True sorts descending self.tasks = {} self.task_tables = {} for task in pending: pr = 'unassigned' if 'project' in task: pr = task['project'] if pr in self.tasks: self.tasks[pr].append(task) else: self.tasks[pr] = [] self.tasks[pr].append(task) #create tables from sorted tasks for project in self.tasks.keys(): table = TaskTable(self.tasks[project]) self.task_tables[project] = table def get_tasks(self, project=None): if project is None: return self.tasks elif project in self.tasks: return self.tasks[project] else: return None def add_task(self, text): #positive lookbehind to match and extract terms #task MUST come before project task = re.search("(?<=task:)(?:(?!project:).)*", text) project = re.search("(?<=project:)\w+", text) tags = re.findall("\+(\S+)", text) urgency = re.search("(?<=urgency:)\w+", text) due = re.search("(?<=due:).*?(?=\s)", text) parsed_task = {} if project is not None: project = project.group().strip() if urgency is not None: urgency = urgency.group().strip().upper() if task is not None: task = task.group().strip() if due is not None: due = due.group().strip() #TODO convert to epoch print(urgency) self.w.task_add(task, project=project, tags=tags, priority=urgency) self.refresh_tasks() parsed_task['task'] = task parsed_task['project'] = project parsed_task['urgency'] = urgency parsed_task['tags'] = tags return parsed_task def print_tasks(self): for project in self.tasks.keys(): proj = "* {0} *".format(project) print(proj) print("*" * len(proj)) print( tabulate(self.tasks[project], headers="keys", tablefmt="pipe")) print() def get_tables(self, project=None): if project is None: return self.task_tables elif project in self.tasks: table = TaskTable(self.tasks[project]) return table else: return None
class ProjectManager: def __init__(self): self.home_dir = str(Path.home()) self.EDITOR = os.environ.get('EDITOR', 'vim') PERSONAL_FOLDER = os.environ.get('PERSONAL_DIRECTORY', 'personal') self.w = TaskWarrior() self.tasks = self.w.load_tasks() self.personal_dir = '{}/core/{}'.format(self.home_dir, PERSONAL_FOLDER) def project_min(self): filedir = '{}/project_notes'.format(self.personal_dir) filename = 'MINIMUM' self.modify_file( filename, filedir, ) def project_note( self, project, description, ): base_dir = '{personal_dir}/project_notes'.format( personal_dir=self.personal_dir) filedir = self.get_full_dir_path(base_dir, project) filename = description if not filename: files = (_file for _file in os.listdir(filedir) if os.path.isfile(os.path.join(filedir, _file))) print("Notes for this project:\n") for _file in files: print(_file) else: self.modify_file( filename, filedir, filename, ) def project_dev_setup( self, project, ): base_dir = '{personal_dir}/projects'.format( personal_dir=self.personal_dir) filedir = self.get_full_dir_path(base_dir, project) readme = '{filedir}/README'.format(filedir=filedir) data = '{filedir}/data'.format(filedir=filedir) data_readme = '{filedir}/data/README'.format(filedir=filedir) poc = '{filedir}/poc'.format(filedir=filedir) prod = '{filedir}/prod'.format(filedir=filedir) utils = '{filedir}/utils'.format(filedir=filedir) utils_readme = '{filedir}/utils/README'.format(filedir=filedir) call(['mkdir', data]) call(['touch', data_readme]) call(['mkdir', poc]) call(['mkdir', prod]) call(['touch', readme]) call(['mkdir', utils]) call(['touch', utils_readme]) def get_task_id( self, project, ): project_id = jsonpath( self.tasks, "$..[?(@.description=='{project}')].id".format(project=project)) return project_id[0] def get_full_dir_path( self, base_dir, project, final_dir=None, ): if final_dir: full_dir_path = '{project}/{final_dir}'.format( project=project, final_dir=final_dir, ) else: full_dir_path = project project_id = jsonpath( self.tasks, "$..[?(@.description=='{project}')].uuid".format(project=project)) current_id = jsonpath( self.tasks, "$..[?('{}' in @.depends)].uuid".format(project_id[0])) while current_id: parent_project = jsonpath( self.tasks, "$..[?(@.uuid=='{}')].description".format(current_id[0])) full_dir_path = '{project}/{path}'.format( project=parent_project[0], path=full_dir_path) current_id = jsonpath( self.tasks, "$..[?('{}' in @.depends)].uuid".format(current_id[0])) full_dir_path = '{base_dir}/{path}'.format(base_dir=base_dir, path=full_dir_path) call(['mkdir', '-p', full_dir_path]) return full_dir_path def create_file( self, filename, filedir, defaulttext='', ): filepath = '{0}/{1}'.format( filedir, filename, ) with open(filepath, 'w') as _in: _in.write(defaulttext) _in.flush() call([self.EDITOR, _in.name]) def modify_file( self, filename, filedir, defaulttext='', ): filepath = '{0}/{1}'.format( filedir, filename, ) if filename in os.listdir(filedir): with open(filepath, 'r') as _in: call([self.EDITOR, _in.name]) else: self.create_file( filename, filedir, defaulttext, )
from taskw import TaskWarrior import time logfile = "/home/noon/Dropbox/Public/taskwarrior.txt" def write_log (active): with open(logfile, "w") as f: if not active or len(active) == 0: print("unknown", file=f) return # otherwise, print everything for (p, d) in active: print("%s, %s" % (p, d), file=f) if __name__ == "__main__": w = TaskWarrior() tasks = w.load_tasks()['pending'] def display_time (epoch): return time.strftime("%a %d %b %Y, %I:%M:%S %p", time.localtime(float(epoch))) def maybe_proj (t): if 'project' in t: return t['project'] else: return 'something' active = [ (maybe_proj(t), display_time(t['start'])) for t in tasks if 'start' in t ] write_log(active)
import copy from taskw import TaskWarrior URL = 'https://habitica.com/api/v3' if len(sys.argv) == 1: print "The Habitica hook does not work with TaskWarrior API v1, please upgrade to Taskwarrior 2.4.3 or higher" sys.exit(0) if not sys.argv[1] == "api:2": print "The Habitica hook only supports TaskWarrior API v2 at this time" sys.exit(0) configarg = sys.argv[4] configfile = configarg[3:] w = TaskWarrior(config_filename=configfile) config = w.load_config() API_KEY = config['habitica']['api_key'] API_USER = config['habitica']['api_user'] headers = { 'Content-Type': 'application/json', 'x-api-user': API_USER, 'x-api-key': API_KEY } def main(): jsonTaskOriginal = json.loads(sys.stdin.readline()) jsonTask = json.loads(sys.stdin.readline())
#!/usr/bin/python from datetime import time, datetime, timedelta from taskw import TaskWarrior from pytz import timezone, utc CONFIG = TaskWarrior.load_config() if 'dst' in CONFIG['default']: if CONFIG['default']['dst'].lower() in ['true', '1', 'yes']: dst = True else: dst = False if 'timezone' in CONFIG['default']: DEFAULT_ZONE = timezone(CONFIG['default']['timezone']) else: DEFAULT_ZONE = utc if 'time' in CONFIG["default"]: DEFAULT_TIME = datetime.strptime(CONFIG['default']['time'], u'%H:%M').time() else: DEFAULT_TIME = time(0, 0, 0, 0, DEFAULT_ZONE) def is_local_midnight(timestamp): tmp = utc.localize(timestamp).astimezone(DEFAULT_ZONE) return tmp.time() == time(0, 0, 0) def set_default_time(timestamp): utcoffset = DEFAULT_ZONE.utcoffset(timestamp, is_dst=dst).days * 24 + DEFAULT_ZONE.utcoffset(timestamp, is_dst=dst).seconds / 3600 tmp = timestamp.replace( hour=DEFAULT_TIME.hour,
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import json from taskw import TaskWarrior label = '◔' ret = { 'name': 'taskwarrior', 'align': 'left', 'label': label } w = TaskWarrior() context = w.config["context"] active = w.filter_tasks({'start.isnt': None}) if active: ret['short_text'] = str(active[0]['id']) ret['full_text'] = str.format("{0}{1[id]}: {1[description]}", label, active[0]) else: ret['short_text'] = "" ret['full_text'] = "none" #if context == 'work': # print(json.dumps(ret)) print(json.dumps(ret, ensure_ascii=False))
return utils.parse_recur(due) except errors.UnsupportedRecurrence: io.error("Unsupported recurrence: '%s'. Please enter a valid value" % due['string']) return io.prompt( 'Set recurrence (todoist style)', default='', value_proc=validation.validate_recur, ) """ Entrypoint """ if __name__ == '__main__': is_help_cmd = '-h' in sys.argv or '--help' in sys.argv todoist_api_key = os.getenv('TODOIST_API_KEY') if todoist_api_key is None and not is_help_cmd: io.error( 'TODOIST_API_KEY environment variable not specified. Exiting.') exit(1) todoist = TodoistAPI(todoist_api_key, cache=TODOIST_CACHE) # Create the TaskWarrior client, overriding config to # create a `todoist_id` field which we'll use to # prevent duplicates taskwarrior = TaskWarrior(config_overrides={ 'uda.todoist_id.type': 'string', }) cli()
def __init__(self, config=None): if config is not None: self.w = TaskWarrior(config_filename=config) else: self.w = TaskWarrior() self.refresh_tasks()
class CursesTask(object): def __init__(self): locale.setlocale(locale.LC_CTYPE, "") self.window = curses.initscr() curses.noecho() curses.cbreak() self.window.keypad(1) self.window.clear() self.window.border() self.lines, self.cols = self.window.getmaxyx() self.day_width = self.cols // 7 self.day_height = (self.lines - 2) // 6 self.task_warrior = TaskWarrior(marshal=True) pending_tasks = self.task_warrior.load_tasks()['pending'] self.tasks = {} now = datetime.now() timezone = LocalTimezone() self.offset = timezone.utcoffset(now) for task in pending_tasks: if 'due' not in task: continue due = task['due'] + self.offset current_date = date(due.year, due.month, due.day) if current_date not in self.tasks: self.tasks[current_date] = list() self.tasks[current_date].append(task) def run(self, show_date=datetime.now()): self.draw_header() first_day, last_day = calendar.monthrange( show_date.year, show_date.month ) number_of_days = first_day + last_day number_of_weeks = (number_of_days // 7) if (number_of_weeks * 7) < number_of_days: number_of_weeks += 1 self.day_height = (self.lines - 1) // number_of_weeks for day in range(1, last_day+1): self.draw_day(show_date.year, show_date.month, day) self.window.getch() def uninit(self): curses.nocbreak() self.window.keypad(0) curses.echo() curses.endwin() def draw_header(self): self.window.addstr(0, 2, datetime.now().strftime('%B %Y')) for day in range(7): top_x = day * self.day_width + (self.day_width // 2) - 3 self.window.addstr(1, top_x, calendar.day_name[day]) def draw_day(self, year, month, day): first_day, last_day = calendar.monthrange(year, month) current_day = first_day + day - 1 week = current_day // 7 top_y = 2 + week * self.day_height top_x = (current_day % 7) * self.day_width bottom_y = top_y + self.day_height bottom_x = top_x + self.day_width curses.textpad.rectangle(self.window, top_y, top_x, bottom_y, bottom_x) label_top_y = top_y label_top_x = top_x + 2 label = str(day) + '.' self.window.addstr(label_top_y, label_top_x, label) date_key = date(year, month, day) if date_key in self.tasks: tasks = self.tasks[date_key] else: tasks = list() self.day_content(top_y, top_x, tasks) def day_content(self, top_y, top_x, tasks): offset_y = 1 for task in tasks: y = top_y + offset_y x = top_x + 1 due = task['due'] + self.offset description = '{} {}'.format( due.strftime('%H:%M'), task['description'].encode('utf-8')[:self.day_width-7] ) self.window.addstr(y, x, description) offset_y += 1
class TestDB(object): def setup(self): # Create some temporary config stuff fd, fname = tempfile.mkstemp(prefix='taskw-testsrc') dname = tempfile.mkdtemp(prefix='taskw-tests-data') with open(fname, 'w') as f: f.writelines(['data.location=%s' % dname]) # Create empty .data files for piece in ['completed', 'pending', 'undo']: with open(os.path.sep.join([dname, piece + '.data']), 'w'): pass # Save names for .tearDown() self.fname, self.dname = fname, dname # Create the taskwarrior db object that each test will use. self.tw = TaskWarrior(config_filename=fname) def tearDown(self): os.remove(self.fname) shutil.rmtree(self.dname) def test_has_two_categories(self): tasks = self.tw.load_tasks() eq_(len(tasks), 2) def test_empty_db(self): tasks = self.tw.load_tasks() eq_(len(sum(tasks.values(), [])), 0) def test_add(self): self.tw.task_add("foobar") tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 1) def test_unchanging_load_tasks(self): tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) def test_completion_raising_unspecified(self): try: self.tw.task_done() assert False except KeyError: assert True def test_completing_task_by_id_unspecified(self): self.tw.task_add("foobar") self.tw.task_done(1) tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) eq_(len(tasks['completed']), 1) eq_(len(sum(tasks.values(), [])), 1) def test_completing_task_by_id_specified(self): self.tw.task_add("foobar") self.tw.task_done(id=1) tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) eq_(len(tasks['completed']), 1) eq_(len(sum(tasks.values(), [])), 1) def test_completing_task_by_uuid(self): self.tw.task_add("foobar") uuid = self.tw.load_tasks()['pending'][0]['uuid'] self.tw.task_done(uuid=uuid) tasks = self.tw.load_tasks() eq_(len(tasks['pending']), 0) eq_(len(tasks['completed']), 1) eq_(len(sum(tasks.values(), [])), 1)
#!/usr/bin/env python3 # -*- coding: utf-8 -*- #strings to translate strListAll = "Zobrazit všechny úkoly" from taskw import TaskWarrior w = TaskWarrior() tasks = w.load_tasks() tasksPending = (tasks['pending']) lot = len(tasksPending) def render_tasks(): rend = str(lot) rend = rend + "|iconName=task-past-due-symbolic-symbolic \n---\n" for i in range(len(tasks['pending'])): rend = rend + tasks['pending'][i][ 'description'] + " | bash='task " + str(i + 1) + "'" + '\n' # str(tasks['pending'][i]['tags']) +"\n" rend = rend + "---\n <span weight='bold'>" + strListAll + "</span> | bash='task' iconName=task-past-due-symbolic-symbolic" return rend print(render_tasks())
#!/usr/bin/env python import datetime import json import re import sys import subprocess from taskw import TaskWarrior TIME_FORMAT = '%Y%m%dT%H%M%SZ' UDA_KEY = 'totalactivetime' w = TaskWarrior() config = w.load_config() if ('max_active_tasks' in config): MAX_ACTIVE = int(config['max_active_tasks']) else: MAX_ACTIVE = 1 ISO8601DURATION = re.compile( "P((\d*)Y)?((\d*)M)?((\d*)D)?T((\d*)H)?((\d*)M)?((\d*)S)?") # Convert duration string into a timedelta object. # Valid formats for duration_str include # - int (in seconds) # - string ending in seconds e.g "123seconds" # - ISO-8601: e.g. "PT1H10M31S" def duration_str_to_time_delta(duration_str): if (duration_str.startswith("P")): match = ISO8601DURATION.match(duration_str) if (match): year = match.group(2)
#!/usr/bin/env python import datetime import json import sys import subprocess from taskw import TaskWarrior TIME_FORMAT = '%Y%m%dT%H%M%SZ' UDA_KEY = 'totalactivetime' w = TaskWarrior() config = w.load_config() if ('max_active_tasks' in config): MAX_ACTIVE = int(config['max_active_tasks']) else: MAX_ACTIVE = 1 def main(): original = json.loads(sys.stdin.readline()) modified = json.loads(sys.stdin.readline()) # An inactive task has just been started. if 'start' in modified and 'start' not in original: # Check if `task +ACTIVE count` is greater than MAX_ACTIVE. If so # prevent this task from starting. p = subprocess.Popen( ['task', '+ACTIVE', 'status:pending', 'count', 'rc.verbose:off'], stdout=subprocess.PIPE) out, err = p.communicate() count = int(out.rstrip())