Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | # SPDX-License-Identifier: GPL-2.0+ # # Copyright 2025 Google LLC # """Workflow types and operations for patman series management""" from datetime import datetime, timedelta import enum class Wtype(str, enum.Enum): """Types of workflow entry""" SENT = 'sent' TODO = 'todo' def friendly_time(now, when): """Format a timestamp in a human-friendly way Args: now (datetime): Current time when (datetime): Timestamp to format Return: str: Friendly string, e.g. 'Tue 15:34', '3d ago', '2w ago' """ delta = now - when days = delta.days if days < 0: days = -days if days >= 14: return f'in {days // 7}w' if days >= 7: return f'in {days}d' return when.strftime('%a %H:%M') if days == 0: return when.strftime('%H:%M') if days < 7: return when.strftime('%a %H:%M') if days < 14: return f'{days}d ago' return f'{days // 7}w ago' def sent(cser, series_id, ser_ver_id=None): """Record that a series was sent and create a follow-up todo Args: cser (CseriesHelper): Series helper with open database series_id (int): ID of the series that was sent ser_ver_id (int or None): ID of the ser_ver record that was sent """ ts = cser.get_now().strftime('%Y-%m-%d %H:%M:%S') cser.db.workflow_archive(Wtype.SENT, series_id) cser.db.workflow_add(Wtype.SENT, series_id, ts, ser_ver_id=ser_ver_id) when = cser.get_now() + timedelta(days=7) todo_ts = when.strftime('%Y-%m-%d %H:%M:%S') cser.db.workflow_archive(Wtype.TODO, series_id) cser.db.workflow_add(Wtype.TODO, series_id, todo_ts) cser.commit() def todo(cser, series, days): """Mark a series as a todo item after a number of days Args: cser (CseriesHelper): Series helper with open database series (str): Name of series to use, or None for current branch days (int): Number of days from now to mark as due """ ser = cser._parse_series(series) cser.db.workflow_archive(Wtype.TODO, ser.idnum) when = cser.get_now() + timedelta(days=days) ts = when.strftime('%Y-%m-%d %H:%M:%S') cser.db.workflow_add(Wtype.TODO, ser.idnum, ts) cser.commit() print(f"Series '{ser.name}' marked for todo on {ts}") def todo_clear(cser, series): """Clear the todo marker for a series Args: cser (CseriesHelper): Series helper with open database series (str): Name of series to use, or None for current branch """ ser = cser._parse_series(series) cser.db.workflow_archive(Wtype.TODO, ser.idnum) cser.commit() print(f"Todo cleared for series '{ser.name}'") def todo_list(cser, show_all): """List series that are due (or scheduled) for attention Args: cser (CseriesHelper): Series helper with open database show_all (bool): True to show all scheduled todos, not just those that are due """ now = cser.get_now().strftime('%Y-%m-%d %H:%M:%S') before = None if show_all else now entries = cser.db.workflow_get_by_type(Wtype.TODO, before=before) if not entries: if show_all: print('No todos scheduled') else: print('No todos due') return print(f"{'Series':17} {'Due':>14} Description") print(f"{'-' * 17} {'-' * 14} {'-' * 30}") for _sid, name, desc, ts in entries: when = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S') delta = when - cser.get_now() days = delta.days if days < 0: due = f'{-days}d overdue' elif days == 0: due = 'today' else: due = f'in {days}d' print(f"{name:17} {due:>14} {desc}") def list_entries(cser, show_all): """List all workflow entries Args: cser (CseriesHelper): Series helper with open database show_all (bool): True to include archived entries """ entries = cser.db.workflow_list(include_archived=show_all) if not entries: print('No workflow entries') return hdr = f"{'Type':6} {'Series':17} {'Ver':>3} {'When':>10}" div = f"{'-' * 6} {'-' * 17} {'-' * 3} {'-' * 10}" if show_all: hdr += ' A' div += ' -' hdr += ' Description' div += ' ' + '-' * 30 print(hdr) print(div) now = cser.get_now() for wtype, name, desc, ts, archived, version in entries: when = datetime.strptime(ts, '%Y-%m-%d %H:%M:%S') friendly = friendly_time(now, when) ver = f'v{version}' if version else '' line = f"{wtype:6} {name:17} {ver:>3} {friendly:>10}" if show_all: line += f" {'*' if archived else ' '}" line += f" {desc}" print(line) |