From 3a06bdee686c3a74e1d60a7b0100626917f112bf Mon Sep 17 00:00:00 2001 From: Ra1ny_Yuki Date: Wed, 18 May 2022 13:26:04 +0800 Subject: [PATCH] v2.2.0 --- lang/en_us.yml | 87 ++++--- lang/zh_cn.yml | 84 ++++--- mcd_task/__init__.py | 7 +- mcd_task/command_actions.py | 439 ++++++++--------------------------- mcd_task/config.py | 41 +--- mcd_task/constants.py | 2 +- mcd_task/global_variables.py | 7 +- mcd_task/responsible.py | 15 +- mcd_task/rtext_components.py | 132 +++++++++++ mcd_task/task_manager.py | 66 ++++-- mcd_task/utils.py | 22 ++ mcdreforged.plugin.json | 2 +- 12 files changed, 421 insertions(+), 483 deletions(-) create mode 100644 mcd_task/rtext_components.py diff --git a/lang/en_us.yml b/lang/en_us.yml index 075d0da..e48ef50 100644 --- a/lang/en_us.yml +++ b/lang/en_us.yml @@ -1,30 +1,31 @@ mcd_task: help_msg: | - §7-----§r MCDR {1} v{2} §7-----§r + §7-----§r MCDR {name} v{ver} §7-----§r §d【Command Help】§r - §7{0} help§r Show this help message - §7{0} list§r List all tasks - §7{0} detail §6§r Show detail of the specified task - §7{0} detail-all§r Show detail of all the tasks - §7{0} add §6 §a[desc(optional)]§r Add a new task - §7{0} remove §6§r Delete a task - §7{0} rename §6 §r Rename a task - §7{0} change §6 §a§r Edit a task description - §7{0} done §6§r Mark a task as done - §7{0} undone §6§r Mark a task as undone - §7{0} deadline §6 §b§r Set a task deadline - §7{0} player §e §rShow tasks of a player - §7{0} priority §6 §9§rSet a task priority - §7{0} res§8ponsible §6 §e§r Set task responsibles - §7{0} unres§8ponsible §6 §e §rDel responsibles - §7{0} list-res§8ponsibles §6 §rList task responsibles + §7{pre} overview§r Show task overview + §7{pre} help§r Show this help message + §7{pre} list§r List all tasks + §7{pre} reload§r Reload this plugin + §7{pre} detail §e§r Show detail of the specified task + §7{pre} list-all§r List full map of the tasks + §7{pre} add §e §a[desc(optional)]§r Add a new task + §7{pre} remove §e§r Delete a task + §7{pre} rename §e §r Rename a task + §7{pre} change §e §a§r Edit a task description + §7{pre} done §e§r Mark a task as done + §7{pre} undone §e§r Mark a task as undone + §7{pre} deadline §e §c§r Set a task deadline + §7{pre} player §e §rShow tasks of a player + §7{pre} priority §e §6§rSet a task priority + §7{pre} res§8ponsible §e §3§r Set task responsibles + §7{pre} unres§8ponsible §e §3 §rDel responsibles §d【Tips】§r - 1. You can click task text to show detail or plus mark to add a task - 2. All the §6§r above can be replaced by §6.§r to access the sub-tasks + 1. Click task text to show detail or plus mark to add a task + 2. Use §e.§r to access the sub-tasks 3. §cDO NOT§r name the task with §cdot(.)§r §d【Command Example】§r - If task §ewitch_farm§r exists, you can add a sub-task to it with this command: - §7!!task add §6witch_farm.floor §aAFK lay black glass§r + You can add a sub-task to task §ewitch_farm§r with this command: + §7{pre} add §ewitch_farm.floor §aAFK lay black glass§r mcdr_help: Manage progression of project tasks overview_help: | §7{0} list§r Show full task list @@ -37,21 +38,20 @@ mcd_task: invalid_number: Invalid number, click to reinput a number resuggest_cmd_hover: Click here to refill command {} rename_task_hover: Click here to rename this task - info_player_hover: Click here to browse player info + info_player_hover: Click here to §6browse §eplayer info§r mark_task_done_hover: Click here to mark task as §edone§r mark_task_undone_hover: Click here to mark task as §eundone§r - info_task_hover: Click here to browse task info time_format: '%b.%d %Y %H:%M:%S' - ddl_set: Task {} now has a deadline for {} days ({}) - task_renamed: 'Task {} has been renamed to {}: ' - player_tasks_title: 'Player {} has {} tasks to do: ' - info_task_title: 'Task information list: ' - add_task_hover: Click here to add a new task - info_task_single_title: 'Task detailed information: ' + ddl_set: "Task deadline has been changed" + task_renamed: 'Task §e{}§a§l has been renamed: ' + player_tasks_title: 'Player §3{}§a§l has §3{}§a§l tasks to do: ' + list_task_title: 'Task information list: ' + add_task_hover: Click here to §6add§e a new task§r + add_sub_task_hover: Click here to §6add§e a new sub-task§r + info_task_title: 'Task detailed information: ' done_task_button: '[Finished tasks...]' done_task_hover: Click here to browse tasks marked as §edone§r - edit_task_hover: Click here to edit description - show_desc_hover: Click here to browse description + info_task_hover: Click here to §6browse§r task §e{} §rdetail task_already_exist: Task already exist, click to browse task list new_task_created: 'New task created: ' detailed_info_task_title: 'Task detailed information list: ' @@ -60,17 +60,30 @@ mcd_task: done_task_list_title: 'Finished task lists: ' on_player_joined: You have {} outdated tasks, please hurry up! on_player_renamed: Detected your nickname changed, inherited {} tasks - list_responsible_title: 'This task has {} responsibles: ' - removed_responsibles_title: 'Removed {} responsibles: ' - added_responsibles_title: 'Added {} responsibles: ' + list_responsible_title: 'This task has §3{}§a§l responsibles: ' + removed_responsibles_title: 'Removed §3{}§a§l responsibles: ' + added_responsibles_title: 'Added §3{}§a§l responsibles: ' illegal_call: This command without argument can only be called as player - set_ddl_hover: Click here to set deadline + set_ddl_hover: Click here to §6set§e deadline help_msg_suggest_hover: Click here to fill §7{}§r - reloaded: "[Task] Plugin reloaded" + reloaded: "[Task] §a§lPlugin reloaded§r" overview_headline: "Top priorities:" no_priority: No priorities found, check full list for more tasks date_approaching: | Deadline is approaching or already passed: {} has_a_high_priority: "This task has a high priority: {}" - detail_priority: "Priority: {}" \ No newline at end of file + detail_priority: "Priority: §6{} §r[✎]" + priority_hover: Click to §6set§e priority of this task + priority_set: Task priority has been changed + detail_desc: 'Task description: §7{} §r[✎]' + desc_hover: Click here to §6edit§e description + detail_deadline: "Deadline: §c{} §r[✎]" + detail_sub: "Sub-tasks: " + not_set: Not set yet + changed_desc_title: Task description changed + done_task_title: Task was marked as done + undone_task_title: Task was marked as undone + detail_res: "Responsibles: §5§l[+]§r" + add_res_hover: Click to §6add§e responsibles§r to this task + rm_res_hover: Click to §6remove§e {} as reponsible§r from this task \ No newline at end of file diff --git a/lang/zh_cn.yml b/lang/zh_cn.yml index fb8dcef..0e803b3 100644 --- a/lang/zh_cn.yml +++ b/lang/zh_cn.yml @@ -1,31 +1,31 @@ mcd_task: help_msg: | - §7-----§r MCDR {1} v{2} §7-----§r + §7-----§r MCDR {name} v{ver} §7-----§r §d【指令帮助】§r - §7{0} §r 显示{3} - §7{0} help§r 显示帮助信息 - §7{0} list§r 显示任务列表 - §7{0} detail §6<任务名称>§r 查看任务详细信息 - §7{0} detail-all§r 查看所有任务详细信息 - §7{0} add §6<任务名称> §a[任务描述(可选)]§r 添加任务 - §7{0} del §6<任务名称>§r 删除任务 - §7{0} rename §6<旧任务名称> §e<新任务名称>§r 重命名任务 - §7{0} change §6<任务名称> §a<新任务描述>§r 修改任务描述 - §7{0} done §6<任务名称>§r 标注任务为已完成 - §7{0} undone §6<任务名称>§r 标注任务为未完成 - §7{0} deadline §6<任务名称> §b<工期(日)>§r 为任务设置工期 - §7{0} player §3<玩家> §r查看玩家任务列表 - §7{0} priority §6<任务名称>§9 <优先级> §r为任务设置优先级 - §7{0} res§8ponsible §6<任务名称> §3<玩家>§r 设置任务的责任人 - §7{0} unres§8ponsible §6<任务名称> §3<玩家> §r移除任务的责任人 - §7{0} list-res§8ponsibles §6<任务名称> §r列出该任务的责任人 + §7{pre} overview§r 显示任务概览 + §7{pre} help§r 显示帮助信息 + §7{pre} list§r 显示任务列表 + §7{pre} reload§r 重载该插件 + §7{pre} detail §e<任务名称>§r 查看任务详细信息 + §7{pre} list-all§r 列出完整的任务表 + §7{pre} add §e<任务名称> §a[任务描述(可选)]§r 添加任务 + §7{pre} del §e<任务名称>§r 删除任务 + §7{pre} rename §e<旧任务名称> §e<新任务名称>§r 重命名任务 + §7{pre} change §e<任务名称> §a<新任务描述>§r 修改任务描述 + §7{pre} done §e<任务名称>§r 标注任务为已完成 + §7{pre} undone §e<任务名称>§r 标注任务为未完成 + §7{pre} deadline §e<任务名称> §c<工期(日)>§r 为任务设置工期 + §7{pre} player §3<玩家> §r查看玩家任务列表 + §7{pre} priority §e<任务名称>§6 <优先级> §r为任务设置优先级 + §7{pre} res§8ponsible §e<任务名称> §3<玩家>§r 设置任务的责任人 + §7{pre} unres§8ponsible §e<任务名称> §3<玩家> §r移除任务的责任人 §d【注意事项】§r 1. 可用鼠标点击任务查看详情,或点击加号快速添加新任务 - 2. 上述所有 §6<任务名称>§r 可以用 §6<任务名称>.<子任务名称>§r 的形式来访问子任务 + 2. 可用 §e<任务名称>§r.§e<子任务名称>§r 的形式来访问子任务 3. 命名子任务时,其名称§c不可包含小数点(即.)§r §d【命令范例】§r (若已经有 §e女巫塔§r 任务, 可使用以下命令添加子任务) - §7!!task add §6女巫塔.铺地板 §a挂机铺黑色玻璃§r + §7{pre} add §6女巫塔.铺地板 §a挂机铺黑色玻璃§r mcdr_help: 工程任务进度管理 overview_help: | §7{0} list§r 显示完整的任务列表 @@ -36,23 +36,22 @@ mcd_task: task_not_found: 任务不存在! 点此查阅任务列表 task_not_found_hover: 点此§6查阅§e任务列表§r invalid_number: 无效的数字! 点此重新输入数字 - resuggest_cmd_hover: 点此重新补全指令 {} + resuggest_cmd_hover: 点此重新补全指令 §7{}§r rename_task_hover: 点此§6重命名§e任务§r info_player_hover: 点此§6查阅§e玩家详情§r mark_task_done_hover: 点此将任务§6设为§e完成§r mark_task_undone_hover: 点此将任务§6设为§e未完成§r - info_task_hover: 点此查阅§e任务详情§r + info_task_hover: 点此§6查阅§r任务 §e{}§r 详情§r time_format: '%Y-%m-%d %H:%M:%S' - ddl_set: 为任务 §e{}§r 设置了 §e{}§r 日(§e{}§r)的截止日期 - task_renamed: '任务{}已被重命名为{}:' - player_tasks_title: '玩家{}肩负了{}项重任: ' - info_task_title: '搬砖信息列表: ' + ddl_set: 已变更任务的截止日期 + task_renamed: '任务 §e{}§a§l 已被重命名:' + player_tasks_title: '玩家 §3{} §a§l肩负了 §3{} §a§l项重任: ' + info_task_title: '任务详细信息: ' + list_task_title: "搬砖信息列表: " add_task_hover: 点此§6添加§e新任务§r - info_task_single_title: '任务详细信息: ' + add_sub_task_hover: 点此§6添加§e新子任务§r done_task_button: '[已完成任务...]' done_task_hover: 点此§6查阅§r被标记为§e完成§r的任务 - edit_task_hover: 点此§6编辑§e任务描述§r - show_desc_hover: 点此查阅§e任务描述§r task_already_exist: 任务已存在, 点此查阅任务列表 new_task_created: '创建了新的任务: ' detailed_info_task_title: '搬砖详细信息列表: ' @@ -61,19 +60,30 @@ mcd_task: done_task_list_title: '已完成任务列表: ' on_player_joined: 您有{}项已逾期的任务, 请赶快填坑! on_player_renamed: 检测到游戏ID变更, 继承了{}项任务 - list_responsible_title: '此任务由{}名玩家承包: ' - removed_responsibles_title: '移除了{}名责任人: ' - added_responsibles_title: '添加了{}名责任人: ' + list_responsible_title: '此任务由 §3{}§ r名玩家承包: ' + removed_responsibles_title: '移除了 §3{}§r 名责任人: ' + added_responsibles_title: '添加了 §3{}§r 名责任人: ' illegal_call: 不含额外参数的该指令仅可作为玩家使用 help_msg_suggest_hover: 点此以填入 §7{}§r - set_ddl_hover: 点此为任务设置工期 - reloaded: "[Task] 插件已重载" + set_ddl_hover: 点此为任务§6设置§e工期§r + reloaded: "[Task] §a§l插件已重载§r" overview_headline: "当务之急: " no_priority: 暂无优先事项, 可查阅完整列表获取更多任务 - priority_set: 已将任务{}的优先级设置为{} + priority_set: 任务优先级已变更 date_approaching: | 截止日期临近或已过: §c{}§r has_a_high_priority: "该任务的优先级较高: §6{}§r" - detail_priority: '优先级: {}' - priority_hover: '点此设置该任务的优先级' + detail_priority: '优先级: §6{} §r[✎]' + priority_hover: '点此§6设置§e该任务的优先级§r' + detail_desc: '任务描述: §7{} §r[✎]' + desc_hover: 点此§6编辑§e任务描述§r + detail_deadline: '截止日期: §c{} §r[✎]' + detail_sub: "子任务: " + not_set: "暂未设定" + changed_desc_title: 已变更任务描述 + done_task_title: 任务被标记为已完成 + undone_task_title: 任务被标记为未完成 + detail_res: "责任人: §5§l[+]§r" + add_res_hover: 点击为该任务§6添加§e责任人§r + rm_res_hover: 点击自此任务§6移除§e责任人 {}§r diff --git a/mcd_task/__init__.py b/mcd_task/__init__.py index e963797..5b15a41 100755 --- a/mcd_task/__init__.py +++ b/mcd_task/__init__.py @@ -1,4 +1,3 @@ -from mcd_task import global_variables from mcd_task.command_actions import * from mcd_task.global_variables import GlobalVariables from mcd_task.task_manager import * @@ -13,7 +12,6 @@ def on_info(server: PluginServerInterface, info: Info): psd = parse(PLAYER_RENAMED, info.content) if psd is not None: inherit_responsible(info, **psd.named) - server.as_plugin_server_interface() if info.is_user and DEBUG_MODE: if info.content.startswith('!!task debug '): @@ -22,7 +20,7 @@ def on_info(server: PluginServerInterface, info: Info): if args[2] == 'base-title': info.get_command_source().reply('Manager title is {}'.format(GlobalVariables.task_manager.title)) elif args[2] == 'full-path' and len(args) == 4: - info.get_command_source().reply(GlobalVariables.task_manager[args[3]].full_path()) + info.get_command_source().reply(GlobalVariables.task_manager[args[3]].titles) elif args[2] == 'player-join': on_player_joined(server, info.player, info) elif args[2] == 'player-renamed' and len(args) == 5: @@ -38,13 +36,12 @@ def on_info(server: PluginServerInterface, info: Info): def on_player_joined(server: PluginServerInterface, player: str, info: Info): player_tasks = [] now_time = float(time.time()) - for t in GlobalVariables.task_manager.get_responsible_manager()[player]: + for t in GlobalVariables.task_manager.responsible_manager[player]: task = GlobalVariables.task_manager[t] if not task.done and now_time > task.deadline != 0: player_tasks.append(task) if len(player_tasks) > 0: task_timed_out(server, player, player_tasks) - info.get_server() def on_load(server: PluginServerInterface, prev_module): diff --git a/mcd_task/command_actions.py b/mcd_task/command_actions.py index ac60a1e..ac16bd1 100755 --- a/mcd_task/command_actions.py +++ b/mcd_task/command_actions.py @@ -6,7 +6,10 @@ from mcd_task.global_variables import GlobalVariables from mcd_task.constants import PREFIX, DEBUG_MODE from mcd_task.exceptions import TaskNotFound -from mcd_task.task_manager import TitleList, TaskBase, Task +from mcd_task.task_manager import Task +from mcd_task.utils import formatted_time, source_name, TitleList +from mcd_task.rtext_components import tr, info_elements, title_text, info_responsibles, add_task_button, \ + list_done_task_button, EditButtonType, info_sub_tasks, indent_text, sub_task_title_text # =============================== @@ -55,14 +58,14 @@ def ensure_not_exist(source: CommandSource, context: CommandContext): permed_literal('detail').then( ensure_task_exist_quotable_text().runs(lambda src, ctx: info_task(src, title=ctx['title'])) ), - permed_literal('detail-all').runs(lambda src: all_tasks_detail(src)), + permed_literal('list-all').runs(lambda src: all_tasks_detail(src)), permed_literal('list-done').runs(lambda src: list_done(src)), permed_literal('add').then( ensure_task_not_exist_quotable_text().runs(lambda src, ctx: add_task(src, ctx['title'])).then( GreedyText('description').runs(lambda src, ctx: add_task(src, ctx['title'], ctx['description'])) ) ), - permed_literal('remove', 'rm').then( + permed_literal('remove', 'rm', 'delete', 'del').then( ensure_task_exist_quotable_text().runs(lambda src, ctx: remove_task(src, ctx['title'])) ), permed_literal('rename').then( @@ -99,9 +102,6 @@ def ensure_not_exist(source: CommandSource, context: CommandContext): GreedyText("players").runs(lambda src, ctx: rm_responsible(src, ctx['title'], ctx['players'])) ) ), - permed_literal("list-responsibles", "list-res").then( - ensure_task_exist_quotable_text().runs(lambda src, ctx: list_responsible(src, ctx['title'])) - ), permed_literal('priority').then( ensure_task_exist_quotable_text().then( Literal('clear').runs(lambda src, ctx: set_task_priority(src, ctx['title'])) @@ -117,232 +117,6 @@ def ensure_not_exist(source: CommandSource, context: CommandContext): server.register_command(root_node) -# ================================= -# | Text process utilities | -# ================================= - - -def tr(key: str, *args, **kwargs): - """ - Translate shortcut - :param key: - :param args: - :param kwargs: - :return: - """ - return GlobalVariables.tr(key, *args, **kwargs) - - -def rclick(msg: Union[RTextMCDRTranslation, str], hover: str, cmd: str, action: RAction = RAction.run_command, - color: Optional[RColor] = None, style: Optional[RStyle] = None) -> RTextBase: - """ - RText shortcut with click events - :param msg: - :param hover: - :param cmd: - :param action: - :param color: - :param style: - :return: - """ - if isinstance(msg, RTextMCDRTranslation): - rt = msg.h(hover).c(action, cmd) - if color is not None: - rt.set_color(color) - if style is not None: - rt.set_styles(style) - return rt - return RText(msg, color, style).h(hover).c(action, cmd) - - -def _player_info_simple(name: str, indent=8, removed=False) -> RTextList: - """ - Generate player text with click events in a line - :param name: - :param indent: - :param removed: - :return: - """ - return _indent_text(indent) + rclick( - name, tr("mcd_task.info_player_hover"), - "{} player {}".format(PREFIX, name), - color=RColor.gold if not removed else RColor.dark_red, - style=RStyle.strikethrough if removed else None - ) - - -def _task_info_simple(raw_titles: Union[TitleList, str], - full=False, sub=False, done=False, idt=None, - desc=False, desc_text: Optional[str] = None, - ddl=None, priority=None): - titles_commands = str(raw_titles).strip('.') - titles = str(raw_titles) if full else TitleList(raw_titles).tail - # Indent text - if idt is None: - idt = 8 if sub else 4 - indent = _indent_text(indent=idt) - - # Done button - done_button = rclick( - "⬛", tr("mcd_task.mark_task_undone_hover"), - "{} undone {}".format(PREFIX, titles_commands), - color=RColor.dark_gray - ) - undone_button = rclick( - "⬜", tr("mcd_task.mark_task_done_hover"), - "{} done {}".format(PREFIX, titles_commands), - color=RColor.white - ) - button = done_button if done else undone_button - - # Task title - title_text = rclick( - titles, tr("mcd_task.info_task_hover"), - "{} detail {}".format(PREFIX, titles_commands), - color=RColor.dark_gray if done else RColor.yellow - ) - - # Rename task - rename_task_button = rclick( - "[✎]", tr("mcd_task.rename_task_hover"), - "{} rename {} ".format(PREFIX, titles_commands), - action=RAction.suggest_command - ) if not sub else '' - - # Description - if desc_text is not None or ddl is not None or priority is not None: - if desc: - description = [''] - if desc_text is not None: - description.append(RTextList( - _indent_text(idt), - RText(desc_text, color=RColor.gray), ' ', - rclick( - "[✎]", tr("mcd_task.edit_task_hover"), - "{} change {} ".format(PREFIX, titles_commands), - action=RAction.suggest_command - ), ' ' - )) - if ddl is not None: - description.append(RTextList( - _indent_text(idt), - rclick( - formatted_time(ddl), tr("mcd_task.set_ddl_hover"), - "{} deadline {} ".format(PREFIX, titles_commands), - action=RAction.suggest_command, color=RColor.red - ) - )) - if priority is not None: - description.append(RTextList( - _indent_text(idt), - tr('mcd_task.detail_priority', priority).set_color(RColor.gold).c( - RAction.suggest_command, "{} priority {} ".format(PREFIX, titles_commands)).h( - tr('mcd_task.priority_hover') - ) - )) - description = RTextBase.join('\n', description) - else: - description = RTextList( - rclick( - "[...]", tr("mcd_task.show_desc_hover"), - "{} detail {} ".format(PREFIX, titles_commands) - ) - ) - else: - description = '' - - return RTextList( - indent, button, ' ', title_text, ' ', rename_task_button, description - ) - - -def _task_info_flexible(title: Union[TitleList, str], sub=False, idt=4): - title = TitleList(title) - target_task = GlobalVariables.task_manager[title] # type: TaskBase - desc = target_task.description - text = RTextList( - '\n', - _task_info_simple( - target_task.full_path(), sub=sub, done=target_task.done, - idt=idt, desc=not sub, desc_text=desc if desc != '' else None, - ddl=target_task.deadline if target_task.deadline != 0 else None, - priority=target_task.priority - ) - ) - if len(target_task.sub_tasks) == 0: - return text - for t in target_task.sub_tasks: - text.append( - _task_info_flexible(t.full_path(), sub=True, idt=idt + 4) - ) - return text - - -def _indent_text(indent: int) -> str: - """ - Get actual indent strings from integer - :param indent: - :return: - """ - ret = '' - for num in range(indent): - ret += ' ' - return ret - - -def formatted_time(timestamp: float, locale: Optional[str] = None) -> str: - """ - Format time text with specified locale - :param timestamp: - :param locale: - :return: - """ - return time.strftime(GlobalVariables.server.tr("mcd_task.time_format", lang=locale), time.localtime(timestamp)) - - -def _add_task_button(title: str = None): - # Add button - title_path = '' if title in ['', None] else '{}.'.format(title) - return rclick( - '[+]', tr("mcd_task.add_task_hover"), '{} add {}'.format(PREFIX, title_path), - action=RAction.suggest_command, color=RColor.red, style=RStyle.bold - ) - - -def _info_task(title: Optional[str] = None, done=False) -> Optional[RTextList]: - target_task = None - if title is not None: - title = title.strip(".") - try: - target_task = GlobalVariables.task_manager[title] - except TaskNotFound: - target_task = None # type: Optional[TaskBase] - - text = RTextList() - - # Task tree - if target_task is None: - for t in GlobalVariables.task_manager.split_sub_tasks_by_done()[0 if not done else 1]: # type: Task - text.append('\n', _task_info_simple(t.full_path(), sub=True, done=t.done, desc=False, idt=4, - desc_text=t.description if t.description != '' else None)) - if not done: - text.append( - '\n', _indent_text(4), - tr("mcd_task.done_task_button").set_hover_text(tr("mcd_task.done_task_hover")).set_click_event( - RAction.run_command, "{} list-done".format(PREFIX)).set_color(RColor.dark_gray) - ) - else: - text.append(_task_info_flexible(target_task.full_path(), sub=False)) - return text - - -def _source_name(source: CommandSource): - if isinstance(source, PlayerCommandSource): - return source.player - else: - return source.__class__.__name__ - - # ========================= # | Command Actions | # ========================= @@ -365,76 +139,87 @@ def cmd_error(source: CommandSource, exception: CommandError): def task_not_found(source: CommandSource): source.reply(tr("mcd_task.task_not_found").h(tr("mcd_task.task_not_found_hover", list_cmd)).set_color(RColor.red).c( RAction.run_command, list_cmd - ) - ) + )) def task_already_exist(source: CommandSource): - source.reply(tr("mcd_task.task_already_exist").set_color(RColor.red).c(RAction.run_command, f'{PREFIX} list').h( - tr("mcd_task.task_not_found_hover", PREFIX) - ) - ) + source.reply(tr("task_already_exist").set_color(RColor.red).c(RAction.run_command, f'{PREFIX} list').h( + tr("task_not_found_hover", PREFIX) + )) def illegal_call(source: CommandSource): - source.reply(tr('mcd_task.illegal_call').set_color(RColor.red)) + source.reply(tr('illegal_call').set_color(RColor.red)) # Info def info_player(source: CommandSource, name: str) -> None: - tasks = GlobalVariables.task_manager.get_responsible_manager()[name] - text = RTextList( - tr("mcd_task.player_tasks_title", name, str(len(tasks))).set_color(RColor.green).set_styles(RStyle.bold), - ) - for t in tasks: - task_status = GlobalVariables.task_manager[t].done - text.append('\n', _task_info_simple(t, full=True, done=task_status, idt=4, sub=False)) - - source.reply(text) + tasks = list(GlobalVariables.task_manager.responsible_manager[name]) + text = [ + tr("player_tasks_title", name, str(len(tasks))).set_color(RColor.green).set_styles(RStyle.bold), + ] + for task in tasks: + text.append(title_text(task, display_full_path=True, display_not_empty_mark=True)) + source.reply(RText.join('\n', text)) -def info_task(source: CommandSource, title: Optional[str] = None, prefix=None, done=False) -> None: - # Task list +def info_task(source: CommandSource, title: str, headline_override: Union[None, str, RTextBase] = None) -> None: + # Get task instance try: - task_list = _info_task(title, done=done) + target_task = GlobalVariables.task_manager[title] except TaskNotFound: task_not_found(source) if DEBUG_MODE: raise return - # Prefix text - if prefix is None: - prefix = tr("mcd_task.info_task_title" if title is None else "mcd_task.info_task_single_title" - ).set_color(RColor.green).set_styles(RStyle.bold) - - add_button = _add_task_button(title) + # Task detail text + headline = tr("info_task_title") + if isinstance(headline_override, str): + headline = tr(headline_override) + if isinstance(headline_override, RTextBase): + headline = headline_override + headline.set_color(RColor.green).set_styles(RStyle.bold) + task_title_text = title_text(target_task, display_full_path=True, with_edit_button=True) + info_desc = info_elements(target_task) + info_ddl = info_elements(target_task, EditButtonType.deadline) + info_priority = info_elements(target_task, EditButtonType.priority) + info_res = info_responsibles(target_task) + info_sub = info_sub_tasks(target_task) - text = RTextList(prefix, add_button, task_list) - if text is not None: - source.reply(text) + # Show task text + source.reply(RText.join('\n', [headline, task_title_text, info_desc, info_ddl, info_priority, info_res, info_sub])) # Others +def list_task(source: CommandSource, done=False): + headline = tr("done_task_list_title" if done else 'list_task_title').set_styles(RStyle.bold).set_color( + RColor.green) + ' ' + add_task_button() + task_list_text = [] + for task in GlobalVariables.task_manager.sub_tasks: + if task.done is done: + task_list_text.append(title_text(task, display_not_empty_mark=True)) + task_list_text = RTextBase.join('\n', task_list_text) + text = [headline, task_list_text] + if not done: + text.append(indent_text(4) + list_done_task_button()) + source.reply(RText.join('\n', [headline, task_list_text, indent_text(4) + list_done_task_button()])) + + def set_task_deadline(source: CommandSource, titles: str, ddl: str) -> None: deadline = float(time.time()) + float(ddl) * 3600 * 24 GlobalVariables.task_manager.set_deadline(TitleList(titles), deadline) - source.reply( - tr("mcd_task.ddl_set", titles, ddl, formatted_time(deadline)).set_color(RColor.green).set_styles(RStyle.bold) - ) + info_task(source, titles, headline_override=tr("ddl_set")) GlobalVariables.log( - f"{_source_name(source)} set task {titles} deadline to {formatted_time(deadline, locale='en_us')}" + f"{source_name(source)} set task {titles} deadline to {formatted_time(deadline, locale='en_us')}" ) -def list_task(source: CommandSource): - info_task(source) - - def task_overview(source: CommandSource): GlobalVariables.debug('Running overview...') - headline = tr('mcd_task.overview_headline').set_styles(RStyle.bold).set_color(RColor.green) + headline = tr('overview_headline').set_styles(RStyle.bold).set_color(RColor.green) + ' ' + add_task_button() + # Get task instances deadline_approaching = GlobalVariables.task_manager.seek_for_item_with_deadline_approaching() GlobalVariables.debug(deadline_approaching) max_length = GlobalVariables.config.overview_maximum_task_amount @@ -443,77 +228,72 @@ def task_overview(source: CommandSource): if priority_amount > 0: with_priorities = GlobalVariables.task_manager.seek_for_item_with_priority() + # Found no matched task handle if len(deadline_approaching) == 0 and len(with_priorities) == 0: - task_text = tr('mcd_task.no_priority').set_color(RColor.yellow) + task_text = tr('no_priority').set_color(RColor.yellow) + # Organize task texts else: task_texts = {} for task in deadline_approaching: if len(task_texts) >= GlobalVariables.config.overview_maximum_task_amount: break - task_texts[task.full_path()] = RText('[!] ', RColor.red).h( - tr('mcd_task.date_approaching', formatted_time(task.deadline)) - ) + _task_info_simple( - task.full_path(), full=True, done=task.done, idt=4, sub=False).set_color( - RColor.red - ) + task_texts[task.titles] = RText('[!] ', RColor.red).h( + tr('date_approaching', formatted_time(task.deadline)) + ) + title_text(task, display_full_path=True) for task in with_priorities: if len(task_texts) >= GlobalVariables.config.overview_maximum_task_amount: break if task.title not in task_texts.keys(): task_texts[task.full_path()] = RText('[!] ', RColor.gold).h( - tr('mcd_task.has_a_high_priority', task.priority) - ) + _task_info_simple( - task.full_path(), full=True, done=task.done, idt=4, sub=False).set_color( - RColor.gold - ) + tr('has_a_high_priority', task.priority) + ) + title_text(task, display_full_path=True) task_text = RTextBase.join('\n', task_texts.values()) - help_message = tr('mcd_task.overview_help', PREFIX).set_translator(GlobalVariables.htr) + help_message = tr('overview_help', PREFIX).set_translator(GlobalVariables.htr) source.reply(RTextBase.join('\n', [headline, task_text, help_message])) def set_task_priority(source: CommandSource, titles: str, priority: Optional[int] = None): GlobalVariables.task_manager.set_priority(TitleList(titles), priority) - source.reply(tr('mcd_task.priority_set', titles, priority).set_color(RColor.green).set_styles(RStyle.bold)) - GlobalVariables.log(f"{_source_name(source)} set task {titles} priority to {priority}") + info_task(source, titles, headline_override=tr('priority_set')) + GlobalVariables.log(f"{source_name(source)} set task {titles} priority to {priority}") def reload_self(source: CommandSource): server = GlobalVariables.server server.reload_plugin(server.get_self_metadata().id) - source.reply(tr('mcd_task.reloaded')) + source.reply(tr('reloaded')) def show_help(source: CommandSource): meta = GlobalVariables.server.get_self_metadata() - source.reply(tr('mcd_task.help_msg', PREFIX, meta.name, meta.version).set_translator(GlobalVariables.htr)) + source.reply(tr('help_msg', pre=PREFIX, name=meta.name, ver=str(meta.version)).set_translator(GlobalVariables.htr)) def add_task(source: CommandSource, titles: str, desc: str = ''): titles = TitleList(titles) titles_for_text = titles.copy() - GlobalVariables.debug(vars(GlobalVariables.task_manager)) GlobalVariables.task_manager.add_task(titles, desc=desc) - info_task(source, title=titles_for_text.head, prefix=tr( - "mcd_task.new_task_created").set_color(RColor.green).set_styles(RStyle.bold)) - GlobalVariables.log(f"{_source_name(source)} created new task named {str(titles_for_text)}") + info_task(source, title=str(titles_for_text), headline_override=tr("new_task_created")) + GlobalVariables.log(f"{source_name(source)} created new task named {str(titles_for_text)}") def all_tasks_detail(source: CommandSource): - task_details = RTextList() + task_details = [tr("detailed_info_task_title").set_color(RColor.green).set_styles(RStyle.bold) + ' ' + + add_task_button()] for task in GlobalVariables.task_manager.sub_tasks: - task_details.append(_task_info_flexible(task.full_path(), sub=False, idt=4)) - prefix = tr("mcd_task.detailed_info_task_title").set_color(RColor.green).set_styles(RStyle.bold) - add_button = _add_task_button() - source.reply(prefix + add_button + task_details) + task_details.append(title_text(task, include_sub=False, display_not_empty_mark=True)) + if len(task.sub_tasks) > 0: + task_details.append(sub_task_title_text(task, indent=8)) + source.reply(RTextBase.join('\n', task_details)) def remove_task(source: CommandSource, titles: str): GlobalVariables.task_manager.delete_task(TitleList(titles)) source.reply(tr("mcd_task.deleted_task", "§e{}§r".format(titles))) - GlobalVariables.log(f"{_source_name(source)} deleted task {titles}") + GlobalVariables.log(f"{source_name(source)} deleted task {titles}") def rename_task(source: CommandSource, old_titles: str, new_title: str) -> None: @@ -524,30 +304,30 @@ def rename_task(source: CommandSource, old_titles: str, new_title: str) -> None: new_titles = TitleList(old_titles) new_titles.pop_tail() new_titles.append(new_title) - info_task(source, title=str(new_titles), prefix=tr("mcd_task.task_renamed", old_titles, str(new_titles))) - GlobalVariables.log(f"{_source_name(source)} renamed {old_titles} to {str(new_titles)}") + info_task(source, title=str(new_titles), headline_override=tr("mcd_task.task_renamed", old_titles)) + GlobalVariables.log(f"{source_name(source)} renamed {old_titles} to {str(new_titles)}") def edit_desc(source: CommandSource, titles: str, desc: str) -> None: GlobalVariables.task_manager.edit_desc(TitleList(titles), desc) - info_task(source, title=titles) - GlobalVariables.log(f"{_source_name(source)} changed task {titles} description to {desc}") + info_task(source, title=titles, headline_override='changed_desc_title') + GlobalVariables.log(f"{source_name(source)} changed task {titles} description to {desc}") def set_done(source: CommandSource, titles: str) -> None: GlobalVariables.task_manager.done_task(TitleList(titles)) - info_task(source, title=titles) - GlobalVariables.log(f"{_source_name(source)} marked task {titles} as done") + info_task(source, title=titles, headline_override='done_task_title') + GlobalVariables.log(f"{source_name(source)} marked task {titles} as done") def set_undone(source: CommandSource, titles: str) -> None: GlobalVariables.task_manager.undone_task(TitleList(titles)) - info_task(source, title=titles) - GlobalVariables.log(f"{_source_name(source)} marked task {titles} as undone") + info_task(source, title=titles, headline_override='undone_task_title') + GlobalVariables.log(f"{source_name(source)} marked task {titles} as undone") def list_done(source: CommandSource): - info_task(source, prefix=tr("mcd_task.done_task_list_title"), done=True) + list_task(source, done=True) def set_responsible(source: CommandSource, titles: str, players: Optional[str] = None) -> None: @@ -559,8 +339,8 @@ def set_responsible(source: CommandSource, titles: str, players: Optional[str] = return players = players.split(' ') num = GlobalVariables.task_manager.set_responsible(TitleList(titles), *players) - list_responsible(source, titles, prefix=tr("mcd_task.added_responsibles_title", num)) - GlobalVariables.log(f"{_source_name(source)} added responsibles for task {str(titles)}: {str(players)}") + info_task(source, titles, headline_override=tr("mcd_task.added_responsibles_title", num)) + GlobalVariables.log(f"{source_name(source)} added responsibles for task {str(titles)}: {str(players)}") def rm_responsible(source: CommandSource, titles: str, players: Optional[str] = None) -> None: @@ -573,49 +353,22 @@ def rm_responsible(source: CommandSource, titles: str, players: Optional[str] = players = players.split('.') removed = GlobalVariables.task_manager.rm_responsible(TitleList(titles), *players) num = len(removed) - list_responsible(source, titles, player_removed=removed, - prefix=tr("mcd_task.removed_responsibles_title", num)) - GlobalVariables.log(f"{_source_name(source)} removed responsibles for task {str}: {str(players)}") - - -def list_responsible(source: CommandSource, titles: str, - player_removed=None, prefix: Optional[RTextMCDRTranslation] = None) -> None: - if player_removed is None: - player_removed = [] - player_list = GlobalVariables.task_manager.get_responsible_manager().get_responsibles(titles) - num = len(player_list) - task_done = GlobalVariables.task_manager[titles].done - text = RTextList( - (tr("mcd_task.list_responsible_title", num) if prefix is None else prefix).set_styles( - RStyle.bold).set_color(RColor.green), - '\n', _task_info_simple(titles, full=True, sub=True, done=task_done, idt=4) - ) - for p in player_list: - text.append( - '\n', - _player_info_simple(p, indent=8) - ) - if player_removed is not None: - for p in player_removed: - text.append( - '\n', - _player_info_simple(p, indent=8, removed=True) - ) - source.reply(text) + info_task(source, titles, headline_override=tr("mcd_task.removed_responsibles_title", num)) + GlobalVariables.log(f"{source_name(source)} removed responsibles for task {str}: {str(players)}") def inherit_responsible(info: Info, old_name: str, new_name: str, debug=False): - resm = GlobalVariables.task_manager.get_responsible_manager() - if old_name in resm.player_work.keys(): - resm.rename_player(old_name, new_name) - num = len(resm[new_name]) + manager = GlobalVariables.task_manager.responsible_manager + if old_name in manager.player_work.keys(): + manager.rename_player(old_name, new_name) + num = len(manager[new_name]) info.get_server().tell(new_name, tr("mcd_task.on_player_renamed", num)) GlobalVariables.logger.debug(tr("mcd_task.on_player_renamed", num), no_check=debug) GlobalVariables.log(f"Detected player rename {old_name} -> {new_name}. Inherited {num} task(s)") -def task_timed_out(server: PluginServerInterface, player: str, player_tasks: List[TaskBase]): - text = tr("mcd_task.on_player_joined", len(player_tasks)).set_color(RColor.red).set_styles(RStyle.bold) +def task_timed_out(server: PluginServerInterface, player: str, player_tasks: List[Task]): + text = tr("on_player_joined", len(player_tasks)).set_color(RColor.red).set_styles(RStyle.bold) for t in player_tasks: - text.append('\n', _task_info_simple(t.full_path(), full=True, sub=True, done=t.done, idt=4)) + text.append('\n', title_text(t, display_full_path=True, display_not_empty_mark=True)) server.tell(player, text) diff --git a/mcd_task/config.py b/mcd_task/config.py index f3b4b25..192340d 100644 --- a/mcd_task/config.py +++ b/mcd_task/config.py @@ -1,38 +1,21 @@ from typing import Any, Dict, Optional -from mcdreforged.api.all import PluginServerInterface, Serializable, deserialize +from mcdreforged.api.all import PluginServerInterface, Serializable from mcd_task.constants import CONFIG_PATH, DEBUG_MODE -class Permission(Serializable): - help: int = 0 - list: int = 0 - detail: int = 0 - detail_all: int = 0 - list_done: int = 0 - add: int = 1 - remove: int = 1 - rename: int = 1 - change: int = 1 - done: int = 1 - undone: int = 1 - deadline: int = 1 - player: int = 2 - responsible: int = 2 - unresponsible: int = 2 - list_responsibles: int = 1 - priority: int = 1 - - @classmethod - def deserialize(cls, data: dict, **kwargs): - for key, value in data.copy().items(): - data[key.replace("-", "_")] = value - return deserialize(data, cls, **kwargs) - - class Config(Serializable): - permission: Permission = Permission.get_default() + permission: Dict[str, int] = { + "help": 0, + "list": 0, + "detail": 0, + "list-all": 0, + "list-done": 0, + "player": 2, + "responsible": 2, + "unresponsible": 2 + } detect_player_rename: bool = True default_overview_instead_of_list: bool = True overview_deadline_warning_threshold: int = 1 # days @@ -69,7 +52,7 @@ def __get_key_from_dict(target_dict: Dict[str, Any], key: str = None) -> Any: return ret def get_permission(self, cmd: str): - return self.permission.serialize().get(cmd.replace('-', '_'), 0) + return self.permission.get(cmd, 1) @staticmethod def __set_key_to_dict(target_dict: Dict[str, Any], key: str, value: Any): diff --git a/mcd_task/constants.py b/mcd_task/constants.py index cf711c5..cf3b87a 100755 --- a/mcd_task/constants.py +++ b/mcd_task/constants.py @@ -22,7 +22,7 @@ LOG_PATH = os.path.join(DATA_FOLDER, "task.log") # If this is True, will enable some debug options -DEBUG_MODE = True +DEBUG_MODE = False # Supported languages LANGUAGES = ["en_us", "zh_cn"] diff --git a/mcd_task/global_variables.py b/mcd_task/global_variables.py index 44f6451..aa735d3 100644 --- a/mcd_task/global_variables.py +++ b/mcd_task/global_variables.py @@ -2,10 +2,7 @@ import re import types from typing import Optional, Union, TYPE_CHECKING - -from mcdreforged.minecraft.rtext import RTextBase, RTextList, RText, RAction -from mcdreforged.plugin.server_interface import ServerInterface -from mcdreforged.utils.logger import MCDReforgedLogger +from mcdreforged.api.all import RTextBase, RTextList, RText, RAction, ServerInterface, MCDReforgedLogger from mcd_task.config import Config from mcd_task.constants import LOG_PATH, PREFIX, DEBUG_MODE @@ -48,6 +45,8 @@ def debug(cls, msg): @classmethod def tr(cls, key: Optional[str], *args, **kwargs): + if not key.startswith('mcd_task.'): + key = f"mcd_task.{key}" return cls.server.rtr(key, *args, **kwargs) @classmethod diff --git a/mcd_task/responsible.py b/mcd_task/responsible.py index aea10c0..d15897c 100644 --- a/mcd_task/responsible.py +++ b/mcd_task/responsible.py @@ -1,6 +1,6 @@ import json import os -from typing import Dict, Set, Union +from typing import Dict, Set, Union, TYPE_CHECKING, Iterator from parse import parse @@ -9,10 +9,15 @@ from mcd_task.exceptions import DuplicatedTask, TaskNotFound +if TYPE_CHECKING: + from mcd_task.task_manager import TaskManager, Task + + class ResponsibleManager: - def __init__(self): + def __init__(self, task_manager: "TaskManager"): self.path = RESG_PATH # type: str self.player_work = {} # type: Dict[str, Set[str]] + self.task_manager = task_manager def rename_player(self, old_name: str, new_name: str, should_save=True): value = self.player_work.pop(old_name) @@ -94,5 +99,7 @@ def get_responsibles(self, task_title: Union['TitleList', str]): ret.add(key) return list(ret) - def __getitem__(self, player: str) -> Set[str]: - return self.player_work.get(player, set()) + def __getitem__(self, player: str) -> Iterator["Task"]: + task_titles = self.player_work.get(player, set()) + for titles in task_titles: + yield self.task_manager[titles] diff --git a/mcd_task/rtext_components.py b/mcd_task/rtext_components.py new file mode 100644 index 0000000..1de089a --- /dev/null +++ b/mcd_task/rtext_components.py @@ -0,0 +1,132 @@ +from mcdreforged.api.rtext import * + +from mcd_task.task_manager import Task +from mcd_task.global_variables import GlobalVariables +from mcd_task.constants import * +from collections import namedtuple +from collections import namedtuple + +from mcdreforged.api.rtext import * + +from mcd_task.constants import * +from mcd_task.global_variables import GlobalVariables +from mcd_task.task_manager import Task + +TypeItems = namedtuple('TypeItems', ["name", 'hover_tr_key', 'cmd_fmt']) + + +class EditButtonType: + rename = TypeItems('name', 'rename_task_hover', PREFIX + " rename {} ") + desc = TypeItems("desc", "edit_task_hover", PREFIX + " change {} ") + priority = TypeItems("priority", "priority_hover", PREFIX + " priority {} ") + deadline = TypeItems("deadline", "set_ddl_hover", PREFIX + " deadline {} ") + + +def tr(key: str, *args, **kwargs): + return GlobalVariables.tr(key, *args, **kwargs) + + +def indent_text(indent: int) -> str: + ret = '' + for num in range(indent): + ret += ' ' + return ret + + +def list_done_task_button(): + return tr('done_task_button').c(RAction.run_command, f'{PREFIX} list-done').h( + tr('done_task_hover')).set_color(RColor.dark_gray) + + +def done_button(task: Task): + # Done button + click_event_to_do = 'undone' if task.done else 'done' + return RText("⬛" if task.done else "⬜", RColor.dark_gray if task.done else RColor.white).h( + tr(f"mark_task_{click_event_to_do}_hover")).c( + RAction.run_command, "{} {} {}".format(PREFIX, click_event_to_do, task.titles) + ) + + +def add_task_button(title: str = None): + # Add button + title_path = '' if title in ['', None] else '{}.'.format(title) + return RText('[+]', RColor.light_purple, RStyle.bold).c( + RAction.suggest_command, '{} add {}'.format(PREFIX, title_path)).h( + tr(f"add{'' if title is None else '_sub'}_task_hover") + ) + + +def edit_button(task: Task, button_type: TypeItems = EditButtonType.rename): + return RText(" [✎]").h(tr(button_type.hover_tr_key)).c(RAction.suggest_command, button_type.cmd_fmt.format(task.titles)) + + +def title_text(task: Task, display_full_path=False, with_edit_button=False, indent=4, display_not_empty_mark=False, + include_sub=True): + edit = '' + if with_edit_button: + edit = edit_button(task) + target_title, title_text_list = task.titles.copy(), [] + while True: + if target_title.is_empty: + break + if not display_full_path and len(title_text_list) == 1: + break + this_title_full = str(target_title) + this_title = target_title.pop_tail() + title_text_list.append( + RText(this_title, + RColor.dark_gray if GlobalVariables.task_manager[this_title_full].done else RColor.yellow).c( + RAction.run_command, f'{PREFIX} detail {this_title_full}').h(tr('info_task_hover', this_title_full)) + ) + title_text_list.reverse() + title = RText.join('§7.§r', title_text_list) + if display_not_empty_mark: + if task.is_not_empty or (include_sub and len(task.sub_tasks) > 0): + title += ' §f[...]' + return indent_text(indent) + done_button(task) + ' ' + title + edit + + +# !!task detail components +def info_elements(task: Task, button_type: TypeItems = EditButtonType.desc, indent=4): + return indent_text(indent) + tr(f'detail_{button_type.name}', task.get_elements(button_type.name)).h( + tr(button_type.hover_tr_key)).c( + RAction.suggest_command, button_type.cmd_fmt.format(task.titles)) + + +def info_responsibles_headline(task: Task): + return tr('detail_res').c( + RAction.suggest_command, '{} res {} '.format(PREFIX, task.titles)).h(tr(f"add_res_hover")) + + +def single_responsible(task: Task, player: str, indent=8, removed=False): + text = indent_text(indent) + RText( + player, RColor.dark_gray if removed else RColor.dark_aqua, RStyle.strikethrough if removed else None).h( + tr('info_player_hover')).c( + RAction.run_command, f'{PREFIX} player {player}') + ' ' + if not removed: + text += RText('[-]', RColor.aqua, RStyle.bold).c( + RAction.run_command, f'{PREFIX} unres {task.titles} {player}').h(tr('rm_res_hover', player)) + return text + + +def info_responsibles(task: Task, indent=4): + text = indent_text(indent) + info_responsibles_headline(task) + for player in task.responsibles: + text += '\n' + single_responsible(task, player, indent=indent + 4) + return text + + +def sub_task_title_text(task: Task, indent=4): + text = [] + for sub in task.sub_tasks: + text.append(indent_text(indent) + title_text(sub)) + if len(sub.sub_tasks) > 0: + text.append(sub_task_title_text(sub, indent + 4)) + return RTextBase.join('\n', text) + + +def info_sub_tasks(task: Task, indent=4): + text = indent_text(indent) + tr('detail_sub') + ' ' + add_task_button(str(task.titles)) + if len(task.sub_tasks) > 0: + text += '\n' + sub_task_title_text(task, indent=indent + 4) + return text diff --git a/mcd_task/task_manager.py b/mcd_task/task_manager.py index 2392230..1717335 100755 --- a/mcd_task/task_manager.py +++ b/mcd_task/task_manager.py @@ -2,19 +2,17 @@ import os import time -from abc import ABC from copy import copy from typing import List, Dict, Tuple, Any, Union, Optional from mcdreforged.api.utils import Serializable, deserialize from mcd_task.exceptions import TaskNotFound, DuplicatedTask -from mcd_task.utils import TitleList +from mcd_task.utils import TitleList, formatted_time from mcd_task.constants import TASK_PATH, DEBUG_MODE from mcd_task.responsible import ResponsibleManager from mcd_task.global_variables import GlobalVariables - SUB_TASKS = 'sub_tasks' @@ -33,7 +31,7 @@ def __init__(self, **kwargs): @property def titles(self): - return TitleList(self.title) + return self.full_path() @property def child_map(self) -> Dict[str, 'Task']: @@ -46,6 +44,18 @@ def child_map(self) -> Dict[str, 'Task']: def child_titles(self) -> List[str]: return list(self.child_map.keys()) + @property + def is_not_empty(self): + if self.deadline != 0: + return True + if self.description != '': + return True + if self.priority is not None: + return True + if len(self.responsibles) > 0: + return True + return False + def full_path(self, titles: 'TitleList' = TitleList()) -> 'TitleList': raise NotImplementedError('Not implemented method: TaskBase.full_path()') @@ -60,7 +70,7 @@ def add_task(self, titles: 'TitleList', desc: str = '') -> None: def next_layer(self, titles: 'TitleList') -> Tuple[str, TitleList]: next_layer_title = titles.pop_head() if next_layer_title not in next_layer_title: - raise TaskNotFound(self.full_path().copy().append(next_layer_title)) + raise TaskNotFound(self.titles.copy().append(next_layer_title)) return next_layer_title, titles def __add_task(self, data: dict): @@ -86,7 +96,7 @@ def set_father(self, father_node: "TaskBase"): return self @classmethod - def deserialize(cls, data: dict, **kwargs) -> "TaskBase": + def deserialize(cls, data: dict, **kwargs): GlobalVariables.debug(data) sub_tasks = copy(data.get('sub_tasks', [])) if not isinstance(sub_tasks, list): @@ -108,7 +118,7 @@ def split_sub_tasks_by_done(self): undones.append(t) return undones, dones - def __getitem__(self, titles: Union[TitleList, str]) -> 'TaskBase': + def __getitem__(self, titles: Union[TitleList, str]) -> Union['Task', 'TaskBase']: if isinstance(titles, str): titles = TitleList(titles) if titles.is_empty: @@ -139,12 +149,13 @@ def seek_for_item_with_deadline_approaching(self, sort=True, with_done=False): result += item.seek_for_item_with_deadline_approaching(sort=False) return sorted(result, key=lambda task: task.deadline, reverse=False) if sort else result + @property + def responsibles(self): + return GlobalVariables.task_manager.responsible_manager.get_responsibles(self.titles) + def __str__(self): return str(self.serialize()) - def __hash__(self): - return has - class Task(TaskBase): def full_path(self, titles: TitleList = TitleList()) -> TitleList: @@ -155,14 +166,26 @@ def reinit(self) -> None: self.title = '' self.done = False self.description = '' - self.sub_tasks = [] # type: List[Task] + self.sub_tasks = [] # type: List[Task] + + def get_elements(self, element_name: str): + mappings = { + 'name': str(self.titles), + 'desc': self.description, + 'priority': '' if self.priority is None else self.priority, + 'deadline': '' if self.deadline == 0 else formatted_time(self.deadline) + } + return mappings.get(element_name) class TaskManager(TaskBase): title: str = "TaskManager" - __responsible_manager = ResponsibleManager() + __responsible_manager = None - def get_responsible_manager(self): + @property + def responsible_manager(self): + if self.__responsible_manager is None: + self.__responsible_manager = ResponsibleManager(self) return self.__responsible_manager def save(self): @@ -179,7 +202,7 @@ def load(cls): js = json.load(fp) manager = cls.deserialize(js) GlobalVariables.debug(manager.serialize()) - manager.get_responsible_manager().load() + manager.responsible_manager.load() return manager def exists(self, titles: TitleList) -> bool: @@ -198,7 +221,7 @@ def exists(self, titles: TitleList) -> bool: def full_path(self, titles: 'TitleList' = TitleList()) -> 'TitleList': return titles -# ========================= + # ========================= def add_task(self, titles: 'TitleList', desc: str = '', should_save=True) -> None: super(TaskManager, self).add_task(titles, desc) @@ -214,7 +237,7 @@ def delete_task(self, titles: 'TitleList', should_save=True) -> None: except KeyError: raise TaskNotFound(titles.copy(), father_delete.full_path().copy()) father_delete.sub_tasks.remove(task) - self.__responsible_manager.remove_task(titles) + self.responsible_manager.remove_task(titles) if should_save: self.save() @@ -224,7 +247,7 @@ def rename_task(self, titles: 'TitleList', new_title: str, should_save=True) -> new_titles = titles.copy() new_titles.pop_tail() new_titles.append(new_title) - self.__responsible_manager.rename_task(titles, new_title) + self.responsible_manager.rename_task(titles, new_title) if should_save: self.save() @@ -252,24 +275,24 @@ def set_responsible(self, titles: TitleList, *res): num = 0 for r in res: try: - self.__responsible_manager.add_work(r, titles, should_save=False) + self.responsible_manager.add_work(r, titles, should_save=False) except DuplicatedTask: pass else: num += 1 - self.__responsible_manager.save() + self.responsible_manager.save() return num def rm_responsible(self, titles: TitleList, *res): removed = set() for r in res: try: - self.__responsible_manager.rm_work(r, titles, should_save=False) + self.responsible_manager.rm_work(r, titles, should_save=False) except TaskNotFound: pass else: removed.add(r) - self.__responsible_manager.save() + self.responsible_manager.save() return removed def set_perm(self, titles: TitleList, perm_level: int, should_save=True) -> None: @@ -282,4 +305,3 @@ def set_priority(self, titles: TitleList, priority: int, should_save=True) -> No self[titles].priority = priority if should_save: self.save() - diff --git a/mcd_task/utils.py b/mcd_task/utils.py index dbe2295..bf44546 100644 --- a/mcd_task/utils.py +++ b/mcd_task/utils.py @@ -1,5 +1,10 @@ +import time from typing import Optional, Union, List +from mcdreforged.command.command_source import CommandSource, PlayerCommandSource + +from mcd_task.global_variables import GlobalVariables + class TitleList: def __init__(self, titles: Optional[Union[str, 'TitleList']] = None): @@ -53,3 +58,20 @@ def is_empty(self) -> bool: # No longer support python 2.x and MCDeamon so no __unicode__ method def __str__(self) -> str: return '.'.join(self.titles) + + +def formatted_time(timestamp: float, locale: Optional[str] = None) -> str: + """ + Format time text with specified locale + :param timestamp: + :param locale: + :return: + """ + return time.strftime(GlobalVariables.server.tr("mcd_task.time_format", lang=locale), time.localtime(timestamp)) + + +def source_name(source: CommandSource): + if isinstance(source, PlayerCommandSource): + return source.player + else: + return source.__class__.__name__ \ No newline at end of file diff --git a/mcdreforged.plugin.json b/mcdreforged.plugin.json index f02fd90..820f318 100644 --- a/mcdreforged.plugin.json +++ b/mcdreforged.plugin.json @@ -1,6 +1,6 @@ { "id": "mcd_task", - "version": "2.2.0-alpha.1", + "version": "2.2.0", "name": "Task", "description": "A plugin to show tasks of project in progress", "author": [