This commit is contained in:
Vitrixxl 2025-11-17 15:15:29 +01:00
parent c90da606a3
commit 072fc047a4
4 changed files with 76 additions and 4 deletions

View File

@ -464,6 +464,10 @@ SCRAPER_RESPONSE_FIELDS = (
"once",
)
SCRAPER_BOOL_FIELDS = (
"once",
)
SCRAPER_INT_FIELDS = (
"last_seen_days",
"first_seen_days",
@ -472,7 +476,6 @@ SCRAPER_INT_FIELDS = (
"enabled",
"enrich_llm",
"only_match",
"once",
)
@ -783,6 +786,11 @@ def create_scraper():
value = payload[field]
data[field] = None if value is None else _parse_int(value, field)
for field in SCRAPER_BOOL_FIELDS:
if field in payload:
value = payload[field]
data[field] = None if value is None else _parse_bool(value, field)
try:
row = _insert_row(SCRAPER_TABLE, data, SCRAPER_RESPONSE_FIELDS)
except psycopg.IntegrityError as exc:
@ -814,6 +822,11 @@ def update_scraper(scraper_id: str):
value = payload[field]
updates[field] = None if value is None else _parse_int(value, field)
for field in SCRAPER_BOOL_FIELDS:
if field in payload:
value = payload[field]
updates[field] = None if value is None else _parse_bool(value, field)
if not updates:
abort(400, description="No updatable fields provided")

View File

@ -48,8 +48,12 @@ export function SchemaAwareJsonField({
}, [value, onChange, resolvedValue]);
useEffect(() => {
setRawValue(JSON.stringify(resolvedValue, null, 2));
}, [resolvedValue]);
// Only sync rawValue with resolvedValue when in schema mode
// to avoid overwriting user input in raw mode
if (mode === "schema") {
setRawValue(JSON.stringify(resolvedValue, null, 2));
}
}, [resolvedValue, mode]);
useEffect(() => {
onValidationChange?.(validation);

View File

@ -17,6 +17,7 @@ const characteristicOptions = [
"has_interphone",
"has_jacuzzi",
"has_land",
"has_land_division",
"has_grenier",
"has_vis_a_vis",
"is_peaceful",

View File

@ -3,7 +3,7 @@
* Ces informations sont injectées dans le schéma après normalisation.
*/
import type { SchemaDefinition, SchemaNode, SchemaObjectNode } from "./types";
import type { SchemaDefinition, SchemaNode, SchemaObjectNode, SchemaEnumNode } from "./types";
export interface FieldMetadata {
label?: string;
@ -538,6 +538,60 @@ function enrichSchemaNode(node: SchemaNode, path: string): SchemaNode {
}
}
// Forcer la multi-sélection pour les classes DPE/GES et types de biens
if (enrichedNode.kind === "enum") {
const multiSelectFields = [
"habitation.climate.epcEnergy",
"habitation.climate.epcClimate",
"type",
];
if (multiSelectFields.includes(path)) {
const enumNode = enrichedNode as SchemaEnumNode;
enumNode.multiple = true;
// Transformer les options pour les rendre plus lisibles
if (path === "habitation.climate.epcEnergy" || path === "habitation.climate.epcClimate") {
enumNode.options = enumNode.options.map((option) => {
if (typeof option === "string") {
// Extraire la classe (A, B, C, etc.) depuis ENERGY_CLASSIFICATION_A
const match = option.match(/_([A-G]|NC|UNKNOWN)$/);
if (match) {
const classValue = match[1];
return {
value: option,
label: classValue === "UNKNOWN" ? "Non spécifié" : classValue,
};
}
}
return option;
});
} else if (path === "type") {
// Labels pour les types de biens
const typeLabels: Record<string, string> = {
"CLASS_UNKNOWN": "Non spécifié",
"CLASS_HOUSE": "Maison",
"CLASS_FLAT": "Appartement",
"CLASS_PROGRAM": "Programme neuf",
"CLASS_SHOP": "Commerce",
"CLASS_PREMISES": "Local commercial",
"CLASS_OFFICE": "Bureau",
"CLASS_LAND": "Terrain",
"CLASS_BUILDING": "Immeuble",
"CLASS_PARKING": "Parking",
};
enumNode.options = enumNode.options.map((option) => {
if (typeof option === "string") {
return {
value: option,
label: typeLabels[option] || option,
};
}
return option;
});
}
}
}
if (enrichedNode.kind === "object") {
const objectNode = enrichedNode as SchemaObjectNode;
objectNode.fields = objectNode.fields.map((field) => {