| Server IP : 188.114.96.3 / Your IP : 216.73.216.143 Web Server : Apache System : Linux sv058.home.net.pl 6.8.2 husak_dirty_pages_fix #1 SMP PREEMPT_DYNAMIC Tue Apr 2 22:06:51 CEST 2024 x86_64 User : ( 3194769) PHP Version : 8.5.12 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /polana/includes/ |
Upload File : |
<?php
declare(strict_types=1);
require_once __DIR__.'/tklm_v9.php';
require_once __DIR__.'/polana_v15_menu.php';
if(is_file(__DIR__.'/polana_v14_names.php'))require_once __DIR__.'/polana_v14_names.php';
function p15_active_menu_ids():array {
// Exactly one valid, current, latest published card per type; never process historical duplicates.
$out=[];$today=date('Y-m-d');
foreach(['food','soft','wine'] as $type){
$r=p15_active_menu($type);
if(!$r || ($r['valid_from'] && $r['valid_from']>$today) || ($r['valid_to'] && $r['valid_to']<$today))continue;
$out[(int)$r['id']]=['id'=>(int)$r['id'],'menu_type'=>$type,'name'=>(string)$r['name'],'status'=>(string)$r['status']];
}
return $out;
}
function p15_fields_rows(int $menuId):array {
$cat=db()->prepare('SELECT * FROM menu_categories WHERE menu_id=? AND active=1 ORDER BY sort_order,id');
$cat->execute([$menuId]);$rows=[];
foreach($cat->fetchAll(PDO::FETCH_ASSOC) as $c){
$rows[]=['table'=>'menu_categories','id'=>(int)$c['id'],'field'=>'name','row'=>$c];
$items=db()->prepare('SELECT * FROM menu_items WHERE category_id=? AND active=1 AND available=1 ORDER BY sort_order,id');
$items->execute([$c['id']]);
foreach($items->fetchAll(PDO::FETCH_ASSOC) as $i){
foreach(['name','description','allergens'] as $field){
if(trim((string)($i[$field]??''))!=='')$rows[]=['table'=>'menu_items','id'=>(int)$i['id'],'field'=>$field,'row'=>$i];
}
}
}
return $rows;
}
function p15_scan_menu(int $menuId,array $langs,bool $force=false):array {
if(!array_key_exists($menuId,p15_active_menu_ids()))throw new RuntimeException('Tylko aktualne opublikowane karty');
v9_init();$created=0;$missing=0;$skipped=0;
$langs=array_values(array_intersect(['en','de','es'],$langs));
if(!$langs)throw new RuntimeException('Zaznacz język');
$old=db()->prepare('SELECT source_hash,status FROM tklm_v9_jobs WHERE entity=? AND entity_id=? AND field=? AND lang=?');
$reset=db()->prepare("UPDATE tklm_v9_jobs SET status='pending',attempts=0,translated_text=NULL,last_error=NULL WHERE entity=? AND entity_id=? AND field=? AND lang=?");
foreach(p15_fields_rows($menuId) as $row){
$source=trim((string)($row['row'][$row['field']]??''));
if($source==='')continue;
foreach($langs as $lang){
$localized=trim((string)($row['row'][$row['field'].'_'.$lang]??''));
$old->execute([$row['table'],$row['id'],$row['field'],$lang]);
$job=$old->fetch(PDO::FETCH_ASSOC);
$changed=$job&&$job['source_hash']!==hash('sha256',$source);
if($localized==='')$missing++;
// Don't replace approved human edits unless the operator explicitly requests retranslation.
if(!$force && !$changed && $localized!==''){$skipped++;continue;}
if(!$force && !$changed && $job && in_array($job['status'],['pending','review'],true)){$skipped++;continue;}
v9_enqueue($row['table'],$row['id'],$row['field'],$lang,$source);
if($force){$reset->execute([$row['table'],$row['id'],$row['field'],$lang]);}
$created++;
}
}
return compact('created','missing','skipped');
}
function p15_menu_queue_stats(int $menuId):array {
$q=db()->prepare("SELECT j.status,COUNT(*) n FROM tklm_v9_jobs j
LEFT JOIN menu_categories c ON j.entity='menu_categories' AND c.id=j.entity_id
LEFT JOIN menu_items i ON j.entity='menu_items' AND i.id=j.entity_id
WHERE ((j.entity='menu_categories' AND c.menu_id=? AND c.active=1) OR (j.entity='menu_items' AND i.category_id IN (SELECT id FROM menu_categories WHERE menu_id=? AND active=1) AND i.active=1 AND i.available=1))
GROUP BY j.status");
$q->execute([$menuId,$menuId]);$stats=['pending'=>0,'review'=>0,'approved'=>0,'failed'=>0];
foreach($q->fetchAll(PDO::FETCH_ASSOC) as $r)$stats[$r['status']]=(int)$r['n'];
$q=db()->prepare("SELECT COUNT(*) FROM tklm_v9_jobs j
LEFT JOIN menu_categories c ON j.entity='menu_categories' AND c.id=j.entity_id
LEFT JOIN menu_items i ON j.entity='menu_items' AND i.id=j.entity_id
WHERE ((j.entity='menu_categories' AND c.menu_id=? AND c.active=1) OR (j.entity='menu_items' AND i.category_id IN (SELECT id FROM menu_categories WHERE menu_id=? AND active=1) AND i.active=1 AND i.available=1))
AND j.status='pending' AND j.attempts>=5");
$q->execute([$menuId,$menuId]);$stats['failed']=(int)$q->fetchColumn();
$stats['eligible']=max(0,$stats['pending']-$stats['failed']);
$stats['fields']=[];
foreach(['en','de','es'] as $lang){
$all=0;$missing=0;foreach(p15_fields_rows($menuId) as $row){
$all++;if(trim((string)($row['row'][$row['field'].'_'.$lang]??''))==='')$missing++;
}
$stats['fields'][$lang]=['total'=>$all,'missing'=>$missing];
}
return $stats;
}
function p15_process_menu_one(int $menuId):array {
if(!array_key_exists($menuId,p15_active_menu_ids()))throw new RuntimeException('Karta nie jest aktywna');
v9_init();$db=db();
if((int)$db->query("SELECT GET_LOCK('polana_v9_browser_batch',0)")->fetchColumn()!==1)return ['busy'=>true];
try{
$q=$db->prepare("SELECT j.* FROM tklm_v9_jobs j
LEFT JOIN menu_categories c ON j.entity='menu_categories' AND c.id=j.entity_id
LEFT JOIN menu_items i ON j.entity='menu_items' AND i.id=j.entity_id
WHERE ((j.entity='menu_categories' AND c.menu_id=? AND c.active=1) OR (j.entity='menu_items' AND i.category_id IN (SELECT id FROM menu_categories WHERE menu_id=? AND active=1) AND i.active=1 AND i.available=1))
AND j.status='pending' AND j.attempts<5 ORDER BY j.updated_at,j.id LIMIT 1");
$q->execute([$menuId,$menuId]);$job=$q->fetch(PDO::FETCH_ASSOC);
if(!$job)return ['busy'=>false,'empty'=>true];
$table=$job['entity'];$field=$job['field'];$lang=$job['lang'];
$allowed=['menu_items'=>['name','description','allergens'],'menu_categories'=>['name']];
if(!isset($allowed[$table])||!in_array($field,$allowed[$table],true)||!in_array($lang,['en','de','es'],true))throw new RuntimeException('Błędne zadanie');
$check=$db->prepare("SELECT `$field` FROM `$table` WHERE id=?");$check->execute([$job['entity_id']]);$source=$check->fetchColumn();
if(!is_string($source)||hash('sha256',trim($source))!==$job['source_hash']){
$db->prepare("UPDATE tklm_v9_jobs SET status='skipped',last_error='Oryginał zmienił się' WHERE id=?")->execute([$job['id']]);
return ['busy'=>false,'skipped'=>true];
}
try {
$key=v9_resolve_translate_key();$api=new TklmTranslateClient($key);
$result=function_exists('p14_translate')?p14_translate($api,$source,$lang):trim((string)($api->translate($source,'pl',$lang)['translatedText']??''));
if($result==='')throw new RuntimeException('Puste tłumaczenie');
$save=$db->prepare("UPDATE tklm_v9_jobs SET status='review',translated_text=?,last_error=NULL WHERE id=? AND status='pending' AND source_hash=?");
$save->execute([$result,$job['id'],$job['source_hash']]);
return ['busy'=>false,'translated'=>true,'language'=>$lang];
}catch(Throwable $e){
error_log('Polana V15 menu translate: '.$e->getMessage());
$save=$db->prepare("UPDATE tklm_v9_jobs SET attempts=attempts+1,last_error=? WHERE id=?");
$save->execute([substr($e->getMessage(),0,500),$job['id']]);
return [
'busy'=>false,
'error'=>'Nie udało się przetłumaczyć zadania',
'job_id'=>(int)$job['id'],
'lang'=>$lang,
'kind'=>preg_match('/(?:HTTP\s+(?:401|403|429|5\d\d)|Polaczenie Translate|timed out|Could not resolve|SSL|connection|nie znaleziono klucza|cURL)/iu',$e->getMessage())?'api':'content',
'detail'=>mb_substr(preg_replace('/(?:[a-f0-9]{48})/i','[ukryty klucz]',$e->getMessage()),0,250,'UTF-8')
];
}
}finally{$db->query("SELECT RELEASE_LOCK('polana_v9_browser_batch')");}
}