634 lines
23 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?
class DromXMLGenerator
{
protected $types = [];
protected $dictionaries = [
"car" => [],
"bus" => [],
"motorhome" => [],
"mobile_crane" => [],
"bulldozer" => [],
"atv" => [],
"truck" => [],
"building" => [],
"misc" => [],
"municipal" => [],
"forestry" => [],
"loader" => [],
"semi_trailer" => [],
"trailer" => [],
"truck_tractor" => [],
"tractor" => [],
"excavator" => [],
];
protected $special_frames_mapping = [
"BUS" => "bus",
"TRUCK" => "truck",
"TRUCK_TRACTOR" => "truck_tractor",
"MOTORHOME" => "motorhome",
"BUILDING" => "building",
"CRANE" => "mobile_crane",
"LOADER" => "loader",
"TRAILER" => "trailer",
"SEMITRAILER" => "semi_trailer",
"EXCAVATOR" => "excavator",
"FARM" => "tractor",
"MISC" => "misc",
"FORESTRY" => "forestry",
"MUNICIPAL" => "municipal",
"ATV" => "atv",
"BULLDOZER" => "bulldozer",
"QUADBIKE" => "atv",
"SNOWMOBILE" => "atv",
"SCOOTER" => "misc",
];
protected $special_frames = [];
protected $xml_cars = null;
protected $xml_special = null;
protected $offers_cars = null;
protected $offers_special = null;
protected $total = 0;
protected $found_cars = 0;
protected $found_special = 0;
protected $valid_cars = 0;
protected $valid_special = 0;
protected $invalid_special = 0;
protected $not_found_special = 0;
protected $p1 = 0;
protected $p2 = 0;
protected $p3 = 0;
protected $p4 = 0;
protected $fuels = [
"БЕНЗИН" => "GASOLINE",
"ГАЗ" => "GAS",
"ДИЗЕЛЬ" => "DIESEL",
"ЭЛЕКТРИЧЕСКИЙ" => "ELECTRICITY",
];
protected $errors = "";
function __construct()
{
$this->xml_cars = new SimpleXMLElement("<?xml version=\"1.0\" encoding=\"utf-8\" ?><avtoxml></avtoxml>");
$this->offers_cars = $this->xml_cars->addChild('Offers');
$this->xml_special = new SimpleXMLElement("<?xml version=\"1.0\" encoding=\"utf-8\" ?><avtoxml></avtoxml>");
$this->offers_special = $this->xml_special->addChild('SpecOffers');
$this->loadTypes();
foreach($this->dictionaries AS $type => $v)
{
$structure = [
"brands" => [],
"models" => [],
"cities" => [],
"bodies" => [],
"colors" => [],
"transmissions" => [],
"engines" => [],
"wheels" => [],
"fuels" => [],
];
$url = "https://www.drom.ru/cached_files/autoload/files/";
if($type === "car")
{
$url = $url."ref.xml";
}
else
{
$url = $url."ref_".$type.".xml";
}
$response_xml = file_get_contents($url);
file_put_contents("/home/bitrix/www/local/ref_".$type.".xml", $response_xml);
$structure_object = new SimpleXMLElement($response_xml);
foreach($structure_object->MarksSpec->MarkSpec AS $mark)
{
$k = mb_strtoupper((string) $mark->sMarkSpec);
$n = (int) $mark->idMarkSpec;
$models = [];
foreach($structure_object->ModelsSpec->ModelSpec AS $model)
{
$mmn = (int) $model->idMarkSpec;
if($mmn === $n)
{
$km = mb_strtoupper((string) $model->sModelSpec);
$nm = (int) $model->idModelSpec;
$models[ $km ] = $nm;
$structure['models'][ $km ] = $nm;
}
}
$structure['brands'][ $k ] = [ "id" => $n, "models" => $models ];
}
foreach($structure_object->Marks->Mark AS $mark)
{
$k = mb_strtoupper((string) $mark->sMark);
$n = (int) $mark->idMark;
$models = [];
foreach($structure_object->Models->Model AS $model)
{
$mmn = (int) $model->idMark;
if($mmn === $n)
{
$km = mb_strtoupper((string) $model->sModel);
$nm = (int) $model->idModel;
$models[ $km ] = $nm;
}
}
$structure['brands'][ $k ] = [ "id" => $n, "models" => $models ];
}
foreach($structure_object->Cities->City AS $city)
{
$k = mb_strtoupper((string) $city->sCity);
$n = (int) $city->idCity;
$structure['cities'][ $k ] = $n;
}
foreach($structure_object->FrameTypes->FrameType AS $frame_type)
{
$k = mb_strtoupper((string) $frame_type->sFrameType);
$n = (int) $frame_type->idFrameType;
$structure['bodies'][ $k ] = $n;
}
foreach($structure_object->Colors->Color AS $color)
{
$k = mb_strtoupper((string) $color->sColor);
$n = (int) $color->idColor;
$structure['colors'][ $k ] = $n;
}
foreach($structure_object->WheelFormulaTypes->WheelFormulaType AS $wheel_type)
{
$k = mb_strtoupper((string) $wheel_type->sWheelFormulaType);
$n = (int) $wheel_type->idWheelFormulaType;
$structure['wheels'][ $k ] = $n;
}
foreach($structure_object->EngineTypesSpec->EngineTypeSpec AS $engine_type)
{
$k = mb_strtoupper((string) $engine_type->sEngineTypeSpec);
$n = (int) $engine_type->idEngineTypeSpec;
$structure['fuels'][ $k ] = $n;
}
$this->dictionaries[ $type ] = $structure;
}
}
function addOffer($entry)
{
$this->total++;
$type = $entry['PROPERTIES']['VEHICLE_SUBTYPE']['VALUE'];
//print $type."\n";
if($type === "Легковой автомобиль")
{
$this->found_cars++;
$this->buildOffer(false, [ 'group' => 'car' ], $entry);
/*
$idMark = $this->getDictionaryRecordId($this->dictionaries['car']['brands'], mb_strtoupper($entry['PROPERTIES']['BRAND']['RELATED']['NAME']));
//print $entry['CODE']." - ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." - ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']." - [".$idMark."] - [".$idModel."]\n";
//if(is_null($idMark)) { $this->errors .= "Не найдена марка ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']."\n"; continue; }
if($idMark !== null)
{
//* id марки авто (см. файл-справочник); если не можете указать id, то укажите название марки текстом в поле sMark
$offer['idMark'] = $idMark;
}
else
{
//* название марки авто (Toyota, Honda, BMW и т.п.); указывать обязательно только в том случае, если не можете указать idMark
$offer['sMark'] = $entry['PROPERTIES']['BRAND']['RELATED']['NAME'];
}
$idModel = $this->getDictionaryRecordId($this->dictionaries['car']['models'], mb_strtoupper($entry['PROPERTIES']['MODEL']['RELATED']['NAME']));
//if(is_null($idModel)) { $this->errors .= "Не найдена модель ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']." для ".".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']."."\n"; continue; }
if($idModel !== null)
{
//* id модели авто (см. файл-справочник); если не можете указать id, то укажите название модели текстом в поле sModel
$offer['idModel'] = $idModel;
}
else
{
//* название модели авто (Land Cruiser, Accord, X5 и т.п.); указывать обязательно только в том случае, если не можете указать idModel
$offer['sModel'] = $entry['PROPERTIES']['MODEL']['RELATED']['NAME'];
}
*/
/*
$payload = [
"idOffer" => $entry['CODE'], //* уникальный id объявления в вашей базе данных
"idMark" => "", //* id марки авто (см. файл-справочник); если не можете указать id, то укажите название марки текстом в поле sMark
"idModel" => "", //* id модели авто (см. файл-справочник); если не можете указать id, то укажите название модели текстом в поле sModel
"sMark" => "", //* название марки авто (Toyota, Honda, BMW и т.п.); указывать обязательно только в том случае, если не можете указать idMark
"sModel" => "", //* название модели авто (Land Cruiser, Accord, X5 и т.п.); указывать обязательно только в том случае, если не можете указать idModel
"idCountry" => "", //id страны, где находится авто (см. файл-справочник, по умолчанию — 0)
"idCity" => "", //id города, в котором находится авто (см. файл-справочник)
"sCity" => "", //название города, в котором находится авто (см. файл-справочник)
"YearOfMade" => "", //год выпуска ТС (только год, 4 цифры: 1995, 2003, 2018 и т.п.)
"VIN" => $entry['PROPERTIES']['VIN']['VALUE'], //* VIN или номер кузова, если VIN отсутствует
"Price" => $entry['PROPERTIES']['PRICE']['VALUE'], //цена в рублях
"NewType" => 0, //признак, что автомобиль новый; 1 — новый, 0 — не новый (с пробегом)
"Volume" => $entry['PROPERTIES']['ENGINE_VOLUME']['VALUE'], //объем двигателя в куб.см (пример: 599, 1300, 2631 и т.п.)
"FrameType" => "???", //тип кузова (см. файл-справочник)
"Color" => "???", //цвет кузова (см. файл-справочник)
"idTransmission" => "???", //id типа трансмиссии (см. файл-справочник)
"sTransmission" => "???", //название типа трансмиссии
"idEngineType" => "", //id типа двигателя (см. файл-справочник)
"sEngineType" => "", //название типа двигателя
"idHybridType" => "", //гибрид (см. файл-справочник)
"sHybridType" => "", //гибрид (да, нет)
"idGbo" => "", //ГБО (см. файл-справочник)
"sGbo" => "", //ГБО (да, нет)
"idDriveType" => "???", //id типа двигателя (см. файл-справочник)
"sDriveType" => "", //название типа двигателя
"sWheelType" => "", //положение руля (левый, правый)
"Haul" => "", //пробег (показания одометра в километрах)
"sComplectation" => "", //название комплектации (в соответствии с каталогом)
"idComplectation" => "", //id комплектации на Дроме (в соответствии с каталогом)
"Additional" => "", //текстовое описание ТС (комплектация, оснащение, тюнинг и т.п.), здесь можно указать сссылку на видео, размещенное на YouTube
"Photos" => "", //секция со списком фотографий (ниже есть подробное описание секции)
"Phone" => "", //номер телефона (поле не должно быть больше 20 символов)
"Whereabouts" => "", //авто в наличии или под заказ (см. файл-справочник)
"Power" => $entry['PROPERTIES']['ENGINE_POWER']['VALUE'], //мощность автомобиля в лошадиных силах
"idCertProgram" => "", //программа сертификации (см. файл-справочник)
"DamagedType" => "", //признак для поврежденных автомобилей (0 - небитые, 1 - битые или не на ходу)
"SOR" => "", //номер свидетельства о регистрации ТС для авто с пробегом (10 символов)
];
*/
}
else
{
$this->found_special++;
$frame = $this->getFrameType(mb_strtoupper($type), mb_strtoupper($entry['PROPERTIES']['MODEL']['RELATED']['NAME']));
if(!is_null($frame))
{
$this->buildOffer(true, $frame, $entry);
}
else
{
$this->invalid_special++;
print $this->invalid_special." НЕ УДАЛОСЬ ОПРЕДЕЛИТЬ ТИП: ".mb_strtoupper($type)." \t\t[ ".$entry['CODE']." ][ ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." ][ ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']." ]\n";
//$this->not_found_special++;
}
}
}
private function buildOffer($special = false, $frame, $entry)
{
//print $entry['CODE']." - ".$type."\n";
$payload = [
"idOffer" => $entry['CODE'], //* уникальный id объявления в вашей базе данных
"YearOfMade" => $entry['PROPERTIES']['YEAR']['VALUE'], //год выпуска ТС (только год, 4 цифры: 1995, 2003, 2018 и т.п.)
"VIN" => $entry['PROPERTIES']['VIN']['VALUE'], //* VIN или номер кузова, если VIN отсутствует
"Price" => $entry['PROPERTIES']['PRICE']['VALUE'], //цена в рублях
"NewType" => 0, //признак, что автомобиль новый; 1 — новый, 0 — не новый (с пробегом)
"Volume" => $entry['PROPERTIES']['ENGINE_VOLUME']['VALUE'], //объем двигателя в куб.см (пример: 599, 1300, 2631 и т.п.)
"Power" => $entry['PROPERTIES']['ENGINE_POWER']['VALUE'], //мощность автомобиля в лошадиных силах
];
if($special)
{
$payload['idFrameTypeSpec'] = $frame['id'];
$payload['idWheelType'] = 2;
if(in_array($frame['group'], [ "bus", "motorhome" ]))
{
$this->invalid_special++;
print $this->invalid_special." НЕВОЗМОЖНО УКАЗААТЬ DriveTypeSpec: ".$entry['CODE']." ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']."\n";
return;
}
if(in_array($frame['group'], [ "trailer", "semi_trailer" ]))
{
$this->invalid_special++;
print $this->invalid_special." НЕВОЗМОЖНО УКААЗАТЬ КОЛИЧЕСТВО ОСЕЙ Axles: ".$entry['CODE']." ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']."\n";
return;
}
if(in_array($frame['group'], [ "truck", "truck_tractor", "mobile_crane", "municipal", ]))
{
$item_name = str_replace("Х", "X", mb_strtoupper($entry['PROPERTIES']['MODEL']['RELATED']['NAME']));
preg_match('/\dX\d/', $item_name, $wheel_type_matches);
$idWheelFormulaType = $this->getDictionaryRecordId($this->dictionaries[ $frame['group'] ]['wheels'], $wheel_type_matches[0]);
if(!is_null($idWheelFormulaType))
{
$payload['idWheelFormulaType'] = $idWheelFormulaType;
}
else
{
$this->invalid_special++;
print $this->invalid_special." НЕТ КОЛЕСНОЙ ФОРМУЛЫ: ".$entry['CODE']." - ".$item_name." [".$wheel_type_matches[0]."]\n";
return;
}
}
if(in_array($frame['group'], [ "bus", "truck", "truck_tractor", "mobile_crane", "loader", "municipal", "atv", "motorhome" ]))
{
if(!empty($entry['PROPERTIES']['ENGINE_FUEL']['VALUE']))
{
$f = mb_strtoupper($entry['PROPERTIES']['ENGINE_FUEL']['VALUE']);
if(isset($this->fuels[$f]))
{
$idEngineTypeSpec = $this->getDictionaryRecordId($this->dictionaries[ $frame['group'] ]['fuels'], $this->fuels[$f]);
if(!is_null($idEngineTypeSpec))
{
$payload['idEngineTypeSpec'] = $idEngineTypeSpec;
}
}
}
}
}
if($special)
{
$patterns = array('/\s\dX\d/i', '/\s\dХ\d/i', '/САМОСВАЛ/i');
$replacements = array('', '', '');
$model_name = preg_replace($patterns, $replacements, mb_strtoupper($entry['PROPERTIES']['MODEL']['RELATED']['NAME']));
$model_name = ltrim(rtrim($model_name));
$model_name_chunks = explode(" ", $model_name);
array_unshift($model_name_chunks, implode('', $model_name_chunks));
$idModel = null;
foreach($model_name_chunks AS $c)
{
$idModel = $this->getDictionaryRecordId($this->dictionaries[ $frame['group'] ]['models'], $c);
if(!is_null($idModel))
{
break;
}
}
if(!is_null($idModel))
{
$payload['idModelSpec'] = $idModel;
}
else
{
$this->invalid_special++;
print $this->invalid_special." НЕВОЗМОЖНО ОПРЕДЕЛИТЬ МОДЕЛЬ ДЛЯ МАРКИ ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME'].": ".$entry['CODE']." - ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']."\n";
return;
}
}
else
{
$idMark = $this->findInDictionaryRecordId($this->dictionaries[ $frame['group'] ]['brands'], mb_strtoupper($entry['PROPERTIES']['BRAND']['RELATED']['NAME']));
//print $type." - ".$frame['group']." - ".$idMark['id']."\n";
if(!is_null($idMark))
{
if(!$special) { $payload['idMark'] = $idMark['id']; }
$this->p1++;
$patterns = array('/\s\dX\d/i', '/\s\dХ\d/i', '/САМОСВАЛ/i');
$replacements = array('', '', '');
$model_name = preg_replace($patterns, $replacements, mb_strtoupper($entry['PROPERTIES']['MODEL']['RELATED']['NAME']));
$model_name = ltrim(rtrim($model_name));
$model_name_chunks = explode(" ", $model_name);
array_unshift($model_name_chunks, implode('', $model_name_chunks));
$idModel = null;
foreach($model_name_chunks AS $c)
{
$idModel = $this->getDictionaryRecordId($this->dictionaries[ $frame['group'] ]['brands'][ mb_strtoupper($entry['PROPERTIES']['BRAND']['RELATED']['NAME']) ]['models'], $c);
if(!is_null($idModel))
{
$idModel = $this->findInDictionaryRecordId($this->dictionaries[ $frame['group'] ]['brands'][ mb_strtoupper($entry['PROPERTIES']['BRAND']['RELATED']['NAME']) ]['models'], mb_strtoupper($c));
if(!is_null($idModel))
{
break;
}
}
}
if(!is_null($idModel))
{
$payload['idModel'] = $idModel;
}
else
{
$payload['sModel'] = $model_name;
}
}
else
{
$this->p2++;
if(!$special)
{
$payload['sMark'] = $entry['PROPERTIES']['BRAND']['RELATED']['NAME'];
$payload['sModel'] = $entry['PROPERTIES']['MODEL']['RELATED']['NAME'];
}
else
{
$this->invalid_special++;
print $this->invalid_special." НЕВОЗМОЖНО ОПРЕДЕЛИТЬ МАРКУ: ".$entry['CODE']." (ТИП ".$frame['group'].") - ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']."\n";
return;
}
//print $entry['PROPERTIES']['BRAND']['RELATED']['NAME']."\n";
}
}
$idCity = $this->getDictionaryRecordId($this->dictionaries[ $frame['group'] ]['cities'], mb_strtoupper($entry['PROPERTIES']['PARKING_CITY']['VALUE']));
if($idCity !== null)
{
//* id города, в котором находится авто (см. файл-справочник)
$payload['idCity'] = $idCity;
}
else
{
if(!$special)
{
//* название города, в котором находится авто (см. файл-справочник)
$payload['sCity'] = $entry['PROPERTIES']['PARKING_CITY']['VALUE'];
}
else
{
$this->invalid_special++;
print $this->invalid_special." НЕВОЗМОЖНО ОПРЕДЕЛИТЬ ГОРОД: ".$entry['CODE']." ".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']." ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']."\n";
return;
}
//if(is_empty($idModel)) { $this->errors .= "Не найдена модель ".$entry['PROPERTIES']['MODEL']['RELATED']['NAME']." для ".".$entry['PROPERTIES']['BRAND']['RELATED']['NAME']."."\n"; continue; }
}
//"idCountry" => "", //id страны, где находится авто (см. файл-справочник, по умолчанию — 0)
//"idCity" => "", //id города, в котором находится авто (см. файл-справочник)
//"sCity" => "", //название города, в котором находится авто (см. файл-справочник)
if($special)
{
$this->valid_special++;
}
else
{
$this->valid_cars++;
}
$offer = $special ? $this->offers_special->addChild('SpecOffer') : $this->offers_cars->addChild('Offer');
foreach($payload AS $k => $v)
{
$offer->addChild($k, $v);
}
if(count($entry['PHOTOS_1080']) > 0)
{
$photos = $offer->addChild("Photos");
$photos->addAttribute('PhotoMain', "https://".SITE_SERVER_NAME.$entry['PHOTOS_1080'][0]);
foreach($entry['PHOTOS_1080'] AS $photo)
{
$photos->addChild("Photo", "https://".SITE_SERVER_NAME.$photo);
}
}
//print $type." MATCH"."\n";
}
private function normalize_string($str)
{
return str_replace(["Ё", "C"], ["Е", "С"], $str);
}
private function getFrameType($type, $model)
{
if($type === "ПРИЦЕП") { $type = "ДРУГИЕ ПРИЦЕПЫ"; }
if($type === "ТЯГАЧ") { $type = "СЕДЕЛЬНЫЙ ТЯГАЧ"; }
if(isset($this->special_frames[ $type ]))
{
return $this->special_frames[ $type ];
}
else
{
$found = [];
foreach($this->special_frames AS $k => $v)
{
if(strpos($this->normalize_string($model), $k) > -1)
{
$found[ strlen($k) ] = $this->special_frames[ $k ];
}
}
if(count($found) > 0)
{
krsort($found);
// print_r($found);
foreach($found AS $f)
{
return $f;
}
// return $found[0];
}
}
//print "[".$this->normalize_string(mb_strtoupper($k))."] <---\n";
return null;
}
private function getDictionaryRecordId($branch, $name)
{
if(isset($branch[ $name ]))
{
return $branch[ $name ];
}
return null;
}
private function findInDictionaryRecordId($branch, $name)
{
foreach($branch AS $k => $v)
{
if(strpos($name, $k) > -1)
{
return $v;
}
}
return null;
}
function save()
{
if($this->total > 0)
{
print "ИТОГО: валидно ".$this->valid_cars + $this->valid_special." из ".$this->total." - ".number_format(($this->valid_cars + $this->valid_special) / (($this->found_cars + $this->found_special) / 100), 2, ".")."%\n";
if($this->found_cars > 0)
{
print "ЛКТ: валидно ".$this->valid_cars." из ".$this->found_cars." - ".number_format($this->valid_cars / ($this->found_cars / 100), 2, ".")."%\n";
}
if($this->found_special > 0)
{
print "СПЕЦТЕХНИКА: валидно ".$this->valid_special." из ".$this->found_special." - ".number_format($this->valid_special / ($this->found_special / 100), 2, ".")."%\n";
}
file_put_contents("/home/bitrix/www/drom-offers.xml", $this->xml_cars->asXML());
file_put_contents("/home/bitrix/www/drom-offers-special.xml", $this->xml_special->asXML());
}
}
private function loadTypes()
{
$types_url = "https://www.drom.ru/cached_files/autoload/files/ref_frame_types.xml";
$response_types_xml = file_get_contents($types_url);
file_put_contents("/home/bitrix/www/local/ref_frame_types.xml", $response_types_xml);
$types_object = new SimpleXMLElement($response_types_xml);
foreach($types_object AS $group => $branch)
{
foreach($branch AS $spec)
{
$g = (string) $group;
$k = (string) $spec->sFrameTypeSpec;
$n = (int) $spec->idFrameTypeSpec;
//print $g." - ".$k."\n";
$this->special_frames[ mb_strtoupper($k) ] = [
"group" => $this->special_frames_mapping[ $g ],
"id" => $n,
];
}
}
}
}