zrb 1.0.0b9__py3-none-any.whl → 1.1.0__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.
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.coveragerc +11 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.gitignore +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_task.py +99 -55
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_util.py +301 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +131 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +128 -5
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/gateway/view/content/my-module/my-entity.html +297 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_create_my_entity.py +53 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_delete_my_entity.py +62 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_read_my_entity.py +65 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_update_my_entity.py +61 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +81 -13
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/navigation_config_file.py +8 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_task.py +8 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +42 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +8 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +10 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/navigation_config_file.py +6 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +56 -12
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task_util.py +10 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +136 -52
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +3 -3
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/view.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +19 -8
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +46 -43
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/8ed025bcc845_create_permissions.py +69 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +5 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +16 -21
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/config/navigation.py +39 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +52 -11
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/schema/navigation.py +95 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +277 -44
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/auth.py +66 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/view.py +33 -8
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/permission.html +311 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/role.html +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/auth/user.html +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/error.html +4 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/login.html +67 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/logout.html +49 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/common/util.js +160 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/style.css +14 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/crud/util.js +94 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/pico-style.css +23 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/script.js +44 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/style.css +102 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/template/default.html +73 -18
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +6 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +1 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +9 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/_util/access_token.py +19 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_create_permission.py +59 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_delete_permission.py +68 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_read_permission.py +71 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_update_permission.py +66 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/test_user_session.py +195 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_health_and_readiness.py +28 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py +15 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_not_found_error.py +16 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test.sh +7 -0
- zrb/runner/web_route/refresh_token_api_route.py +1 -1
- zrb/runner/web_route/static/refresh-token.template.js +9 -0
- zrb/runner/web_route/static/static_route.py +1 -1
- zrb/task/base_task.py +10 -10
- zrb/util/codemod/modification_mode.py +3 -0
- zrb/util/codemod/modify_class.py +58 -0
- zrb/util/codemod/modify_class_parent.py +68 -0
- zrb/util/codemod/modify_class_property.py +128 -0
- zrb/util/codemod/modify_dict.py +75 -0
- zrb/util/codemod/modify_function.py +65 -0
- zrb/util/codemod/modify_function_call.py +68 -0
- zrb/util/codemod/modify_method.py +88 -0
- zrb/util/codemod/{prepend_code_to_module.py → modify_module.py} +2 -3
- zrb/util/file.py +3 -2
- zrb/util/load.py +13 -7
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/METADATA +2 -2
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/RECORD +80 -46
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/migrate.py +0 -3
- zrb/util/codemod/append_code_to_class.py +0 -35
- zrb/util/codemod/append_code_to_function.py +0 -38
- zrb/util/codemod/append_code_to_method.py +0 -55
- zrb/util/codemod/append_key_to_dict.py +0 -51
- zrb/util/codemod/append_param_to_function_call.py +0 -39
- zrb/util/codemod/prepend_parent_to_class.py +0 -38
- zrb/util/codemod/prepend_property_to_class.py +0 -55
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/WHEEL +0 -0
- {zrb-1.0.0b9.dist-info → zrb-1.1.0.dist-info}/entry_points.txt +0 -0
@@ -1,93 +1,137 @@
|
|
1
1
|
import os
|
2
2
|
|
3
|
+
from my_app_name._zrb.column.add_column_util import (
|
4
|
+
update_fastapp_schema,
|
5
|
+
update_fastapp_test_create,
|
6
|
+
update_fastapp_test_delete,
|
7
|
+
update_fastapp_test_read,
|
8
|
+
update_fastapp_test_update,
|
9
|
+
update_fastapp_ui,
|
10
|
+
)
|
3
11
|
from my_app_name._zrb.config import APP_DIR
|
4
12
|
from my_app_name._zrb.format_task import format_my_app_name_code
|
5
13
|
from my_app_name._zrb.group import app_create_group
|
6
14
|
from my_app_name._zrb.input import (
|
7
15
|
existing_entity_input,
|
16
|
+
existing_module_input,
|
8
17
|
new_column_input,
|
9
18
|
new_column_type_input,
|
10
19
|
)
|
11
|
-
from my_app_name._zrb.util import get_existing_schema_names
|
20
|
+
from my_app_name._zrb.util import get_existing_module_names, get_existing_schema_names
|
12
21
|
|
13
22
|
from zrb import AnyContext, Task, make_task
|
14
|
-
from zrb.util.codemod.prepend_property_to_class import prepend_property_to_class
|
15
|
-
from zrb.util.file import read_file, write_file
|
16
|
-
from zrb.util.string.conversion import to_pascal_case, to_snake_case
|
17
23
|
|
18
24
|
|
19
25
|
@make_task(
|
20
|
-
name="validate-add-
|
21
|
-
input=
|
26
|
+
name="validate-add-fastapp-column",
|
27
|
+
input=[
|
28
|
+
existing_module_input,
|
29
|
+
existing_entity_input,
|
30
|
+
],
|
22
31
|
retries=0,
|
23
32
|
)
|
24
|
-
async def
|
33
|
+
async def validate_add_fastapp_column(ctx: AnyContext):
|
34
|
+
module_name = ctx.input.module
|
35
|
+
if module_name not in get_existing_module_names():
|
36
|
+
raise ValueError(f"Module not exist: {module_name}")
|
25
37
|
schema_name = ctx.input.entity
|
26
38
|
if schema_name not in get_existing_schema_names():
|
27
39
|
raise ValueError(f"Schema not exist: {schema_name}")
|
28
40
|
|
29
41
|
|
30
|
-
|
31
|
-
name="update-
|
42
|
+
update_fastapp_schema_task = Task(
|
43
|
+
name="update-fastapp-schema",
|
44
|
+
input=[
|
45
|
+
existing_module_input,
|
46
|
+
existing_entity_input,
|
47
|
+
new_column_input,
|
48
|
+
new_column_type_input,
|
49
|
+
],
|
50
|
+
action=update_fastapp_schema,
|
51
|
+
retries=0,
|
52
|
+
upstream=validate_add_fastapp_column,
|
53
|
+
)
|
54
|
+
|
55
|
+
update_fastapp_ui_task = Task(
|
56
|
+
name="update-fastapp-ui",
|
32
57
|
input=[
|
58
|
+
existing_module_input,
|
33
59
|
existing_entity_input,
|
34
60
|
new_column_input,
|
35
61
|
new_column_type_input,
|
36
62
|
],
|
63
|
+
action=update_fastapp_ui,
|
37
64
|
retries=0,
|
38
|
-
upstream=
|
65
|
+
upstream=validate_add_fastapp_column,
|
39
66
|
)
|
40
|
-
def update_my_app_name_schema(ctx: AnyContext):
|
41
|
-
snake_entity_name = to_snake_case(ctx.input.entity)
|
42
|
-
pascal_entity_name = to_pascal_case(ctx.input.entity)
|
43
|
-
schema_file_path = os.path.join(APP_DIR, "schema", f"{snake_entity_name}.py")
|
44
|
-
existing_code = read_file(schema_file_path)
|
45
|
-
snake_column_name = to_snake_case(ctx.input.column)
|
46
|
-
column_type = ctx.input.type
|
47
|
-
# Base
|
48
|
-
new_code = prepend_property_to_class(
|
49
|
-
original_code=existing_code,
|
50
|
-
class_name=f"{pascal_entity_name}Base",
|
51
|
-
property_name=snake_column_name,
|
52
|
-
annotation=column_type,
|
53
|
-
default_value=_get_default_value(column_type),
|
54
|
-
)
|
55
|
-
# Update
|
56
|
-
new_code = prepend_property_to_class(
|
57
|
-
original_code=new_code,
|
58
|
-
class_name=f"{pascal_entity_name}Update",
|
59
|
-
property_name=snake_column_name,
|
60
|
-
annotation=f"{column_type} | None",
|
61
|
-
default_value="None",
|
62
|
-
)
|
63
|
-
# Table
|
64
|
-
new_code = prepend_property_to_class(
|
65
|
-
original_code=new_code,
|
66
|
-
class_name=f"{pascal_entity_name}",
|
67
|
-
property_name=snake_column_name,
|
68
|
-
annotation=f"{column_type} | None",
|
69
|
-
default_value="Field(index=False)",
|
70
|
-
)
|
71
|
-
write_file(schema_file_path, new_code)
|
72
67
|
|
68
|
+
update_fastapp_test_create_task = Task(
|
69
|
+
name="update-fastapp-test-create",
|
70
|
+
input=[
|
71
|
+
existing_module_input,
|
72
|
+
existing_entity_input,
|
73
|
+
new_column_input,
|
74
|
+
new_column_type_input,
|
75
|
+
],
|
76
|
+
action=update_fastapp_test_create,
|
77
|
+
retries=0,
|
78
|
+
upstream=validate_add_fastapp_column,
|
79
|
+
)
|
73
80
|
|
74
|
-
|
81
|
+
update_fastapp_test_read_task = Task(
|
82
|
+
name="update-fastapp-test-read",
|
83
|
+
input=[
|
84
|
+
existing_module_input,
|
85
|
+
existing_entity_input,
|
86
|
+
new_column_input,
|
87
|
+
new_column_type_input,
|
88
|
+
],
|
89
|
+
action=update_fastapp_test_read,
|
90
|
+
retries=0,
|
91
|
+
upstream=validate_add_fastapp_column,
|
92
|
+
)
|
93
|
+
|
94
|
+
update_fastapp_test_update_task = Task(
|
95
|
+
name="update-fastapp-test-update",
|
96
|
+
input=[
|
97
|
+
existing_module_input,
|
98
|
+
existing_entity_input,
|
99
|
+
new_column_input,
|
100
|
+
new_column_type_input,
|
101
|
+
],
|
102
|
+
action=update_fastapp_test_update,
|
103
|
+
retries=0,
|
104
|
+
upstream=validate_add_fastapp_column,
|
105
|
+
)
|
106
|
+
|
107
|
+
update_fastapp_test_delete_task = Task(
|
108
|
+
name="update-fastapp-test-delete",
|
109
|
+
input=[
|
110
|
+
existing_module_input,
|
111
|
+
existing_entity_input,
|
112
|
+
new_column_input,
|
113
|
+
new_column_type_input,
|
114
|
+
],
|
115
|
+
action=update_fastapp_test_delete,
|
116
|
+
retries=0,
|
117
|
+
upstream=validate_add_fastapp_column,
|
118
|
+
)
|
119
|
+
|
120
|
+
|
121
|
+
add_fastapp_column = app_create_group.add_task(
|
75
122
|
Task(
|
76
|
-
name="add-
|
123
|
+
name="add-fastapp-column",
|
77
124
|
description="📊 Create new column on an entity",
|
78
|
-
upstream=
|
125
|
+
upstream=[
|
126
|
+
update_fastapp_schema_task,
|
127
|
+
update_fastapp_ui_task,
|
128
|
+
update_fastapp_test_create_task,
|
129
|
+
update_fastapp_test_read_task,
|
130
|
+
update_fastapp_test_update_task,
|
131
|
+
update_fastapp_test_delete_task,
|
132
|
+
],
|
79
133
|
successor=format_my_app_name_code,
|
80
134
|
retries=0,
|
81
135
|
),
|
82
136
|
alias="column",
|
83
137
|
)
|
84
|
-
|
85
|
-
|
86
|
-
def _get_default_value(data_type: str) -> str:
|
87
|
-
if data_type == "str":
|
88
|
-
return '""'
|
89
|
-
if data_type in ("int", "float"):
|
90
|
-
return "0"
|
91
|
-
if data_type == "bool":
|
92
|
-
return "True"
|
93
|
-
return "None"
|
@@ -0,0 +1,301 @@
|
|
1
|
+
import os
|
2
|
+
import re
|
3
|
+
import textwrap
|
4
|
+
|
5
|
+
from bs4 import BeautifulSoup, formatter
|
6
|
+
from my_app_name._zrb.config import APP_DIR
|
7
|
+
|
8
|
+
from zrb.context.any_context import AnyContext
|
9
|
+
from zrb.util.codemod.modify_class import append_code_to_class
|
10
|
+
from zrb.util.codemod.modify_class_parent import prepend_parent_class
|
11
|
+
from zrb.util.codemod.modify_class_property import append_property_to_class
|
12
|
+
from zrb.util.codemod.modify_function import append_code_to_function
|
13
|
+
from zrb.util.codemod.modify_module import prepend_code_to_module
|
14
|
+
from zrb.util.file import read_file, write_file
|
15
|
+
from zrb.util.string.conversion import (
|
16
|
+
to_human_case,
|
17
|
+
to_kebab_case,
|
18
|
+
to_pascal_case,
|
19
|
+
to_snake_case,
|
20
|
+
)
|
21
|
+
|
22
|
+
|
23
|
+
def update_fastapp_schema(ctx: AnyContext):
|
24
|
+
snake_entity_name = to_snake_case(ctx.input.entity)
|
25
|
+
pascal_entity_name = to_pascal_case(ctx.input.entity)
|
26
|
+
snake_column_name = to_snake_case(ctx.input.column)
|
27
|
+
column_type = ctx.input.type
|
28
|
+
schema_file_path = os.path.join(APP_DIR, "schema", f"{snake_entity_name}.py")
|
29
|
+
existing_code = read_file(schema_file_path)
|
30
|
+
# Base
|
31
|
+
new_code = append_property_to_class(
|
32
|
+
original_code=existing_code,
|
33
|
+
class_name=f"{pascal_entity_name}Base",
|
34
|
+
property_name=snake_column_name,
|
35
|
+
annotation=column_type,
|
36
|
+
default_value=_get_default_column_value(column_type),
|
37
|
+
)
|
38
|
+
# Update
|
39
|
+
new_code = append_property_to_class(
|
40
|
+
original_code=new_code,
|
41
|
+
class_name=f"{pascal_entity_name}Update",
|
42
|
+
property_name=snake_column_name,
|
43
|
+
annotation=f"{column_type} | None",
|
44
|
+
default_value="None",
|
45
|
+
)
|
46
|
+
# Table
|
47
|
+
new_code = append_property_to_class(
|
48
|
+
original_code=new_code,
|
49
|
+
class_name=f"{pascal_entity_name}",
|
50
|
+
property_name=snake_column_name,
|
51
|
+
annotation=f"{column_type} | None",
|
52
|
+
default_value="Field(index=False)",
|
53
|
+
)
|
54
|
+
write_file(schema_file_path, new_code)
|
55
|
+
|
56
|
+
|
57
|
+
def _get_default_column_value(data_type: str) -> str:
|
58
|
+
if data_type == "str":
|
59
|
+
return '""'
|
60
|
+
if data_type in ("int", "float"):
|
61
|
+
return "0"
|
62
|
+
if data_type == "bool":
|
63
|
+
return "True"
|
64
|
+
return "None"
|
65
|
+
|
66
|
+
|
67
|
+
def update_fastapp_ui(ctx: AnyContext):
|
68
|
+
kebab_module_name = to_kebab_case(ctx.input.module)
|
69
|
+
kebab_entity_name = to_kebab_case(ctx.input.entity)
|
70
|
+
snake_column_name = to_snake_case(ctx.input.column)
|
71
|
+
human_column_name = to_human_case(ctx.input.column).title()
|
72
|
+
subroute_file_path = os.path.join(
|
73
|
+
APP_DIR,
|
74
|
+
"module",
|
75
|
+
"gateway",
|
76
|
+
"view",
|
77
|
+
"content",
|
78
|
+
kebab_module_name,
|
79
|
+
f"{kebab_entity_name}.html",
|
80
|
+
)
|
81
|
+
existing_code = read_file(subroute_file_path)
|
82
|
+
# Add table header
|
83
|
+
new_code = _add_th_before_last(
|
84
|
+
existing_code, table_id="crud-table", th_content=human_column_name
|
85
|
+
)
|
86
|
+
# Forms
|
87
|
+
new_code = _add_input_to_form(
|
88
|
+
new_code,
|
89
|
+
form_id="crud-create-form",
|
90
|
+
column_label=human_column_name,
|
91
|
+
column_name=snake_column_name,
|
92
|
+
)
|
93
|
+
new_code = _add_input_to_form(
|
94
|
+
new_code,
|
95
|
+
form_id="crud-update-form",
|
96
|
+
column_label=human_column_name,
|
97
|
+
column_name=snake_column_name,
|
98
|
+
)
|
99
|
+
new_code = _add_input_to_form(
|
100
|
+
new_code,
|
101
|
+
form_id="crud-delete-form",
|
102
|
+
column_label=human_column_name,
|
103
|
+
column_name=snake_column_name,
|
104
|
+
)
|
105
|
+
# JS Function
|
106
|
+
new_code = _alter_js_function_returned_array(
|
107
|
+
new_code,
|
108
|
+
js_function_name="getRowComponents",
|
109
|
+
js_array_name="rowComponents",
|
110
|
+
js_new_value=f"`<td>${{row.{snake_column_name}}}</td>`",
|
111
|
+
)
|
112
|
+
write_file(subroute_file_path, new_code)
|
113
|
+
|
114
|
+
|
115
|
+
def _add_th_before_last(html_str, table_id, th_content):
|
116
|
+
# Use the html.parser; you might try html5lib if you find that it preserves formatting better.
|
117
|
+
soup = BeautifulSoup(html_str, "html.parser")
|
118
|
+
# Locate the table with the specified id
|
119
|
+
table = soup.find("table", id=table_id)
|
120
|
+
if not table:
|
121
|
+
# Table not found; return original HTML unchanged.
|
122
|
+
return html_str
|
123
|
+
# Find the thead element in the table.
|
124
|
+
thead = table.find("thead")
|
125
|
+
if not thead:
|
126
|
+
return html_str
|
127
|
+
# For this example, we assume there's a single row (<tr>) in the thead.
|
128
|
+
row = thead.find("tr")
|
129
|
+
if not row:
|
130
|
+
return html_str
|
131
|
+
# Find all existing th elements in the row.
|
132
|
+
th_elements = row.find_all("th")
|
133
|
+
if not th_elements:
|
134
|
+
return html_str
|
135
|
+
# Create a new th element and set its content.
|
136
|
+
new_th = soup.new_tag("th")
|
137
|
+
new_th.string = th_content
|
138
|
+
# Insert the new th right before the last existing th.
|
139
|
+
th_elements[-1].insert_before(new_th)
|
140
|
+
# Return the modified HTML as a string.
|
141
|
+
return soup.prettify(
|
142
|
+
formatter=formatter.HTMLFormatter(indent=_infer_html_indent_width(html_str))
|
143
|
+
)
|
144
|
+
|
145
|
+
|
146
|
+
def _add_input_to_form(
|
147
|
+
html_str: str, form_id: str, column_label: str, column_name: str
|
148
|
+
) -> str:
|
149
|
+
soup = BeautifulSoup(html_str, "html.parser")
|
150
|
+
# Find the form by id.
|
151
|
+
form = soup.find("form", id=form_id)
|
152
|
+
if not form:
|
153
|
+
return html_str # Return unchanged if no matching form is found.
|
154
|
+
# Create a new label element with the provided column label.
|
155
|
+
new_label = soup.new_tag("label")
|
156
|
+
new_label.append(f"{column_label}: ")
|
157
|
+
# Create a new input element with the provided column name.
|
158
|
+
new_input = soup.new_tag(
|
159
|
+
"input", attrs={"type": "text", "name": column_name, "required": "required"}
|
160
|
+
)
|
161
|
+
new_label.append(new_input)
|
162
|
+
# Look for a footer element inside the form.
|
163
|
+
footer = form.find("footer")
|
164
|
+
if footer:
|
165
|
+
# Insert the new label before the footer.
|
166
|
+
footer.insert_before(new_label)
|
167
|
+
else:
|
168
|
+
# If no footer exists, simply append the new label to the form.
|
169
|
+
form.append(new_label)
|
170
|
+
return soup.prettify(
|
171
|
+
formatter=formatter.HTMLFormatter(indent=_infer_html_indent_width(html_str))
|
172
|
+
)
|
173
|
+
|
174
|
+
|
175
|
+
def _infer_html_indent_width(html_str: str) -> int:
|
176
|
+
"""
|
177
|
+
Infer the indentation width (number of spaces) from the HTML string.
|
178
|
+
It looks for the first non-empty line that starts with whitespace
|
179
|
+
followed by '<' and returns the number of leading spaces.
|
180
|
+
If none is found, defaults to 2.
|
181
|
+
"""
|
182
|
+
for line in textwrap.dedent(html_str).splitlines():
|
183
|
+
stripped = line.lstrip()
|
184
|
+
if stripped.startswith("<") and line != stripped:
|
185
|
+
return len(line) - len(stripped)
|
186
|
+
return 2
|
187
|
+
|
188
|
+
|
189
|
+
def _alter_js_function_returned_array(
|
190
|
+
html_str: str, js_function_name: str, js_array_name: str, js_new_value: str
|
191
|
+
) -> str:
|
192
|
+
"""
|
193
|
+
Inserts a new push into the specified JavaScript function.
|
194
|
+
|
195
|
+
It finds the function definition with the given js_function_name,
|
196
|
+
then looks for the first return statement inside the function body,
|
197
|
+
and inserts a new line that pushes js_new_value into js_arr_name.
|
198
|
+
|
199
|
+
Parameters:
|
200
|
+
html_str (str): The HTML containing the JavaScript.
|
201
|
+
js_function_name (str): The name of the JavaScript function to modify.
|
202
|
+
js_arr_name (str): The name of the array inside that function.
|
203
|
+
js_new_value (str): The new value to push. (Pass the JS literal as a string,
|
204
|
+
e.g. "`<td>NEW</td>`" or '"<td>NEW</td>"'.)
|
205
|
+
|
206
|
+
Returns:
|
207
|
+
str: The modified HTML.
|
208
|
+
"""
|
209
|
+
# This pattern finds:
|
210
|
+
# 1. The function signature and opening brace.
|
211
|
+
# 2. All content (non-greedily) until we hit a newline containing a return statement.
|
212
|
+
# 3. Captures the newline and leading whitespace (indent) of the return statement.
|
213
|
+
# 4. Captures the rest of the return line.
|
214
|
+
pattern = (
|
215
|
+
r"(function\s+"
|
216
|
+
+ re.escape(js_function_name)
|
217
|
+
+ r"\s*\([^)]*\)\s*\{)" # group1: function header
|
218
|
+
r"([\s\S]*?)" # group2: code before return
|
219
|
+
r"(\n(\s*)return\s+[^;]+;)" # group3: newline+return line, group4: indent
|
220
|
+
)
|
221
|
+
|
222
|
+
def replacer(match):
|
223
|
+
header = match.group(1)
|
224
|
+
before_return = match.group(2)
|
225
|
+
return_line = match.group(3)
|
226
|
+
indent = match.group(4)
|
227
|
+
# Create the new push statement. We add a newline plus the same indent.
|
228
|
+
# The resulting line will be, e.g.,
|
229
|
+
# " rowComponents.push(`<td>NEW</td>`);"
|
230
|
+
injection = f"\n{indent}{js_array_name}.push({js_new_value});"
|
231
|
+
return header + before_return + injection + return_line
|
232
|
+
|
233
|
+
# Use re.sub to replace only the first occurrence of the function
|
234
|
+
new_html, count = re.subn(
|
235
|
+
pattern, replacer, html_str, count=1, flags=re.MULTILINE | re.DOTALL
|
236
|
+
)
|
237
|
+
return new_html
|
238
|
+
|
239
|
+
|
240
|
+
def update_fastapp_test_create(ctx: AnyContext):
|
241
|
+
snake_module_name = to_snake_case(ctx.input.module)
|
242
|
+
snake_entity_name = to_snake_case(ctx.input.entity)
|
243
|
+
test_file_path = os.path.join(
|
244
|
+
APP_DIR,
|
245
|
+
"test",
|
246
|
+
snake_module_name,
|
247
|
+
snake_entity_name,
|
248
|
+
f"test_create_{snake_entity_name}.py",
|
249
|
+
)
|
250
|
+
existing_code = read_file(test_file_path)
|
251
|
+
new_code = existing_code
|
252
|
+
# TODO: update test
|
253
|
+
write_file(test_file_path, new_code)
|
254
|
+
|
255
|
+
|
256
|
+
def update_fastapp_test_read(ctx: AnyContext):
|
257
|
+
snake_module_name = to_snake_case(ctx.input.module)
|
258
|
+
snake_entity_name = to_snake_case(ctx.input.entity)
|
259
|
+
test_file_path = os.path.join(
|
260
|
+
APP_DIR,
|
261
|
+
"test",
|
262
|
+
snake_module_name,
|
263
|
+
snake_entity_name,
|
264
|
+
f"test_read_{snake_entity_name}.py",
|
265
|
+
)
|
266
|
+
existing_code = read_file(test_file_path)
|
267
|
+
new_code = existing_code
|
268
|
+
# TODO: update test
|
269
|
+
write_file(test_file_path, new_code)
|
270
|
+
|
271
|
+
|
272
|
+
def update_fastapp_test_update(ctx: AnyContext):
|
273
|
+
snake_module_name = to_snake_case(ctx.input.module)
|
274
|
+
snake_entity_name = to_snake_case(ctx.input.entity)
|
275
|
+
test_file_path = os.path.join(
|
276
|
+
APP_DIR,
|
277
|
+
"test",
|
278
|
+
snake_module_name,
|
279
|
+
snake_entity_name,
|
280
|
+
f"test_update_{snake_entity_name}.py",
|
281
|
+
)
|
282
|
+
existing_code = read_file(test_file_path)
|
283
|
+
new_code = existing_code
|
284
|
+
# TODO: update test
|
285
|
+
write_file(test_file_path, new_code)
|
286
|
+
|
287
|
+
|
288
|
+
def update_fastapp_test_delete(ctx: AnyContext):
|
289
|
+
snake_module_name = to_snake_case(ctx.input.module)
|
290
|
+
snake_entity_name = to_snake_case(ctx.input.entity)
|
291
|
+
test_file_path = os.path.join(
|
292
|
+
APP_DIR,
|
293
|
+
"test",
|
294
|
+
snake_module_name,
|
295
|
+
snake_entity_name,
|
296
|
+
f"test_delete_{snake_entity_name}.py",
|
297
|
+
)
|
298
|
+
existing_code = read_file(test_file_path)
|
299
|
+
new_code = existing_code
|
300
|
+
# TODO: update test
|
301
|
+
write_file(test_file_path, new_code)
|
@@ -13,6 +13,11 @@ MONOLITH_ENV_VARS = {
|
|
13
13
|
"MY_APP_NAME_MODE": "monolith",
|
14
14
|
"MY_APP_NAME_MODULES": "",
|
15
15
|
}
|
16
|
+
TEST_ENV_VARS = {
|
17
|
+
"MY_APP_NAME_DB_URL": f"sqlite:///{APP_DIR}/test.db",
|
18
|
+
"MY_APP_NAME_AUTH_PRIORITIZE_NEW_SESSION": "1", # Need this because we will launch a new user session for each test
|
19
|
+
"MY_APP_NAME_AUTH_GUEST_USER_PERMISSIONs": "", # Guest user should not has any privilege for testing
|
20
|
+
}
|
16
21
|
|
17
22
|
if platform.system() == "Windows":
|
18
23
|
ACTIVATE_VENV_SCRIPT = "Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser; . .venv\\Scripts\\Activate" # noqa
|