zrb 1.0.0a15__py3-none-any.whl → 1.0.0a18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. zrb/__main__.py +3 -0
  2. zrb/builtin/__init__.py +2 -2
  3. zrb/builtin/git.py +16 -8
  4. zrb/builtin/git_subtree.py +7 -2
  5. zrb/builtin/llm/tool/rag.py +2 -2
  6. zrb/builtin/project/add/fastapp/fastapp_input.py +16 -0
  7. zrb/builtin/project/add/fastapp/fastapp_task.py +78 -0
  8. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.flake8 +3 -0
  9. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/create_column_task.py +14 -0
  10. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +128 -0
  11. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +213 -0
  12. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/any_client_method.py +27 -0
  13. zrb/builtin/project/add/{fastapp_template/_zrb/entity/module_template → fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module}/service/my_entity/my_entity_usecase.py +9 -10
  14. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/factory.py +13 -0
  15. zrb/builtin/project/add/{fastapp_template/_zrb/entity/module_template → fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module}/service/my_entity/repository/my_entity_db_repository.py +14 -9
  16. zrb/builtin/project/add/{fastapp_template/_zrb/entity/module_template → fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module}/service/my_entity/repository/my_entity_repository.py +6 -7
  17. zrb/builtin/project/add/{fastapp_template/_zrb/entity/schema.template.py → fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/schema/my_entity.py} +8 -0
  18. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/format_task.py +17 -0
  19. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/_zrb/input.py +1 -4
  20. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_task.py +85 -0
  21. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +154 -0
  22. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/any_client.py +7 -0
  23. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/api_client.py +6 -0
  24. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/direct_client.py +6 -0
  25. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/factory.py +9 -0
  26. zrb/builtin/project/add/{fastapp_template/_zrb/module/module_template → fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module}/migration/env.py +2 -4
  27. zrb/builtin/project/add/{fastapp_template/module/auth → fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module}/migration/script.py.mako +1 -0
  28. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/route.py +33 -0
  29. zrb/builtin/project/add/{fastapp_template/_zrb/main.py → fastapp/fastapp_template/my_app_name/_zrb/task.py} +12 -14
  30. zrb/builtin/project/add/{fastapp_template/_zrb/helper.py → fastapp/fastapp_template/my_app_name/_zrb/util.py} +1 -1
  31. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/_zrb/venv_task.py +1 -1
  32. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/common/app.py +2 -2
  33. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/common/base_db_repository.py +1 -1
  34. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/common/base_usecase.py +19 -6
  35. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/common/db_engine.py +1 -1
  36. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/config.py +1 -0
  37. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/main.py +7 -0
  38. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/migrate.py +3 -0
  39. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/auth/client/any_client.py +10 -4
  40. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/api_client.py +7 -0
  41. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/direct_client.py +6 -0
  42. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/factory.py +9 -0
  43. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/auth/migration/env.py +2 -4
  44. zrb/builtin/project/add/{fastapp_template/module/gateway → fastapp/fastapp_template/my_app_name/module/auth}/migration/script.py.mako +1 -0
  45. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/auth/migration_metadata.py +1 -1
  46. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/route.py +37 -0
  47. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/factory.py +13 -0
  48. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/auth/service/user/repository/user_db_repository.py +13 -7
  49. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository.py +42 -0
  50. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/auth/service/user/user_usecase.py +13 -8
  51. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/gateway/migration/env.py +2 -4
  52. zrb/builtin/project/add/{fastapp_template/_zrb/module/module_template → fastapp/fastapp_template/my_app_name/module/gateway}/migration/script.py.mako +1 -0
  53. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +43 -0
  54. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +0 -0
  55. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/requirements.txt +1 -1
  56. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/__init__.py +0 -0
  57. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/schema/permission.py +8 -0
  58. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/schema/role.py +8 -0
  59. zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/schema/user.py +8 -0
  60. zrb/builtin/project/add/fastapp/fastapp_util.py +46 -0
  61. zrb/builtin/project/create/{create.py → project_task.py} +1 -1
  62. zrb/builtin/python.py +4 -1
  63. zrb/builtin/setup/asdf/asdf_helper.py +4 -8
  64. zrb/builtin/setup/tmux/tmux.py +7 -12
  65. zrb/builtin/todo.py +45 -29
  66. zrb/callback/callback.py +0 -1
  67. zrb/cmd/cmd_val.py +2 -2
  68. zrb/config.py +1 -0
  69. zrb/content_transformer/content_transformer.py +8 -7
  70. zrb/context/any_context.py +6 -6
  71. zrb/group/group.py +0 -1
  72. zrb/input/base_input.py +13 -1
  73. zrb/input/text_input.py +4 -4
  74. zrb/runner/cli.py +0 -1
  75. zrb/runner/web_app.py +5 -2
  76. zrb/runner/web_controller/group_info_ui/controller.py +6 -14
  77. zrb/runner/web_controller/home_page/controller.py +6 -14
  78. zrb/runner/web_controller/task_ui/controller.py +12 -19
  79. zrb/session_state_logger/any_session_state_logger.py +0 -1
  80. zrb/session_state_logger/file_session_state_logger.py +4 -8
  81. zrb/task/base_trigger.py +0 -1
  82. zrb/task/cmd_task.py +1 -1
  83. zrb/task/llm_task.py +3 -6
  84. zrb/task/make_task.py +0 -1
  85. zrb/task/scaffolder.py +18 -4
  86. zrb/task/scheduler.py +0 -1
  87. zrb/util/cmd/command.py +0 -1
  88. zrb/util/codemod/{add_code_to_class.py → append_code_to_class.py} +4 -4
  89. zrb/util/codemod/{add_code_to_function.py → append_code_to_function.py} +5 -3
  90. zrb/util/codemod/{add_code_to_method.py → append_code_to_method.py} +3 -3
  91. zrb/util/codemod/{add_key_to_dict.py → append_key_to_dict.py} +1 -1
  92. zrb/util/codemod/{add_param_to_function_call.py → append_param_to_function_call.py} +1 -1
  93. zrb/util/codemod/{add_code_to_module.py → prepend_code_to_module.py} +2 -2
  94. zrb/util/codemod/{add_parent_to_class.py → prepend_parent_to_class.py} +1 -1
  95. zrb/util/codemod/{add_property_to_class.py → prepend_property_to_class.py} +1 -1
  96. zrb/util/file.py +18 -0
  97. zrb/util/git_subtree.py +3 -4
  98. zrb/util/todo.py +105 -24
  99. zrb/xcom/xcom.py +0 -1
  100. {zrb-1.0.0a15.dist-info → zrb-1.0.0a18.dist-info}/METADATA +2 -2
  101. zrb-1.0.0a18.dist-info/RECORD +240 -0
  102. zrb/builtin/project/add/fastapp.py +0 -87
  103. zrb/builtin/project/add/fastapp_template/_zrb/column/create_column_task.py +0 -11
  104. zrb/builtin/project/add/fastapp_template/_zrb/entity/create_entity_task.py +0 -196
  105. zrb/builtin/project/add/fastapp_template/_zrb/entity/module_template/service/my_entity/repository/factory.py +0 -13
  106. zrb/builtin/project/add/fastapp_template/_zrb/module/create_module_task.py +0 -136
  107. zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/any_client.py +0 -27
  108. zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/api_client.py +0 -6
  109. zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/direct_client.py +0 -6
  110. zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/client/factory.py +0 -9
  111. zrb/builtin/project/add/fastapp_template/_zrb/module/module_template/route.py +0 -19
  112. zrb/builtin/project/add/fastapp_template/main.py +0 -7
  113. zrb/builtin/project/add/fastapp_template/migrate.py +0 -3
  114. zrb/builtin/project/add/fastapp_template/module/auth/client/api_client.py +0 -7
  115. zrb/builtin/project/add/fastapp_template/module/auth/client/direct_client.py +0 -6
  116. zrb/builtin/project/add/fastapp_template/module/auth/client/factory.py +0 -9
  117. zrb/builtin/project/add/fastapp_template/module/auth/migration/versions/3093c7336477_add_user_table.py +0 -37
  118. zrb/builtin/project/add/fastapp_template/module/auth/route.py +0 -22
  119. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/factory.py +0 -13
  120. zrb/builtin/project/add/fastapp_template/module/auth/service/user/repository/user_repository.py +0 -34
  121. zrb/builtin/project/add/fastapp_template/module/gateway/route.py +0 -27
  122. zrb-1.0.0a15.dist-info/RECORD +0 -231
  123. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/.gitignore +0 -0
  124. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/README.md +0 -0
  125. /zrb/builtin/project/add/{__init__.py → fastapp/fastapp_template/my_app_name/__init__.py} +0 -0
  126. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/_zrb/config.py +0 -0
  127. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/_zrb/group.py +0 -0
  128. /zrb/builtin/project/add/{fastapp_template/__init__.py → fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py} +0 -0
  129. /zrb/builtin/project/add/{fastapp_template/_zrb/module/module_template → fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module}/alembic.ini +0 -0
  130. /zrb/builtin/project/add/{fastapp_template/_zrb/module/module_template → fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module}/migration/README +0 -0
  131. /zrb/builtin/project/add/{fastapp_template/module/gateway → fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module}/migration/versions/.gitkeep +0 -0
  132. /zrb/builtin/project/add/{fastapp_template/_zrb/module/module_template → fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module}/migration_metadata.py +0 -0
  133. /zrb/builtin/project/add/{fastapp_template/_zrb/module/module_template → fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module}/service/__init__.py +0 -0
  134. /zrb/builtin/project/add/{fastapp_template/_zrb/module/run_module.template.py → fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py} +0 -0
  135. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/common/__init__.py +0 -0
  136. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/common/error.py +0 -0
  137. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/common/schema.py +0 -0
  138. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/__init__.py +0 -0
  139. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/auth/alembic.ini +0 -0
  140. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/auth/migration/README +0 -0
  141. /zrb/builtin/project/add/{fastapp_template/_zrb/module/module_template → fastapp/fastapp_template/my_app_name/module/auth}/migration/versions/3093c7336477_add_user_table.py +0 -0
  142. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/auth/service/__init__.py +0 -0
  143. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/auth/service/user/__init__.py +0 -0
  144. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/auth/service/user/repository/__init__.py +0 -0
  145. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/gateway/alembic.ini +0 -0
  146. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/gateway/migration/README +0 -0
  147. /zrb/builtin/project/add/{fastapp_template/schema/__init__.py → fastapp/fastapp_template/my_app_name/module/gateway/migration/versions/.gitkeep} +0 -0
  148. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/module/gateway/migration_metadata.py +0 -0
  149. /zrb/builtin/project/add/{fastapp_template → fastapp/fastapp_template/my_app_name}/template.env +0 -0
  150. {zrb-1.0.0a15.dist-info → zrb-1.0.0a18.dist-info}/WHEEL +0 -0
  151. {zrb-1.0.0a15.dist-info → zrb-1.0.0a18.dist-info}/entry_points.txt +0 -0
zrb/builtin/todo.py CHANGED
@@ -4,19 +4,21 @@ import os
4
4
  from typing import Any
5
5
 
6
6
  from zrb.builtin.group import todo_group
7
- from zrb.config import TODO_DIR, TODO_VISUAL_FILTER
7
+ from zrb.config import TODO_DIR, TODO_RETENTION, TODO_VISUAL_FILTER
8
8
  from zrb.context.any_context import AnyContext
9
9
  from zrb.input.str_input import StrInput
10
10
  from zrb.input.text_input import TextInput
11
11
  from zrb.task.make_task import make_task
12
+ from zrb.util.file import read_file, write_file
12
13
  from zrb.util.todo import (
13
14
  TodoTaskModel,
14
- add_durations,
15
+ add_duration,
15
16
  cascade_todo_task,
16
17
  get_visual_todo_card,
17
18
  get_visual_todo_list,
18
19
  line_to_todo_task,
19
20
  load_todo_list,
21
+ parse_duration,
20
22
  save_todo_list,
21
23
  select_todo_task,
22
24
  todo_task_to_line,
@@ -119,8 +121,7 @@ def show_todo(ctx: AnyContext):
119
121
  log_work_path = os.path.join(TODO_DIR, "log-work", f"{task_id}.json")
120
122
  log_work_list = []
121
123
  if os.path.isfile(log_work_path):
122
- with open(log_work_path, "r") as f:
123
- log_work_list = json.loads(f.read())
124
+ log_work_list = json.loads(read_file(log_work_path))
124
125
  return get_visual_todo_card(todo_task, log_work_list)
125
126
 
126
127
 
@@ -165,11 +166,17 @@ def archive_todo(ctx: AnyContext):
165
166
  todo_list: list[TodoTaskModel] = []
166
167
  if os.path.isfile(todo_file_path):
167
168
  todo_list = load_todo_list(todo_file_path)
168
- working_todo_list = [
169
- todo_task for todo_task in todo_list if not todo_task.completed
170
- ]
169
+ retention_duration = datetime.timedelta(seconds=parse_duration(TODO_RETENTION))
170
+ threshold_date = datetime.date.today() - retention_duration
171
171
  new_archived_todo_list = [
172
- todo_task for todo_task in todo_list if todo_task.completed
172
+ todo_task
173
+ for todo_task in todo_list
174
+ if todo_task.completed
175
+ and todo_task.completion_date is not None
176
+ and todo_task.completion_date < threshold_date
177
+ ]
178
+ working_todo_list = [
179
+ todo_task for todo_task in todo_list if todo_task not in new_archived_todo_list
173
180
  ]
174
181
  if len(new_archived_todo_list) == 0:
175
182
  ctx.print("No completed task to archive")
@@ -193,10 +200,9 @@ def archive_todo(ctx: AnyContext):
193
200
  input=[
194
201
  StrInput(name="keyword", prompt="Task keyword", description="Task Keyword"),
195
202
  StrInput(
196
- name="start",
197
- prompt="Working start time (%Y-%m-%d %H:%M:%S)",
198
- description="Working start time",
199
- default_str=lambda _: _get_default_start(),
203
+ name="log",
204
+ prompt="Working log",
205
+ description="Working log",
200
206
  ),
201
207
  StrInput(
202
208
  name="duration",
@@ -205,9 +211,10 @@ def archive_todo(ctx: AnyContext):
205
211
  default_str="30m",
206
212
  ),
207
213
  StrInput(
208
- name="log",
209
- prompt="Working log",
210
- description="Working log",
214
+ name="stop",
215
+ prompt="Working stop time (%Y-%m-%d %H:%M:%S)",
216
+ description="Working stop time",
217
+ default_str=lambda _: _get_default_stop_work_time_str(),
211
218
  ),
212
219
  ],
213
220
  description="🕒 Log work todo",
@@ -226,8 +233,10 @@ def log_todo(ctx: AnyContext):
226
233
  return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
227
234
  # Update todo task
228
235
  todo_task = cascade_todo_task(todo_task)
229
- current_duration = todo_task.keyval.get("duration", "0")
230
- todo_task.keyval["duration"] = add_durations(current_duration, ctx.input.duration)
236
+ current_duration_str = todo_task.keyval.get("duration", "0")
237
+ todo_task.keyval["duration"] = add_duration(
238
+ current_duration_str, ctx.input.duration
239
+ )
231
240
  # Save todo list
232
241
  save_todo_list(todo_file_path, todo_list)
233
242
  # Add log work
@@ -237,24 +246,26 @@ def log_todo(ctx: AnyContext):
237
246
  log_work_dir, f"{todo_task.keyval.get('id')}.json"
238
247
  )
239
248
  if os.path.isfile(log_work_file_path):
240
- with open(log_work_file_path, "r") as f:
241
- log_work_json = f.read()
249
+ log_work_json = read_file(log_work_file_path)
242
250
  else:
243
251
  log_work_json = "[]"
244
252
  log_work: list[dict[str, Any]] = json.loads(log_work_json)
253
+ start_work_time_str = _get_start_work_time_str(ctx.input.stop, ctx.input.duration)
245
254
  log_work.append(
246
- {"log": ctx.input.log, "duration": ctx.input.duration, "start": ctx.input.start}
255
+ {
256
+ "log": ctx.input.log,
257
+ "duration": ctx.input.duration,
258
+ "start": start_work_time_str,
259
+ }
247
260
  )
248
261
  # save todo with log work
249
- with open(log_work_file_path, "w") as f:
250
- f.write(json.dumps(log_work, indent=2))
262
+ write_file(log_work_file_path, json.dumps(log_work, indent=2))
251
263
  # get log work list
252
264
  task_id = todo_task.keyval.get("id", "")
253
265
  log_work_path = os.path.join(TODO_DIR, "log-work", f"{task_id}.json")
254
266
  log_work_list = []
255
267
  if os.path.isfile(log_work_path):
256
- with open(log_work_path, "r") as f:
257
- log_work_list = json.loads(f.read())
268
+ log_work_list = json.loads(read_file(log_work_path))
258
269
  return "\n".join(
259
270
  [
260
271
  get_visual_todo_list(todo_list, TODO_VISUAL_FILTER),
@@ -264,7 +275,14 @@ def log_todo(ctx: AnyContext):
264
275
  )
265
276
 
266
277
 
267
- def _get_default_start() -> str:
278
+ def _get_start_work_time_str(stop_work_time_str: str, work_duration_str: str) -> str:
279
+ work_duration = parse_duration(work_duration_str)
280
+ stop_work_time = datetime.datetime.strptime(stop_work_time_str, "%Y-%m-%d %H:%M:%S")
281
+ start_work_time = stop_work_time - datetime.timedelta(seconds=work_duration)
282
+ return start_work_time.strftime("%Y-%m-%d %H:%M:%S")
283
+
284
+
285
+ def _get_default_stop_work_time_str() -> str:
268
286
  return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
269
287
 
270
288
 
@@ -290,8 +308,7 @@ def edit_todo(ctx: AnyContext):
290
308
  ]
291
309
  new_content = "\n".join(todo_task_to_line(todo_task) for todo_task in todo_list)
292
310
  todo_file_path = os.path.join(TODO_DIR, "todo.txt")
293
- with open(todo_file_path, "w") as f:
294
- f.write(new_content)
311
+ write_file(todo_file_path, new_content)
295
312
  todo_list = load_todo_list(todo_file_path)
296
313
  return get_visual_todo_list(todo_list, TODO_VISUAL_FILTER)
297
314
 
@@ -300,5 +317,4 @@ def _get_todo_txt_content() -> str:
300
317
  todo_file_path = os.path.join(TODO_DIR, "todo.txt")
301
318
  if not os.path.isfile(todo_file_path):
302
319
  return ""
303
- with open(todo_file_path, "r") as f:
304
- return f.read()
320
+ return read_file(todo_file_path)
zrb/callback/callback.py CHANGED
@@ -8,7 +8,6 @@ from zrb.util.attr import get_str_dict_attr
8
8
 
9
9
 
10
10
  class Callback(AnyCallback):
11
-
12
11
  def __init__(
13
12
  self,
14
13
  input_mapping: StrDictAttr,
zrb/cmd/cmd_val.py CHANGED
@@ -3,6 +3,7 @@ from collections.abc import Callable
3
3
 
4
4
  from zrb.attr.type import fstring
5
5
  from zrb.context.context import Context
6
+ from zrb.util.file import read_file
6
7
 
7
8
 
8
9
  class AnyCmdVal(ABC):
@@ -18,8 +19,7 @@ class CmdPath(AnyCmdVal):
18
19
 
19
20
  def to_str(self, ctx: Context) -> str:
20
21
  file_path = ctx.render(self._path) if self._auto_render else self._path
21
- with open(file_path) as file:
22
- return file.read()
22
+ return read_file(file_path)
23
23
 
24
24
 
25
25
  class Cmd(AnyCmdVal):
zrb/config.py CHANGED
@@ -56,6 +56,7 @@ SESSION_LOG_DIR = os.getenv(
56
56
  )
57
57
  TODO_DIR = os.getenv("ZRB_TODO_DIR", os.path.expanduser(os.path.join("~", "todo")))
58
58
  TODO_VISUAL_FILTER = os.getenv("ZRB_TODO_FILTER", "")
59
+ TODO_RETENTION = os.getenv("ZRB_TODO_RETENTION", "2w")
59
60
  VERSION = metadata.version("zrb")
60
61
  WEB_HTTP_PORT = int(os.getenv("ZRB_WEB_HTTP_PORT", "21213"))
61
62
  LLM_MODEL = os.getenv("ZRB_LLM_MODEL", "ollama_chat/llama3.1")
@@ -4,16 +4,16 @@ from collections.abc import Callable
4
4
 
5
5
  from zrb.content_transformer.any_content_transformer import AnyContentTransformer
6
6
  from zrb.context.any_context import AnyContext
7
+ from zrb.util.file import read_file, write_file
7
8
 
8
9
 
9
10
  class ContentTransformer(AnyContentTransformer):
10
-
11
11
  def __init__(
12
12
  self,
13
13
  match: list[str] | str | Callable[[AnyContext, str], bool],
14
14
  transform: (
15
15
  dict[str, str | Callable[[AnyContext], str]]
16
- | Callable[[AnyContext, str], str]
16
+ | Callable[[AnyContext, str], None]
17
17
  ),
18
18
  auto_render: bool = True,
19
19
  ):
@@ -24,7 +24,10 @@ class ContentTransformer(AnyContentTransformer):
24
24
  def match(self, ctx: AnyContext, file_path: str) -> bool:
25
25
  if callable(self._match):
26
26
  return self._match(ctx, file_path)
27
- patterns = [self._match] if isinstance(self._match, str) else self._match
27
+ if isinstance(self._match, str):
28
+ patterns = [self._match]
29
+ else:
30
+ patterns = self._match
28
31
  for pattern in patterns:
29
32
  try:
30
33
  if re.fullmatch(pattern, file_path):
@@ -40,12 +43,10 @@ class ContentTransformer(AnyContentTransformer):
40
43
  keyword: self._get_str_replacement(ctx, replacement)
41
44
  for keyword, replacement in self._transform_file.items()
42
45
  }
43
- with open(file_path, "r") as f:
44
- content = f.read()
46
+ content = read_file(file_path)
45
47
  for keyword, replacement in transform_map.items():
46
48
  content = content.replace(keyword, replacement)
47
- with open(file_path, "w") as f:
48
- f.write(content)
49
+ write_file(file_path, content)
49
50
 
50
51
  def _get_str_replacement(
51
52
  self, ctx: AnyContext, replacement: str | Callable[[AnyContext], str]
@@ -42,7 +42,7 @@ class AnyContext(AnySharedContext):
42
42
  sep: str | None = " ",
43
43
  end: str | None = "\n",
44
44
  file: TextIO | None = sys.stderr,
45
- flush: bool = True
45
+ flush: bool = True,
46
46
  ):
47
47
  """Prints values to the specified output stream.
48
48
 
@@ -62,7 +62,7 @@ class AnyContext(AnySharedContext):
62
62
  sep: str | None = " ",
63
63
  end: str | None = "\n",
64
64
  file: TextIO | None = sys.stderr,
65
- flush: bool = True
65
+ flush: bool = True,
66
66
  ):
67
67
  """Logs debug-level messages.
68
68
 
@@ -82,7 +82,7 @@ class AnyContext(AnySharedContext):
82
82
  sep: str | None = " ",
83
83
  end: str | None = "\n",
84
84
  file: TextIO | None = sys.stderr,
85
- flush: bool = True
85
+ flush: bool = True,
86
86
  ):
87
87
  """Logs info-level messages.
88
88
 
@@ -102,7 +102,7 @@ class AnyContext(AnySharedContext):
102
102
  sep: str | None = " ",
103
103
  end: str | None = "\n",
104
104
  file: TextIO | None = sys.stderr,
105
- flush: bool = True
105
+ flush: bool = True,
106
106
  ):
107
107
  """Logs warning-level messages.
108
108
 
@@ -122,7 +122,7 @@ class AnyContext(AnySharedContext):
122
122
  sep: str | None = " ",
123
123
  end: str | None = "\n",
124
124
  file: TextIO | None = sys.stderr,
125
- flush: bool = True
125
+ flush: bool = True,
126
126
  ):
127
127
  """Logs error-level messages.
128
128
 
@@ -142,7 +142,7 @@ class AnyContext(AnySharedContext):
142
142
  sep: str | None = " ",
143
143
  end: str | None = "\n",
144
144
  file: TextIO | None = sys.stderr,
145
- flush: bool = True
145
+ flush: bool = True,
146
146
  ):
147
147
  """Logs critical-level messages.
148
148
 
zrb/group/group.py CHANGED
@@ -3,7 +3,6 @@ from zrb.task.any_task import AnyTask
3
3
 
4
4
 
5
5
  class Group(AnyGroup):
6
-
7
6
  def __init__(
8
7
  self, name: str, description: str | None = None, banner: str | None = None
9
8
  ):
zrb/input/base_input.py CHANGED
@@ -4,6 +4,7 @@ from zrb.attr.type import StrAttr
4
4
  from zrb.context.any_shared_context import AnySharedContext
5
5
  from zrb.input.any_input import AnyInput
6
6
  from zrb.util.attr import get_str_attr
7
+ from zrb.util.string.conversion import to_snake_case
7
8
 
8
9
 
9
10
  class BaseInput(AnyInput):
@@ -49,7 +50,18 @@ class BaseInput(AnyInput):
49
50
  ):
50
51
  if str_value is None:
51
52
  str_value = self._get_default_str(shared_ctx)
52
- shared_ctx.input[self.name] = self._parse_str_value(str_value)
53
+ value = self._parse_str_value(str_value)
54
+ if self.name in shared_ctx.input:
55
+ raise ValueError(f"Input already defined in the context: {self.name}")
56
+ shared_ctx.input[self.name] = value
57
+ # We want to be able to access ctx.input["project-name"] as
58
+ # ctx.input.project_name
59
+ snake_key = to_snake_case(self.name)
60
+ if snake_key == self.name:
61
+ return
62
+ if snake_key in shared_ctx.input:
63
+ raise ValueError("Input already defined in the context: {snake_key}")
64
+ shared_ctx.input[snake_key] = value
53
65
 
54
66
  def _parse_str_value(self, str_value: str) -> Any:
55
67
  """Override this to transform str_value"""
zrb/input/text_input.py CHANGED
@@ -6,6 +6,7 @@ from collections.abc import Callable
6
6
  from zrb.config import DEFAULT_EDITOR
7
7
  from zrb.context.any_shared_context import AnySharedContext
8
8
  from zrb.input.base_input import BaseInput
9
+ from zrb.util.file import read_file
9
10
 
10
11
 
11
12
  class TextInput(BaseInput):
@@ -83,9 +84,8 @@ class TextInput(BaseInput):
83
84
  # Open the editor
84
85
  subprocess.call([self._editor, temp_file_name])
85
86
  # Read the edited content
86
- with open(temp_file_name, "r") as temp_file:
87
- edited_content = temp_file.read()
88
- parts = [text.strip() for text in edited_content.split(prompt_message, 1)]
89
- edited_content = "\n".join(parts).lstrip()
87
+ edited_content = read_file(temp_file_name)
88
+ parts = [text.strip() for text in edited_content.split(prompt_message, 1)]
89
+ edited_content = "\n".join(parts).lstrip()
90
90
  os.remove(temp_file_name)
91
91
  return edited_content
zrb/runner/cli.py CHANGED
@@ -20,7 +20,6 @@ from zrb.util.string.conversion import double_quote
20
20
 
21
21
 
22
22
  class Cli(Group):
23
-
24
23
  def run(self, args: list[str] = []):
25
24
  load_zrb_init()
26
25
  kwargs, args = self._extract_kwargs_from_args(args)
zrb/runner/web_app.py CHANGED
@@ -75,7 +75,7 @@ def create_app(root_group: AnyGroup, port: int = WEB_HTTP_PORT):
75
75
  return handle_group_info_ui(root_group, node, url)
76
76
  raise HTTPException(status_code=404, detail="Not Found")
77
77
 
78
- @app.post("/api/{path:path}")
78
+ @app.post("/api/sessions/{path:path}")
79
79
  async def create_new_session(
80
80
  path: str, request: Request = None
81
81
  ) -> NewSessionResponse:
@@ -96,7 +96,10 @@ def create_app(root_group: AnyGroup, port: int = WEB_HTTP_PORT):
96
96
  return NewSessionResponse(session_name=session.name)
97
97
  raise HTTPException(status_code=404, detail="Not Found")
98
98
 
99
- @app.get("/api/{path:path}", response_model=SessionStateLog | SessionStateLogList)
99
+ @app.get(
100
+ "/api/sessions/{path:path}",
101
+ response_model=SessionStateLog | SessionStateLogList,
102
+ )
100
103
  async def get_session(
101
104
  path: str,
102
105
  min_start_query: str = Query(default=None, alias="from"),
@@ -1,25 +1,17 @@
1
1
  import os
2
2
 
3
3
  from zrb.group.any_group import AnyGroup
4
+ from zrb.util.file import read_file
4
5
  from zrb.util.group import get_non_empty_subgroups, get_subtasks
5
6
  from zrb.util.string.format import fstring_format
6
7
 
7
8
  _DIR = os.path.dirname(__file__)
8
9
 
9
- with open(os.path.join(_DIR, "view.html"), "r") as f:
10
- _VIEW_TEMPLATE = f.read()
11
-
12
- with open(os.path.join(_DIR, "partial", "group_info.html")) as f:
13
- _GROUP_INFO_TEMPLATE = f.read()
14
-
15
- with open(os.path.join(_DIR, "partial", "group_li.html")) as f:
16
- _GROUP_LI_TEMPLATE = f.read()
17
-
18
- with open(os.path.join(_DIR, "partial", "task_info.html")) as f:
19
- _TASK_INFO_TEMPLATE = f.read()
20
-
21
- with open(os.path.join(_DIR, "partial", "task_li.html")) as f:
22
- _TASK_LI_TEMPLATE = f.read()
10
+ _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
11
+ _GROUP_INFO_TEMPLATE = read_file(os.path.join(_DIR, "partial", "group_info.html"))
12
+ _GROUP_LI_TEMPLATE = read_file(os.path.join(_DIR, "partial", "group_li.html"))
13
+ _TASK_INFO_TEMPLATE = read_file(os.path.join(_DIR, "partial", "task_info.html"))
14
+ _TASK_LI_TEMPLATE = read_file(os.path.join(_DIR, "partial", "task_li.html"))
23
15
 
24
16
 
25
17
  def handle_group_info_ui(root_group: AnyGroup, group: AnyGroup, url: str):
@@ -1,25 +1,17 @@
1
1
  import os
2
2
 
3
3
  from zrb.group.any_group import AnyGroup
4
+ from zrb.util.file import read_file
4
5
  from zrb.util.group import get_non_empty_subgroups, get_subtasks
5
6
  from zrb.util.string.format import fstring_format
6
7
 
7
8
  _DIR = os.path.dirname(__file__)
8
9
 
9
- with open(os.path.join(_DIR, "view.html"), "r") as f:
10
- _VIEW_TEMPLATE = f.read()
11
-
12
- with open(os.path.join(_DIR, "partial", "group_info.html")) as f:
13
- _GROUP_INFO_TEMPLATE = f.read()
14
-
15
- with open(os.path.join(_DIR, "partial", "group_li.html")) as f:
16
- _GROUP_LI_TEMPLATE = f.read()
17
-
18
- with open(os.path.join(_DIR, "partial", "task_info.html")) as f:
19
- _TASK_INFO_TEMPLATE = f.read()
20
-
21
- with open(os.path.join(_DIR, "partial", "task_li.html")) as f:
22
- _TASK_LI_TEMPLATE = f.read()
10
+ _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
11
+ _GROUP_INFO_TEMPLATE = read_file(os.path.join(_DIR, "partial", "group_info.html"))
12
+ _GROUP_LI_TEMPLATE = read_file(os.path.join(_DIR, "partial", "group_li.html"))
13
+ _TASK_INFO_TEMPLATE = read_file(os.path.join(_DIR, "partial", "task_info.html"))
14
+ _TASK_LI_TEMPLATE = read_file(os.path.join(_DIR, "partial", "task_li.html"))
23
15
 
24
16
 
25
17
  def handle_home_page(root_group: AnyGroup):
@@ -1,30 +1,23 @@
1
1
  import os
2
2
 
3
- from zrb.context.shared_context import SharedContext
4
3
  from zrb.group.any_group import AnyGroup
5
4
  from zrb.session.any_session import AnySession
6
5
  from zrb.task.any_task import AnyTask
6
+ from zrb.util.file import read_file
7
7
  from zrb.util.string.format import fstring_format
8
8
 
9
9
  _DIR = os.path.dirname(__file__)
10
10
 
11
- with open(os.path.join(_DIR, "view.html"), "r") as f:
12
- _VIEW_TEMPLATE = f.read()
13
-
14
- with open(os.path.join(_DIR, "partial", "input.html")) as f:
15
- _TASK_INPUT_TEMPLATE = f.read()
16
-
17
- with open(os.path.join(_DIR, "partial", "main.js")) as f:
18
- _MAIN_SCRIPT = f.read()
19
-
20
- with open(os.path.join(_DIR, "partial", "show-existing-session.js")) as f:
21
- _SHOW_EXISTING_SESSION_SCRIPT = f.read()
22
-
23
- with open(os.path.join(_DIR, "partial", "visualize-history.js")) as f:
24
- _VISUALIZE_HISTORY_SCRIPT = f.read()
25
-
26
- with open(os.path.join(_DIR, "partial", "common-util.js")) as f:
27
- _COMMON_UTIL_SCRIPT = f.read()
11
+ _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
12
+ _TASK_INPUT_TEMPLATE = read_file(os.path.join(_DIR, "partial", "input.html"))
13
+ _MAIN_SCRIPT = read_file(os.path.join(_DIR, "partial", "main.js"))
14
+ _SHOW_EXISTING_SESSION_SCRIPT = read_file(
15
+ os.path.join(_DIR, "partial", "show-existing-session.js")
16
+ )
17
+ _VISUALIZE_HISTORY_SCRIPT = read_file(
18
+ os.path.join(_DIR, "partial", "visualize-history.js")
19
+ )
20
+ _COMMON_UTIL_SCRIPT = read_file(os.path.join(_DIR, "partial", "common-util.js"))
28
21
 
29
22
 
30
23
  def handle_task_ui(
@@ -40,7 +33,7 @@ def handle_task_ui(
40
33
  parent_url = "/".join(parent_url_parts)
41
34
  # Assemble api url
42
35
  api_url_parts = list(url_parts)
43
- api_url_parts[1] = "api"
36
+ api_url_parts[1] = "api/sessions"
44
37
  api_url = "/".join(api_url_parts)
45
38
  # Assemble ui url
46
39
  ui_url_parts = list(api_url_parts)
@@ -5,7 +5,6 @@ from zrb.session_state_log.session_state_log import SessionStateLog, SessionStat
5
5
 
6
6
 
7
7
  class AnySessionStateLogger(ABC):
8
-
9
8
  @abstractmethod
10
9
  def write(self, session_log: SessionStateLog):
11
10
  pass
@@ -3,10 +3,10 @@ import os
3
3
 
4
4
  from zrb.session_state_log.session_state_log import SessionStateLog, SessionStateLogList
5
5
  from zrb.session_state_logger.any_session_state_logger import AnySessionStateLogger
6
+ from zrb.util.file import read_file, write_file
6
7
 
7
8
 
8
9
  class FileSessionStateLogger(AnySessionStateLogger):
9
-
10
10
  def __init__(self, session_log_dir: str):
11
11
  self._session_log_dir = session_log_dir
12
12
 
@@ -15,20 +15,16 @@ class FileSessionStateLogger(AnySessionStateLogger):
15
15
  session_dir_path = os.path.dirname(session_file_path)
16
16
  if not os.path.isdir(session_dir_path):
17
17
  os.makedirs(session_dir_path, exist_ok=True)
18
- with open(session_file_path, "w") as f:
19
- f.write(session_log.model_dump_json())
18
+ write_file(session_file_path, session_log.model_dump_json())
20
19
  start_time = session_log.start_time
21
20
  if start_time == "":
22
21
  return
23
22
  timeline_dir_path = self._get_timeline_dir_path(session_log)
24
- os.makedirs(timeline_dir_path, exist_ok=True)
25
- with open(os.path.join(timeline_dir_path, session_log.name), "w"):
26
- pass
23
+ write_file(os.path.join(timeline_dir_path, session_log.name), "")
27
24
 
28
25
  def read(self, session_name: str) -> SessionStateLog:
29
26
  session_file_path = self._get_session_file_path(session_name)
30
- with open(session_file_path, "r") as f:
31
- return SessionStateLog.model_validate_json(f.read())
27
+ return SessionStateLog.model_validate_json(read_file(session_file_path))
32
28
 
33
29
  def list(
34
30
  self,
zrb/task/base_trigger.py CHANGED
@@ -19,7 +19,6 @@ from zrb.xcom.xcom import Xcom
19
19
 
20
20
 
21
21
  class BaseTrigger(BaseTask):
22
-
23
22
  def __init__(
24
23
  self,
25
24
  name: str,
zrb/task/cmd_task.py CHANGED
@@ -132,7 +132,7 @@ class CmdTask(BaseTask):
132
132
  if return_code != 0:
133
133
  ctx.log_error(f"Exit status: {return_code}")
134
134
  raise Exception(
135
- f"Process {self._name} exited ({return_code}): {cmd_result.stderr}"
135
+ f"Process {self._name} exited ({return_code}): {cmd_result.error}"
136
136
  )
137
137
  ctx.log_info(f"Exit status: {return_code}")
138
138
  return cmd_result
zrb/task/llm_task.py CHANGED
@@ -15,6 +15,7 @@ from zrb.task.any_task import AnyTask
15
15
  from zrb.task.base_task import BaseTask
16
16
  from zrb.util.attr import get_str_attr
17
17
  from zrb.util.cli.style import stylize_faint
18
+ from zrb.util.file import read_file, write_file
18
19
  from zrb.util.llm.tool import callable_to_tool_schema
19
20
 
20
21
  ListOfDict = list[dict[str, Any]]
@@ -32,7 +33,6 @@ def scratchpad(thought: str) -> str:
32
33
 
33
34
 
34
35
  class LLMTask(BaseTask):
35
-
36
36
  def __init__(
37
37
  self,
38
38
  name: str,
@@ -171,9 +171,7 @@ class LLMTask(BaseTask):
171
171
  messages.append(tool_call_message)
172
172
  continue
173
173
  if history_file != "":
174
- os.makedirs(os.path.dirname(history_file), exist_ok=True)
175
- with open(history_file, "w") as f:
176
- f.write(json.dumps(messages, indent=2))
174
+ write_file(history_file, json.dumps(messages, indent=2))
177
175
  return response_message.content
178
176
 
179
177
  def _get_model(self, ctx: AnyContext) -> str:
@@ -211,8 +209,7 @@ class LLMTask(BaseTask):
211
209
  and history_file != ""
212
210
  and os.path.isfile(history_file)
213
211
  ):
214
- with open(history_file, "r") as f:
215
- return json.loads(f.read())
212
+ return json.loads(read_file(history_file))
216
213
  return self._history
217
214
 
218
215
  def _get_history_file(self, ctx: AnyContext) -> str:
zrb/task/make_task.py CHANGED
@@ -32,7 +32,6 @@ def make_task(
32
32
  group: AnyGroup | None = None,
33
33
  alias: str | None = None,
34
34
  ) -> Callable[[Callable[[AnyContext], Any]], AnyTask]:
35
-
36
35
  def _make_task(fn: Callable[[AnyContext], Any]) -> BaseTask:
37
36
  task = BaseTask(
38
37
  name=name,