참고소스 수정본

This commit is contained in:
LASTA_DEV01\lasta
2026-05-12 19:40:31 +09:00
parent 0f34a451fc
commit 2e9204243d
8708 changed files with 3259488 additions and 869 deletions

View File

@@ -0,0 +1,2 @@
"""Domain adapters keep domain-specific semantics out of ontology core."""

View File

@@ -0,0 +1,35 @@
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
@dataclass(frozen=True, slots=True)
class DomainAdapter:
"""Domain-specific semantics plugged into the generic ontology engine."""
domain: str
relation_rules: dict[str, Any] = field(default_factory=dict)
entity_type_aliases: dict[str, str] = field(default_factory=dict)
entity_name_aliases: dict[str, dict[str, str]] = field(default_factory=dict)
def relation_rule(self, predicate: str) -> Any | None:
return self.relation_rules.get(predicate)
def normalize_entity_type(self, raw_type: str) -> str | None:
key = compact_key(raw_type)
return self.entity_type_aliases.get(key)
def normalize_entity_name(self, raw_name: str, entity_type: str | None = None) -> str | None:
if not entity_type:
return None
aliases = self.entity_name_aliases.get(entity_type, {})
return aliases.get(canonical_key(raw_name))
def compact_key(value: Any) -> str:
return "".join(ch for ch in str(value or "").strip().lower() if ch.isalnum())
def canonical_key(value: Any) -> str:
return " ".join(str(value or "").strip().lower().split())

View File

@@ -0,0 +1,2 @@
"""E-commerce domain adapters."""

View File

@@ -0,0 +1,158 @@
from __future__ import annotations
from crawler_platform.app.adapters.base import DomainAdapter
from crawler_platform.app.core.ontology.relation_schema import RelationRule
PRODUCT_TYPES = {"Perfume", "Product"}
PERFUME_RELATION_RULES: dict[str, RelationRule] = {
"hasBrand": RelationRule(
"hasBrand",
PRODUCT_TYPES,
{"Brand"},
page_types={"ProductPage"},
source_zones={"product_title", "product_summary", "product_description", "product_detail"},
min_confidence=0.72,
),
"hasTopNote": RelationRule(
"hasTopNote",
PRODUCT_TYPES,
{"Note"},
page_types={"ProductPage"},
source_zones={"product_description", "product_detail"},
min_confidence=0.78,
),
"hasMiddleNote": RelationRule(
"hasMiddleNote",
PRODUCT_TYPES,
{"Note"},
page_types={"ProductPage"},
source_zones={"product_description", "product_detail"},
min_confidence=0.78,
),
"hasBaseNote": RelationRule(
"hasBaseNote",
PRODUCT_TYPES,
{"Note"},
page_types={"ProductPage"},
source_zones={"product_description", "product_detail"},
min_confidence=0.78,
),
"hasAccord": RelationRule(
"hasAccord",
PRODUCT_TYPES,
{"Accord"},
page_types={"ProductPage"},
source_zones={"product_description", "product_detail"},
min_confidence=0.74,
),
"evokesMood": RelationRule(
"evokesMood",
PRODUCT_TYPES,
{"Mood"},
page_types={"ProductPage"},
source_zones={"product_description", "product_detail", "review_body"},
min_confidence=0.75,
),
"suitableForSeason": RelationRule(
"suitableForSeason",
PRODUCT_TYPES,
{"Season"},
page_types={"ProductPage"},
source_zones={"product_description", "product_detail", "review_body"},
min_confidence=0.75,
),
"suitableForOccasion": RelationRule(
"suitableForOccasion",
PRODUCT_TYPES,
{"Occasion"},
page_types={"ProductPage"},
source_zones={"product_description", "product_detail", "review_body"},
min_confidence=0.75,
),
"hasReviewKeyword": RelationRule(
"hasReviewKeyword",
PRODUCT_TYPES,
{"Review"},
page_types={"ProductPage", "ReviewPage"},
source_zones={"review_body", "product_description", "product_detail"},
min_confidence=0.78,
),
"soldBy": RelationRule(
"soldBy",
{"Perfume", "Product", "Brand"},
{"Brand"},
page_types={"ProductPage", "BrandStoryPage"},
source_zones={"product_detail", "product_description", "brand_story_body"},
min_confidence=0.82,
),
"hasPrice": RelationRule(
"hasPrice",
PRODUCT_TYPES,
literal_value=True,
page_types={"ProductPage"},
source_zones={"product_summary", "product_detail"},
min_confidence=0.82,
),
"similarTo": RelationRule(
"similarTo",
PRODUCT_TYPES,
PRODUCT_TYPES,
page_types={"ProductPage"},
source_zones={"product_description", "product_detail"},
min_confidence=0.86,
),
}
PERFUME_TYPE_ALIASES = {
"perfumeproduct": "Perfume",
"fragrance": "Perfume",
"product": "Perfume",
"fragrancenote": "Note",
"ingredient": "Note",
"scentnote": "Note",
"note": "Note",
"brand": "Brand",
"accord": "Accord",
"mood": "Mood",
"season": "Season",
"occasion": "Occasion",
"reviewkeyword": "Review",
"review": "Review",
"price": "Price",
}
PERFUME_NAME_ALIASES = {
"Brand": {
"forment": "FORMENT",
"theforment": "FORMENT",
"the forment": "FORMENT",
"forment korea": "FORMENT",
},
"Note": {
"musk": "Musk",
"powdery": "Powdery",
"floral": "Floral",
"citrus": "Citrus",
},
"Accord": {
"musk": "Musk",
"powdery": "Powdery",
"floral": "Floral",
"citrus": "Citrus",
},
"Mood": {
"fresh": "Fresh",
"cozy": "Cozy",
"romantic": "Romantic",
"elegant": "Elegant",
},
}
PERFUME_ADAPTER = DomainAdapter(
domain="perfume",
relation_rules=PERFUME_RELATION_RULES,
entity_type_aliases=PERFUME_TYPE_ALIASES,
entity_name_aliases=PERFUME_NAME_ALIASES,
)

View File

@@ -0,0 +1,15 @@
from __future__ import annotations
from crawler_platform.app.adapters.base import DomainAdapter
from crawler_platform.app.adapters.ecommerce.perfume import PERFUME_ADAPTER
ADAPTERS: dict[str, DomainAdapter] = {
PERFUME_ADAPTER.domain: PERFUME_ADAPTER,
}
def adapter_for_domain(domain: str | None) -> DomainAdapter | None:
if not domain:
return None
return ADAPTERS.get(str(domain).strip().lower())