Created
October 5, 2016 04:46
-
-
Save jamespan/cb364331daeb10682cde4b535fa1a7e9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# coding=utf-8 | |
import datetime | |
import dateutil.parser | |
import pytz | |
import argparse | |
from collections import OrderedDict | |
import frontmatter | |
class Task(object): | |
def __init__(self, name): | |
self.name = name | |
self.completed = False | |
self.complete_date = None | |
self.due_date = None | |
self.defer_date = None | |
self.estimated_minutes = None | |
self._note = '' | |
self.note_metadata = None | |
self.note_body = None | |
@property | |
def note(self): | |
return self._note | |
@note.setter | |
def note(self, value): | |
self._note = value | |
metadata, content = frontmatter.parse(value) | |
self.note_metadata = metadata | |
self.note_body = content | |
class Project(object): | |
status_map = { | |
1178816616: 'ONHOLD', | |
1178816609: 'ACTIVE', | |
} | |
def __init__(self, name): | |
self.name = name | |
self._status = None | |
self.tasks = [] | |
def add_task(self, task): | |
self.tasks.append(task) | |
@property | |
def status(self): | |
return self._status | |
@status.setter | |
def status(self, value): | |
if value in Project.status_map: | |
self._status = Project.status_map[value] | |
else: | |
self._status = str(value) | |
def completed_tasks(self): | |
return filter(lambda x: x.completed, self.tasks) | |
def incomplete_tasks(self): | |
return filter(lambda x: not x.completed, self.tasks) | |
def working_tasks(self): | |
return filter(lambda x: not x.completed and 'progress' in x.note_metadata, self.tasks) | |
class Workload(object): | |
def __init__(self, projects=None): | |
if projects is None: | |
projects = OrderedDict() | |
self.projects = projects | |
def add_project(self, project): | |
self.projects[project.name] = project | |
def get_project(self, name): | |
return self.projects[name] | |
def has_project(self, name): | |
return name in self.projects | |
def report(self): | |
rpt = '' | |
for name, project in self.projects.iteritems(): | |
indent_level = 0 | |
rpt += ' ' * indent_level + '+ ' + name + '\n' | |
indent_level += 2 | |
for task in project.tasks: | |
if task.complete_date is not None: | |
rpt += ' ' * indent_level + '+ ' + task.name + '\n' | |
return rpt | |
def build_workload(workload, filters): | |
# from Foundation import * | |
from ScriptingBridge import SBApplication | |
omniFocus = SBApplication.applicationWithBundleIdentifier_("com.omnigroup.OmniFocus2") | |
omniFocusDocument = omniFocus.defaultDocument() | |
for omniFocusProject in omniFocusDocument.flattenedProjects(): | |
folder = omniFocusProject.folder().get() | |
if folder is not None: | |
folder = folder.name() | |
if 'folder' in filters and filters['folder'] and folder not in filters['folder']: | |
continue | |
project = Project(omniFocusProject.name()) | |
project.status = omniFocusProject.status() | |
for omniFocusTask in omniFocusProject.rootTask().flattenedTasks(): | |
t = Task(omniFocusTask.name()) | |
if omniFocusTask.completed(): | |
complete_date = dateutil.parser.parse(str(omniFocusTask.completionDate().get())) | |
if 'start' in filters and complete_date < filters['start']: | |
continue | |
t.complete_date = complete_date | |
t.completed = True | |
defer_date = str(omniFocusTask.deferDate().get()) | |
if defer_date is not None and defer_date.strip() != 'None': | |
t.defer_date = dateutil.parser.parse(defer_date) | |
due_date = str(omniFocusTask.dueDate().get()) | |
if due_date is not None and due_date.strip() != 'None': | |
t.due_date = dateutil.parser.parse(due_date) | |
estimated = omniFocusTask.estimatedMinutes().get() | |
if estimated is not None: | |
t.estimated_minutes = int(estimated) | |
t.note = omniFocusTask.note().text() | |
project.add_task(t) | |
if len(project.tasks) > 0: | |
workload.add_project(project) | |
parser = argparse.ArgumentParser('Omni Weekly') | |
parser.add_argument('--folder', dest='folders', default=[], action='append', | |
help='project folders to scan and generate report') | |
args = parser.parse_args() | |
filter_folder = args.folders | |
filter_deadline = datetime.datetime.now(pytz.timezone('Asia/Shanghai')) | |
filter_period_days = 7 | |
filter_start = filter_deadline - datetime.timedelta(days=filter_period_days) | |
class JinjaReportWorkload(Workload): | |
def __init__(self, projects=None): | |
super(JinjaReportWorkload, self).__init__(projects) | |
self.template = u""" | |
1. 已经完成: | |
{%- for name, project in workload.projects.iteritems() %} | |
{%- if project.completed_tasks() %} | |
+ {{ name }} | |
{%- for task in project.tasks %} | |
{%- if task.completed %} | |
+ {{ task.name }}{%- if task.note_body -%}:{{ task.note_body }}{%- endif %} | |
{%- if task.estimated_minutes or task.note_metadata.issue %} | |
{% if task.estimated_minutes -%}[{{ task.estimated_minutes//25 }}🍅]{%- endif -%} | |
{%- if task.note_metadata.issue -%}[[Aone]({{ task.note_metadata.issue }})]{%- endif -%} | |
{%- endif -%} | |
{%- endif -%} | |
{%- endfor -%} | |
{%- endif -%} | |
{%- endfor %} | |
2. 仍在进行: | |
{%- for name, project in workload.projects.iteritems() %} | |
{%- if project.working_tasks() %} | |
+ {{ name }} | |
{%- for task in project.tasks %} | |
{%- if not task.completed and task.note_metadata.progress %} | |
+ {{ task.name }}{%- if task.note_body -%}:{{ task.note_body }} {% else %} {% endif %} | |
[估计进度 {{ task.note_metadata.progress }}] | |
{%- if task.note_metadata.issue -%}[[Aone]({{ task.note_metadata.issue }})]{%- endif -%} | |
{%- endif -%} | |
{%- endfor -%} | |
{%- endif -%} | |
{%- endfor %} | |
3. 下周计划: | |
{%- for name, project in workload.projects.iteritems() %} | |
{%- if project.status == 'ACTIVE' and project.incomplete_tasks() %} | |
+ {{ name }} | |
{%- for task in project.tasks %} | |
{%- if not task.completed %} | |
+ {{ task.name }} | |
{%- endif -%} | |
{%- endfor -%} | |
{%- endif -%} | |
{%- endfor -%} | |
""" | |
def report(self): | |
from jinja2 import Environment | |
return Environment().from_string(self.template).render({'workload': self}) | |
Workload = JinjaReportWorkload | |
workload = Workload() | |
build_workload(workload, { | |
'folder': filter_folder, | |
'start': filter_start | |
}) | |
print workload.report().encode('utf-8') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment