pilot.linkstec 0.0.32__py3-none-any.whl → 0.0.91__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.
- pilot/base/__init__.py +0 -0
- pilot/base/ai_call.py +38 -0
- pilot/base/ai_info.py +20 -0
- pilot/base/chage_file_tag_base.py +73 -0
- pilot/base/db_operation_base.py +536 -0
- pilot/base/delete_commnents_base.py +306 -0
- pilot/base/file_operation.py +44 -0
- pilot/base/get_file_encoding.py +14 -0
- pilot/base/make_parsing_java_file_order_base.py +154 -0
- pilot/base/split_file_base.py +256 -0
- pilot/client/__init__.py +0 -0
- pilot/client/ai_client.py +75 -0
- pilot/config/config_reader.py +81 -43
- pilot/create_python/__init__.py +0 -0
- pilot/create_python/config/__init__.py +0 -0
- pilot/create_python/create_python.py +150 -0
- pilot/create_python/sample/__init__.py +0 -0
- pilot/create_python/sample/child_sample/__init__.py +0 -0
- pilot/create_python/sample/child_sample/job/__init__.py +0 -0
- pilot/create_python/sample/config/__init__.py +0 -0
- pilot/db/__init__.py +0 -0
- pilot/db/create_table.py +34 -0
- pilot/db/db_connect.py +49 -0
- pilot/db/db_main.py +293 -0
- pilot/db/db_util.py +508 -0
- pilot/db/ddl/__init__.py +18 -0
- pilot/db/dml/__init__.py +18 -0
- pilot/db/sql_executor.py +62 -0
- pilot/db/sql_loader.py +233 -0
- pilot/db/sql_service.py +55 -0
- pilot/file_tool/__init__.py +0 -0
- pilot/file_tool/create_prompt_file.py +75 -0
- pilot/file_tool/json_file_tool.py +103 -0
- pilot/job/base/__init__.py +0 -0
- pilot/job/base/convert/__init__.py +0 -0
- pilot/job/base/convert/encodingTransformerJob.py +16 -0
- pilot/job/base/convert/tabReplaceJob.py +27 -0
- pilot/job/base/generate/__init__.py +0 -0
- pilot/job/base/generate/generateJsonBaseJob.py +42 -0
- pilot/job/base/generate/generateTextBaseJob.py +40 -0
- pilot/job/impl/base_job.py +4 -0
- pilot/prompt/__init__.py +0 -0
- pilot/unit/impl/base_unit.py +1 -0
- {pilot_linkstec-0.0.32.dist-info → pilot_linkstec-0.0.91.dist-info}/METADATA +1 -1
- pilot_linkstec-0.0.91.dist-info/RECORD +75 -0
- pilot_linkstec-0.0.32.dist-info/RECORD +0 -35
- {pilot_linkstec-0.0.32.dist-info → pilot_linkstec-0.0.91.dist-info}/WHEEL +0 -0
- {pilot_linkstec-0.0.32.dist-info → pilot_linkstec-0.0.91.dist-info}/licenses/LICENSE +0 -0
- {pilot_linkstec-0.0.32.dist-info → pilot_linkstec-0.0.91.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()
|
pilot/client/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
from pilot.config.config_reader import get_config
|
|
4
|
+
from pilot.logging.logger import get_logger
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AIClient:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.logger = get_logger(__name__)
|
|
10
|
+
self.config_dto = get_config()
|
|
11
|
+
self.headers = {"Content-Type": "application/json;charset=utf-8"}
|
|
12
|
+
|
|
13
|
+
def call(self, user_prompt: str, system_prompt: str = "") -> str:
|
|
14
|
+
messages = []
|
|
15
|
+
if system_prompt:
|
|
16
|
+
messages.append({"role": "system", "content": system_prompt})
|
|
17
|
+
messages.append({"role": "user", "content": user_prompt})
|
|
18
|
+
|
|
19
|
+
request_data = self._build_request_payload(messages)
|
|
20
|
+
|
|
21
|
+
response_data = self._send_post_request(self.api_url, self.headers, request_data)
|
|
22
|
+
if not isinstance(response_data, dict):
|
|
23
|
+
self.logger.error("無効なAPI応答またはリクエスト失敗")
|
|
24
|
+
return ""
|
|
25
|
+
|
|
26
|
+
result_text = self._extract_response_content(response_data)
|
|
27
|
+
return result_text
|
|
28
|
+
|
|
29
|
+
def _build_request_payload(self, messages: list[dict]) -> dict:
|
|
30
|
+
raise NotImplementedError("サブクラスでリクエストペイロードの構築を実装してください")
|
|
31
|
+
|
|
32
|
+
def _send_post_request(self, url: str, headers: dict, data: dict) -> dict or str:
|
|
33
|
+
try:
|
|
34
|
+
response = requests.post(url, headers=headers, json=data)
|
|
35
|
+
except Exception as e:
|
|
36
|
+
self.logger.error(f"リクエスト失敗: {e}")
|
|
37
|
+
return ""
|
|
38
|
+
if response.status_code != 200:
|
|
39
|
+
self.logger.error(f"ステータスコード {response.status_code}: {response.text}")
|
|
40
|
+
return ""
|
|
41
|
+
try:
|
|
42
|
+
return response.json()
|
|
43
|
+
except Exception as e:
|
|
44
|
+
self.logger.error(f"JSON解析失敗: {e}")
|
|
45
|
+
return ""
|
|
46
|
+
|
|
47
|
+
def _extract_response_content(self, response: dict) -> str:
|
|
48
|
+
raise NotImplementedError("サブクラスでレスポンスの解析を実装してください")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class LMStudioClient(AIClient):
|
|
52
|
+
def __init__(self):
|
|
53
|
+
super().__init__()
|
|
54
|
+
self.api_url = self.config_dto.lm_studio_api_url
|
|
55
|
+
self.model_name = self.config_dto.lm_studio_model_name
|
|
56
|
+
|
|
57
|
+
def _build_request_payload(self, messages: list[dict]) -> dict:
|
|
58
|
+
payload = {
|
|
59
|
+
"model": self.model_name,
|
|
60
|
+
"stream": False,
|
|
61
|
+
"temperature": 0.8,
|
|
62
|
+
"max_tokens": 15000,
|
|
63
|
+
"messages": messages,
|
|
64
|
+
}
|
|
65
|
+
return payload
|
|
66
|
+
|
|
67
|
+
def _extract_response_content(self, response: dict) -> str:
|
|
68
|
+
if not isinstance(response, dict):
|
|
69
|
+
return str(response)
|
|
70
|
+
if "usage" in response:
|
|
71
|
+
self.logger.debug(f"使用状況: {response['usage']}")
|
|
72
|
+
choices = response.get("choices", [])
|
|
73
|
+
if choices:
|
|
74
|
+
return choices[0].get("message", {}).get("content") or str(response)
|
|
75
|
+
return str(response)
|
pilot/config/config_reader.py
CHANGED
|
@@ -1,39 +1,54 @@
|
|
|
1
1
|
import configparser
|
|
2
|
-
import os
|
|
3
2
|
import inspect
|
|
3
|
+
import os
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
6
7
|
|
|
7
8
|
@dataclass
|
|
8
9
|
class ConfigDTO:
|
|
9
|
-
work_space: str
|
|
10
|
-
threads: int
|
|
11
10
|
project: str
|
|
11
|
+
log_level: str
|
|
12
|
+
threads: int
|
|
13
|
+
lm_studio_api_url: str
|
|
14
|
+
lm_studio_model_name: str
|
|
15
|
+
work_space: str
|
|
16
|
+
copy_path: str
|
|
17
|
+
json_file_path:str
|
|
12
18
|
steps: list[str]
|
|
13
|
-
|
|
14
|
-
runsteps: list[str]
|
|
15
|
-
multisteps: list[str]
|
|
19
|
+
|
|
16
20
|
|
|
17
21
|
class ConfigReader:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
_instance = None
|
|
23
|
+
_loaded = False
|
|
24
|
+
|
|
25
|
+
def __new__(cls, filepath=None):
|
|
26
|
+
if cls._instance is None:
|
|
27
|
+
cls._instance = super().__new__(cls)
|
|
28
|
+
return cls._instance
|
|
29
|
+
|
|
30
|
+
def __init__(self, filepath=None):
|
|
31
|
+
if self._loaded:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
if filepath is None:
|
|
36
|
+
filepath = self.find_config_path()
|
|
37
|
+
if not os.path.exists(filepath):
|
|
38
|
+
raise FileNotFoundError(f"設定ファイルが見つかりません: {filepath}")
|
|
39
|
+
|
|
40
|
+
self.config = configparser.ConfigParser()
|
|
41
|
+
self.config.optionxform = str
|
|
42
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
43
|
+
content = f.read()
|
|
44
|
+
if not content.lstrip().startswith('['):
|
|
45
|
+
content = '[DEFAULT]\n' + content
|
|
46
|
+
self.config.read_string(content)
|
|
47
|
+
|
|
48
|
+
self._loaded = True
|
|
49
|
+
except Exception as e:
|
|
50
|
+
print(f"設定ファイル読み込みエラー: {e}")
|
|
51
|
+
raise
|
|
37
52
|
|
|
38
53
|
@classmethod
|
|
39
54
|
def find_config_path(cls):
|
|
@@ -52,10 +67,11 @@ class ConfigReader:
|
|
|
52
67
|
|
|
53
68
|
base_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
|
54
69
|
fallback_path = os.path.join(base_dir, 'config', 'control.properties')
|
|
70
|
+
|
|
55
71
|
if os.path.exists(fallback_path):
|
|
56
72
|
return fallback_path
|
|
57
73
|
|
|
58
|
-
raise FileNotFoundError("control.properties
|
|
74
|
+
raise FileNotFoundError("control.properties が期待される場所に見つかりません")
|
|
59
75
|
|
|
60
76
|
def get(self, section, option, fallback=None, cast_type=str):
|
|
61
77
|
try:
|
|
@@ -71,25 +87,47 @@ class ConfigReader:
|
|
|
71
87
|
return fallback
|
|
72
88
|
|
|
73
89
|
def get_dto(self) -> ConfigDTO:
|
|
74
|
-
input_path = self.get('DEFAULT', 'input_path', fallback='.')
|
|
75
|
-
work_space = self.get('DEFAULT', 'work_space', fallback='.')
|
|
76
|
-
threads = int(self.get('DEFAULT', 'threads', fallback=1))
|
|
77
90
|
project = self.get('DEFAULT', 'project', fallback='')
|
|
91
|
+
log_level = self.get('DEFAULT', 'log_level', fallback='INFO')
|
|
92
|
+
threads = self.get('DEFAULT', 'threads', fallback=1, cast_type=int)
|
|
93
|
+
lm_studio_api_url = self.get('DEFAULT', 'lm_studio_api_url', fallback='.')
|
|
94
|
+
lm_studio_model_name = self.get('DEFAULT', 'lm_studio_model_name', fallback='.')
|
|
95
|
+
work_space = self.get('DEFAULT', 'work_space', fallback='.')
|
|
96
|
+
copy_path = self.get('DEFAULT', 'copy_file_path', fallback='.')
|
|
97
|
+
json_file_path = self.get('DEFAULT', 'json_file_path', fallback='.')
|
|
78
98
|
steps_str = self.get('DEFAULT', 'steps', fallback='')
|
|
99
|
+
|
|
79
100
|
steps = [s.strip() for s in steps_str.split(',')] if steps_str else []
|
|
80
|
-
skipsteps_str = self.get('DEFAULT', 'skipsteps', fallback='')
|
|
81
|
-
skipsteps = [s.strip() for s in skipsteps_str.split(',')] if skipsteps_str else []
|
|
82
|
-
runsteps_str = self.get('DEFAULT', 'runsteps', fallback='')
|
|
83
|
-
runsteps = [s.strip() for s in runsteps_str.split(',')] if runsteps_str else []
|
|
84
|
-
multisteps_str = self.get('DEFAULT', 'multisteps', fallback='')
|
|
85
|
-
multisteps = [s.strip() for s in multisteps_str.split(',')] if multisteps_str else []
|
|
86
101
|
|
|
87
102
|
return ConfigDTO(
|
|
88
|
-
work_space=work_space,
|
|
89
|
-
threads=threads,
|
|
90
103
|
project=project,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
104
|
+
log_level=log_level,
|
|
105
|
+
threads=threads,
|
|
106
|
+
lm_studio_api_url=lm_studio_api_url,
|
|
107
|
+
lm_studio_model_name=lm_studio_model_name,
|
|
108
|
+
work_space=work_space,
|
|
109
|
+
copy_path=copy_path,
|
|
110
|
+
json_file_path=json_file_path,
|
|
111
|
+
steps=steps
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# 全局設定管理用の変数
|
|
116
|
+
_global_config: Optional[ConfigDTO] = None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def init_config(filepath=None):
|
|
120
|
+
global _global_config
|
|
121
|
+
try:
|
|
122
|
+
config_reader = ConfigReader(filepath)
|
|
123
|
+
_global_config = config_reader.get_dto()
|
|
124
|
+
except Exception as e:
|
|
125
|
+
print(f"設定初期化エラー: {e}")
|
|
126
|
+
raise
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_config() -> ConfigDTO:
|
|
130
|
+
global _global_config
|
|
131
|
+
if _global_config is None:
|
|
132
|
+
raise RuntimeError("設定が初期化されていません。init_config() を最初に呼び出してください。")
|
|
133
|
+
return _global_config
|
|
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
|
|
File without changes
|
pilot/db/__init__.py
ADDED
|
File without changes
|