Update model translations
This commit is contained in:
100
cars_100.txt
Normal file
100
cars_100.txt
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
1. Chevrolet | Spark | 2012 | id=42291468
|
||||||
|
2. Mercedes-Benz | G-Class W465 | 2025 | id=42290455
|
||||||
|
3. Honda | Civic | 2017 | id=41122992
|
||||||
|
4. Honda | Civic | 2017 | id=41122190
|
||||||
|
5. Hyundai | Grandeur IG | 2019 | id=42291487
|
||||||
|
6. Mercedes-Benz | S-Class W223 | 2021 | id=42289505
|
||||||
|
7. Kia | K5 | 2011 | id=42291450
|
||||||
|
8. KG Mobility | Very New Tivoli | 2022 | id=41711722
|
||||||
|
9. Mercedes-Benz | CLS-Class C257 | 2019 | id=42260678
|
||||||
|
10. Kia | Seltos 2nd gen | 2026 | id=42291463
|
||||||
|
11. BMW | X7 | 2023 | id=42291443
|
||||||
|
12. Hyundai | Sonata | 2022 | id=42291455
|
||||||
|
13. Mercedes-Benz | E-Class W213 | 2020 | id=42291456
|
||||||
|
14. Hyundai | Grandeur Hybrid | 2025 | id=42291436
|
||||||
|
15. Mercedes-Benz | C-Class W205 | 2019 | id=42291451
|
||||||
|
16. Hyundai | Santa Fe TM | 2019 | id=42291444
|
||||||
|
17. Mercedes-Benz | M-Class W164 | 2007 | id=42257783
|
||||||
|
18. Land Rover | Discovery 4 | 2015 | id=42261349
|
||||||
|
19. BMW | 3 Series | 2020 | id=42224479
|
||||||
|
20. Land Rover | Range Rover Velar | 2019 | id=42273669
|
||||||
|
21. Porsche | Cayenne | 2023 | id=42291413
|
||||||
|
22. KG Mobility | Very New Tivoli | 2020 | id=42291412
|
||||||
|
23. Hyundai | Avante MD | 2011 | id=42291395
|
||||||
|
24. Chevrolet | All New Camaro | 2017 | id=42284504
|
||||||
|
25. Renault Korea | SM6 | 2017 | id=42278601
|
||||||
|
26. Kia | Mohave | 2012 | id=41576139
|
||||||
|
27. Kia | Carnival 4th gen | 2023 | id=42291411
|
||||||
|
28. Kia | All New K7 | 2016 | id=41951116
|
||||||
|
29. Kia | Carnival 4th gen | 2023 | id=42283927
|
||||||
|
30. BMW | 3 Series | 2006 | id=42291399
|
||||||
|
31. Hyundai | Ioniq 6 | 2023 | id=42291352
|
||||||
|
32. Hyundai | New Grand Starex | 2020 | id=41953927
|
||||||
|
33. KG Mobility | Korando Sport | 2016 | id=41949895
|
||||||
|
34. Renault Korea | New QM6 | 2020 | id=42291394
|
||||||
|
35. Mercedes-Benz | E-Class W214 | 2024 | id=42291297
|
||||||
|
36. Volvo | S60 | 2016 | id=42291398
|
||||||
|
37. BMW | 3 Series | 2017 | id=42291386
|
||||||
|
38. Kia | EV6 | 2022 | id=42291336
|
||||||
|
39. KG Mobility | 2nd gen | 2026 | id=42291391
|
||||||
|
40. Volkswagen | Jetta 7th gen | 2024 | id=42277008
|
||||||
|
41. KG Mobility | 2nd gen | 2026 | id=42279398
|
||||||
|
42. Chevrolet | Spark | 2012 | id=42291385
|
||||||
|
43. Kia | New K9 2nd gen | 2022 | id=42291378
|
||||||
|
44. Kia | Carnival 4th gen | 2021 | id=42291381
|
||||||
|
45. Audi | A7 | 2015 | id=42291370
|
||||||
|
46. Kia | Carnival 4th gen | 2021 | id=42283942
|
||||||
|
47. Hyundai | Grandeur HG | 2016 | id=42291377
|
||||||
|
48. Kia | Carnival 4th gen | 2021 | id=41916671
|
||||||
|
49. Hyundai | New Palisade | 2023 | id=42291375
|
||||||
|
50. Porsche | Panamera | 2024 | id=42290634
|
||||||
|
51. Genesis | EQ900 | 2018 | id=42291315
|
||||||
|
52. Hyundai | | 2015 | id=42291074
|
||||||
|
53. Hyundai | Sonata | 2020 | id=42279668
|
||||||
|
54. Hyundai | New Grandeur IG Hybrid | 2023 | id=42291312
|
||||||
|
55. Kia | New Kia Ray | 2026 | id=42291083
|
||||||
|
56. Mercedes-Benz | GLC-Class X253 | 2019 | id=42291373
|
||||||
|
57. Hyundai | New Tucson | 2025 | id=42286946
|
||||||
|
58. Hyundai | New Palisade | 2023 | id=42291369
|
||||||
|
59. BMW | 3 Series | 2017 | id=42291367
|
||||||
|
60. Mercedes-Benz | E-Class W213 | 2021 | id=42281942
|
||||||
|
61. Kia | K8 | 2023 | id=42291332
|
||||||
|
62. KG Mobility | Tivoli Air | 2017 | id=42291259
|
||||||
|
63. Renault Korea | SM6 | 2017 | id=42291349
|
||||||
|
64. Chevrolet | Cruze | 2014 | id=42291366
|
||||||
|
65. Mercedes-Benz | E-Class W212 | 2015 | id=42291345
|
||||||
|
66. Kia | K8 Hybrid | 2023 | id=42291363
|
||||||
|
67. Renault Korea | NewQM3 | 2019 | id=42291364
|
||||||
|
68. Mercedes-Benz | S-Class W222 | 2018 | id=42291350
|
||||||
|
69. Hyundai | Palisade | 2021 | id=42291359
|
||||||
|
70. Hyundai | Grand Starex | 2018 | id=42291358
|
||||||
|
71. Mercedes-Benz | GLS-Class X167 | 2022 | id=42291347
|
||||||
|
72. Hyundai | Palisade | 2021 | id=42278631
|
||||||
|
73. Peugeot | 2008 | 2016 | id=42289865
|
||||||
|
74. Mercedes-Benz | E-Class W213 | 2019 | id=42291344
|
||||||
|
75. Hyundai | Grand Starex | 2017 | id=42273594
|
||||||
|
76. Kia | All New Sorento | 2015 | id=42273586
|
||||||
|
77. Kia | New Morning | 2016 | id=42254096
|
||||||
|
78. Hyundai | Santa Fe DM | 2013 | id=42291313
|
||||||
|
79. Mercedes-Benz | E-Class W213 | 2019 | id=42287351
|
||||||
|
80. Kia | All New K7 | 2017 | id=42291339
|
||||||
|
81. Kia | Niro | 2017 | id=42291337
|
||||||
|
82. Infiniti | Q30 | 2019 | id=42291333
|
||||||
|
83. Kia | Niro | 2017 | id=42289711
|
||||||
|
84. Mercedes-Benz | E-Class W213 | 2020 | id=42291331
|
||||||
|
85. Ford | Explorer 6th gen | 2023 | id=42291323
|
||||||
|
86. Infiniti | Q30 | 2019 | id=42284562
|
||||||
|
87. BMW | 3 Series | 2024 | id=42291329
|
||||||
|
88. Genesis | GV70 | 2025 | id=42279323
|
||||||
|
89. Hyundai | Grandeur HG | 2012 | id=42291328
|
||||||
|
90. Mercedes-Benz | E-Class W213 | 2020 | id=42263463
|
||||||
|
91. BMW | 3 Series | 2024 | id=42289258
|
||||||
|
92. Genesis | GV70 | 2025 | id=42279316
|
||||||
|
93. Hyundai | Grandeur HG | 2012 | id=42288803
|
||||||
|
94. Kia | Ray | 2014 | id=42291325
|
||||||
|
95. Porsche | Cayenne | 2021 | id=42289156
|
||||||
|
96. KG Mobility | Tivoli Air | 2018 | id=41570718
|
||||||
|
97. Toyota | Crown Crossover | 2024 | id=42291308
|
||||||
|
98. Kia | New Sportage R | 2014 | id=42252147
|
||||||
|
99. Genesis | GV80 | 2024 | id=42291320
|
||||||
|
100. Kia | Ray | 2014 | id=42279563
|
||||||
@@ -26,6 +26,9 @@ from .translations import (
|
|||||||
FUEL_TYPE_MAP,
|
FUEL_TYPE_MAP,
|
||||||
GEARBOX_MAP_KO,
|
GEARBOX_MAP_KO,
|
||||||
MODEL_BODY_TYPE_MAP,
|
MODEL_BODY_TYPE_MAP,
|
||||||
|
MODEL_EXACT_TRANSLATIONS,
|
||||||
|
MODEL_PHRASE_TRANSLATIONS,
|
||||||
|
MODEL_TOKEN_TRANSLATIONS,
|
||||||
MODEL_TRANSLATIONS,
|
MODEL_TRANSLATIONS,
|
||||||
SELL_TYPE_MAP,
|
SELL_TYPE_MAP,
|
||||||
expand_allowed_brands,
|
expand_allowed_brands,
|
||||||
@@ -173,7 +176,7 @@ class EncarMapper:
|
|||||||
model_raw = self._as_str(payload.get("Model") or "UNKNOWN")
|
model_raw = self._as_str(payload.get("Model") or "UNKNOWN")
|
||||||
badge = self._as_str(payload.get("Badge") or "")
|
badge = self._as_str(payload.get("Badge") or "")
|
||||||
badge_detail = self._as_str(payload.get("BadgeDetail") or "")
|
badge_detail = self._as_str(payload.get("BadgeDetail") or "")
|
||||||
model = self._translate_text(model_raw)
|
model = self._translate_model(model_raw)
|
||||||
|
|
||||||
year = self._to_year(payload.get("FormYear") or payload.get("Year"))
|
year = self._to_year(payload.get("FormYear") or payload.get("Year"))
|
||||||
# Price в 만원 (10 000 KRW) → переводим в KRW
|
# Price в 만원 (10 000 KRW) → переводим в KRW
|
||||||
@@ -352,6 +355,68 @@ class EncarMapper:
|
|||||||
translated = re.sub(r"\s+", " ", translated).strip()
|
translated = re.sub(r"\s+", " ", translated).strip()
|
||||||
return translated
|
return translated
|
||||||
|
|
||||||
|
def _has_korean(self, text: str) -> bool:
|
||||||
|
return bool(re.search(r"[가-힣]", text))
|
||||||
|
|
||||||
|
def _normalize_model_text(self, text: str) -> str:
|
||||||
|
normalized = self._as_str(text)
|
||||||
|
if not normalized:
|
||||||
|
return normalized
|
||||||
|
# На оригинальном сайте Encar скобок нет — убираем скобки вместе с содержимым.
|
||||||
|
normalized = re.sub(r"[\(\[(【][^)\])】]*[\)\])】]?", " ", normalized)
|
||||||
|
normalized = normalized.replace("·", " ").replace("/", " ")
|
||||||
|
normalized = re.sub(r"(?<=[A-Za-z])-클래스", " 클래스", normalized)
|
||||||
|
normalized = re.sub(r"(?<=[0-9])시리즈", "시리즈", normalized)
|
||||||
|
normalized = re.sub(r"\s+", " ", normalized).strip()
|
||||||
|
return normalized
|
||||||
|
|
||||||
|
def _translate_model(self, text: str) -> str:
|
||||||
|
"""Безопасно переводит модель Encar: точные совпадения + корейские фрагменты.
|
||||||
|
|
||||||
|
Старый общий replace ломал смешанные названия. Здесь английские строки
|
||||||
|
возвращаются как есть, а короткие корейские токены заменяются только
|
||||||
|
когда они реально присутствуют как отдельный смысловой фрагмент.
|
||||||
|
"""
|
||||||
|
original = self._normalize_model_text(text)
|
||||||
|
if not original or not self._has_korean(original):
|
||||||
|
return original
|
||||||
|
|
||||||
|
exact = MODEL_EXACT_TRANSLATIONS.get(original)
|
||||||
|
if exact:
|
||||||
|
return exact
|
||||||
|
|
||||||
|
translated = original
|
||||||
|
|
||||||
|
# Сначала длинные точные модели и фразы — чтобы "렉с턴 스포츠 칸"
|
||||||
|
# не превратился в "Rexton Sport 칸".
|
||||||
|
combined = {**MODEL_EXACT_TRANSLATIONS, **MODEL_PHRASE_TRANSLATIONS, **MODEL_TRANSLATIONS}
|
||||||
|
for source, target in sorted(combined.items(), key=lambda item: len(item[0]), reverse=True):
|
||||||
|
if source in translated:
|
||||||
|
# Заменяем только когда корейский ключ стоит как отдельный
|
||||||
|
# смысловой фрагмент (границы — не корейские символы). Иначе
|
||||||
|
# короткий ключ "레이" (Ray) резал бы "슈팅브레이크" → "슈팅브Ray크".
|
||||||
|
pattern = rf"(?<![가-힣]){re.escape(source)}(?![가-힣])"
|
||||||
|
translated = re.sub(pattern, target, translated)
|
||||||
|
|
||||||
|
# Потом безопасные короткие токены.
|
||||||
|
for source, target in sorted(MODEL_TOKEN_TRANSLATIONS.items(), key=lambda item: len(item[0]), reverse=True):
|
||||||
|
translated = re.sub(rf"(?<![A-Za-z0-9가-힣]){re.escape(source)}(?![A-Za-z0-9가-힣])", target, translated)
|
||||||
|
|
||||||
|
# Типовые слитные варианты Encar: 5시리즈, S-클래스.
|
||||||
|
translated = re.sub(r"(?<=\d)시리즈", " Series", translated)
|
||||||
|
translated = re.sub(r"(?<=[A-Za-z])-?클래스", "-Class", translated)
|
||||||
|
translated = re.sub(r"\b([A-Z]{1,4}) Class\b", r"\1-Class", translated)
|
||||||
|
|
||||||
|
# Оставшиеся непереведённые корейские фрагменты отбрасываем — как на
|
||||||
|
# оригинальном сайте (напр. "슈팅브레이크" исчезает, не портя слово).
|
||||||
|
translated = re.sub(r"[가-힣]+", " ", translated)
|
||||||
|
# Чистим осиротевшую пунктуацию/скобки после удаления корейского.
|
||||||
|
translated = re.sub(r"\(\s*\)", " ", translated)
|
||||||
|
translated = re.sub(r"\s*-\s*(?=$|\))", "", translated)
|
||||||
|
translated = re.sub(r"\s+([),])", r"\1", translated)
|
||||||
|
translated = re.sub(r"\s+", " ", translated).strip(" -")
|
||||||
|
return translated
|
||||||
|
|
||||||
def _translate_color(self, color_raw: str) -> str:
|
def _translate_color(self, color_raw: str) -> str:
|
||||||
value = self._as_str(color_raw)
|
value = self._as_str(color_raw)
|
||||||
if not value:
|
if not value:
|
||||||
|
|||||||
326
encar_scraper/partner_dictionary.py
Normal file
326
encar_scraper/partner_dictionary.py
Normal file
@@ -0,0 +1,326 @@
|
|||||||
|
"""Korean -> English translation tables for EnCar (partner-provided basis).
|
||||||
|
|
||||||
|
Источник переводов от партнёра. Этот файл — эталон: бренды и модели берём
|
||||||
|
отсюда в первую очередь. Держим секционные словари плюс один плоский для
|
||||||
|
простых поисков.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
ENCAR_KO_EN_TABLES: dict[str, dict[str, str]] = {
|
||||||
|
"makers": {
|
||||||
|
"포르쉐": "Porsche",
|
||||||
|
"메르세데스-벤츠": "Mercedes-Benz",
|
||||||
|
"메르세데스 벤츠": "Mercedes-Benz",
|
||||||
|
"벤츠": "Mercedes-Benz",
|
||||||
|
"메르세데스-AMG": "Mercedes-AMG",
|
||||||
|
"메르세데스 AMG": "Mercedes-AMG",
|
||||||
|
"AMG": "AMG",
|
||||||
|
"BMW": "BMW",
|
||||||
|
"아우디": "Audi",
|
||||||
|
"폭스바겐": "Volkswagen",
|
||||||
|
"렉서스": "Lexus",
|
||||||
|
"토요타": "Toyota",
|
||||||
|
"닛산": "Nissan",
|
||||||
|
"인피니티": "Infiniti",
|
||||||
|
"혼다": "Honda",
|
||||||
|
"마쯔다": "Mazda",
|
||||||
|
"스바루": "Subaru",
|
||||||
|
"미쓰비시": "Mitsubishi",
|
||||||
|
"랜드로버": "Land Rover",
|
||||||
|
"재규어": "Jaguar",
|
||||||
|
"지프": "Jeep",
|
||||||
|
"볼보": "Volvo",
|
||||||
|
"푸조": "Peugeot",
|
||||||
|
"르노": "Renault",
|
||||||
|
"미니": "MINI",
|
||||||
|
"로터스": "Lotus",
|
||||||
|
"벤틀리": "Bentley",
|
||||||
|
"롤스로이스": "Rolls-Royce",
|
||||||
|
"람보르기니": "Lamborghini",
|
||||||
|
"페라리": "Ferrari",
|
||||||
|
"맥라렌": "McLaren",
|
||||||
|
"애스턴마틴": "Aston Martin",
|
||||||
|
"애스톤마틴": "Aston Martin",
|
||||||
|
"마세라티": "Maserati",
|
||||||
|
"코닉세그": "Koenigsegg",
|
||||||
|
"파가니": "Pagani",
|
||||||
|
"부가티": "Bugatti",
|
||||||
|
"캐딜락": "Cadillac",
|
||||||
|
"쉐보레": "Chevrolet",
|
||||||
|
"포드": "Ford",
|
||||||
|
"링컨": "Lincoln",
|
||||||
|
"크라이슬러": "Chrysler",
|
||||||
|
"닷지": "Dodge",
|
||||||
|
"램": "Ram",
|
||||||
|
"테슬라": "Tesla",
|
||||||
|
"제네시스": "Genesis",
|
||||||
|
"현대": "Hyundai",
|
||||||
|
"기아": "Kia",
|
||||||
|
"KG모빌리티": "KG Mobility",
|
||||||
|
"쌍용": "SsangYong",
|
||||||
|
"알파로메오": "Alfa Romeo",
|
||||||
|
"피아트": "Fiat",
|
||||||
|
"시트로엥": "Citroen",
|
||||||
|
"스마트": "Smart",
|
||||||
|
"폴스타": "Polestar",
|
||||||
|
"리비안": "Rivian",
|
||||||
|
"루시드": "Lucid",
|
||||||
|
"BYD": "BYD",
|
||||||
|
"지리": "Geely",
|
||||||
|
"홍치": "Hongqi",
|
||||||
|
"이스즈": "Isuzu",
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"718 박스터": "718 Boxster",
|
||||||
|
"718 카이맨": "718 Cayman",
|
||||||
|
"911": "911",
|
||||||
|
"911 카브리올레": "911 Cabriolet",
|
||||||
|
"카이엔": "Cayenne",
|
||||||
|
"카이엔 쿠페": "Cayenne Coupe",
|
||||||
|
"파나메라": "Panamera",
|
||||||
|
"파나메라 스포츠 투리스모": "Panamera Sport Turismo",
|
||||||
|
"마칸": "Macan",
|
||||||
|
"타이칸": "Taycan",
|
||||||
|
"타이칸 크로스 투리스모": "Taycan Cross Turismo",
|
||||||
|
"918 스파이더": "918 Spyder",
|
||||||
|
"카레라 GT": "Carrera GT",
|
||||||
|
"718 스파이더": "718 Spyder",
|
||||||
|
"박스터": "Boxster",
|
||||||
|
"카이맨": "Cayman",
|
||||||
|
"4.0 스파이더": "4.0 Spyder",
|
||||||
|
"C 클래스": "C-Class",
|
||||||
|
"E 클래스": "E-Class",
|
||||||
|
"S 클래스": "S-Class",
|
||||||
|
"A 클래스": "A-Class",
|
||||||
|
"B 클래스": "B-Class",
|
||||||
|
"CLA": "CLA",
|
||||||
|
"CLS": "CLS",
|
||||||
|
"SL": "SL",
|
||||||
|
"SLK": "SLK",
|
||||||
|
"G 클래스": "G-Class",
|
||||||
|
"GLA": "GLA",
|
||||||
|
"GLB": "GLB",
|
||||||
|
"GLC": "GLC",
|
||||||
|
"GLE": "GLE",
|
||||||
|
"GLS": "GLS",
|
||||||
|
"AMG GT": "AMG GT",
|
||||||
|
"AMG GT 4도어 쿠페": "AMG GT 4-Door Coupe",
|
||||||
|
"V 클래스": "V-Class",
|
||||||
|
"1 시리즈": "1 Series",
|
||||||
|
"2 시리즈": "2 Series",
|
||||||
|
"3 시리즈": "3 Series",
|
||||||
|
"4 시리즈": "4 Series",
|
||||||
|
"5 시리즈": "5 Series",
|
||||||
|
"6 시리즈": "6 Series",
|
||||||
|
"7 시리즈": "7 Series",
|
||||||
|
"8 시리즈": "8 Series",
|
||||||
|
"M2": "M2",
|
||||||
|
"M3": "M3",
|
||||||
|
"M4": "M4",
|
||||||
|
"M5": "M5",
|
||||||
|
"M8": "M8",
|
||||||
|
"X1": "X1",
|
||||||
|
"X2": "X2",
|
||||||
|
"X3": "X3",
|
||||||
|
"X4": "X4",
|
||||||
|
"X5": "X5",
|
||||||
|
"X6": "X6",
|
||||||
|
"X7": "X7",
|
||||||
|
"Z4": "Z4",
|
||||||
|
"A3": "A3",
|
||||||
|
"A4": "A4",
|
||||||
|
"A5": "A5",
|
||||||
|
"A6": "A6",
|
||||||
|
"A7": "A7",
|
||||||
|
"A8": "A8",
|
||||||
|
"Q3": "Q3",
|
||||||
|
"Q5": "Q5",
|
||||||
|
"Q7": "Q7",
|
||||||
|
"Q8": "Q8",
|
||||||
|
"R8": "R8",
|
||||||
|
"TT": "TT",
|
||||||
|
"고스트": "Ghost",
|
||||||
|
"팬텀": "Phantom",
|
||||||
|
"레이스": "Wraith",
|
||||||
|
"던": "Dawn",
|
||||||
|
"컬리넌": "Cullinan",
|
||||||
|
"스펙터": "Spectre",
|
||||||
|
"벤테이가": "Bentayga",
|
||||||
|
"컨티넨탈 GT": "Continental GT",
|
||||||
|
"플라잉스퍼": "Flying Spur",
|
||||||
|
"우라칸": "Huracan",
|
||||||
|
"아벤타도르": "Aventador",
|
||||||
|
"우루스": "Urus",
|
||||||
|
"레부엘토": "Revuelto",
|
||||||
|
"로마": "Roma",
|
||||||
|
"포르토피노": "Portofino",
|
||||||
|
"SF90 스트라달레": "SF90 Stradale",
|
||||||
|
"F8 트리뷰토": "F8 Tributo",
|
||||||
|
"콰트로포르테": "Quattroporte",
|
||||||
|
"기블리": "Ghibli",
|
||||||
|
"르반떼": "Levante",
|
||||||
|
"그란투리스모": "GranTurismo",
|
||||||
|
"반퀴시": "Vanquish",
|
||||||
|
"밴티지": "Vantage",
|
||||||
|
"DB11": "DB11",
|
||||||
|
"DB12": "DB12",
|
||||||
|
"DBS": "DBS",
|
||||||
|
},
|
||||||
|
"body_types": {
|
||||||
|
"세단": "Sedan",
|
||||||
|
"쿠페": "Coupe",
|
||||||
|
"컨버터블": "Convertible",
|
||||||
|
"카브리올레": "Cabriolet",
|
||||||
|
"오픈카": "Open Top",
|
||||||
|
"해치백": "Hatchback",
|
||||||
|
"왜건": "Wagon",
|
||||||
|
"스테이션왜건": "Station Wagon",
|
||||||
|
"SUV": "SUV",
|
||||||
|
"크로스오버": "Crossover",
|
||||||
|
"픽업트럭": "Pickup Truck",
|
||||||
|
"밴": "Van",
|
||||||
|
"미니밴": "Minivan",
|
||||||
|
"트럭": "Truck",
|
||||||
|
"쿠페형 SUV": "Coupe SUV",
|
||||||
|
"기타": "Other",
|
||||||
|
},
|
||||||
|
"fuel_types": {
|
||||||
|
"가솔린": "Gasoline",
|
||||||
|
"디젤": "Diesel",
|
||||||
|
"경유": "Diesel",
|
||||||
|
"하이브리드": "Hybrid",
|
||||||
|
"가솔린 하이브리드": "Gasoline Hybrid",
|
||||||
|
"플러그인 하이브리드": "Plug-in Hybrid",
|
||||||
|
"전기": "Electric",
|
||||||
|
"전기차": "Electric",
|
||||||
|
"수소": "Hydrogen",
|
||||||
|
"LPG": "LPG",
|
||||||
|
"LNG": "LNG",
|
||||||
|
"가솔린+전기": "Gasoline + Electric",
|
||||||
|
"가솔린 + 전기": "Gasoline + Electric",
|
||||||
|
},
|
||||||
|
"transmissions": {
|
||||||
|
"수동": "Manual",
|
||||||
|
"오토": "Automatic",
|
||||||
|
"자동": "Automatic",
|
||||||
|
"자동변속": "Automatic",
|
||||||
|
"세미오토": "Semi-automatic",
|
||||||
|
"CVT": "CVT",
|
||||||
|
"듀얼클러치": "Dual-clutch",
|
||||||
|
},
|
||||||
|
"sell_types": {
|
||||||
|
"일반": "General",
|
||||||
|
"리스": "Lease",
|
||||||
|
"렌트": "Rent",
|
||||||
|
"할부": "Installment",
|
||||||
|
},
|
||||||
|
"lease_types": {
|
||||||
|
"운용리스": "Operating Lease",
|
||||||
|
"금융리스": "Financial Lease",
|
||||||
|
},
|
||||||
|
"regions": {
|
||||||
|
"서울": "Seoul",
|
||||||
|
"부산": "Busan",
|
||||||
|
"인천": "Incheon",
|
||||||
|
"대구": "Daegu",
|
||||||
|
"대전": "Daejeon",
|
||||||
|
"광주": "Gwangju",
|
||||||
|
"울산": "Ulsan",
|
||||||
|
"세종": "Sejong",
|
||||||
|
"경기": "Gyeonggi",
|
||||||
|
"강원": "Gangwon",
|
||||||
|
"충북": "Chungbuk",
|
||||||
|
"충남": "Chungnam",
|
||||||
|
"전북": "Jeonbuk",
|
||||||
|
"전남": "Jeonnam",
|
||||||
|
"경북": "Gyeongbuk",
|
||||||
|
"경남": "Gyeongnam",
|
||||||
|
"제주": "Jeju",
|
||||||
|
},
|
||||||
|
"dealer_companies": {
|
||||||
|
"(주)기억": "Gieok Co., Ltd.",
|
||||||
|
"(주)네바퀴": "Four Wheels Co., Ltd.",
|
||||||
|
"(주)에이스타모터스": "A-Star Motors Co., Ltd.",
|
||||||
|
"(주)우진 오토모빌": "Woojin Automobile Co., Ltd.",
|
||||||
|
"(주)지온컴퍼니": "Zion Company Co., Ltd.",
|
||||||
|
"(주)플로시모터스": "Flossy Motors Co., Ltd.",
|
||||||
|
"㈜오토핸즈": "Auto Hands Co., Ltd.",
|
||||||
|
"GD카": "GD Car",
|
||||||
|
"MK모터스": "MK Motors",
|
||||||
|
"꿈차": "Dream Car",
|
||||||
|
"마이카4": "My Car 4",
|
||||||
|
"블루오토": "Blue Auto",
|
||||||
|
"세영모빌리티(주)": "Seyoung Mobility Co., Ltd.",
|
||||||
|
"스마일자동차": "Smile Motors",
|
||||||
|
"오토링크": "Auto Link",
|
||||||
|
"오토핸즈": "Auto Hands",
|
||||||
|
"우리들모터스": "Woorideul Motors",
|
||||||
|
"주식회사 모터바겐": "Motorwagen Co., Ltd.",
|
||||||
|
"주식회사 아이오토스": "iAutos Co., Ltd.",
|
||||||
|
"주식회사이들컴퍼니": "Ideul Company Co., Ltd.",
|
||||||
|
"카메이트": "Car Mate",
|
||||||
|
"카톡모터스": "Katok Motors",
|
||||||
|
"카피디": "Car PD",
|
||||||
|
"포르쉐 인증 중고차 센터 성수": "Porsche Certified Used Car Center Seongsu",
|
||||||
|
"포르쉐인증중고차센터성수": "Porsche Certified Used Car Center Seongsu",
|
||||||
|
},
|
||||||
|
"dealer_names": {
|
||||||
|
"고준용": "Go Jun-yong",
|
||||||
|
"공민중": "Gong Min-jung",
|
||||||
|
"김경진": "Kim Gyeong-jin",
|
||||||
|
"김명진": "Kim Myeong-jin",
|
||||||
|
"김민수": "Kim Min-su",
|
||||||
|
"김성언": "Kim Seong-eon",
|
||||||
|
"김성진": "Kim Seong-jin",
|
||||||
|
"김영균": "Kim Yeong-gyun",
|
||||||
|
"김완섭": "Kim Wan-seop",
|
||||||
|
"김현진": "Kim Hyeon-jin",
|
||||||
|
"김형진": "Kim Hyeong-jin",
|
||||||
|
"김호영": "Kim Ho-yeong",
|
||||||
|
"문명기": "Moon Myeong-gi",
|
||||||
|
"박원석": "Park Won-seok",
|
||||||
|
"박재현": "Park Jae-hyeon",
|
||||||
|
"방지윤": "Bang Ji-yun",
|
||||||
|
"송슬기": "Song Seul-gi",
|
||||||
|
"신영철": "Shin Yeong-cheol",
|
||||||
|
"신호진": "Shin Ho-jin",
|
||||||
|
"양시언": "Yang Si-eon",
|
||||||
|
"양태규": "Yang Tae-gyu",
|
||||||
|
"윤지완": "Yoon Ji-wan",
|
||||||
|
"이경민": "Lee Gyeong-min",
|
||||||
|
"이재욱": "Lee Jae-uk",
|
||||||
|
"임종현": "Lim Jong-hyeon",
|
||||||
|
"임형욱": "Lim Hyeong-uk",
|
||||||
|
"전태준": "Jeon Tae-jun",
|
||||||
|
"정상필": "Jeong Sang-pil",
|
||||||
|
"조경식": "Jo Gyeong-sik",
|
||||||
|
"조승재": "Jo Seung-jae",
|
||||||
|
"조환성": "Jo Hwan-seong",
|
||||||
|
"차상훈": "Cha Sang-hun",
|
||||||
|
"최경욱": "Choi Gyeong-uk",
|
||||||
|
"하상열": "Ha Sang-yeol",
|
||||||
|
"허지훈": "Heo Ji-hun",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _build_flat_map() -> dict[str, str]:
|
||||||
|
result: dict[str, str] = {}
|
||||||
|
for section in ENCAR_KO_EN_TABLES.values():
|
||||||
|
result.update(section)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
ENCAR_KO_EN_TRANSLATIONS: dict[str, str] = _build_flat_map()
|
||||||
|
|
||||||
|
|
||||||
|
def translate_encar_ko_to_en(value: str, section: str | None = None) -> str:
|
||||||
|
"""Перевод корейского термина Encar на английский.
|
||||||
|
|
||||||
|
Если задан `section`, используется только словарь этой секции. Иначе —
|
||||||
|
плоский объединённый словарь.
|
||||||
|
"""
|
||||||
|
if section is not None:
|
||||||
|
return ENCAR_KO_EN_TABLES.get(section, {}).get(value, value)
|
||||||
|
return ENCAR_KO_EN_TRANSLATIONS.get(value, value)
|
||||||
@@ -1,5 +1,15 @@
|
|||||||
# Словари переводов корейского текста русский / английский.
|
# Словари переводов корейского текста русский / английский.
|
||||||
# Используется в encar.py для маппинга данных Encar API.
|
# Используется в encar.py для маппинга данных Encar API.
|
||||||
|
#
|
||||||
|
# Эталон переводов брендов и моделей — словарь партнёра
|
||||||
|
# (`partner_dictionary.py`). Он подмешивается ниже как база: значения оттуда
|
||||||
|
# имеют приоритет для брендов и моделей, а специфичные для проекта записи
|
||||||
|
# (корейские модели Hyundai/Kia/Genesis и т.п., которых нет у партнёра)
|
||||||
|
# сохраняются как дополнение.
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from .partner_dictionary import ENCAR_KO_EN_TABLES as _PARTNER_TABLES
|
||||||
|
|
||||||
|
|
||||||
# Топливо
|
# Топливо
|
||||||
@@ -176,6 +186,12 @@ BRAND_TRANSLATIONS = {
|
|||||||
"기타 수입차": "Other Imported",
|
"기타 수입차": "Other Imported",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Подмешиваем бренды из эталонного словаря партнёра как базу. Существующие
|
||||||
|
# записи проекта имеют приоритет (не перезаписываются), новые из словаря
|
||||||
|
# партнёра добавляются.
|
||||||
|
for _ko, _en in _PARTNER_TABLES["makers"].items():
|
||||||
|
BRAND_TRANSLATIONS.setdefault(_ko, _en)
|
||||||
|
|
||||||
|
|
||||||
# Бренды: английский → алиасы (корейский + русский)
|
# Бренды: английский → алиасы (корейский + русский)
|
||||||
|
|
||||||
@@ -277,6 +293,240 @@ def expand_allowed_brands(brands: set[str] | None) -> set[str] | None:
|
|||||||
|
|
||||||
# Модели / комплектации: корейский → английский
|
# Модели / комплектации: корейский → английский
|
||||||
|
|
||||||
|
# Точные переводы базовых названий моделей Encar. Этот словарь используется
|
||||||
|
# перед частичным переводом и не должен содержать общие слова вроде "뉴".
|
||||||
|
MODEL_EXACT_TRANSLATIONS = {
|
||||||
|
# BMW / Mercedes-Benz
|
||||||
|
"1시리즈": "1 Series",
|
||||||
|
"2시리즈": "2 Series",
|
||||||
|
"3시리즈": "3 Series",
|
||||||
|
"4시리즈": "4 Series",
|
||||||
|
"5시리즈": "5 Series",
|
||||||
|
"6시리즈": "6 Series",
|
||||||
|
"7시리즈": "7 Series",
|
||||||
|
"8시리즈": "8 Series",
|
||||||
|
"A-클래스": "A-Class",
|
||||||
|
"B-클래스": "B-Class",
|
||||||
|
"C-클래스": "C-Class",
|
||||||
|
"E-클래스": "E-Class",
|
||||||
|
"S-클래스": "S-Class",
|
||||||
|
"SL-클래스": "SL-Class",
|
||||||
|
"SLK-클래스": "SLK-Class",
|
||||||
|
"GLA-클래스": "GLA-Class",
|
||||||
|
"GLB-클래스": "GLB-Class",
|
||||||
|
"GLC-클래스": "GLC-Class",
|
||||||
|
"GLE-클래스": "GLE-Class",
|
||||||
|
"GLS-클래스": "GLS-Class",
|
||||||
|
"V-클래스": "V-Class",
|
||||||
|
# Hyundai / Kia / Genesis
|
||||||
|
"팰리세이드": "Palisade",
|
||||||
|
"싼타페": "Santa Fe",
|
||||||
|
"투싼": "Tucson",
|
||||||
|
"아반떼": "Avante",
|
||||||
|
"쏘나타": "Sonata",
|
||||||
|
"쏘나타 디 엣지": "Sonata The Edge",
|
||||||
|
"그랜저": "Grandeur",
|
||||||
|
"아이오닉5": "Ioniq 5",
|
||||||
|
"아이오닉6": "Ioniq 6",
|
||||||
|
"아이오닉": "Ioniq",
|
||||||
|
"넥쏘": "Nexo",
|
||||||
|
"벨로스터": "Veloster",
|
||||||
|
"코나": "Kona",
|
||||||
|
"캐스퍼": "Casper",
|
||||||
|
"베뉴": "Venue",
|
||||||
|
"스타렉스": "Starex",
|
||||||
|
"스타리아": "Staria",
|
||||||
|
"아슬란": "Aslan",
|
||||||
|
"베라크루즈": "Veracruz",
|
||||||
|
"카니발": "Carnival",
|
||||||
|
"쏘렌토": "Sorento",
|
||||||
|
"스포티지": "Sportage",
|
||||||
|
"에스컬레이드": "Escalade",
|
||||||
|
"프리미어": "Premier",
|
||||||
|
"1세대": "1st gen",
|
||||||
|
"2세대": "2nd gen",
|
||||||
|
"3세대": "3rd gen",
|
||||||
|
"4세대": "4th gen",
|
||||||
|
"5세대": "5th gen",
|
||||||
|
"6세대": "6th gen",
|
||||||
|
"7세대": "7th gen",
|
||||||
|
"8세대": "8th gen",
|
||||||
|
"모닝": "Morning",
|
||||||
|
"레이": "Ray",
|
||||||
|
"셀토스": "Seltos",
|
||||||
|
"니로": "Niro",
|
||||||
|
"스팅어": "Stinger",
|
||||||
|
"쏘울": "Soul",
|
||||||
|
"모하비": "Mohave",
|
||||||
|
"프라이드": "Pride",
|
||||||
|
"스토닉": "Stonic",
|
||||||
|
"카렌스": "Carens",
|
||||||
|
"제네시스": "Genesis",
|
||||||
|
# Chevrolet / Renault Korea / KG Mobility
|
||||||
|
"스파크": "Spark",
|
||||||
|
"말리부": "Malibu",
|
||||||
|
"트랙스": "Trax",
|
||||||
|
"크루즈": "Cruze",
|
||||||
|
"올란도": "Orlando",
|
||||||
|
"트레일블레이저": "Trailblazer",
|
||||||
|
"콜로라도": "Colorado",
|
||||||
|
"트래버스": "Traverse",
|
||||||
|
"캡티바": "Captiva",
|
||||||
|
"이쿼녹스": "Equinox",
|
||||||
|
"아베오": "Aveo",
|
||||||
|
"볼트": "Volt",
|
||||||
|
"임팔라": "Impala",
|
||||||
|
"맥스크루즈": "Max Cruz",
|
||||||
|
"렉스턴 스포츠 칸": "Rexton Sport Khan",
|
||||||
|
"렉스턴 스포츠": "Rexton Sport",
|
||||||
|
"렉스턴": "Rexton",
|
||||||
|
"토레스": "Torres",
|
||||||
|
"티볼리 에어": "Tivoli Air",
|
||||||
|
"티볼리 아머": "Tivoli Armor",
|
||||||
|
"티볼리": "Tivoli",
|
||||||
|
"코란도 투리스모": "Korando Turismo",
|
||||||
|
"코란도 스포츠": "Korando Sport",
|
||||||
|
"코란도": "Korando",
|
||||||
|
"체어맨": "Chairman",
|
||||||
|
"오피러스": "Opirus",
|
||||||
|
# Import models written in Korean
|
||||||
|
"익스플로러": "Explorer",
|
||||||
|
"머스탱": "Mustang",
|
||||||
|
"카이엔": "Cayenne",
|
||||||
|
"카이엔 쿠페": "Cayenne Coupe",
|
||||||
|
"파나메라": "Panamera",
|
||||||
|
"파나메라 스포츠 투리스모": "Panamera Sport Turismo",
|
||||||
|
"마칸": "Macan",
|
||||||
|
"타이칸": "Taycan",
|
||||||
|
"타이칸 크로스 투리스모": "Taycan Cross Turismo",
|
||||||
|
"918 스파이더": "918 Spyder",
|
||||||
|
"718 스파이더": "718 Spyder",
|
||||||
|
"4.0 스파이더": "4.0 Spyder",
|
||||||
|
"박스터": "Boxster",
|
||||||
|
"카이맨": "Cayman",
|
||||||
|
"911 카브리올레": "911 Cabriolet",
|
||||||
|
"카레라": "Carrera",
|
||||||
|
"티구안": "Tiguan",
|
||||||
|
"골프": "Golf",
|
||||||
|
"제타": "Jetta",
|
||||||
|
"투아렉": "Touareg",
|
||||||
|
"파사트": "Passat",
|
||||||
|
"아테온": "Arteon",
|
||||||
|
"쿠퍼": "Cooper",
|
||||||
|
"컨트리맨": "Countryman",
|
||||||
|
"클럽맨": "Clubman",
|
||||||
|
"디스커버리": "Discovery",
|
||||||
|
"레인지로버": "Range Rover",
|
||||||
|
"이보크": "Evoque",
|
||||||
|
"벨라": "Velar",
|
||||||
|
"디펜더": "Defender",
|
||||||
|
"랭글러": "Wrangler",
|
||||||
|
"체로키": "Cherokee",
|
||||||
|
"레니게이드": "Renegade",
|
||||||
|
"컴패스": "Compass",
|
||||||
|
"루비콘": "Rubicon",
|
||||||
|
"에비에이터": "Aviator",
|
||||||
|
"르반떼": "Levante",
|
||||||
|
"기블리": "Ghibli",
|
||||||
|
"어코드": "Accord",
|
||||||
|
"캠리": "Camry",
|
||||||
|
"프리우스": "Prius",
|
||||||
|
"타스만": "Tasman",
|
||||||
|
"토러스": "Taurus",
|
||||||
|
"카마로": "Camaro",
|
||||||
|
"비틀": "Beetle",
|
||||||
|
"e-트론": "e-tron",
|
||||||
|
"글래디에이터": "Gladiator",
|
||||||
|
"노틸러스": "Nautilus",
|
||||||
|
"시로코": "Scirocco",
|
||||||
|
"아틀라스": "Atlas",
|
||||||
|
"알파드": "Alphard",
|
||||||
|
"익스페디션": "Expedition",
|
||||||
|
"익스프레스Van": "Express Van",
|
||||||
|
"짐니": "Jimny",
|
||||||
|
"쿠가": "Kuga",
|
||||||
|
"클리오": "Clio",
|
||||||
|
"파일럿": "Pilot",
|
||||||
|
"콰트로포르테": "Quattroporte",
|
||||||
|
"고스트": "Ghost",
|
||||||
|
"팬텀": "Phantom",
|
||||||
|
"레이스": "Wraith",
|
||||||
|
"던": "Dawn",
|
||||||
|
"컬리넌": "Cullinan",
|
||||||
|
"스펙터": "Spectre",
|
||||||
|
"벤테이가": "Bentayga",
|
||||||
|
"컨티넨탈 GT": "Continental GT",
|
||||||
|
"플라잉스퍼": "Flying Spur",
|
||||||
|
"우라칸": "Huracan",
|
||||||
|
"아벤타도르": "Aventador",
|
||||||
|
"우루스": "Urus",
|
||||||
|
"레부엘토": "Revuelto",
|
||||||
|
"로마": "Roma",
|
||||||
|
"포르토피노": "Portofino",
|
||||||
|
"SF90 스트라달레": "SF90 Stradale",
|
||||||
|
"F8 트리뷰토": "F8 Tributo",
|
||||||
|
"그란투리스모": "GranTurismo",
|
||||||
|
"반퀴시": "Vanquish",
|
||||||
|
"밴티지": "Vantage",
|
||||||
|
"AMG GT 4도어 쿠페": "AMG GT 4-Door Coupe",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Подмешиваем модели из эталонного словаря партнёра как базу. Существующие
|
||||||
|
# записи проекта имеют приоритет. Дополнительно добавляем нормализованные
|
||||||
|
# варианты ключей партнёра (напр. "5 시리즈" -> "5시리즈", "S 클래스" -> "S-클래스"),
|
||||||
|
# т.к. в сырых данных Encar формат отличается пробелами/дефисами.
|
||||||
|
def _register_partner_models() -> None:
|
||||||
|
for _ko, _en in _PARTNER_TABLES["models"].items():
|
||||||
|
MODEL_EXACT_TRANSLATIONS.setdefault(_ko, _en)
|
||||||
|
# "5 시리즈" -> "5시리즈"
|
||||||
|
_compact = re.sub(r"(?<=\d)\s+시리즈", "시리즈", _ko)
|
||||||
|
# "S 클래스" -> "S-클래스"
|
||||||
|
_compact = re.sub(r"(?<=[A-Za-z])\s+클래스", "-클래스", _compact)
|
||||||
|
if _compact != _ko:
|
||||||
|
MODEL_EXACT_TRANSLATIONS.setdefault(_compact, _en)
|
||||||
|
|
||||||
|
|
||||||
|
_register_partner_models()
|
||||||
|
|
||||||
|
# Безопасные составные фразы. Применяются только к строкам, где есть корейский текст.
|
||||||
|
MODEL_PHRASE_TRANSLATIONS = {
|
||||||
|
"올 뉴": "All New",
|
||||||
|
"더 뉴": "New",
|
||||||
|
"디 올 뉴": "The All New",
|
||||||
|
"베리 뉴": "Very New",
|
||||||
|
"베리": "Very",
|
||||||
|
"뷰티풀": "Beautiful",
|
||||||
|
"넥스트": "Next",
|
||||||
|
"디 ": "The ",
|
||||||
|
"디 엣지": "The Edge",
|
||||||
|
"액티브 투어러": "Active Tourer",
|
||||||
|
"투어러": "Tourer",
|
||||||
|
"그란쿠페": "GranCoupe",
|
||||||
|
"그란 쿠페": "GranCoupe",
|
||||||
|
"스포츠 칸": "Sport Khan",
|
||||||
|
"스포츠": "Sport",
|
||||||
|
"하이브리드": "Hybrid",
|
||||||
|
"일렉트릭": "Electric",
|
||||||
|
"네오": "Neo",
|
||||||
|
"트랜스폼": "Transform",
|
||||||
|
"프라임": "Prime",
|
||||||
|
"마스터": "Master",
|
||||||
|
"어반": "Urban",
|
||||||
|
"볼드": "Bold",
|
||||||
|
"아머": "Armor",
|
||||||
|
"에어": "Air",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Короткие токены переводятся только как отдельные токены, не внутри других слов.
|
||||||
|
MODEL_TOKEN_TRANSLATIONS = {
|
||||||
|
"시리즈": "Series",
|
||||||
|
"클래스": "Class",
|
||||||
|
"세대": "gen",
|
||||||
|
"뉴": "New",
|
||||||
|
"올": "All",
|
||||||
|
"칸": "Khan",
|
||||||
|
}
|
||||||
|
|
||||||
MODEL_TRANSLATIONS = {
|
MODEL_TRANSLATIONS = {
|
||||||
# --- Топливо / тип двигателя ---
|
# --- Топливо / тип двигателя ---
|
||||||
"가솔린": "Gasoline",
|
"가솔린": "Gasoline",
|
||||||
@@ -551,6 +801,21 @@ MODEL_TRANSLATIONS = {
|
|||||||
"그란투리스모": "GranTurismo",
|
"그란투리스모": "GranTurismo",
|
||||||
"반퀴시": "Vanquish",
|
"반퀴시": "Vanquish",
|
||||||
"밴티지": "Vantage",
|
"밴티지": "Vantage",
|
||||||
|
# --- Дополнительные модели, встреченные в данных Encar ---
|
||||||
|
"시에라": "Sierra",
|
||||||
|
"시빅": "Civic",
|
||||||
|
"레인저": "Ranger",
|
||||||
|
"콜레오스": "Koleos",
|
||||||
|
"그랑 콜레오스": "Grand Koleos",
|
||||||
|
"페이톤": "Phaeton",
|
||||||
|
"티록": "T-Roc",
|
||||||
|
"큐브": "Cube",
|
||||||
|
"크라운": "Crown",
|
||||||
|
"라이즈": "Rise",
|
||||||
|
"시로코": "Scirocco",
|
||||||
|
"이오스": "Eos",
|
||||||
|
"비틀": "Beetle",
|
||||||
|
"폴로": "Polo",
|
||||||
# --- Служебные / категории ---
|
# --- Служебные / категории ---
|
||||||
"(세부등급": "(sub-trim",
|
"(세부등급": "(sub-trim",
|
||||||
"없음)": "none)",
|
"없음)": "none)",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ dependencies = [
|
|||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
|
"httpx>=0.28.0",
|
||||||
"pytest>=8.3.0",
|
"pytest>=8.3.0",
|
||||||
"pytest-cov>=5.0.0",
|
"pytest-cov>=5.0.0",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ class TestEncarMapper(unittest.TestCase):
|
|||||||
self.assertEqual(record.origin_id, "encar:41421262")
|
self.assertEqual(record.origin_id, "encar:41421262")
|
||||||
self.assertEqual(record.parser_id, "encar:41421262")
|
self.assertEqual(record.parser_id, "encar:41421262")
|
||||||
self.assertEqual(record.brand, "Hyundai")
|
self.assertEqual(record.brand, "Hyundai")
|
||||||
self.assertEqual(record.model, "G90 (RS4)")
|
# Скобки удаляются — на оригинальном сайте Encar их нет.
|
||||||
|
self.assertEqual(record.model, "G90")
|
||||||
self.assertEqual(record.year, 2023)
|
self.assertEqual(record.year, 2023)
|
||||||
self.assertEqual(record.price, 85900000) # 8590 만원 × 10000
|
self.assertEqual(record.price, 85900000) # 8590 만원 × 10000
|
||||||
self.assertEqual(record.mileage, 37923)
|
self.assertEqual(record.mileage, 37923)
|
||||||
@@ -83,10 +84,52 @@ class TestEncarMapper(unittest.TestCase):
|
|||||||
self.assertEqual(record.parser_id, "encar:41878507")
|
self.assertEqual(record.parser_id, "encar:41878507")
|
||||||
self.assertIn("carid=41878507", record.origin_url)
|
self.assertIn("carid=41878507", record.origin_url)
|
||||||
|
|
||||||
|
def test_translate_model_keeps_english_text(self):
|
||||||
|
mapper = EncarMapper()
|
||||||
|
|
||||||
|
self.assertEqual(mapper._translate_model("All New Carnival"), "All New Carnival")
|
||||||
|
self.assertEqual(mapper._translate_model("New Kia Ray"), "New Kia Ray")
|
||||||
|
self.assertEqual(mapper._translate_model("K5 Hybrid 3rd gen"), "K5 Hybrid 3rd gen")
|
||||||
|
|
||||||
|
def test_translate_model_handles_korean_fragments(self):
|
||||||
|
mapper = EncarMapper()
|
||||||
|
|
||||||
|
cases = {
|
||||||
|
# Скобки и их содержимое отбрасываются как на сайте Encar.
|
||||||
|
"5시리즈 (G30)": "5 Series",
|
||||||
|
"S-클래스 W223": "S-Class W223",
|
||||||
|
"Sonata 디 엣지(DN8)": "Sonata The Edge",
|
||||||
|
"렉스턴 Sport 칸": "Rexton Sport Khan",
|
||||||
|
"티볼리 Air": "Tivoli Air",
|
||||||
|
"코란도 투리스모": "Korando Turismo",
|
||||||
|
"Kia Carnival 4세대": "Kia Carnival 4th gen",
|
||||||
|
"Cadillac 에스컬레이드 5세대": "Cadillac Escalade 5th gen",
|
||||||
|
"K7 프리미어": "K7 Premier",
|
||||||
|
}
|
||||||
|
|
||||||
|
for raw, expected in cases.items():
|
||||||
|
with self.subTest(raw=raw):
|
||||||
|
self.assertEqual(mapper._translate_model(raw), expected)
|
||||||
|
|
||||||
|
def test_translate_model_drops_untranslated_korean(self):
|
||||||
|
mapper = EncarMapper()
|
||||||
|
|
||||||
|
cases = {
|
||||||
|
# "레이" (Ray) не должен резать слово "슈팅브레이크" изнутри.
|
||||||
|
"슈팅브레이크": "",
|
||||||
|
"The New G70 슈팅브레이크": "The New G70",
|
||||||
|
"G70 (슈팅브레이크)": "G70",
|
||||||
|
}
|
||||||
|
|
||||||
|
for raw, expected in cases.items():
|
||||||
|
with self.subTest(raw=raw):
|
||||||
|
self.assertEqual(mapper._translate_model(raw), expected)
|
||||||
|
|
||||||
|
|
||||||
class TestEncarScraper(unittest.TestCase):
|
class TestEncarScraper(unittest.TestCase):
|
||||||
def test_collect_listing_limits_results(self):
|
def test_collect_listing_limits_results(self):
|
||||||
scraper = EncarScraper()
|
scraper = EncarScraper()
|
||||||
|
|
||||||
scraper._fetch_listing_page = MagicMock(side_effect=[
|
scraper._fetch_listing_page = MagicMock(side_effect=[
|
||||||
{"SearchResults": [{"Id": 1}, {"Id": 2}]},
|
{"SearchResults": [{"Id": 1}, {"Id": 2}]},
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -195,11 +195,14 @@ class TestAPILoad(unittest.TestCase):
|
|||||||
persistence: PersistenceService = app.state.persistence
|
persistence: PersistenceService = app.state.persistence
|
||||||
persistence.upsert_cars_batch([_make_car(i, images=4) for i in range(200)])
|
persistence.upsert_cars_batch([_make_car(i, images=4) for i in range(200)])
|
||||||
|
|
||||||
|
cls.app = app
|
||||||
cls.client = TestClient(app)
|
cls.client = TestClient(app)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
import os
|
import os
|
||||||
|
cls.client.close()
|
||||||
|
cls.app.state.persistence.engine.dispose()
|
||||||
if os.path.exists("test_api_load.db"):
|
if os.path.exists("test_api_load.db"):
|
||||||
os.remove("test_api_load.db")
|
os.remove("test_api_load.db")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user