From 2c25aabc6ae8adeddc0c3d47c60391610a39c957 Mon Sep 17 00:00:00 2001 From: Campbell Alden Date: Thu, 20 Jul 2023 13:35:52 +0900 Subject: [PATCH] Full swing off of python onto rust --- .gitignore | 3 +- Cargo.toml | 8 +++ dump.py | 184 ----------------------------------------------- requirements.txt | 1 - shell.nix | 22 ++---- src/main.rs | 3 + 6 files changed, 17 insertions(+), 204 deletions(-) create mode 100644 Cargo.toml delete mode 100644 dump.py delete mode 100644 requirements.txt create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore index 93526df..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -venv/ -__pycache__/ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..cc82340 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "day-reporter" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/dump.py b/dump.py deleted file mode 100644 index 1196faf..0000000 --- a/dump.py +++ /dev/null @@ -1,184 +0,0 @@ -import things -import argparse -import re -import random -from datetime import datetime, timedelta - -with open('./emojis.txt', 'r') as emoji_file: - EMOJIS = [l.strip() for l in emoji_file.readlines()] - -def mine_tags(tags, place): - if place is None or not 'tags' in place: - return tags - - tags.extend(place['tags']) - -def has_tag(task, tag): - area = things.areas(uuid=task['area']) if 'area' in task else None - project = things.projects(uuid=task['project']) if 'project' in task else None - heading_project = things.projects(uuid=things.get(uuid=task['heading'])['project']) if 'heading' in task else None - project_area = things.areas(uuid=project['area']) if project is not None and 'area' in project else None - tags = [] - mine_tags(tags, task) - mine_tags(tags, area) - mine_tags(tags, heading_project) - mine_tags(tags, project) - mine_tags(tags, project_area) - return tag in tags - -def tasks_to_heirarchy(top_level_comment, tasks): - heir = {} - for task in tasks: - task_leaf = (task, {}) - if 'project' not in task and 'heading' not in task: - heir[task['uuid']] = task_leaf - elif 'heading' in task: - h_uuid = task['heading'] - head = things.get(h_uuid) - - p_uuid = head['project'] # This must exist - proj = things.get(p_uuid) - proj_title, proj_sublevel = heir[p_uuid] if p_uuid in heir else (proj, {}) - head_title, head_sublevel = proj_sublevel[h_uuid] if h_uuid in proj_sublevel else (head, {}) - head_sublevel[task['uuid']] = task_leaf - proj_sublevel[head['uuid']] = (head_title, head_sublevel) - heir[p_uuid] = (proj_title, proj_sublevel) - else: # The project must not be None here - p_uuid = task['project'] - proj = things.get(p_uuid) - proj_title, proj_sublevel = heir[p_uuid] if p_uuid in heir else (proj, {}) - proj_sublevel[task['uuid']] = task_leaf - heir[p_uuid] = (proj_title, proj_sublevel) - - return (top_level_comment, heir) - -def parse_note(note): - note_block_pattern = r"```report\n([\s\S]*?)\n?```" - return ' '.join(re.findall(note_block_pattern, note)) - -def make_recursive_formatter(formatter): - def recursive_format(structure, depth): - (node, branches) = structure - notes = parse_note(node['notes']) - node_str = formatter.node(node, notes) - if len(branches) == 1: - item = list(branches.values())[0] - node_str += formatter.single(recursive_format(item, depth)) - elif len(branches) > 1: - new_depth = depth + 2 - bullets = [formatter.bullet(f"{recursive_format(item, new_depth)}", new_depth) for item in branches.values()] - node_str += ''.join(map(lambda b: f'\n{b}', bullets)) - - return node_str - return recursive_format - -class TodayFormatter: - def node(self, node, notes): - return f"{node['title']}{'' if notes == '' else f' {notes}'}" - - def single(self, subtext): - return f" > {subtext}" - - def bullet(self, subtext, depth): - space = ''.join([' '] * depth) - return f"{space}- {subtext}" - -class LogbookFormatter: - def node(self, node, notes): - node_text = f"{node['title']}{'' if notes == '' or node['status'] == 'canceled' else f' {notes}'}" - if node['status'] == 'canceled': - node_text = f"~{node_text}~" - - return node_text - - def single(self, subtext): - return f" > {subtext}" - - def bullet(self, subtext, depth): - space = ''.join([' '] * depth) - return f"{space}- {subtext}" - -# Note: the characters on the RHS are all "lookalikes". The do not == the left side -VOWEL_MAP = { - 'a': 'а', - 'e': 'e', - 'i': 'і', - 'o': 'о', - 'u': 'ս', -} - -def sanitize_name(name): - for normal_ch, special_ch in VOWEL_MAP.items(): - name = name.replace(normal_ch, special_ch) - - return name - -def sanitize_string(s, mapping): - for original_str, new_str in mapping.items(): - s = s.replace(original_str, new_str) - - return s - -def sanitize_mentions(task): - if 'tags' not in task: - return - - people_tags = { t[1:]: sanitize_name(t[1:]) for t in task['tags'] if t[0] == '@' } - # For now just sanitize title and notes (headings might also make sense?) - task['title'] = sanitize_string(task['title'], people_tags) - task['notes'] = sanitize_string(task['notes'], people_tags) - -def same_date(d1, d2): - day1 = str(d1).split()[0] - day2 = str(d2).split()[0] - return day1 == day2 - -def generate_signoff_message(target_tag): - format_tasks = make_recursive_formatter(LogbookFormatter()) - import datetime - today = datetime.datetime.today() - reportable = list(filter(lambda t: has_tag(t, target_tag) and same_date(today, t['stop_date']) and t['status'] == 'completed', things.logbook())) - - for task in reportable: - sanitize_mentions(task) - - structured_tasks = tasks_to_heirarchy({ 'title': 'Stopping now', 'notes': '', 'status': '' }, sorted(reportable, key=by_modified_timestamp)) - return format_tasks(structured_tasks, 0) - -def by_modified_timestamp(val): - return datetime.fromisoformat(val['modified']) - -def generate_today_message(target_tag): - format_tasks = make_recursive_formatter(TodayFormatter()) - reportable = list(filter(lambda t: has_tag(t, target_tag), things.today())) - - for task in reportable: - sanitize_mentions(task) - - top_level_comment = ' '.join(map(lambda e: f':{e}:', random.choices(EMOJIS, k=3))) - structured_tasks = tasks_to_heirarchy({ 'title': top_level_comment, 'notes': '' }, sorted(reportable, key=by_modified_timestamp)) - return format_tasks(structured_tasks, 0) - -def generate_track_message(target_tag): - t = datetime.today() - timedelta(days=14) - projects = filter(lambda x: x['type'] == 'project', things.logbook()) - recent_projects = filter(lambda x: datetime.fromisoformat(x['modified']) > t, projects) - selected_projects = filter(lambda x: 'tags' in x and target_tag in x['tags'], recent_projects) - return '\n'.join(map(lambda x: x['title'], selected_projects)) - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description="Generate iknow_vacation_remote style messages from Things 3 Tasks") - parser.add_argument('tags', metavar='TAG', type=str, nargs="+", help="a word in the overall tag") - parser.add_argument('--signoff', action=argparse.BooleanOptionalAction, help="Get tasks from the logbook and generate output for a signoff message") - parser.add_argument('--track', action=argparse.BooleanOptionalAction, help="Get tasks from the logbook and generate output for a track meeting") - args = parser.parse_args() - target_tag = ' '.join(args.tags) - - if args.signoff: - print(generate_signoff_message(target_tag)) - elif args.track: - print(generate_track_message(target_tag)) - else: - print(generate_today_message(target_tag)) - - diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d534b71..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -things.py diff --git a/shell.nix b/shell.nix index 473bd0b..157b677 100644 --- a/shell.nix +++ b/shell.nix @@ -1,20 +1,8 @@ -with import {}; -with pkgs.python3Packages; - -stdenv.mkDerivation { - name = "python"; +{ pkgs ? import {} }: +pkgs.mkShell { buildInputs = [ - pip - python310 - virtualenv - ]; - - shellHook = '' - SOURCE_DATE_EPOCH=$(date +%s) # so that we can use python wheels - virtualenv --no-setuptools venv > /dev/null - export PATH=$PWD/venv/bin:$PATH > /dev/null - export PIP_DISABLE_PIP_VERSION_CHECK=1 - pip install -r requirements.txt > /dev/null - ''; + pkgs.rustc + pkgs.cargo + ] ++ [ pkgs.libiconv pkgs.darwin.apple_sdk.frameworks.CoreServices ]; } diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +}