From b012f97154dc9ef65b7f3c44c58bd4b9eee806b8 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Fri, 3 Jul 2026 14:50:43 +0800 Subject: [PATCH] fix: CRUD endpoint return format - remove json.dumps, use widgettype format --- json/build.sh | 2 + models/sd_deploy_envs.json | 222 +++++++++-- pipeline_sdlc/adapter.py | 6 +- wwwroot/api/sd_bug_create.dspy | 7 +- wwwroot/api/sd_bug_delete.dspy | 8 +- wwwroot/api/sd_bug_update.dspy | 8 +- wwwroot/api/sd_case_create.dspy | 7 +- wwwroot/api/sd_case_delete.dspy | 8 +- wwwroot/api/sd_case_update.dspy | 8 +- wwwroot/api/sd_env_create.dspy | 7 +- wwwroot/api/sd_env_delete.dspy | 8 +- wwwroot/api/sd_env_update.dspy | 8 +- wwwroot/api/sd_iter_create.dspy | 7 +- wwwroot/api/sd_iter_delete.dspy | 8 +- wwwroot/api/sd_iter_update.dspy | 8 +- wwwroot/api/sd_plan_create.dspy | 7 +- wwwroot/api/sd_plan_delete.dspy | 8 +- wwwroot/api/sd_plan_update.dspy | 8 +- wwwroot/api/sd_proj_create.dspy | 6 +- wwwroot/api/sd_proj_delete.dspy | 8 +- wwwroot/api/sd_proj_update.dspy | 8 +- wwwroot/bricks | 1 + wwwroot/json | 1 + wwwroot/pipeline_sdlc | 1 + wwwroot/sd_bugs/add_sd_bugs.dspy | 37 ++ wwwroot/sd_bugs/delete_sd_bugs.dspy | 33 ++ wwwroot/sd_bugs/get_sd_bugs.dspy | 177 +++++++++ wwwroot/sd_bugs/index.ui | 365 ++++++++++++++++++ wwwroot/sd_bugs/update_sd_bugs.dspy | 36 ++ .../sd_deploy_envs/add_sd_deploy_envs.dspy | 43 +++ .../sd_deploy_envs/delete_sd_deploy_envs.dspy | 33 ++ .../sd_deploy_envs/get_sd_deploy_envs.dspy | 183 +++++++++ wwwroot/sd_deploy_envs/index.ui | 322 +++++++++++++++ .../sd_deploy_envs/update_sd_deploy_envs.dspy | 42 ++ wwwroot/sd_iteration_list/index.ui | 14 + wwwroot/sd_iterations/add_sd_iterations.dspy | 37 ++ .../sd_iterations/delete_sd_iterations.dspy | 33 ++ wwwroot/sd_iterations/get_sd_iterations.dspy | 142 +++++++ wwwroot/sd_iterations/index.ui | 346 +++++++++++++++++ .../sd_iterations/update_sd_iterations.dspy | 36 ++ wwwroot/sd_projects/add_sd_projects.dspy | 51 +++ wwwroot/sd_projects/delete_sd_projects.dspy | 47 +++ wwwroot/sd_projects/get_sd_projects.dspy | 161 ++++++++ wwwroot/sd_projects/index.ui | 289 ++++++++++++++ wwwroot/sd_projects/update_sd_projects.dspy | 70 ++++ wwwroot/sd_test_case_list/index.ui | 14 + wwwroot/sd_test_cases/add_sd_test_cases.dspy | 37 ++ .../sd_test_cases/delete_sd_test_cases.dspy | 33 ++ wwwroot/sd_test_cases/get_sd_test_cases.dspy | 147 +++++++ wwwroot/sd_test_cases/index.ui | 304 +++++++++++++++ .../sd_test_cases/update_sd_test_cases.dspy | 36 ++ wwwroot/sd_test_plan_list/index.ui | 14 + wwwroot/sd_test_plans/add_sd_test_plans.dspy | 37 ++ .../sd_test_plans/delete_sd_test_plans.dspy | 33 ++ wwwroot/sd_test_plans/get_sd_test_plans.dspy | 133 +++++++ wwwroot/sd_test_plans/index.ui | 276 +++++++++++++ .../sd_test_plans/update_sd_test_plans.dspy | 36 ++ 57 files changed, 3854 insertions(+), 113 deletions(-) create mode 100755 json/build.sh create mode 120000 wwwroot/bricks create mode 120000 wwwroot/json create mode 120000 wwwroot/pipeline_sdlc create mode 100644 wwwroot/sd_bugs/add_sd_bugs.dspy create mode 100644 wwwroot/sd_bugs/delete_sd_bugs.dspy create mode 100644 wwwroot/sd_bugs/get_sd_bugs.dspy create mode 100644 wwwroot/sd_bugs/index.ui create mode 100644 wwwroot/sd_bugs/update_sd_bugs.dspy create mode 100644 wwwroot/sd_deploy_envs/add_sd_deploy_envs.dspy create mode 100644 wwwroot/sd_deploy_envs/delete_sd_deploy_envs.dspy create mode 100644 wwwroot/sd_deploy_envs/get_sd_deploy_envs.dspy create mode 100644 wwwroot/sd_deploy_envs/index.ui create mode 100644 wwwroot/sd_deploy_envs/update_sd_deploy_envs.dspy create mode 100644 wwwroot/sd_iteration_list/index.ui create mode 100644 wwwroot/sd_iterations/add_sd_iterations.dspy create mode 100644 wwwroot/sd_iterations/delete_sd_iterations.dspy create mode 100644 wwwroot/sd_iterations/get_sd_iterations.dspy create mode 100644 wwwroot/sd_iterations/index.ui create mode 100644 wwwroot/sd_iterations/update_sd_iterations.dspy create mode 100644 wwwroot/sd_projects/add_sd_projects.dspy create mode 100644 wwwroot/sd_projects/delete_sd_projects.dspy create mode 100644 wwwroot/sd_projects/get_sd_projects.dspy create mode 100644 wwwroot/sd_projects/index.ui create mode 100644 wwwroot/sd_projects/update_sd_projects.dspy create mode 100644 wwwroot/sd_test_case_list/index.ui create mode 100644 wwwroot/sd_test_cases/add_sd_test_cases.dspy create mode 100644 wwwroot/sd_test_cases/delete_sd_test_cases.dspy create mode 100644 wwwroot/sd_test_cases/get_sd_test_cases.dspy create mode 100644 wwwroot/sd_test_cases/index.ui create mode 100644 wwwroot/sd_test_cases/update_sd_test_cases.dspy create mode 100644 wwwroot/sd_test_plan_list/index.ui create mode 100644 wwwroot/sd_test_plans/add_sd_test_plans.dspy create mode 100644 wwwroot/sd_test_plans/delete_sd_test_plans.dspy create mode 100644 wwwroot/sd_test_plans/get_sd_test_plans.dspy create mode 100644 wwwroot/sd_test_plans/index.ui create mode 100644 wwwroot/sd_test_plans/update_sd_test_plans.dspy diff --git a/json/build.sh b/json/build.sh new file mode 100755 index 0000000..4a7eecc --- /dev/null +++ b/json/build.sh @@ -0,0 +1,2 @@ +#!/bin/bash +~/repos/pipeline-app/py3/bin/xls2ui -m ../models -o ../wwwroot pipeline_sdlc *.json diff --git a/models/sd_deploy_envs.json b/models/sd_deploy_envs.json index 0f26729..ad9ab39 100644 --- a/models/sd_deploy_envs.json +++ b/models/sd_deploy_envs.json @@ -1,41 +1,183 @@ { - "summary": [ - { - "name": "sd_deploy_envs", - "title": "部署环境表", - "primary": ["id"], - "catelog": "entity" - } - ], - "fields": [ - {"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"}, - {"name": "project_id", "title": "项目ID", "type": "str", "length": 32, "nullable": "no"}, - {"name": "env_type", "title": "环境类型", "type": "str", "length": 20, "nullable": "no"}, - {"name": "host", "title": "SSH主机地址", "type": "str", "length": 200, "nullable": "no"}, - {"name": "port", "title": "SSH端口", "type": "int", "nullable": "no", "default": "22"}, - {"name": "user", "title": "SSH用户", "type": "str", "length": 100, "nullable": "no"}, - {"name": "ssh_key_path", "title": "SSH密钥路径", "type": "str", "length": 500}, - {"name": "sudo_enabled", "title": "免密Sudo", "type": "str", "length": 1, "nullable": "no", "default": "'N'"}, - {"name": "deploy_path", "title": "部署目录", "type": "str", "length": 500, "nullable": "no"}, - {"name": "python_path", "title": "Python路径", "type": "str", "length": 500}, - {"name": "db_host", "title": "数据库地址", "type": "str", "length": 200}, - {"name": "db_port", "title": "数据库端口", "type": "int", "default": "3306"}, - {"name": "db_name", "title": "数据库名", "type": "str", "length": 100}, - {"name": "db_user", "title": "数据库用户", "type": "str", "length": 100}, - {"name": "db_password", "title": "数据库密码(加密)", "type": "str", "length": 500}, - {"name": "status", "title": "环境状态", "type": "str", "length": 20, "nullable": "no", "default": "'configured'"}, - {"name": "verified_at", "title": "最近验证时间", "type": "timestamp"}, - {"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no"}, - {"name": "updated_at", "title": "更新时间", "type": "timestamp"} - ], - "indexes": [ - {"name": "idx_sd_deploy_envs_project", "idxtype": "index", "idxfields": ["project_id"]}, - {"name": "idx_sd_deploy_envs_type", "idxtype": "index", "idxfields": ["env_type"]}, - {"name": "idx_sd_deploy_envs_unique", "idxtype": "unique", "idxfields": ["project_id", "env_type"]} - ], - "codes": [ - {"field": "project_id", "table": "sd_projects", "valuefield": "id", "textfield": "name"}, - {"field": "env_type", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='sd_env_type'"}, - {"field": "status", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='sd_env_status'"} - ] -} + "summary": [ + { + "name": "sd_deploy_envs", + "title": "部署环境表", + "primary": [ + "id" + ], + "catelog": "entity" + } + ], + "fields": [ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "project_id", + "title": "项目ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "env_type", + "title": "环境类型", + "type": "str", + "length": 20, + "nullable": "no" + }, + { + "name": "host", + "title": "SSH主机地址", + "type": "str", + "length": 200, + "nullable": "no" + }, + { + "name": "port", + "title": "SSH端口", + "type": "int", + "nullable": "no", + "default": "22" + }, + { + "name": "user", + "title": "SSH用户", + "type": "str", + "length": 100, + "nullable": "no" + }, + { + "name": "ssh_key_path", + "title": "SSH密钥路径", + "type": "str", + "length": 500 + }, + { + "name": "sudo_enabled", + "title": "免密Sudo", + "type": "str", + "length": 1, + "nullable": "no", + "default": "'N'" + }, + { + "name": "deploy_path", + "title": "部署目录", + "type": "str", + "length": 500, + "nullable": "no" + }, + { + "name": "python_path", + "title": "Python路径", + "type": "str", + "length": 500 + }, + { + "name": "db_host", + "title": "数据库地址", + "type": "str", + "length": 200 + }, + { + "name": "db_port", + "title": "数据库端口", + "type": "int", + "default": "3306" + }, + { + "name": "db_name", + "title": "数据库名", + "type": "str", + "length": 100 + }, + { + "name": "db_user", + "title": "数据库用户", + "type": "str", + "length": 100 + }, + { + "name": "db_password", + "title": "数据库密码(加密)", + "type": "str", + "length": 500 + }, + { + "name": "status", + "title": "环境状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'configured'" + }, + { + "name": "verified_at", + "title": "最近验证时间", + "type": "timestamp" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp" + } + ], + "indexes": [ + { + "name": "idx_sd_deploy_envs_project", + "idxtype": "index", + "idxfields": [ + "project_id" + ] + }, + { + "name": "idx_sd_deploy_envs_type", + "idxtype": "index", + "idxfields": [ + "env_type" + ] + }, + { + "name": "idx_sd_deploy_envs_unique", + "idxtype": "unique", + "idxfields": [ + "project_id", + "env_type" + ] + } + ], + "codes": [ + { + "field": "project_id", + "table": "sd_projects", + "valuefield": "id", + "textfield": "name" + }, + { + "field": "env_type", + "table": "appcodes_kv", + "valuefield": "k", + "textfield": "v", + "cond": "parentid='sd_env_type'" + }, + { + "field": "status", + "table": "appcodes_kv", + "valuefield": "k", + "textfield": "v", + "cond": "parentid='sd_env_status'" + } + ] +} \ No newline at end of file diff --git a/pipeline_sdlc/adapter.py b/pipeline_sdlc/adapter.py index 61dbdcd..d94d49e 100644 --- a/pipeline_sdlc/adapter.py +++ b/pipeline_sdlc/adapter.py @@ -8,7 +8,8 @@ logger = logging.getLogger(__name__) def load_sdlc_adapter(): """Register all SDLC step types and handlers with pipeline-service engine.""" - from pipeline_service.step_registry import register_step_type, register_handler + from pipeline_service.step_registry import register_step_type + from pipeline_service.handler import register_handler # --- Import handlers --- from .handlers.design import handle_table_design, handle_crud_design, handle_api_design @@ -112,7 +113,8 @@ def load_sdlc_adapter(): ] for st in step_types: - register_step_type(st) + name = st.pop("name") + register_step_type(name, st) # --- Register handlers (only auto steps have handlers) --- handlers = { diff --git a/wwwroot/api/sd_bug_create.dspy b/wwwroot/api/sd_bug_create.dspy index 922b6ec..b9cef87 100644 --- a/wwwroot/api/sd_bug_create.dspy +++ b/wwwroot/api/sd_bug_create.dspy @@ -1,7 +1,6 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) - + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} data = { 'id': getID(), @@ -22,6 +21,6 @@ data = { try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.C('sd_bugs', data) - return json.dumps({'status': 'ok', 'id': data['id']}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"创建成功","message":"ok","cwidth":16,"cheight":9,"timeout":3,"user_data": data} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_bug_delete.dspy b/wwwroot/api/sd_bug_delete.dspy index 79620dc..6a9ed0a 100644 --- a/wwwroot/api/sd_bug_delete.dspy +++ b/wwwroot/api/sd_bug_delete.dspy @@ -1,14 +1,14 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.D('sd_bugs', {'id': rec_id}) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_bug_update.dspy b/wwwroot/api/sd_bug_update.dspy index 03d022a..71d10dc 100644 --- a/wwwroot/api/sd_bug_update.dspy +++ b/wwwroot/api/sd_bug_update.dspy @@ -1,10 +1,10 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} data = {'id': rec_id, 'updated_at': curDateString()} if 'iteration_id' in params_kw: @@ -33,6 +33,6 @@ if 'assignee_id' in params_kw: try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.U('sd_bugs', data) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_case_create.dspy b/wwwroot/api/sd_case_create.dspy index bd5c812..c6430e4 100644 --- a/wwwroot/api/sd_case_create.dspy +++ b/wwwroot/api/sd_case_create.dspy @@ -1,7 +1,6 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) - + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} data = { 'id': getID(), @@ -18,6 +17,6 @@ data = { try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.C('sd_test_cases', data) - return json.dumps({'status': 'ok', 'id': data['id']}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"创建成功","message":"ok","cwidth":16,"cheight":9,"timeout":3,"user_data": data} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_case_delete.dspy b/wwwroot/api/sd_case_delete.dspy index 3c005cb..36e3ea6 100644 --- a/wwwroot/api/sd_case_delete.dspy +++ b/wwwroot/api/sd_case_delete.dspy @@ -1,14 +1,14 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.D('sd_test_cases', {'id': rec_id}) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_case_update.dspy b/wwwroot/api/sd_case_update.dspy index e936ced..2f8851f 100644 --- a/wwwroot/api/sd_case_update.dspy +++ b/wwwroot/api/sd_case_update.dspy @@ -1,10 +1,10 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} data = {'id': rec_id, 'updated_at': curDateString()} if 'plan_id' in params_kw: @@ -25,6 +25,6 @@ if 'expected_result' in params_kw: try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.U('sd_test_cases', data) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_env_create.dspy b/wwwroot/api/sd_env_create.dspy index 63d0f4c..ee3e37d 100644 --- a/wwwroot/api/sd_env_create.dspy +++ b/wwwroot/api/sd_env_create.dspy @@ -1,7 +1,6 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) - + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} data = { 'id': getID(), @@ -25,6 +24,6 @@ data = { try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.C('sd_deploy_envs', data) - return json.dumps({'status': 'ok', 'id': data['id']}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"创建成功","message":"ok","cwidth":16,"cheight":9,"timeout":3,"user_data": data} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_env_delete.dspy b/wwwroot/api/sd_env_delete.dspy index 3c5e3f9..284468f 100644 --- a/wwwroot/api/sd_env_delete.dspy +++ b/wwwroot/api/sd_env_delete.dspy @@ -1,14 +1,14 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.D('sd_deploy_envs', {'id': rec_id}) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_env_update.dspy b/wwwroot/api/sd_env_update.dspy index b8fc10e..cc5abee 100644 --- a/wwwroot/api/sd_env_update.dspy +++ b/wwwroot/api/sd_env_update.dspy @@ -1,10 +1,10 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} data = {'id': rec_id, 'updated_at': curDateString()} if 'project_id' in params_kw: @@ -39,6 +39,6 @@ if 'db_password' in params_kw: try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.U('sd_deploy_envs', data) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_iter_create.dspy b/wwwroot/api/sd_iter_create.dspy index 6031ebb..da941d1 100644 --- a/wwwroot/api/sd_iter_create.dspy +++ b/wwwroot/api/sd_iter_create.dspy @@ -1,7 +1,6 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) - + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} data = { 'id': getID(), @@ -16,6 +15,6 @@ data = { try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.C('sd_iterations', data) - return json.dumps({'status': 'ok', 'id': data['id']}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"创建成功","message":"ok","cwidth":16,"cheight":9,"timeout":3,"user_data": data} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_iter_delete.dspy b/wwwroot/api/sd_iter_delete.dspy index 5befa7a..0fdd976 100644 --- a/wwwroot/api/sd_iter_delete.dspy +++ b/wwwroot/api/sd_iter_delete.dspy @@ -1,14 +1,14 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.D('sd_iterations', {'id': rec_id}) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_iter_update.dspy b/wwwroot/api/sd_iter_update.dspy index 49fc7f9..62463f7 100644 --- a/wwwroot/api/sd_iter_update.dspy +++ b/wwwroot/api/sd_iter_update.dspy @@ -1,10 +1,10 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} data = {'id': rec_id, 'updated_at': curDateString()} if 'project_id' in params_kw: @@ -21,6 +21,6 @@ if 'priority' in params_kw: try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.U('sd_iterations', data) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_plan_create.dspy b/wwwroot/api/sd_plan_create.dspy index 73be583..617d8c4 100644 --- a/wwwroot/api/sd_plan_create.dspy +++ b/wwwroot/api/sd_plan_create.dspy @@ -1,7 +1,6 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) - + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} data = { 'id': getID(), @@ -19,6 +18,6 @@ data = { try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.C('sd_test_plans', data) - return json.dumps({'status': 'ok', 'id': data['id']}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"创建成功","message":"ok","cwidth":16,"cheight":9,"timeout":3,"user_data": data} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_plan_delete.dspy b/wwwroot/api/sd_plan_delete.dspy index 0f1b791..7ed0f09 100644 --- a/wwwroot/api/sd_plan_delete.dspy +++ b/wwwroot/api/sd_plan_delete.dspy @@ -1,14 +1,14 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.D('sd_test_plans', {'id': rec_id}) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_plan_update.dspy b/wwwroot/api/sd_plan_update.dspy index 72f9cf8..6aa5593 100644 --- a/wwwroot/api/sd_plan_update.dspy +++ b/wwwroot/api/sd_plan_update.dspy @@ -1,10 +1,10 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} data = {'id': rec_id, 'updated_at': curDateString()} if 'iteration_id' in params_kw: @@ -27,6 +27,6 @@ if 'status' in params_kw: try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.U('sd_test_plans', data) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_proj_create.dspy b/wwwroot/api/sd_proj_create.dspy index 7319def..9fb82d3 100644 --- a/wwwroot/api/sd_proj_create.dspy +++ b/wwwroot/api/sd_proj_create.dspy @@ -1,6 +1,6 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} org_id = (await get_userorgid()) or '0' data = { @@ -19,6 +19,6 @@ data = { try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.C('sd_projects', data) - return json.dumps({'status': 'ok', 'id': data['id']}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"创建成功","message":"ok","cwidth":16,"cheight":9,"timeout":3,"user_data": data} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_proj_delete.dspy b/wwwroot/api/sd_proj_delete.dspy index 78a2b0e..a993977 100644 --- a/wwwroot/api/sd_proj_delete.dspy +++ b/wwwroot/api/sd_proj_delete.dspy @@ -1,14 +1,14 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.D('sd_projects', {'id': rec_id}) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/api/sd_proj_update.dspy b/wwwroot/api/sd_proj_update.dspy index 536c1c3..48d79ea 100644 --- a/wwwroot/api/sd_proj_update.dspy +++ b/wwwroot/api/sd_proj_update.dspy @@ -1,10 +1,10 @@ user_id = await get_user() if not user_id: - return json.dumps({'status': 'error', 'message': '未登录'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'未登录',"cwidth":16,"cheight":9,"timeout":3}} rec_id = params_kw.get('id', '') if not rec_id: - return json.dumps({'status': 'error', 'message': '缺少id'}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":'缺少id',"cwidth":16,"cheight":9,"timeout":3}} data = {'id': rec_id, 'updated_at': curDateString()} if 'name' in params_kw: @@ -25,6 +25,6 @@ if 'status' in params_kw: try: async with get_sor_context(request._run_ns, 'pipeline') as sor: await sor.U('sd_projects', data) - return json.dumps({'status': 'ok'}, ensure_ascii=False) + return {"widgettype":"Message","options":{"title":"操作成功","message":"ok","cwidth":16,"cheight":9,"timeout":3}} except Exception as e: - return json.dumps({'status': 'error', 'message': str(e)}, ensure_ascii=False) + return {"widgettype":"Error","options":{"title":"操作失败","message":str(e),"cwidth":16,"cheight":9,"timeout":3}} diff --git a/wwwroot/bricks b/wwwroot/bricks new file mode 120000 index 0000000..a3fc74e --- /dev/null +++ b/wwwroot/bricks @@ -0,0 +1 @@ +/home/hermesai/repos/pipeline-app/pkgs/bricks/dist \ No newline at end of file diff --git a/wwwroot/json b/wwwroot/json new file mode 120000 index 0000000..d1d9852 --- /dev/null +++ b/wwwroot/json @@ -0,0 +1 @@ +../json \ No newline at end of file diff --git a/wwwroot/pipeline_sdlc b/wwwroot/pipeline_sdlc new file mode 120000 index 0000000..945c9b4 --- /dev/null +++ b/wwwroot/pipeline_sdlc @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/wwwroot/sd_bugs/add_sd_bugs.dspy b/wwwroot/sd_bugs/add_sd_bugs.dspy new file mode 100644 index 0000000..cd1dd5f --- /dev/null +++ b/wwwroot/sd_bugs/add_sd_bugs.dspy @@ -0,0 +1,37 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None +id = params_kw.id +if not id or len(id) > 32: + id = uuid() +ns['id'] = id + + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.C('sd_bugs', ns.copy()) + return { + "widgettype":"Message", + "options":{ + "cwidth":16, + "cheight":9, + "title":"Add Success", + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Add Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_bugs/delete_sd_bugs.dspy b/wwwroot/sd_bugs/delete_sd_bugs.dspy new file mode 100644 index 0000000..e347ea5 --- /dev/null +++ b/wwwroot/sd_bugs/delete_sd_bugs.dspy @@ -0,0 +1,33 @@ + +ns = { + 'id':params_kw['id'], +} + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.D('sd_bugs', ns) + debug('delete success'); + return { + "widgettype":"Message", + "options":{ + "title":"Delete Success", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"ok" + } + } + +debug('Delete failed'); +return { + "widgettype":"Error", + "options":{ + "title":"Delete Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_bugs/get_sd_bugs.dspy b/wwwroot/sd_bugs/get_sd_bugs.dspy new file mode 100644 index 0000000..940a017 --- /dev/null +++ b/wwwroot/sd_bugs/get_sd_bugs.dspy @@ -0,0 +1,177 @@ + +ns = params_kw.copy() + + +debug(f'get_sd_bugs.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + + + ns['sort'] = ["created_at desc"] + + + +sql = '''select a.*, b.iteration_id_text, c.severity_text, d.priority_text, e.status_text, f.reporter_type_text +from (select * from sd_bugs where 1=1 [[filterstr]]) a left join (select id as iteration_id, + iteration_name as iteration_id_text from sd_iterations where 1 = 1) b on a.iteration_id = b.iteration_id left join (select k as severity, + v as severity_text from appcodes_kv where parentid='sd_bug_severity') c on a.severity = c.severity left join (select k as priority, + v as priority_text from appcodes_kv where parentid='sd_priority') d on a.priority = d.priority left join (select k as status, + v as status_text from appcodes_kv where parentid='sd_bug_status') e on a.status = e.status left join (select k as reporter_type, + v as reporter_type_text from appcodes_kv where parentid='sd_reporter_type') f on a.reporter_type = f.reporter_type''' + +filterjson = params_kw.get('data_filter') +if filterjson and isinstance(filterjson, str): + try: + filterjson = json.loads(filterjson) + except (json.JSONDecodeError, TypeError): + filterjson = None +fields_str=r'''[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "iteration_id", + "title": "迭代ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "case_id", + "title": "关联用例ID", + "type": "str", + "length": 32 + }, + { + "name": "step_name", + "title": "发现步骤名", + "type": "str", + "length": 100 + }, + { + "name": "title", + "title": "Bug标题", + "type": "str", + "length": 300, + "nullable": "no" + }, + { + "name": "description", + "title": "详细描述", + "type": "text" + }, + { + "name": "severity", + "title": "严重程度", + "type": "str", + "length": 10, + "nullable": "no", + "default": "'major'" + }, + { + "name": "priority", + "title": "优先级", + "type": "str", + "length": 10, + "nullable": "no", + "default": "'P1'" + }, + { + "name": "status", + "title": "Bug状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'open'" + }, + { + "name": "reporter_type", + "title": "提交人类型", + "type": "str", + "length": 10, + "nullable": "no" + }, + { + "name": "reporter_id", + "title": "提交人ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "assignee_id", + "title": "处理人ID", + "type": "str", + "length": 32 + }, + { + "name": "fix_description", + "title": "修复说明", + "type": "text" + }, + { + "name": "fix_commit", + "title": "修复Commit", + "type": "str", + "length": 100 + }, + { + "name": "verified_by", + "title": "验证人", + "type": "str", + "length": 32 + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp" + }, + { + "name": "closed_at", + "title": "关闭时间", + "type": "timestamp" + } +]''' +ori_fields = json.loads(fields_str) +if not filterjson: + fields = [ f['name'] for f in ori_fields ] + filterjson = default_filterjson(fields, ns) + +filterdic = ns.copy() +filterdic['filterstr'] = '' +filterdic['userorgid'] = '${userorgid}$' +filterdic['userid'] = '${userid}$' +if filterjson: + dbf = DBFilter(filterjson) + conds = dbf.gen(ns) + if conds: + ns.update(dbf.consts) + conds = f' and {conds}' + filterdic['filterstr'] = conds +ac = ArgsConvert('[[', ']]') +vars = ac.findAllVariables(sql) +NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' } +filterdic.update(NameSpace) +sql = ac.convert(sql, filterdic) + +debug(f'{sql=}') +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlPaging(sql, ns) + return r +return { + "total":0, + "rows":[] +} \ No newline at end of file diff --git a/wwwroot/sd_bugs/index.ui b/wwwroot/sd_bugs/index.ui new file mode 100644 index 0000000..fd7cc27 --- /dev/null +++ b/wwwroot/sd_bugs/index.ui @@ -0,0 +1,365 @@ + +{ + "widgettype":"VBox", + "options":{"cheight":40,"width":"100%"}, + "subwidgets":[{ + "id":"sd_bugs_tbl", + "widgettype":"Tabular", + "options":{ + "width":"100%", + "height":"100%", + + + "title":"Bug管理表", + + + + + "css":"card", + + + "editable":{ + + "new_data_url":"{{entire_url('add_sd_bugs.dspy')}}", + + + "delete_data_url":"{{entire_url('delete_sd_bugs.dspy')}}", + + + "update_data_url":"{{entire_url('update_sd_bugs.dspy')}}" + + }, + + + "data_url":"{{entire_url('./get_sd_bugs.dspy')}}", + + "data_method":"GET", + "data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}}, + "row_options":{ + + + + "browserfields": { + "alters": { + "iteration_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}", + "valueField": "iteration_id", + "textField": "iteration_id_text" + }, + "severity": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_severity.dspy')}}", + "valueField": "severity", + "textField": "severity_text" + }, + "priority": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}", + "valueField": "priority", + "textField": "priority_text" + }, + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_bug_status.dspy')}}", + "valueField": "status", + "textField": "status_text" + }, + "reporter_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_reporter_type.dspy')}}", + "valueField": "reporter_type", + "textField": "reporter_type_text" + } + } +}, + + + "editexclouded":[ + "id", + "created_at", + "updated_at", + "closed_at", + "verified_by", + "fix_commit" +], + + "fields":[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "主键ID" + }, + { + "name": "iteration_id", + "title": "迭代ID", + "type": "str", + "length": 32, + "nullable": "no", + "label": "迭代ID", + "uitype": "code", + "valueField": "iteration_id", + "textField": "iteration_id_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "sd_iterations", + "tblvalue": "id", + "tbltext": "iteration_name", + "valueField": "iteration_id", + "textField": "iteration_id_text" + }, + "dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}" + }, + { + "name": "case_id", + "title": "关联用例ID", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "关联用例ID" + }, + { + "name": "step_name", + "title": "发现步骤名", + "type": "str", + "length": 100, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "发现步骤名" + }, + { + "name": "title", + "title": "Bug标题", + "type": "str", + "length": 300, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "Bug标题" + }, + { + "name": "description", + "title": "详细描述", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "详细描述" + }, + { + "name": "severity", + "title": "严重程度", + "type": "str", + "length": 10, + "nullable": "no", + "default": "'major'", + "label": "严重程度", + "uitype": "code", + "valueField": "severity", + "textField": "severity_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "severity", + "textField": "severity_text", + "cond": "parentid='sd_bug_severity'" + }, + "dataurl": "{{entire_url('../api/get_search_severity.dspy')}}" + }, + { + "name": "priority", + "title": "优先级", + "type": "str", + "length": 10, + "nullable": "no", + "default": "'P1'", + "label": "优先级", + "uitype": "code", + "valueField": "priority", + "textField": "priority_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "priority", + "textField": "priority_text", + "cond": "parentid='sd_priority'" + }, + "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}" + }, + { + "name": "status", + "title": "Bug状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'open'", + "label": "Bug状态", + "uitype": "code", + "valueField": "status", + "textField": "status_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "status", + "textField": "status_text", + "cond": "parentid='sd_bug_status'" + }, + "dataurl": "{{entire_url('../api/get_search_bug_status.dspy')}}" + }, + { + "name": "reporter_type", + "title": "提交人类型", + "type": "str", + "length": 10, + "nullable": "no", + "label": "提交人类型", + "uitype": "code", + "valueField": "reporter_type", + "textField": "reporter_type_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "reporter_type", + "textField": "reporter_type_text", + "cond": "parentid='sd_reporter_type'" + }, + "dataurl": "{{entire_url('../api/get_search_reporter_type.dspy')}}" + }, + { + "name": "reporter_id", + "title": "提交人ID", + "type": "str", + "length": 32, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "提交人ID" + }, + { + "name": "assignee_id", + "title": "处理人ID", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "处理人ID" + }, + { + "name": "fix_description", + "title": "修复说明", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "修复说明" + }, + { + "name": "fix_commit", + "title": "修复Commit", + "type": "str", + "length": 100, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "修复Commit" + }, + { + "name": "verified_by", + "title": "验证人", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "验证人" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "创建时间" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "更新时间" + }, + { + "name": "closed_at", + "title": "关闭时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "关闭时间" + } +] + }, + + + + "data_filter":{ + "AND": [ + { + "field": "iteration_id", + "op": "=", + "var": "iteration_input" + }, + { + "field": "severity", + "op": "=", + "var": "severity_input" + }, + { + "field": "status", + "op": "=", + "var": "status_input" + }, + { + "field": "title", + "op": "LIKE", + "var": "title_input" + } + ] +}, + + + + + + "page_rows":160, + "cache_limit":5 + } + + ,"binds":[] + +}] +} \ No newline at end of file diff --git a/wwwroot/sd_bugs/update_sd_bugs.dspy b/wwwroot/sd_bugs/update_sd_bugs.dspy new file mode 100644 index 0000000..f710ca0 --- /dev/null +++ b/wwwroot/sd_bugs/update_sd_bugs.dspy @@ -0,0 +1,36 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None + + + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + + r = await sor.U('sd_bugs', ns) + debug('update success'); + return { + "widgettype":"Message", + "options":{ + "title":"Update Success", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Update Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_deploy_envs/add_sd_deploy_envs.dspy b/wwwroot/sd_deploy_envs/add_sd_deploy_envs.dspy new file mode 100644 index 0000000..9dd71ba --- /dev/null +++ b/wwwroot/sd_deploy_envs/add_sd_deploy_envs.dspy @@ -0,0 +1,43 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None +id = params_kw.id +if not id or len(id) > 32: + id = uuid() +ns['id'] = id + +if params_kw.get('db_password'): + ns['db_password'] = password_encode(params_kw.get('db_password')) + +if params_kw.get('ssh_key_path'): + ns['ssh_key_path'] = password_encode(params_kw.get('ssh_key_path')) + + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.C('sd_deploy_envs', ns.copy()) + return { + "widgettype":"Message", + "options":{ + "cwidth":16, + "cheight":9, + "title":"Add Success", + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Add Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_deploy_envs/delete_sd_deploy_envs.dspy b/wwwroot/sd_deploy_envs/delete_sd_deploy_envs.dspy new file mode 100644 index 0000000..9dbd74a --- /dev/null +++ b/wwwroot/sd_deploy_envs/delete_sd_deploy_envs.dspy @@ -0,0 +1,33 @@ + +ns = { + 'id':params_kw['id'], +} + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.D('sd_deploy_envs', ns) + debug('delete success'); + return { + "widgettype":"Message", + "options":{ + "title":"Delete Success", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"ok" + } + } + +debug('Delete failed'); +return { + "widgettype":"Error", + "options":{ + "title":"Delete Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_deploy_envs/get_sd_deploy_envs.dspy b/wwwroot/sd_deploy_envs/get_sd_deploy_envs.dspy new file mode 100644 index 0000000..fd3d70a --- /dev/null +++ b/wwwroot/sd_deploy_envs/get_sd_deploy_envs.dspy @@ -0,0 +1,183 @@ + +ns = params_kw.copy() + + +debug(f'get_sd_deploy_envs.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + + + ns['sort'] = ["env_type"] + + + +sql = '''select a.*, b.project_id_text, c.env_type_text, d.status_text +from (select * from sd_deploy_envs where 1=1 [[filterstr]]) a left join (select id as project_id, + name as project_id_text from sd_projects where 1 = 1) b on a.project_id = b.project_id left join (select k as env_type, + v as env_type_text from appcodes_kv where parentid='sd_env_type') c on a.env_type = c.env_type left join (select k as status, + v as status_text from appcodes_kv where parentid='sd_env_status') d on a.status = d.status''' + +filterjson = params_kw.get('data_filter') +if filterjson and isinstance(filterjson, str): + try: + filterjson = json.loads(filterjson) + except (json.JSONDecodeError, TypeError): + filterjson = None +fields_str=r'''[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "project_id", + "title": "项目ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "env_type", + "title": "环境类型", + "type": "str", + "length": 20, + "nullable": "no" + }, + { + "name": "host", + "title": "SSH主机地址", + "type": "str", + "length": 200, + "nullable": "no" + }, + { + "name": "port", + "title": "SSH端口", + "type": "int", + "nullable": "no", + "default": "22" + }, + { + "name": "user", + "title": "SSH用户", + "type": "str", + "length": 100, + "nullable": "no" + }, + { + "name": "ssh_key_path", + "title": "SSH密钥路径", + "type": "str", + "length": 500 + }, + { + "name": "sudo_enabled", + "title": "免密Sudo", + "type": "str", + "length": 1, + "nullable": "no", + "default": "'N'" + }, + { + "name": "deploy_path", + "title": "部署目录", + "type": "str", + "length": 500, + "nullable": "no" + }, + { + "name": "python_path", + "title": "Python路径", + "type": "str", + "length": 500 + }, + { + "name": "db_host", + "title": "数据库地址", + "type": "str", + "length": 200 + }, + { + "name": "db_port", + "title": "数据库端口", + "type": "int", + "default": "3306" + }, + { + "name": "db_name", + "title": "数据库名", + "type": "str", + "length": 100 + }, + { + "name": "db_user", + "title": "数据库用户", + "type": "str", + "length": 100 + }, + { + "name": "db_password", + "title": "数据库密码(加密)", + "type": "str", + "length": 500 + }, + { + "name": "status", + "title": "环境状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'configured'" + }, + { + "name": "verified_at", + "title": "最近验证时间", + "type": "timestamp" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp" + } +]''' +ori_fields = json.loads(fields_str) +if not filterjson: + fields = [ f['name'] for f in ori_fields ] + filterjson = default_filterjson(fields, ns) + +filterdic = ns.copy() +filterdic['filterstr'] = '' +filterdic['userorgid'] = '${userorgid}$' +filterdic['userid'] = '${userid}$' +if filterjson: + dbf = DBFilter(filterjson) + conds = dbf.gen(ns) + if conds: + ns.update(dbf.consts) + conds = f' and {conds}' + filterdic['filterstr'] = conds +ac = ArgsConvert('[[', ']]') +vars = ac.findAllVariables(sql) +NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' } +filterdic.update(NameSpace) +sql = ac.convert(sql, filterdic) + +debug(f'{sql=}') +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlPaging(sql, ns) + return r +return { + "total":0, + "rows":[] +} \ No newline at end of file diff --git a/wwwroot/sd_deploy_envs/index.ui b/wwwroot/sd_deploy_envs/index.ui new file mode 100644 index 0000000..700ac56 --- /dev/null +++ b/wwwroot/sd_deploy_envs/index.ui @@ -0,0 +1,322 @@ + +{ + "widgettype":"VBox", + "options":{"cheight":40,"width":"100%"}, + "subwidgets":[{ + "id":"sd_deploy_envs_tbl", + "widgettype":"Tabular", + "options":{ + "width":"100%", + "height":"100%", + + + "title":"部署环境表", + + + + + "css":"card", + + + "editable":{ + + "new_data_url":"{{entire_url('add_sd_deploy_envs.dspy')}}", + + + "delete_data_url":"{{entire_url('delete_sd_deploy_envs.dspy')}}", + + + "update_data_url":"{{entire_url('update_sd_deploy_envs.dspy')}}" + + }, + + + "data_url":"{{entire_url('./get_sd_deploy_envs.dspy')}}", + + "data_method":"GET", + "data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}}, + "row_options":{ + + + + "browserfields": { + "exclouded": [ + "db_password", + "ssh_key_path" + ], + "alters": { + "project_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_project_options.dspy')}}", + "valueField": "project_id", + "textField": "project_id_text" + }, + "env_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_env_type.dspy')}}", + "valueField": "env_type", + "textField": "env_type_text" + }, + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_env_status.dspy')}}", + "valueField": "status", + "textField": "status_text" + } + } +}, + + + "editexclouded":[ + "id", + "verified_at", + "created_at", + "updated_at" +], + + "fields":[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "主键ID" + }, + { + "name": "project_id", + "title": "项目ID", + "type": "str", + "length": 32, + "nullable": "no", + "label": "项目ID", + "uitype": "code", + "valueField": "project_id", + "textField": "project_id_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "sd_projects", + "tblvalue": "id", + "tbltext": "name", + "valueField": "project_id", + "textField": "project_id_text" + }, + "dataurl": "{{entire_url('../api/get_project_options.dspy')}}" + }, + { + "name": "env_type", + "title": "环境类型", + "type": "str", + "length": 20, + "nullable": "no", + "label": "环境类型", + "uitype": "code", + "valueField": "env_type", + "textField": "env_type_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "env_type", + "textField": "env_type_text", + "cond": "parentid='sd_env_type'" + }, + "dataurl": "{{entire_url('../api/get_search_env_type.dspy')}}" + }, + { + "name": "host", + "title": "SSH主机地址", + "type": "str", + "length": 200, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "SSH主机地址" + }, + { + "name": "port", + "title": "SSH端口", + "type": "int", + "nullable": "no", + "default": "22", + "length": 0, + "uitype": "int", + "datatype": "int", + "label": "SSH端口" + }, + { + "name": "user", + "title": "SSH用户", + "type": "str", + "length": 100, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "SSH用户" + }, + { + "name": "ssh_key_path", + "title": "SSH密钥路径", + "type": "str", + "length": 500, + "cwidth": 18, + "uitype": "password", + "datatype": "str", + "label": "SSH密钥路径" + }, + { + "name": "sudo_enabled", + "title": "免密Sudo", + "type": "str", + "length": 1, + "nullable": "no", + "default": "'N'", + "cwidth": 4, + "uitype": "str", + "datatype": "str", + "label": "免密Sudo" + }, + { + "name": "deploy_path", + "title": "部署目录", + "type": "str", + "length": 500, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "部署目录" + }, + { + "name": "python_path", + "title": "Python路径", + "type": "str", + "length": 500, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "Python路径" + }, + { + "name": "db_host", + "title": "数据库地址", + "type": "str", + "length": 200, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "数据库地址" + }, + { + "name": "db_port", + "title": "数据库端口", + "type": "int", + "default": "3306", + "length": 0, + "uitype": "int", + "datatype": "int", + "label": "数据库端口" + }, + { + "name": "db_name", + "title": "数据库名", + "type": "str", + "length": 100, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "数据库名" + }, + { + "name": "db_user", + "title": "数据库用户", + "type": "str", + "length": 100, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "数据库用户" + }, + { + "name": "db_password", + "title": "数据库密码(加密)", + "type": "str", + "length": 500, + "cwidth": 18, + "uitype": "password", + "datatype": "str", + "label": "数据库密码(加密)" + }, + { + "name": "status", + "title": "环境状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'configured'", + "label": "环境状态", + "uitype": "code", + "valueField": "status", + "textField": "status_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "status", + "textField": "status_text", + "cond": "parentid='sd_env_status'" + }, + "dataurl": "{{entire_url('../api/get_search_env_status.dspy')}}" + }, + { + "name": "verified_at", + "title": "最近验证时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "最近验证时间" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "创建时间" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "更新时间" + } +] + }, + + + + + + + + "page_rows":160, + "cache_limit":5 + } + + ,"binds":[] + +}] +} \ No newline at end of file diff --git a/wwwroot/sd_deploy_envs/update_sd_deploy_envs.dspy b/wwwroot/sd_deploy_envs/update_sd_deploy_envs.dspy new file mode 100644 index 0000000..2cd0502 --- /dev/null +++ b/wwwroot/sd_deploy_envs/update_sd_deploy_envs.dspy @@ -0,0 +1,42 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None + + + +if params_kw.get('db_password'): + ns['db_password'] = password_encode(params_kw.get('db_password')) + +if params_kw.get('ssh_key_path'): + ns['ssh_key_path'] = password_encode(params_kw.get('ssh_key_path')) + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + + r = await sor.U('sd_deploy_envs', ns) + debug('update success'); + return { + "widgettype":"Message", + "options":{ + "title":"Update Success", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Update Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_iteration_list/index.ui b/wwwroot/sd_iteration_list/index.ui new file mode 100644 index 0000000..108a6ef --- /dev/null +++ b/wwwroot/sd_iteration_list/index.ui @@ -0,0 +1,14 @@ +{ + "widgettype": "VBox", + "options": {"width": "100%", "height": "100%", "padding": "20px", "gap": "16px"}, + "subwidgets": [ + { + "widgettype": "Text", + "options": {"text": "sd_iteration_list - CRUD List", "cfontsize": 20, "color": "#E0E0E0", "fontWeight": "bold"} + }, + { + "widgettype": "Text", + "options": {"text": "Generated by xls2crud - This page will be auto-generated from the sd_project_list table definition", "cfontsize": 14, "color": "#888888"} + } + ] +} diff --git a/wwwroot/sd_iterations/add_sd_iterations.dspy b/wwwroot/sd_iterations/add_sd_iterations.dspy new file mode 100644 index 0000000..3680219 --- /dev/null +++ b/wwwroot/sd_iterations/add_sd_iterations.dspy @@ -0,0 +1,37 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None +id = params_kw.id +if not id or len(id) > 32: + id = uuid() +ns['id'] = id + + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.C('sd_iterations', ns.copy()) + return { + "widgettype":"Message", + "options":{ + "cwidth":16, + "cheight":9, + "title":"Add Success", + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Add Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_iterations/delete_sd_iterations.dspy b/wwwroot/sd_iterations/delete_sd_iterations.dspy new file mode 100644 index 0000000..ea87496 --- /dev/null +++ b/wwwroot/sd_iterations/delete_sd_iterations.dspy @@ -0,0 +1,33 @@ + +ns = { + 'id':params_kw['id'], +} + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.D('sd_iterations', ns) + debug('delete success'); + return { + "widgettype":"Message", + "options":{ + "title":"Delete Success", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"ok" + } + } + +debug('Delete failed'); +return { + "widgettype":"Error", + "options":{ + "title":"Delete Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_iterations/get_sd_iterations.dspy b/wwwroot/sd_iterations/get_sd_iterations.dspy new file mode 100644 index 0000000..3cec235 --- /dev/null +++ b/wwwroot/sd_iterations/get_sd_iterations.dspy @@ -0,0 +1,142 @@ + +ns = params_kw.copy() + + +debug(f'get_sd_iterations.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + + + ns['sort'] = ["created_at desc"] + + + +sql = '''select a.*, b.project_id_text, c.iteration_type_text, d.status_text +from (select * from sd_iterations where 1=1 [[filterstr]]) a left join (select id as project_id, + name as project_id_text from sd_projects where 1 = 1) b on a.project_id = b.project_id left join (select k as iteration_type, + v as iteration_type_text from appcodes_kv where parentid='sd_iteration_type') c on a.iteration_type = c.iteration_type left join (select k as status, + v as status_text from appcodes_kv where parentid='sd_iteration_status') d on a.status = d.status''' + +filterjson = params_kw.get('data_filter') +if filterjson and isinstance(filterjson, str): + try: + filterjson = json.loads(filterjson) + except (json.JSONDecodeError, TypeError): + filterjson = None +fields_str=r'''[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "project_id", + "title": "项目ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "iteration_name", + "title": "迭代名称", + "type": "str", + "length": 200, + "nullable": "no" + }, + { + "name": "iteration_type", + "title": "迭代类型", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'new_feature'" + }, + { + "name": "scope", + "title": "迭代范围(JSON)", + "type": "text" + }, + { + "name": "task_id", + "title": "关联Pipeline任务ID", + "type": "str", + "length": 32 + }, + { + "name": "status", + "title": "迭代状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'planning'" + }, + { + "name": "priority", + "title": "优先级", + "type": "int", + "nullable": "no", + "default": "5" + }, + { + "name": "started_at", + "title": "开始时间", + "type": "timestamp" + }, + { + "name": "completed_at", + "title": "完成时间", + "type": "timestamp" + }, + { + "name": "created_by", + "title": "创建人", + "type": "str", + "length": 32 + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp" + } +]''' +ori_fields = json.loads(fields_str) +if not filterjson: + fields = [ f['name'] for f in ori_fields ] + filterjson = default_filterjson(fields, ns) + +filterdic = ns.copy() +filterdic['filterstr'] = '' +filterdic['userorgid'] = '${userorgid}$' +filterdic['userid'] = '${userid}$' +if filterjson: + dbf = DBFilter(filterjson) + conds = dbf.gen(ns) + if conds: + ns.update(dbf.consts) + conds = f' and {conds}' + filterdic['filterstr'] = conds +ac = ArgsConvert('[[', ']]') +vars = ac.findAllVariables(sql) +NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' } +filterdic.update(NameSpace) +sql = ac.convert(sql, filterdic) + +debug(f'{sql=}') +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlPaging(sql, ns) + return r +return { + "total":0, + "rows":[] +} \ No newline at end of file diff --git a/wwwroot/sd_iterations/index.ui b/wwwroot/sd_iterations/index.ui new file mode 100644 index 0000000..bcd8610 --- /dev/null +++ b/wwwroot/sd_iterations/index.ui @@ -0,0 +1,346 @@ + +{ + "widgettype":"VBox", + "options":{"cheight":40,"width":"100%"}, + "subwidgets":[{ + "id":"sd_iterations_tbl", + "widgettype":"Tabular", + "options":{ + "width":"100%", + "height":"100%", + + + "title":"SDLC迭代表", + + + + + "toolbar":{ + "tools": [ + { + "selected_row": true, + "name": "sd_bugs", + "icon": "{{entire_url('/imgs/sd_bugs.svg')}}", + "label": "Bug列表" + }, + { + "selected_row": true, + "name": "sd_test_plans", + "icon": "{{entire_url('/imgs/sd_test_plans.svg')}}", + "label": "测试方案" + } + ] +}, + + "css":"card", + + + "editable":{ + + "new_data_url":"{{entire_url('add_sd_iterations.dspy')}}", + + + "delete_data_url":"{{entire_url('delete_sd_iterations.dspy')}}", + + + "update_data_url":"{{entire_url('update_sd_iterations.dspy')}}" + + }, + + + "data_url":"{{entire_url('./get_sd_iterations.dspy')}}", + + "data_method":"GET", + "data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}}, + "row_options":{ + + + + "browserfields": { + "exclouded": [ + "scope" + ], + "alters": { + "project_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_project_options.dspy')}}", + "valueField": "project_id", + "textField": "project_id_text" + }, + "iteration_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_iteration_type.dspy')}}", + "valueField": "iteration_type", + "textField": "iteration_type_text" + }, + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_iteration_status.dspy')}}", + "valueField": "status", + "textField": "status_text" + } + } +}, + + + "editexclouded":[ + "id", + "task_id", + "started_at", + "completed_at", + "created_at", + "updated_at" +], + + "fields":[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "主键ID" + }, + { + "name": "project_id", + "title": "项目ID", + "type": "str", + "length": 32, + "nullable": "no", + "label": "项目ID", + "uitype": "code", + "valueField": "project_id", + "textField": "project_id_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "sd_projects", + "tblvalue": "id", + "tbltext": "name", + "valueField": "project_id", + "textField": "project_id_text" + }, + "dataurl": "{{entire_url('../api/get_project_options.dspy')}}" + }, + { + "name": "iteration_name", + "title": "迭代名称", + "type": "str", + "length": 200, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "迭代名称" + }, + { + "name": "iteration_type", + "title": "迭代类型", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'new_feature'", + "label": "迭代类型", + "uitype": "code", + "valueField": "iteration_type", + "textField": "iteration_type_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "iteration_type", + "textField": "iteration_type_text", + "cond": "parentid='sd_iteration_type'" + }, + "dataurl": "{{entire_url('../api/get_search_iteration_type.dspy')}}" + }, + { + "name": "scope", + "title": "迭代范围(JSON)", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "迭代范围(JSON)" + }, + { + "name": "task_id", + "title": "关联Pipeline任务ID", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "关联Pipeline任务ID" + }, + { + "name": "status", + "title": "迭代状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'planning'", + "label": "迭代状态", + "uitype": "code", + "valueField": "status", + "textField": "status_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "status", + "textField": "status_text", + "cond": "parentid='sd_iteration_status'" + }, + "dataurl": "{{entire_url('../api/get_search_iteration_status.dspy')}}" + }, + { + "name": "priority", + "title": "优先级", + "type": "int", + "nullable": "no", + "default": "5", + "length": 0, + "uitype": "int", + "datatype": "int", + "label": "优先级" + }, + { + "name": "started_at", + "title": "开始时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "开始时间" + }, + { + "name": "completed_at", + "title": "完成时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "完成时间" + }, + { + "name": "created_by", + "title": "创建人", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "创建人" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "创建时间" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "更新时间" + } +] + }, + + + + "data_filter":{ + "AND": [ + { + "field": "project_id", + "op": "=", + "var": "project_input" + }, + { + "field": "iteration_name", + "op": "LIKE", + "var": "name_input" + }, + { + "field": "status", + "op": "=", + "var": "status_input" + } + ] +}, + + + + + + "page_rows":160, + "cache_limit":5 + } + + ,"binds":[ + { + "wid": "self", + "event": "sd_bugs", + "actiontype": "urlwidget", + "target": "PopupWindow", + "popup_options": { + "title": "Bug列表", + "icon": "{{entire_url('/appbase/get_icon.dspy')}}?id=sd_bugs", + "resizable": true, + "height": "70%", + "width": "70%" + }, + "params_mapping": { + "mapping": { + "id": "iteration_id", + "referer_widget": "referer_widget" + }, + "need_other": false + }, + "options": { + "method": "POST", + "params": {}, + "url": "{{entire_url('../sd_bug_list?iteration_id=${id}')}}" + } + }, + { + "wid": "self", + "event": "sd_test_plans", + "actiontype": "urlwidget", + "target": "PopupWindow", + "popup_options": { + "title": "测试方案", + "icon": "{{entire_url('/appbase/get_icon.dspy')}}?id=sd_test_plans", + "resizable": true, + "height": "70%", + "width": "70%" + }, + "params_mapping": { + "mapping": { + "id": "iteration_id", + "referer_widget": "referer_widget" + }, + "need_other": false + }, + "options": { + "method": "POST", + "params": {}, + "url": "{{entire_url('../sd_test_plan_list?iteration_id=${id}')}}" + } + } +] + +}] +} \ No newline at end of file diff --git a/wwwroot/sd_iterations/update_sd_iterations.dspy b/wwwroot/sd_iterations/update_sd_iterations.dspy new file mode 100644 index 0000000..03d0b99 --- /dev/null +++ b/wwwroot/sd_iterations/update_sd_iterations.dspy @@ -0,0 +1,36 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None + + + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + + r = await sor.U('sd_iterations', ns) + debug('update success'); + return { + "widgettype":"Message", + "options":{ + "title":"Update Success", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Update Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_projects/add_sd_projects.dspy b/wwwroot/sd_projects/add_sd_projects.dspy new file mode 100644 index 0000000..d9f3e85 --- /dev/null +++ b/wwwroot/sd_projects/add_sd_projects.dspy @@ -0,0 +1,51 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None +id = params_kw.id +if not id or len(id) > 32: + id = uuid() +ns['id'] = id + + + +userorgid = await get_userorgid() +if not userorgid: + return { + "widgettype":"Error", + "options":{ + "title":"Authorization Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"Please login" + } + } +ns['org_id'] = userorgid + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.C('sd_projects', ns.copy()) + return { + "widgettype":"Message", + "options":{ + "cwidth":16, + "cheight":9, + "title":"Add Success", + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Add Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_projects/delete_sd_projects.dspy b/wwwroot/sd_projects/delete_sd_projects.dspy new file mode 100644 index 0000000..30cc1bd --- /dev/null +++ b/wwwroot/sd_projects/delete_sd_projects.dspy @@ -0,0 +1,47 @@ + +ns = { + 'id':params_kw['id'], +} + + +userorgid = await get_userorgid() +if not userorgid: + return { + "widgettype":"Error", + "options":{ + "title":"Authorization Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"Please login" + } + } +ns['org_id'] = userorgid + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.D('sd_projects', ns) + debug('delete success'); + return { + "widgettype":"Message", + "options":{ + "title":"Delete Success", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"ok" + } + } + +debug('Delete failed'); +return { + "widgettype":"Error", + "options":{ + "title":"Delete Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_projects/get_sd_projects.dspy b/wwwroot/sd_projects/get_sd_projects.dspy new file mode 100644 index 0000000..940f85f --- /dev/null +++ b/wwwroot/sd_projects/get_sd_projects.dspy @@ -0,0 +1,161 @@ + +ns = params_kw.copy() + + +userorgid = await get_userorgid() +if not userorgid: + return { + "widgettype":"Error", + "options":{ + "title":"Authorization Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"Please login" + } + } +ns['org_id'] = userorgid +ns['userorgid'] = userorgid + +debug(f'get_sd_projects.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + + + ns['sort'] = ["created_at desc"] + + + +sql = '''select a.*, b.status_text, c.project_type_text +from (select * from sd_projects where 1=1 [[filterstr]]) a left join (select k as status, + v as status_text from appcodes_kv where parentid='sd_project_status') b on a.status = b.status left join (select k as project_type, + v as project_type_text from appcodes_kv where parentid='sd_project_type') c on a.project_type = c.project_type''' + +filterjson = params_kw.get('data_filter') +if filterjson and isinstance(filterjson, str): + try: + filterjson = json.loads(filterjson) + except (json.JSONDecodeError, TypeError): + filterjson = None +fields_str=r'''[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "name", + "title": "项目名称", + "type": "str", + "length": 200, + "nullable": "no" + }, + { + "name": "description", + "title": "项目描述", + "type": "text" + }, + { + "name": "project_type", + "title": "项目类型", + "type": "str", + "length": 50, + "nullable": "no", + "default": "'web_app'" + }, + { + "name": "tech_stack", + "title": "技术栈配置(JSON)", + "type": "text" + }, + { + "name": "repo_url", + "title": "代码仓库地址", + "type": "str", + "length": 500 + }, + { + "name": "pipeline_id", + "title": "关联Pipeline定义ID", + "type": "str", + "length": 32 + }, + { + "name": "status", + "title": "项目状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'draft'" + }, + { + "name": "org_id", + "title": "组织ID", + "type": "str", + "length": 32, + "nullable": "no", + "default": "'0'" + }, + { + "name": "created_by", + "title": "创建人", + "type": "str", + "length": 32 + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp" + } +]''' +ori_fields = json.loads(fields_str) +if not filterjson: + fields = [ f['name'] for f in ori_fields ] + filterjson = default_filterjson(fields, ns) + +# 确保 logined 过滤条件始终生效 +if filterjson: + if not isinstance(filterjson, dict) or 'AND' not in filterjson: + filterjson = {'AND': [filterjson] if filterjson else []} + + filterjson['AND'].append({'field': 'org_id', 'op': '=', 'var': '__logined_orgid__'}) + ns['__logined_orgid__'] = userorgid + + + +filterdic = ns.copy() +filterdic['filterstr'] = '' +filterdic['userorgid'] = '${userorgid}$' +filterdic['userid'] = '${userid}$' +if filterjson: + dbf = DBFilter(filterjson) + conds = dbf.gen(ns) + if conds: + ns.update(dbf.consts) + conds = f' and {conds}' + filterdic['filterstr'] = conds +ac = ArgsConvert('[[', ']]') +vars = ac.findAllVariables(sql) +NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' } +filterdic.update(NameSpace) +sql = ac.convert(sql, filterdic) + +debug(f'{sql=}') +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlPaging(sql, ns) + return r +return { + "total":0, + "rows":[] +} \ No newline at end of file diff --git a/wwwroot/sd_projects/index.ui b/wwwroot/sd_projects/index.ui new file mode 100644 index 0000000..1b039f2 --- /dev/null +++ b/wwwroot/sd_projects/index.ui @@ -0,0 +1,289 @@ + +{ + "widgettype":"VBox", + "options":{"cheight":40,"width":"100%"}, + "subwidgets":[{ + "id":"sd_projects_tbl", + "widgettype":"Tabular", + "options":{ + "width":"100%", + "height":"100%", + + + "title":"SDLC项目表", + + + + + "toolbar":{ + "tools": [ + { + "selected_row": true, + "name": "sd_iterations", + "icon": "{{entire_url('/imgs/sd_iterations.svg')}}", + "label": "迭代列表" + } + ] +}, + + "css":"card", + + + "editable":{ + + "new_data_url":"{{entire_url('add_sd_projects.dspy')}}", + + + "delete_data_url":"{{entire_url('delete_sd_projects.dspy')}}", + + + "update_data_url":"{{entire_url('update_sd_projects.dspy')}}" + + }, + + + "data_url":"{{entire_url('./get_sd_projects.dspy')}}", + + "data_method":"GET", + "data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}}, + "row_options":{ + + + + "browserfields": { + "exclouded": [ + "tech_stack" + ], + "alters": { + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_status.dspy')}}", + "valueField": "status", + "textField": "status_text" + }, + "project_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_project_type.dspy')}}", + "valueField": "project_type", + "textField": "project_type_text" + } + } +}, + + + "editexclouded":[ + "id", + "created_at", + "updated_at", + "org_id" +], + + "fields":[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "主键ID" + }, + { + "name": "name", + "title": "项目名称", + "type": "str", + "length": 200, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "项目名称" + }, + { + "name": "description", + "title": "项目描述", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "项目描述" + }, + { + "name": "project_type", + "title": "项目类型", + "type": "str", + "length": 50, + "nullable": "no", + "default": "'web_app'", + "label": "项目类型", + "uitype": "code", + "valueField": "project_type", + "textField": "project_type_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "project_type", + "textField": "project_type_text", + "cond": "parentid='sd_project_type'" + }, + "dataurl": "{{entire_url('../api/get_search_project_type.dspy')}}" + }, + { + "name": "tech_stack", + "title": "技术栈配置(JSON)", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "技术栈配置(JSON)" + }, + { + "name": "repo_url", + "title": "代码仓库地址", + "type": "str", + "length": 500, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "代码仓库地址" + }, + { + "name": "pipeline_id", + "title": "关联Pipeline定义ID", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "关联Pipeline定义ID" + }, + { + "name": "status", + "title": "项目状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'draft'", + "label": "项目状态", + "uitype": "code", + "valueField": "status", + "textField": "status_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "status", + "textField": "status_text", + "cond": "parentid='sd_project_status'" + }, + "dataurl": "{{entire_url('../api/get_search_status.dspy')}}" + }, + { + "name": "org_id", + "title": "组织ID", + "type": "str", + "length": 32, + "nullable": "no", + "default": "'0'", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "组织ID" + }, + { + "name": "created_by", + "title": "创建人", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "创建人" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "创建时间" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "更新时间" + } +] + }, + + + + "data_filter":{ + "AND": [ + { + "field": "name", + "op": "LIKE", + "var": "name_input" + }, + { + "field": "status", + "op": "=", + "var": "status_input" + }, + { + "field": "project_type", + "op": "=", + "var": "type_input" + } + ] +}, + + + + + + "page_rows":160, + "cache_limit":5 + } + + ,"binds":[ + { + "wid": "self", + "event": "sd_iterations", + "actiontype": "urlwidget", + "target": "PopupWindow", + "popup_options": { + "title": "迭代列表", + "icon": "{{entire_url('/appbase/get_icon.dspy')}}?id=sd_iterations", + "resizable": true, + "height": "70%", + "width": "70%" + }, + "params_mapping": { + "mapping": { + "id": "project_id", + "referer_widget": "referer_widget" + }, + "need_other": false + }, + "options": { + "method": "POST", + "params": {}, + "url": "{{entire_url('../sd_iteration_list?project_id=${id}')}}" + } + } +] + +}] +} \ No newline at end of file diff --git a/wwwroot/sd_projects/update_sd_projects.dspy b/wwwroot/sd_projects/update_sd_projects.dspy new file mode 100644 index 0000000..54f1c2f --- /dev/null +++ b/wwwroot/sd_projects/update_sd_projects.dspy @@ -0,0 +1,70 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None + + +userorgid = await get_userorgid() +if not userorgid: + return { + "widgettype":"Error", + "options":{ + "title":"Authorization Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"Please login" + } + } +ns['org_id'] = userorgid + + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + + ns1 = { + + "org_id": userorgid, + + + "id": params_kw.id + } + recs = await sor.R('sd_projects', ns1) + if len(recs) < 1: + return { + "widgettype":"Error", + "options":{ + "title":"Update Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"Record no exist or with wrong ownership" + } + } + + r = await sor.U('sd_projects', ns) + debug('update success'); + return { + "widgettype":"Message", + "options":{ + "title":"Update Success", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Update Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_test_case_list/index.ui b/wwwroot/sd_test_case_list/index.ui new file mode 100644 index 0000000..7fff95d --- /dev/null +++ b/wwwroot/sd_test_case_list/index.ui @@ -0,0 +1,14 @@ +{ + "widgettype": "VBox", + "options": {"width": "100%", "height": "100%", "padding": "20px", "gap": "16px"}, + "subwidgets": [ + { + "widgettype": "Text", + "options": {"text": "sd_test_case_list - CRUD List", "cfontsize": 20, "color": "#E0E0E0", "fontWeight": "bold"} + }, + { + "widgettype": "Text", + "options": {"text": "Generated by xls2crud - This page will be auto-generated from the sd_project_list table definition", "cfontsize": 14, "color": "#888888"} + } + ] +} diff --git a/wwwroot/sd_test_cases/add_sd_test_cases.dspy b/wwwroot/sd_test_cases/add_sd_test_cases.dspy new file mode 100644 index 0000000..0899c4d --- /dev/null +++ b/wwwroot/sd_test_cases/add_sd_test_cases.dspy @@ -0,0 +1,37 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None +id = params_kw.id +if not id or len(id) > 32: + id = uuid() +ns['id'] = id + + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.C('sd_test_cases', ns.copy()) + return { + "widgettype":"Message", + "options":{ + "cwidth":16, + "cheight":9, + "title":"Add Success", + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Add Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_test_cases/delete_sd_test_cases.dspy b/wwwroot/sd_test_cases/delete_sd_test_cases.dspy new file mode 100644 index 0000000..2f2db37 --- /dev/null +++ b/wwwroot/sd_test_cases/delete_sd_test_cases.dspy @@ -0,0 +1,33 @@ + +ns = { + 'id':params_kw['id'], +} + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.D('sd_test_cases', ns) + debug('delete success'); + return { + "widgettype":"Message", + "options":{ + "title":"Delete Success", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"ok" + } + } + +debug('Delete failed'); +return { + "widgettype":"Error", + "options":{ + "title":"Delete Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_test_cases/get_sd_test_cases.dspy b/wwwroot/sd_test_cases/get_sd_test_cases.dspy new file mode 100644 index 0000000..ee3d5c7 --- /dev/null +++ b/wwwroot/sd_test_cases/get_sd_test_cases.dspy @@ -0,0 +1,147 @@ + +ns = params_kw.copy() + + +debug(f'get_sd_test_cases.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + + + ns['sort'] = ["priority desc","created_at desc"] + + + +sql = '''select a.*, b.plan_id_text, c.case_type_text, d.status_text, e.priority_text +from (select * from sd_test_cases where 1=1 [[filterstr]]) a left join (select id as plan_id, + plan_name as plan_id_text from sd_test_plans where 1 = 1) b on a.plan_id = b.plan_id left join (select k as case_type, + v as case_type_text from appcodes_kv where parentid='sd_test_case_type') c on a.case_type = c.case_type left join (select k as status, + v as status_text from appcodes_kv where parentid='sd_test_case_status') d on a.status = d.status left join (select k as priority, + v as priority_text from appcodes_kv where parentid='sd_priority') e on a.priority = e.priority''' + +filterjson = params_kw.get('data_filter') +if filterjson and isinstance(filterjson, str): + try: + filterjson = json.loads(filterjson) + except (json.JSONDecodeError, TypeError): + filterjson = None +fields_str=r'''[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "plan_id", + "title": "方案ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "case_name", + "title": "用例名称", + "type": "str", + "length": 200, + "nullable": "no" + }, + { + "name": "case_type", + "title": "用例类型", + "type": "str", + "length": 20, + "nullable": "no" + }, + { + "name": "priority", + "title": "优先级", + "type": "str", + "length": 10, + "nullable": "no", + "default": "'P2'" + }, + { + "name": "precondition", + "title": "前置条件", + "type": "text" + }, + { + "name": "steps", + "title": "测试步骤(JSON数组)", + "type": "text" + }, + { + "name": "expected_result", + "title": "预期结果", + "type": "text" + }, + { + "name": "actual_result", + "title": "实际结果", + "type": "text" + }, + { + "name": "status", + "title": "用例状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'pending'" + }, + { + "name": "executed_by", + "title": "执行人", + "type": "str", + "length": 32 + }, + { + "name": "executed_at", + "title": "执行时间", + "type": "timestamp" + }, + { + "name": "duration_ms", + "title": "执行耗时(毫秒)", + "type": "int" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no" + } +]''' +ori_fields = json.loads(fields_str) +if not filterjson: + fields = [ f['name'] for f in ori_fields ] + filterjson = default_filterjson(fields, ns) + +filterdic = ns.copy() +filterdic['filterstr'] = '' +filterdic['userorgid'] = '${userorgid}$' +filterdic['userid'] = '${userid}$' +if filterjson: + dbf = DBFilter(filterjson) + conds = dbf.gen(ns) + if conds: + ns.update(dbf.consts) + conds = f' and {conds}' + filterdic['filterstr'] = conds +ac = ArgsConvert('[[', ']]') +vars = ac.findAllVariables(sql) +NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' } +filterdic.update(NameSpace) +sql = ac.convert(sql, filterdic) + +debug(f'{sql=}') +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlPaging(sql, ns) + return r +return { + "total":0, + "rows":[] +} \ No newline at end of file diff --git a/wwwroot/sd_test_cases/index.ui b/wwwroot/sd_test_cases/index.ui new file mode 100644 index 0000000..118e8aa --- /dev/null +++ b/wwwroot/sd_test_cases/index.ui @@ -0,0 +1,304 @@ + +{ + "widgettype":"VBox", + "options":{"cheight":40,"width":"100%"}, + "subwidgets":[{ + "id":"sd_test_cases_tbl", + "widgettype":"Tabular", + "options":{ + "width":"100%", + "height":"100%", + + + "title":"测试用例表", + + + + + "css":"card", + + + "editable":{ + + "new_data_url":"{{entire_url('add_sd_test_cases.dspy')}}", + + + "delete_data_url":"{{entire_url('delete_sd_test_cases.dspy')}}", + + + "update_data_url":"{{entire_url('update_sd_test_cases.dspy')}}" + + }, + + + "data_url":"{{entire_url('./get_sd_test_cases.dspy')}}", + + "data_method":"GET", + "data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}}, + "row_options":{ + + + + "browserfields": { + "alters": { + "plan_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_test_plan_options.dspy')}}", + "valueField": "plan_id", + "textField": "plan_id_text" + }, + "case_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_case_type.dspy')}}", + "valueField": "case_type", + "textField": "case_type_text" + }, + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_case_status.dspy')}}", + "valueField": "status", + "textField": "status_text" + }, + "priority": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}", + "valueField": "priority", + "textField": "priority_text" + } + } +}, + + + "editexclouded":[ + "id", + "actual_result", + "executed_by", + "executed_at", + "duration_ms", + "created_at" +], + + "fields":[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "主键ID" + }, + { + "name": "plan_id", + "title": "方案ID", + "type": "str", + "length": 32, + "nullable": "no", + "label": "方案ID", + "uitype": "code", + "valueField": "plan_id", + "textField": "plan_id_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "sd_test_plans", + "tblvalue": "id", + "tbltext": "plan_name", + "valueField": "plan_id", + "textField": "plan_id_text" + }, + "dataurl": "{{entire_url('../api/get_test_plan_options.dspy')}}" + }, + { + "name": "case_name", + "title": "用例名称", + "type": "str", + "length": 200, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "用例名称" + }, + { + "name": "case_type", + "title": "用例类型", + "type": "str", + "length": 20, + "nullable": "no", + "label": "用例类型", + "uitype": "code", + "valueField": "case_type", + "textField": "case_type_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "case_type", + "textField": "case_type_text", + "cond": "parentid='sd_test_case_type'" + }, + "dataurl": "{{entire_url('../api/get_search_case_type.dspy')}}" + }, + { + "name": "priority", + "title": "优先级", + "type": "str", + "length": 10, + "nullable": "no", + "default": "'P2'", + "label": "优先级", + "uitype": "code", + "valueField": "priority", + "textField": "priority_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "priority", + "textField": "priority_text", + "cond": "parentid='sd_priority'" + }, + "dataurl": "{{entire_url('../api/get_search_priority.dspy')}}" + }, + { + "name": "precondition", + "title": "前置条件", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "前置条件" + }, + { + "name": "steps", + "title": "测试步骤(JSON数组)", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "测试步骤(JSON数组)" + }, + { + "name": "expected_result", + "title": "预期结果", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "预期结果" + }, + { + "name": "actual_result", + "title": "实际结果", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "实际结果" + }, + { + "name": "status", + "title": "用例状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'pending'", + "label": "用例状态", + "uitype": "code", + "valueField": "status", + "textField": "status_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "status", + "textField": "status_text", + "cond": "parentid='sd_test_case_status'" + }, + "dataurl": "{{entire_url('../api/get_search_case_status.dspy')}}" + }, + { + "name": "executed_by", + "title": "执行人", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "执行人" + }, + { + "name": "executed_at", + "title": "执行时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "执行时间" + }, + { + "name": "duration_ms", + "title": "执行耗时(毫秒)", + "type": "int", + "length": 0, + "uitype": "int", + "datatype": "int", + "label": "执行耗时(毫秒)" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "创建时间" + } +] + }, + + + + "data_filter":{ + "AND": [ + { + "field": "plan_id", + "op": "=", + "var": "plan_input" + }, + { + "field": "case_type", + "op": "=", + "var": "type_input" + }, + { + "field": "status", + "op": "=", + "var": "status_input" + }, + { + "field": "priority", + "op": "=", + "var": "priority_input" + } + ] +}, + + + + + + "page_rows":160, + "cache_limit":5 + } + + ,"binds":[] + +}] +} \ No newline at end of file diff --git a/wwwroot/sd_test_cases/update_sd_test_cases.dspy b/wwwroot/sd_test_cases/update_sd_test_cases.dspy new file mode 100644 index 0000000..4fb5433 --- /dev/null +++ b/wwwroot/sd_test_cases/update_sd_test_cases.dspy @@ -0,0 +1,36 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None + + + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + + r = await sor.U('sd_test_cases', ns) + debug('update success'); + return { + "widgettype":"Message", + "options":{ + "title":"Update Success", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Update Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_test_plan_list/index.ui b/wwwroot/sd_test_plan_list/index.ui new file mode 100644 index 0000000..6ee3189 --- /dev/null +++ b/wwwroot/sd_test_plan_list/index.ui @@ -0,0 +1,14 @@ +{ + "widgettype": "VBox", + "options": {"width": "100%", "height": "100%", "padding": "20px", "gap": "16px"}, + "subwidgets": [ + { + "widgettype": "Text", + "options": {"text": "sd_test_plan_list - CRUD List", "cfontsize": 20, "color": "#E0E0E0", "fontWeight": "bold"} + }, + { + "widgettype": "Text", + "options": {"text": "Generated by xls2crud - This page will be auto-generated from the sd_project_list table definition", "cfontsize": 14, "color": "#888888"} + } + ] +} diff --git a/wwwroot/sd_test_plans/add_sd_test_plans.dspy b/wwwroot/sd_test_plans/add_sd_test_plans.dspy new file mode 100644 index 0000000..ff4fd7d --- /dev/null +++ b/wwwroot/sd_test_plans/add_sd_test_plans.dspy @@ -0,0 +1,37 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None +id = params_kw.id +if not id or len(id) > 32: + id = uuid() +ns['id'] = id + + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.C('sd_test_plans', ns.copy()) + return { + "widgettype":"Message", + "options":{ + "cwidth":16, + "cheight":9, + "title":"Add Success", + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Add Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_test_plans/delete_sd_test_plans.dspy b/wwwroot/sd_test_plans/delete_sd_test_plans.dspy new file mode 100644 index 0000000..6a7b11b --- /dev/null +++ b/wwwroot/sd_test_plans/delete_sd_test_plans.dspy @@ -0,0 +1,33 @@ + +ns = { + 'id':params_kw['id'], +} + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.D('sd_test_plans', ns) + debug('delete success'); + return { + "widgettype":"Message", + "options":{ + "title":"Delete Success", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"ok" + } + } + +debug('Delete failed'); +return { + "widgettype":"Error", + "options":{ + "title":"Delete Error", + "timeout":3, + "cwidth":16, + "cheight":9, + "message":"failed" + } +} \ No newline at end of file diff --git a/wwwroot/sd_test_plans/get_sd_test_plans.dspy b/wwwroot/sd_test_plans/get_sd_test_plans.dspy new file mode 100644 index 0000000..a09d0ad --- /dev/null +++ b/wwwroot/sd_test_plans/get_sd_test_plans.dspy @@ -0,0 +1,133 @@ + +ns = params_kw.copy() + + +debug(f'get_sd_test_plans.dspy:{ns=}') +if not ns.get('page'): + ns['page'] = 1 +if not ns.get('sort'): + + + ns['sort'] = ["created_at desc"] + + + +sql = '''select a.*, b.iteration_id_text, c.plan_type_text, d.status_text +from (select * from sd_test_plans where 1=1 [[filterstr]]) a left join (select id as iteration_id, + iteration_name as iteration_id_text from sd_iterations where 1 = 1) b on a.iteration_id = b.iteration_id left join (select k as plan_type, + v as plan_type_text from appcodes_kv where parentid='sd_test_plan_type') c on a.plan_type = c.plan_type left join (select k as status, + v as status_text from appcodes_kv where parentid='sd_test_plan_status') d on a.status = d.status''' + +filterjson = params_kw.get('data_filter') +if filterjson and isinstance(filterjson, str): + try: + filterjson = json.loads(filterjson) + except (json.JSONDecodeError, TypeError): + filterjson = None +fields_str=r'''[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "iteration_id", + "title": "迭代ID", + "type": "str", + "length": 32, + "nullable": "no" + }, + { + "name": "plan_name", + "title": "方案名称", + "type": "str", + "length": 200, + "nullable": "no" + }, + { + "name": "plan_type", + "title": "方案类型", + "type": "str", + "length": 20, + "nullable": "no" + }, + { + "name": "scope", + "title": "测试范围", + "type": "text" + }, + { + "name": "environment", + "title": "测试环境要求(JSON)", + "type": "text" + }, + { + "name": "entry_criteria", + "title": "准入条件", + "type": "text" + }, + { + "name": "exit_criteria", + "title": "准出条件", + "type": "text" + }, + { + "name": "status", + "title": "方案状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'draft'" + }, + { + "name": "created_by", + "title": "创建人", + "type": "str", + "length": 32 + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp" + } +]''' +ori_fields = json.loads(fields_str) +if not filterjson: + fields = [ f['name'] for f in ori_fields ] + filterjson = default_filterjson(fields, ns) + +filterdic = ns.copy() +filterdic['filterstr'] = '' +filterdic['userorgid'] = '${userorgid}$' +filterdic['userid'] = '${userid}$' +if filterjson: + dbf = DBFilter(filterjson) + conds = dbf.gen(ns) + if conds: + ns.update(dbf.consts) + conds = f' and {conds}' + filterdic['filterstr'] = conds +ac = ArgsConvert('[[', ']]') +vars = ac.findAllVariables(sql) +NameSpace = {v:'${' + v + '}$' for v in vars if v != 'filterstr' } +filterdic.update(NameSpace) +sql = ac.convert(sql, filterdic) + +debug(f'{sql=}') +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + r = await sor.sqlPaging(sql, ns) + return r +return { + "total":0, + "rows":[] +} \ No newline at end of file diff --git a/wwwroot/sd_test_plans/index.ui b/wwwroot/sd_test_plans/index.ui new file mode 100644 index 0000000..e47778d --- /dev/null +++ b/wwwroot/sd_test_plans/index.ui @@ -0,0 +1,276 @@ + +{ + "widgettype":"VBox", + "options":{"cheight":40,"width":"100%"}, + "subwidgets":[{ + "id":"sd_test_plans_tbl", + "widgettype":"Tabular", + "options":{ + "width":"100%", + "height":"100%", + + + "title":"测试方案表", + + + + + "toolbar":{ + "tools": [ + { + "selected_row": true, + "name": "sd_test_cases", + "icon": "{{entire_url('/imgs/sd_test_cases.svg')}}", + "label": "测试用例" + } + ] +}, + + "css":"card", + + + "editable":{ + + "new_data_url":"{{entire_url('add_sd_test_plans.dspy')}}", + + + "delete_data_url":"{{entire_url('delete_sd_test_plans.dspy')}}", + + + "update_data_url":"{{entire_url('update_sd_test_plans.dspy')}}" + + }, + + + "data_url":"{{entire_url('./get_sd_test_plans.dspy')}}", + + "data_method":"GET", + "data_params":{{json.dumps(params_kw, indent=4, ensure_ascii=False)}}, + "row_options":{ + + + + "browserfields": { + "alters": { + "iteration_id": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}", + "valueField": "iteration_id", + "textField": "iteration_id_text" + }, + "plan_type": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_plan_type.dspy')}}", + "valueField": "plan_type", + "textField": "plan_type_text" + }, + "status": { + "uitype": "code", + "dataurl": "{{entire_url('../api/get_search_plan_status.dspy')}}", + "valueField": "status", + "textField": "status_text" + } + } +}, + + + "editexclouded":[ + "id", + "created_at", + "updated_at" +], + + "fields":[ + { + "name": "id", + "title": "主键ID", + "type": "str", + "length": 32, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "主键ID" + }, + { + "name": "iteration_id", + "title": "迭代ID", + "type": "str", + "length": 32, + "nullable": "no", + "label": "迭代ID", + "uitype": "code", + "valueField": "iteration_id", + "textField": "iteration_id_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "sd_iterations", + "tblvalue": "id", + "tbltext": "iteration_name", + "valueField": "iteration_id", + "textField": "iteration_id_text" + }, + "dataurl": "{{entire_url('../api/get_iteration_options.dspy')}}" + }, + { + "name": "plan_name", + "title": "方案名称", + "type": "str", + "length": 200, + "nullable": "no", + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "方案名称" + }, + { + "name": "plan_type", + "title": "方案类型", + "type": "str", + "length": 20, + "nullable": "no", + "label": "方案类型", + "uitype": "code", + "valueField": "plan_type", + "textField": "plan_type_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "plan_type", + "textField": "plan_type_text", + "cond": "parentid='sd_test_plan_type'" + }, + "dataurl": "{{entire_url('../api/get_search_plan_type.dspy')}}" + }, + { + "name": "scope", + "title": "测试范围", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "测试范围" + }, + { + "name": "environment", + "title": "测试环境要求(JSON)", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "测试环境要求(JSON)" + }, + { + "name": "entry_criteria", + "title": "准入条件", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "准入条件" + }, + { + "name": "exit_criteria", + "title": "准出条件", + "type": "text", + "length": 0, + "uitype": "text", + "datatype": "text", + "label": "准出条件" + }, + { + "name": "status", + "title": "方案状态", + "type": "str", + "length": 20, + "nullable": "no", + "default": "'draft'", + "label": "方案状态", + "uitype": "code", + "valueField": "status", + "textField": "status_text", + "params": { + "dbname": "{{get_module_dbname('pipeline_sdlc')}}", + "table": "appcodes_kv", + "tblvalue": "k", + "tbltext": "v", + "valueField": "status", + "textField": "status_text", + "cond": "parentid='sd_test_plan_status'" + }, + "dataurl": "{{entire_url('../api/get_search_plan_status.dspy')}}" + }, + { + "name": "created_by", + "title": "创建人", + "type": "str", + "length": 32, + "cwidth": 18, + "uitype": "str", + "datatype": "str", + "label": "创建人" + }, + { + "name": "created_at", + "title": "创建时间", + "type": "timestamp", + "nullable": "no", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "创建时间" + }, + { + "name": "updated_at", + "title": "更新时间", + "type": "timestamp", + "length": 0, + "uitype": "str", + "datatype": "timestamp", + "label": "更新时间" + } +] + }, + + + + + + + + "page_rows":160, + "cache_limit":5 + } + + ,"binds":[ + { + "wid": "self", + "event": "sd_test_cases", + "actiontype": "urlwidget", + "target": "PopupWindow", + "popup_options": { + "title": "测试用例", + "icon": "{{entire_url('/appbase/get_icon.dspy')}}?id=sd_test_cases", + "resizable": true, + "height": "70%", + "width": "70%" + }, + "params_mapping": { + "mapping": { + "id": "plan_id", + "referer_widget": "referer_widget" + }, + "need_other": false + }, + "options": { + "method": "POST", + "params": {}, + "url": "{{entire_url('../sd_test_case_list?plan_id=${id}')}}" + } + } +] + +}] +} \ No newline at end of file diff --git a/wwwroot/sd_test_plans/update_sd_test_plans.dspy b/wwwroot/sd_test_plans/update_sd_test_plans.dspy new file mode 100644 index 0000000..279e020 --- /dev/null +++ b/wwwroot/sd_test_plans/update_sd_test_plans.dspy @@ -0,0 +1,36 @@ + +ns = params_kw.copy() +for k,v in ns.items(): + if v == 'NaN' or v == 'null': + ns[k] = None + + + + +db = DBPools() +dbname = get_module_dbname('pipeline_sdlc') +async with db.sqlorContext(dbname) as sor: + + r = await sor.U('sd_test_plans', ns) + debug('update success'); + return { + "widgettype":"Message", + "options":{ + "title":"Update Success", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"ok" + } + } + +return { + "widgettype":"Error", + "options":{ + "title":"Update Error", + "cwidth":16, + "cheight":9, + "timeout":3, + "message":"failed" + } +} \ No newline at end of file