pilot.linkstec 0.0.32__py3-none-any.whl → 0.0.90__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. pilot/base/__init__.py +0 -0
  2. pilot/base/ai_call.py +38 -0
  3. pilot/base/ai_info.py +20 -0
  4. pilot/base/chage_file_tag_base.py +73 -0
  5. pilot/base/db_operation_base.py +536 -0
  6. pilot/base/delete_commnents_base.py +306 -0
  7. pilot/base/file_operation.py +44 -0
  8. pilot/base/get_file_encoding.py +14 -0
  9. pilot/base/make_parsing_java_file_order_base.py +154 -0
  10. pilot/base/split_file_base.py +256 -0
  11. pilot/create_python/__init__.py +0 -0
  12. pilot/create_python/config/__init__.py +0 -0
  13. pilot/create_python/create_python.py +150 -0
  14. pilot/create_python/sample/__init__.py +0 -0
  15. pilot/create_python/sample/child_sample/__init__.py +0 -0
  16. pilot/create_python/sample/child_sample/job/__init__.py +0 -0
  17. pilot/create_python/sample/config/__init__.py +0 -0
  18. pilot/db/__init__.py +0 -0
  19. pilot/db/create_table.py +34 -0
  20. pilot/db/db_connect.py +49 -0
  21. pilot/db/db_main.py +293 -0
  22. pilot/db/db_util.py +508 -0
  23. pilot/db/ddl/__init__.py +18 -0
  24. pilot/db/dml/__init__.py +18 -0
  25. pilot/db/sql_executor.py +62 -0
  26. pilot/db/sql_loader.py +233 -0
  27. pilot/db/sql_service.py +55 -0
  28. pilot/file_tool/__init__.py +0 -0
  29. pilot/file_tool/create_prompt_file.py +75 -0
  30. pilot/file_tool/json_file_tool.py +103 -0
  31. pilot/prompt/__init__.py +0 -0
  32. {pilot_linkstec-0.0.32.dist-info → pilot_linkstec-0.0.90.dist-info}/METADATA +1 -1
  33. pilot_linkstec-0.0.90.dist-info/RECORD +66 -0
  34. pilot_linkstec-0.0.32.dist-info/RECORD +0 -35
  35. {pilot_linkstec-0.0.32.dist-info → pilot_linkstec-0.0.90.dist-info}/WHEEL +0 -0
  36. {pilot_linkstec-0.0.32.dist-info → pilot_linkstec-0.0.90.dist-info}/licenses/LICENSE +0 -0
  37. {pilot_linkstec-0.0.32.dist-info → pilot_linkstec-0.0.90.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,256 @@
1
+ import copy
2
+ import json
3
+ import re
4
+ from pathlib import Path
5
+ from typing import List, Dict, Tuple
6
+
7
+ from lxml import etree as ET
8
+
9
+ from pilot.job.impl.base_job import BaseJob
10
+
11
+ from base.file_operation import read_file_lines, write_json_file
12
+
13
+
14
+ class SplitFile(BaseJob):
15
+
16
+ @staticmethod
17
+ def split_java_methods(java_code: str):
18
+ """
19
+ 与えられた単一の完全な Java ソースコードから
20
+ 1. 各メソッド(コンストラクタ含む)本体(コメント・アノテーション付き)
21
+ 2. メソッドを除いた残りのクラスコード
22
+ を抽出し、指示通りの JSON 文字列を返す。
23
+
24
+ 【前提】
25
+ * 入力は 1 ファイル分だけ。
26
+ * クラスは 1 つ(内部クラス・列挙型があっても OK)。
27
+ * メソッドはネストしない(ローカルクラス・ローカルメソッドは対象外)。
28
+ * 文字列リテラルやコメント中の波括弧は正規表現だけでは完全に安全に扱えないが、
29
+ 本ユーティリティは「実務上よく出会う」コードを対象に設計している。
30
+ 必要なら JavaParser 等の本格的パーサに差し替えて下さい。
31
+
32
+ 【出力形式】
33
+ JSON 文字列(UTF‑8)で、質問文に書かれたスキーマ通り。
34
+ """
35
+ # ------------------------------------------------------------------
36
+ # 1️⃣ パッケージ・インポートの取得(無い場合は空文字列)
37
+ # ------------------------------------------------------------------
38
+ package_pat = re.compile(r'^\s*package\s+([\w\.]+)\s*;\s*', re.MULTILINE)
39
+ pkg_match = package_pat.search(java_code)
40
+ package_name = pkg_match.group(1) if pkg_match else ""
41
+
42
+ # ------------------------------------------------------------------
43
+ # 2️⃣ クラス宣言の取得(クラス名だけ抜き出す)
44
+ # ------------------------------------------------------------------
45
+ # class / interface / enum のいずれかが対象。アノテーションや修飾子は無視。
46
+ class_pat = re.compile(
47
+ r'''(?x) # Verbose
48
+ (?:@\w+(?:\s*\([^)]*\))?\s*)* # 前置アノテーション(任意)
49
+ (public|protected|private)?\s* # アクセス修飾子(任意)
50
+ (final|abstract)?\s* # class 修飾子(任意)
51
+ (class|interface|enum)\s+ # キーワード
52
+ (?P<name>\w+) # クラス名(取得対象)
53
+ (?:\s*<[^>]*>)? # ジェネリクス(任意)
54
+ (?:\s+extends\s+[^{]+)? # extends 句(任意)
55
+ (?:\s+implements\s+[^{]+)? # implements 句(任意)
56
+ \s*{ # 開始波括弧
57
+ ''')
58
+ class_match = class_pat.search(java_code)
59
+ if not class_match:
60
+ raise ValueError("クラス宣言が見つかりませんでした。")
61
+ class_name = class_match.group('name')
62
+
63
+ # ------------------------------------------------------------------
64
+ # 3️⃣ メソッド(=コンストラクタも含む)全体を抽出
65
+ # ------------------------------------------------------------------
66
+ # 正規表現で「コメント・アノテーション + 修飾子 + 戻り値(またはコンストラクタ) + 名前 + パラメータ + 本体」までを取得。
67
+ # コメントは //, /** */ , /* */ のいずれか。アノテーションは @ で始まる。
68
+ # 本体は波括弧の対称性を数えて取得(re.DOTALL で改行も含める)。
69
+ method_pat = re.compile(
70
+ r'''(?x) # Verbose
71
+ (?:
72
+ (?:/\*\*.*?\*/\s*)? # Javadoc コメント(任意)
73
+ (?:/\*.*?\*/\s*)? # ブロックコメント(任意)
74
+ (?:\/\/[^\n]*\s*)? # 行コメント(任意)
75
+ )*
76
+ (?:@\w+(?:\s*\([^)]*\))?\s*)* # アノテーション(0 個以上)
77
+ (?:public|protected|private|\s)* # アクセス修飾子(任意)
78
+ (?:static\s+)? # static 修飾子(任意)
79
+ (?:final\s+|synchronized\s+|abstract\s+|native\s+)* # 余分な修飾子(任意)
80
+ (?:<[^>]+>\s+)? # メソッドレベルのジェネリクス(任意)
81
+ (?:[\w\<\>\[\]]+\s+)? # 戻り値型(コンストラクタなら無い)
82
+ (?P<name>\w+) # メソッド名(コンストラクタはクラス名)
83
+ \s*\( # パラメータ開始
84
+ [^\)]* # 中身は簡易的に「) まで」スキップ
85
+ \)\s*
86
+ (?:throws\s+[^{]+)? # throws 句(任意)
87
+ \{ # 本体開始
88
+ ''')
89
+
90
+ # メソッド本体の波括弧対称性を数えるためのヘルパー
91
+ def extract_body(start_idx: int) -> Tuple[int, str]:
92
+ """start_idx は '{' の位置。対になる '}' まで走査して全文字列を返す。"""
93
+ depth = 0
94
+ i = start_idx
95
+ while i < len(java_code):
96
+ ch = java_code[i]
97
+ if ch == '{':
98
+ depth += 1
99
+ elif ch == '}':
100
+ depth -= 1
101
+ if depth == 0:
102
+ # i を含めて抜き出す
103
+ return i + 1, java_code[start_idx:i + 1]
104
+ i += 1
105
+ raise ValueError("メソッド本体の終端 '}' が見つかりません。")
106
+
107
+ # すべてのメソッド情報を格納するリスト
108
+ methods: List[Dict] = []
109
+
110
+ # 走査インデックス
111
+ pos = 0
112
+ while True:
113
+ m = method_pat.search(java_code, pos)
114
+ if not m:
115
+ break
116
+
117
+ # メソッド名取得
118
+ method_name = m.group('name')
119
+ # コンストラクタかどうか判定(名前がクラス名と同じならコンストラクタ)
120
+ is_constructor = method_name == class_name
121
+
122
+ # メソッド宣言全体の開始位置(コメント・アノテーションを含むので、直前に遡る)
123
+ decl_start = m.start()
124
+ # ただし上記正規表現はコメント・アノテーションを「0 回以上」含めているので
125
+ # ここで取得した start はすでにそれらを含んだ位置になる。
126
+
127
+ # 本体開始波括弧のインデックス
128
+ brace_idx = java_code.find('{', m.end() - 1)
129
+ if brace_idx == -1:
130
+ raise ValueError("メソッド本体開始 '{' が見つかりません。")
131
+
132
+ # 本体全体を取得
133
+ body_end, body_str = extract_body(brace_idx)
134
+
135
+ # 完全なメソッド文字列(宣言 + 本体)
136
+ method_full = java_code[decl_start:body_end]
137
+
138
+ # 位置情報を保持して後で「残りコード」から除去できるようにする
139
+ methods.append({
140
+ "methodName": method_name,
141
+ "isConstructor": is_constructor,
142
+ "code": method_full,
143
+ "start": decl_start,
144
+ "end": body_end
145
+ })
146
+
147
+ # 次の検索は本体終了位置から続ける
148
+ pos = body_end
149
+
150
+ # ------------------------------------------------------------------
151
+ # 4️⃣ 「残りコード」作成(メソッド領域を空白に置換してからトリム)
152
+ # ------------------------------------------------------------------
153
+ remaining = list(java_code) # 文字列を可変リストに
154
+ for m in methods:
155
+ # メソッド領域はスペースで埋めておく(行番号・インデントを保つため)
156
+ for i in range(m["start"], m["end"]):
157
+ remaining[i] = ' '
158
+ remaining_code = ''.join(remaining)
159
+
160
+ # 余計な空行を削除(ただしクラスの波括弧は残す)
161
+ remaining_code = re.sub(r'\n\s*\n', '\n', remaining_code).strip() + '\n'
162
+
163
+ # ------------------------------------------------------------------
164
+ # 5️⃣ JSON オブジェクト生成
165
+ # ------------------------------------------------------------------
166
+ json_files: List[Dict] = []
167
+ for m in methods:
168
+ file_name = f"{class_name}_{m['methodName']}.txt"
169
+ # エスケープ処理(JSON 用に \ と " をエスケープ)
170
+ escaped_code = m["code"].replace('\\', '\\\\').replace('"', '\\"')
171
+ escaped_remaining = remaining_code.replace('\\', '\\\\').replace('"', '\\"')
172
+
173
+ json_files.append({
174
+ "fileName": file_name,
175
+ "className": class_name,
176
+ "methodName": m["methodName"],
177
+ "package": package_name,
178
+ "code": escaped_code,
179
+ "remainingClassCode": escaped_remaining
180
+ })
181
+
182
+ result = {"files": json_files}
183
+ return json.dumps(result, ensure_ascii=False, indent=2)
184
+
185
+ @staticmethod
186
+ def _extract_statement_nodes(root: ET.Element) -> List[ET.Element]:
187
+ """返回所有可执行语句的 Element(保留完整节点)。"""
188
+ stmts = []
189
+ for tag in ("select", "insert", "update", "delete"):
190
+ stmts.extend(root.findall(f".//{tag}"))
191
+ return stmts
192
+
193
+
194
+ def split_mybatis_xml(self, xml_path, out_dir: Path) -> List:
195
+ """
196
+ 把根节点中的每条语句(select/insert/update/delete)按照它们的 id
197
+ 写入独立文件。每个子文件只包含单条语句本身(不再包裹 <mapper>)。
198
+ """
199
+ parser = ET.XMLParser(remove_blank_text=False) # 保留原始缩进、换行
200
+ tree = ET.parse(str(xml_path), parser=parser)
201
+ root = tree.getroot()
202
+
203
+ out_dir.mkdir(parents=True, exist_ok=True)
204
+
205
+ out_put_list = []
206
+ for stmt in self._extract_statement_nodes(root):
207
+ stmt_id = stmt.get("id")
208
+ if not stmt_id: # 没有 id 的语句直接跳过
209
+ continue
210
+
211
+ # 复制一份,防止修改原始树结构
212
+ stmt_copy = copy.deepcopy(stmt)
213
+
214
+ # 生成文件路径(这里仍然使用 .xml,当然你可以改成 .sql、.txt …)
215
+ out_path = out_dir / f"{stmt_id}.xml"
216
+
217
+ # 把单条语句写入文件
218
+ # - xml_declaration=False → 不输出 <?xml …?> 声明
219
+ # - pretty_print=True → 保持原始的缩进格式
220
+ tree = ET.ElementTree(stmt_copy)
221
+ tree.write(
222
+ str(out_path),
223
+ encoding="utf-8",
224
+ xml_declaration=False,
225
+ pretty_print=True
226
+ )
227
+ print(f"✔ 生成 {out_path}")
228
+ out_put_list.append(out_path)
229
+
230
+ return out_put_list
231
+
232
+
233
+ def run(self):
234
+ try:
235
+
236
+ lines = read_file_lines(self.file_path)
237
+
238
+ str_code = ''.join(lines)
239
+
240
+ remove_file_type = self.__getattribute__('file_type')
241
+ output_file_path = self.__getattribute__('target_file_path')
242
+ match remove_file_type:
243
+ case 'Java':
244
+ _split_result = self.split_java_methods(str_code)
245
+ write_json_file(json.loads(_split_result), output_file_path)
246
+ case 'mybatis':
247
+ _split_file_list = self.split_mybatis_xml(self.file_path, output_file_path)
248
+ setattr(self, 'split_file_list', _split_file_list)
249
+ case _:
250
+ _split_result =None
251
+
252
+ except Exception as e:
253
+ self.logger.error(f"{__name__}異常終了. {e}")
254
+ return
255
+
256
+ super().run()
File without changes
File without changes
@@ -0,0 +1,150 @@
1
+ import configparser
2
+ import os
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+
6
+ def generate_code():
7
+ # マインコンバート
8
+ cwd = Path(os.getcwd())
9
+
10
+ config_file_path = os.path.join(cwd, 'config', 'create_file.properties')
11
+ config_dto = read_config_file(config_file_path)
12
+
13
+ target_project_root_path = cwd.parent
14
+ # サンプルファイル作成
15
+ print('CONFIGファイルを作成する')
16
+ sample_config_file_path = os.path.join(cwd, 'sample', 'config', 'properties.txt')
17
+ replacements = {
18
+ "WORK_SPACE": config_dto.work_space,
19
+ "PROJECT_NAME": os.path.basename(target_project_root_path),
20
+ "STEP_NAME": "step_000,step_001,step_002"
21
+ }
22
+ target_project_config_path = os.path.join(target_project_root_path, 'config', config_dto.sub_project_name + '_' + config_dto.sub_source_folder + '_' + config_dto.sub_sub_source_folder_1+'.properties')
23
+ read_replace_create_file(sample_config_file_path, replacements, Path(target_project_config_path))
24
+
25
+ print('Main Jobファイルを作成する')
26
+ # サブファイルコントロール、ジョブ、ユニットファイルの作成
27
+ target_project_sub_project_path = os.path.join(target_project_root_path, 'src', config_dto.sub_project_name, config_dto.sub_source_folder, config_dto.sub_sub_source_folder_1)
28
+ # サンプルサブコントロールファイルの作成
29
+ sample_job_file_path = os.path.join(cwd, 'sample', 'child_sample', 'job.txt')
30
+ replacements_job = {}
31
+ target_project_sub_job_path = os.path.join(target_project_sub_project_path, config_dto.sub_source_folder + '_' + config_dto.sub_sub_source_folder_1 + '_job.py')
32
+ read_replace_create_file(sample_job_file_path, replacements_job, Path(target_project_sub_job_path))
33
+
34
+ print('Main Unitファイルを作成する')
35
+ sample_unit_file_path = os.path.join(cwd, 'sample', 'child_sample', 'unit.txt')
36
+ replacements_unit = {
37
+ "JOB_PACKAGE": 'src.'+ config_dto.sub_project_name + '.'+ config_dto.sub_source_folder + '.' + config_dto.sub_sub_source_folder_1 + '.' + os.path.basename(target_project_sub_job_path).split('.')[0],
38
+ "GYOMU_JOB_PACKAGE_000": 'src.'+ config_dto.sub_project_name + '.' + config_dto.sub_source_folder + '.' + config_dto.sub_sub_source_folder_1 + '.jobs.job_000',
39
+ "GYOMU_JOB_PACKAGE_001": 'src.' + config_dto.sub_project_name + '.' + config_dto.sub_source_folder + '.' + config_dto.sub_sub_source_folder_1 + '.jobs.job_001',
40
+ "GYOMU_JOB_PACKAGE_002": 'src.' + config_dto.sub_project_name + '.' + config_dto.sub_source_folder + '.' + config_dto.sub_sub_source_folder_1 + '.jobs.job_002'
41
+ }
42
+ target_project_unit_path = os.path.join(target_project_sub_project_path, config_dto.sub_source_folder + '_' + config_dto.sub_sub_source_folder_1 + '_unit.py' )
43
+ read_replace_create_file(sample_unit_file_path, replacements_unit, Path(target_project_unit_path))
44
+
45
+ print('Main Controllerファイルを作成する')
46
+ sample_controller_file_path = os.path.join(cwd, 'sample', 'child_sample', 'controller.txt')
47
+ replacements_control = {
48
+ "UNIT_PACKAGE": 'src.'+ config_dto.sub_project_name + '.'+ config_dto.sub_source_folder + '.'+ config_dto.sub_sub_source_folder_1 + '.' + os.path.basename(target_project_unit_path).split('.')[0]
49
+ }
50
+ target_project_sub_controller_path = os.path.join(target_project_sub_project_path, config_dto.sub_source_folder + '_' + config_dto.sub_sub_source_folder_1 + '_control.py')
51
+ read_replace_create_file(sample_controller_file_path, replacements_control, Path(target_project_sub_controller_path))
52
+
53
+ init_file_path = Path(os.path.join(target_project_sub_project_path, '__init__.py'))
54
+ create_file(init_file_path,'')
55
+
56
+ print('業務Job用フォルダを作成する')
57
+ job_file_path = Path(os.path.join(target_project_sub_project_path,'jobs'))
58
+ job_file_path.mkdir(parents=True, exist_ok=True)
59
+ sample_job000_file_path = os.path.join(cwd, 'sample', 'child_sample', 'job' ,'job_000.txt')
60
+ sample_job_sample_file_path = os.path.join(cwd, 'sample', 'child_sample', 'job', 'job_sample.txt')
61
+ replacements_job000 = {
62
+ 'JOB_PACKAGE': 'src.'+ config_dto.sub_project_name + '.'+ config_dto.sub_source_folder + '.' + config_dto.sub_sub_source_folder_1 + '.' + os.path.basename(target_project_sub_job_path).split('.')[0]
63
+ }
64
+ target_project_job000_path = os.path.join(target_project_sub_project_path, 'jobs','job_000.py')
65
+ target_project_job001_path = os.path.join(target_project_sub_project_path, 'jobs','job_001.py')
66
+ target_project_job002_path = os.path.join(target_project_sub_project_path, 'jobs', 'job_002.py')
67
+ read_replace_create_file(sample_job000_file_path, replacements_job000, Path(target_project_job000_path))
68
+ replacements_job001 = {
69
+ 'JOB_NAME':'Job_001',
70
+ 'JOB_PACKAGE': 'src.'+ config_dto.sub_project_name + '.'+ config_dto.sub_source_folder + '.' + config_dto.sub_sub_source_folder_1 + '.' + os.path.basename(target_project_sub_job_path).split('.')[0]
71
+ }
72
+ read_replace_create_file(sample_job_sample_file_path, replacements_job001, Path(target_project_job001_path))
73
+ replacements_job002 = {
74
+ 'JOB_NAME': 'Job_002',
75
+ 'JOB_PACKAGE': 'src.'+ config_dto.sub_project_name + '.'+ config_dto.sub_source_folder + '.' + config_dto.sub_sub_source_folder_1 + '.' + os.path.basename(target_project_sub_job_path).split('.')[0]
76
+ }
77
+ read_replace_create_file(sample_job_sample_file_path, replacements_job002, Path(target_project_job002_path))
78
+
79
+ init_job_file_path = Path(os.path.join(target_project_sub_project_path, 'jobs', '__init__.py'))
80
+ create_file(init_job_file_path,'')
81
+
82
+ print('Main controllerファイルを作成する')
83
+ # サンプルパス
84
+ sample_main_controller_file_path = os.path.join(cwd, 'sample', 'main_convert.txt')
85
+ # パラメーター
86
+ replacements = {
87
+ "CONTROLLER_PACKAGE": 'src.'+ config_dto.sub_project_name + '.' + config_dto.sub_source_folder + '.' + config_dto.sub_sub_source_folder_1 +'.' + os.path.basename(target_project_sub_controller_path).split('.')[0],
88
+ "PYTHON_CONFIG_PROPERTIES": config_dto.sub_project_name + '_' + config_dto.sub_source_folder + '_' + config_dto.sub_sub_source_folder_1 + '.properties'
89
+ }
90
+ # 目標ファイルパス
91
+ target_project_main_controller_path = Path(os.path.join(target_project_root_path, config_dto.sub_project_name + '_'
92
+ + config_dto.sub_source_folder + '_' + config_dto.sub_sub_source_folder_1 +'_controller.py'))
93
+ read_replace_create_file(sample_main_controller_file_path, replacements, target_project_main_controller_path)
94
+
95
+
96
+ def read_replace_create_file(sample_file_path:str, replacements, target_file_path):
97
+ file_content = read_file(sample_file_path)
98
+ after_replace_content = replace_template_vars(file_content, replacements)
99
+
100
+ create_file(target_file_path, after_replace_content)
101
+
102
+ def read_file(template_file_path) -> str:
103
+ with open(template_file_path, 'r', encoding='utf-8') as f:
104
+ template_file = f.read()
105
+ return template_file
106
+
107
+ def replace_template_vars(template: str, replacements: dict) -> str:
108
+ result = template
109
+ if replacements:
110
+ for key, value in replacements.items():
111
+ placeholder = f"{{{{{key}}}}}"
112
+ result = result.replace(placeholder, str(value))
113
+ return result
114
+
115
+
116
+ def create_file(target_path, target_file_content):
117
+ target_path.parent.mkdir(parents=True, exist_ok=True)
118
+ with open(target_path, 'w', encoding='utf-8') as f:
119
+ f.write(target_file_content)
120
+
121
+ def read_config_file(find_config_path):
122
+ config = configparser.ConfigParser()
123
+ config.optionxform = str
124
+
125
+ with open(find_config_path, 'r', encoding='utf-8') as f:
126
+ content = f.read()
127
+ if not content.lstrip().startswith('['):
128
+ content = '[DEFAULT]\n' + content
129
+ config.read_string(content)
130
+
131
+ work_space = config.get('DEFAULT', 'work_space', fallback='.')
132
+ str_sub_project_name = config.get('DEFAULT', 'sub_project_name', fallback='.')
133
+ str_sub_source_folder = config.get('DEFAULT', 'sub_source_folder', fallback='.')
134
+ str_source_folder_1 = config.get('DEFAULT', 'sub_sub_source_folder_1', fallback='.')
135
+ return ConfigDTO(
136
+ work_space=work_space,
137
+ sub_project_name=str_sub_project_name,
138
+ sub_source_folder=str_sub_source_folder,
139
+ sub_sub_source_folder_1=str_source_folder_1
140
+ )
141
+
142
+ @dataclass
143
+ class ConfigDTO:
144
+ work_space: str
145
+ sub_project_name:str
146
+ sub_source_folder:str
147
+ sub_sub_source_folder_1:str
148
+
149
+ if __name__ == "__main__":
150
+ generate_code()
File without changes
File without changes
File without changes
pilot/db/__init__.py ADDED
File without changes
@@ -0,0 +1,34 @@
1
+ from pathlib import Path
2
+
3
+ from db.sql_executor import exec_ddl
4
+
5
+
6
+ this_path = Path(__file__).resolve()
7
+
8
+ this_dir = this_path.parent
9
+
10
+
11
+
12
+
13
+
14
+ ddl_file_path_list = [
15
+ # this_dir / 'ddl' / 'METHOD_CLASS_INFO.sql',
16
+ # this_dir / 'ddl' / 'METHOD_FILE_INFO.sql',
17
+ this_dir / 'ddl' / 'METHOD_INFO.sql',
18
+ # this_dir / 'ddl' / 'METHOD_PARAMETER_INFO.sql',
19
+ # this_dir / 'ddl' / 'METHOD_PARSING_INFO.sql',
20
+ # this_dir / 'ddl' / 'METHOD_SUB_METHOD_INFO.sql',
21
+ # this_dir / 'ddl' / 'SQL_CONDITION_INFO.sql',
22
+ # this_dir / 'ddl' / 'SQL_CONTENT_INFO.sql',
23
+ # this_dir / 'ddl' / 'SQL_FILE_INFO.sql',
24
+ # this_dir / 'ddl' / 'SQL_TABLE_INFO.sql',
25
+ # this_dir / 'ddl' / 'SQL_PARAMETER_INFO.sql',
26
+ # this_dir / 'ddl' / 'SQL_PARSING_INFO.sql',
27
+ # this_dir / 'ddl' / 'SQL_COLUMN_INFO.sql',
28
+ # this_dir / 'ddl' / 'TABLE_INFO.sql',
29
+ # this_dir / 'ddl' / 'TABLE_COLUMN_INFO.sql'
30
+ ]
31
+
32
+
33
+ for ddl_file_path in ddl_file_path_list:
34
+ exec_ddl(str(ddl_file_path))
pilot/db/db_connect.py ADDED
@@ -0,0 +1,49 @@
1
+ import sqlite3
2
+ from pathlib import Path
3
+ from contextlib import contextmanager
4
+ from typing import Generator
5
+
6
+ # -------------------- 配置 --------------------
7
+ DB_FILE = Path(__file__).with_name("app.sqlite3") # 默认数据库文件
8
+ # ------------------------------------------------
9
+
10
+ def _create_connection(db_path: Path = DB_FILE) -> sqlite3.Connection:
11
+ """
12
+ 创建并返回一个 SQLite 连接对象。
13
+ - 自动创建文件(如果不存在)。
14
+ - 开启 WAL 模式,提高并发读取性能。
15
+ """
16
+ conn = sqlite3.connect(db_path, detect_types=sqlite3.PARSE_DECLTYPES)
17
+ # 让查询结果可以像字典一样访问(可选)
18
+ conn.row_factory = sqlite3.Row
19
+ # 开启 Write‑Ahead Logging(一次性设置即可)
20
+ conn.execute("PRAGMA journal_mode=WAL;")
21
+ return conn
22
+
23
+
24
+ @contextmanager
25
+ def get_connection(db_path: Path = DB_FILE) -> Generator[sqlite3.Connection, None, None]:
26
+ """
27
+ 上下文管理器:自动打开连接、提交事务并在结束时关闭。
28
+ 使用方式:
29
+ with get_connection() as conn:
30
+ conn.execute(...)
31
+ """
32
+ conn = _create_connection(db_path)
33
+ try:
34
+ yield conn
35
+ conn.commit() # 正常退出时提交事务
36
+ except Exception: # 发生异常则回滚
37
+ conn.rollback()
38
+ raise
39
+ finally:
40
+ conn.close()
41
+
42
+
43
+ def get_raw_connection(db_path: Path = DB_FILE) -> sqlite3.Connection:
44
+ """
45
+ 如果你不想使用上下文管理器,而是自行控制生命周期,
46
+ 可以直接调用此函数获取一个 Connection 对象。
47
+ 注意:使用完后务必手动 `conn.close()`
48
+ """
49
+ return _create_connection(db_path)