Template pipeline: JS→Python -arvomuunnokset korjattu
Ongelma: generoiduissa Python-tiedostoissa JS-booleanit (false/true) päätyvät sellaisenaan Python-koodiin, jossa ne eivät ole valideja. Lisäksi datetime-importit puuttuivat kun LLM antoi extra_imports-kentässä pelkän "datetime"-merkkijonon eikä kokonaista import-lausetta. Korjaukset: - pyLiteral(): muuntaa JS-arvot Python-literaaleiksi (false→False jne.) - pyJsonLiteral(): testidatan serialisointi Python-dict-muodossa - tmplSchemas: datetime-importit tunnistetaan automaattisesti kentistä - tmplModels + tmplSchemas: oletusarvot pyLiteral()-funktion kautta - tmplTests: JSON.stringify korvattu pyJsonLiteral():lla - Validaattori: tunnistaa nyt datetime-import-puutteet ja JS-booleanit Testattu: molemmat aiemmin rikkinäiset speksit generoivat nyt toimivan koodin — 6/6 pytest-testiä läpi molemmilla.
This commit is contained in:
@@ -193,6 +193,19 @@ import Settings from "../components/Settings.astro";
|
|||||||
if (fname === 'schemas.py') {
|
if (fname === 'schemas.py') {
|
||||||
if (/:\s*date\b/.test(code) && !/from datetime import/.test(code))
|
if (/:\s*date\b/.test(code) && !/from datetime import/.test(code))
|
||||||
issues.push('ISSUE: schemas.py: käyttää date-tyyppiä mutta "from datetime import date" puuttuu');
|
issues.push('ISSUE: schemas.py: käyttää date-tyyppiä mutta "from datetime import date" puuttuu');
|
||||||
|
if (/:\s*datetime\b/.test(code) && !/from datetime import/.test(code))
|
||||||
|
issues.push('ISSUE: schemas.py: käyttää datetime-tyyppiä mutta "from datetime import datetime" puuttuu');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4b. Python-syntaksi: JS-booleanit (false/true ilman isoa alkukirjainta)
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i];
|
||||||
|
if (/^\s*#/.test(line) || /^\s*$/.test(line)) continue;
|
||||||
|
// Etsi false/true joita ei ole merkkijonon sisällä ja jotka eivät ole osa isompaa sanaa
|
||||||
|
if (/(?<!["\w])false(?![\w"])/.test(line))
|
||||||
|
issues.push(`ISSUE: ${fname}:${i+1}: "false" ei ole Python — pitäisi olla "False"`);
|
||||||
|
if (/(?<!["\w])true(?![\w"])/.test(line))
|
||||||
|
issues.push(`ISSUE: ${fname}:${i+1}: "true" ei ole Python — pitäisi olla "True"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. test_main.py: ei saa uudelleenmääritellä appia tai modeleita
|
// 5. test_main.py: ei saa uudelleenmääritellä appia tai modeleita
|
||||||
@@ -1121,6 +1134,29 @@ OUTPUT FORMAT:
|
|||||||
|
|
||||||
// === Template Pipeline — rakennuspalaset ===
|
// === Template Pipeline — rakennuspalaset ===
|
||||||
|
|
||||||
|
// JS-arvo → Python-literaali: false→False, true→True, "teksti"→'"teksti"'
|
||||||
|
function pyLiteral(val) {
|
||||||
|
if (val === true) return 'True';
|
||||||
|
if (val === false) return 'False';
|
||||||
|
if (val === null || val === undefined) return 'None';
|
||||||
|
if (typeof val === 'string') return `"${val}"`;
|
||||||
|
return String(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON-merkkijono jossa booleanit on Python-muodossa: {"a": True, "b": False}
|
||||||
|
function pyJsonLiteral(obj) {
|
||||||
|
const parts = Object.entries(obj).map(([k, v]) => {
|
||||||
|
let pyVal;
|
||||||
|
if (v === true) pyVal = 'True';
|
||||||
|
else if (v === false) pyVal = 'False';
|
||||||
|
else if (v === null) pyVal = 'None';
|
||||||
|
else if (typeof v === 'string') pyVal = `"${v}"`;
|
||||||
|
else pyVal = String(v);
|
||||||
|
return `"${k}":${pyVal}`;
|
||||||
|
});
|
||||||
|
return '{' + parts.join(',') + '}';
|
||||||
|
}
|
||||||
|
|
||||||
const SPEC_SYSTEM = `You are a software architect who designs database schemas for Python web applications.
|
const SPEC_SYSTEM = `You are a software architect who designs database schemas for Python web applications.
|
||||||
|
|
||||||
THINK STEP BY STEP before outputting JSON:
|
THINK STEP BY STEP before outputting JSON:
|
||||||
@@ -1179,7 +1215,7 @@ Blog → Author: name,email,bio(Text|None) / Post: title, content(Text), author_
|
|||||||
for (const f of e.fields) {
|
for (const f of e.fields) {
|
||||||
let parts = [`Column(${f.sa_type}`];
|
let parts = [`Column(${f.sa_type}`];
|
||||||
if (!f.nullable) parts.push('nullable=False');
|
if (!f.nullable) parts.push('nullable=False');
|
||||||
if (f.default !== null && f.default !== undefined) parts.push(typeof f.default === 'string' ? `default="${f.default}"` : `default=${f.default}`);
|
if (f.default !== null && f.default !== undefined) parts.push(`default=${pyLiteral(f.default)}`);
|
||||||
code += ` ${f.name} = ${parts.join(', ')})\n`;
|
code += ` ${f.name} = ${parts.join(', ')})\n`;
|
||||||
}
|
}
|
||||||
code += '\n';
|
code += '\n';
|
||||||
@@ -1189,13 +1225,27 @@ Blog → Author: name,email,bio(Text|None) / Post: title, content(Text), author_
|
|||||||
}
|
}
|
||||||
|
|
||||||
function tmplSchemas(spec) {
|
function tmplSchemas(spec) {
|
||||||
|
// Tunnista tarvittavat datetime-importit kenttätyypeistä
|
||||||
|
const dtTypes = new Set();
|
||||||
|
for (const e of spec.entities) for (const f of e.fields) {
|
||||||
|
if (/\bdate\b/i.test(f.py_type) && !/datetime/.test(f.py_type)) dtTypes.add('date');
|
||||||
|
if (/\bdatetime\b/i.test(f.py_type)) dtTypes.add('datetime');
|
||||||
|
}
|
||||||
|
|
||||||
let code = 'from pydantic import BaseModel\n';
|
let code = 'from pydantic import BaseModel\n';
|
||||||
for (const imp of (spec.extra_imports || [])) code += imp + '\n';
|
if (dtTypes.size > 0) code += `from datetime import ${[...dtTypes].sort().join(', ')}\n`;
|
||||||
|
|
||||||
|
// extra_imports: suodata pois pelkät nimet kuten "datetime" (jo käsitelty yllä)
|
||||||
|
for (const imp of (spec.extra_imports || [])) {
|
||||||
|
if (/^(date|datetime)$/.test(imp.trim())) continue; // käsitelty jo
|
||||||
|
if (/^from\s/.test(imp) || /^import\s/.test(imp)) code += imp + '\n';
|
||||||
|
}
|
||||||
code += '\n';
|
code += '\n';
|
||||||
|
|
||||||
for (const e of spec.entities) {
|
for (const e of spec.entities) {
|
||||||
code += `class ${e.name}Create(BaseModel):\n`;
|
code += `class ${e.name}Create(BaseModel):\n`;
|
||||||
for (const f of e.fields) {
|
for (const f of e.fields) {
|
||||||
if (f.default !== null && f.default !== undefined) code += ` ${f.name}: ${f.py_type} = ${typeof f.default === 'string' ? '"'+f.default+'"' : f.default}\n`;
|
if (f.default !== null && f.default !== undefined) code += ` ${f.name}: ${f.py_type} = ${pyLiteral(f.default)}\n`;
|
||||||
else if (f.nullable && f.py_type.includes('None')) code += ` ${f.name}: ${f.py_type} = None\n`;
|
else if (f.nullable && f.py_type.includes('None')) code += ` ${f.name}: ${f.py_type} = None\n`;
|
||||||
else code += ` ${f.name}: ${f.py_type}\n`;
|
else code += ` ${f.name}: ${f.py_type}\n`;
|
||||||
}
|
}
|
||||||
@@ -1253,11 +1303,11 @@ Blog → Author: name,email,bio(Text|None) / Post: title, content(Text), author_
|
|||||||
else if (f.py_type.includes('bool')) testData[f.name] = true;
|
else if (f.py_type.includes('bool')) testData[f.name] = true;
|
||||||
else if (f.py_type.includes('date')) testData[f.name] = '2024-01-15';
|
else if (f.py_type.includes('date')) testData[f.name] = '2024-01-15';
|
||||||
}
|
}
|
||||||
const td = JSON.stringify(testData);
|
const td = pyJsonLiteral(testData);
|
||||||
const firstStr = e.fields.find(f => f.py_type.includes('str') && f.name !== 'status');
|
const firstStr = e.fields.find(f => f.py_type.includes('str') && f.name !== 'status');
|
||||||
const updateData = {...testData};
|
const updateData = {...testData};
|
||||||
if (firstStr) updateData[firstStr.name] = `Updated ${firstStr.name}`;
|
if (firstStr) updateData[firstStr.name] = `Updated ${firstStr.name}`;
|
||||||
const ud = JSON.stringify(updateData);
|
const ud = pyJsonLiteral(updateData);
|
||||||
|
|
||||||
code += `def test_create_${lo}():\n response = client.post('/${tb}/', json=${td})\n assert response.status_code == 201\n assert 'id' in response.json()\n\n`;
|
code += `def test_create_${lo}():\n response = client.post('/${tb}/', json=${td})\n assert response.status_code == 201\n assert 'id' in response.json()\n\n`;
|
||||||
code += `def test_list_${lo}s():\n client.post('/${tb}/', json=${td})\n response = client.get('/${tb}/')\n assert response.status_code == 200\n assert len(response.json()) >= 1\n\n`;
|
code += `def test_list_${lo}s():\n client.post('/${tb}/', json=${td})\n response = client.get('/${tb}/')\n assert response.status_code == 200\n assert len(response.json()) >= 1\n\n`;
|
||||||
|
|||||||
BIN
projektit/luodut/rest-api-kyttjhallinnalle (1).zip
Normal file
BIN
projektit/luodut/rest-api-kyttjhallinnalle (1).zip
Normal file
Binary file not shown.
BIN
projektit/luodut/rest-api-kyttjhallinnalle.zip
Normal file
BIN
projektit/luodut/rest-api-kyttjhallinnalle.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user