Adb netzgebiet/add manual rimo import
This commit is contained in:
@@ -24,6 +24,9 @@ class ADBNetzgebietController extends mfBaseController {
|
||||
"GET_URL" => $this::getUrl("ADBNetzgebiet/getNetzgebiete"),
|
||||
"SAVE_URL" => $this::getUrl("ADBNetzgebiet/save"),
|
||||
"HISTORY_URL" => $this::getUrl("ADBNetzgebiet/getHistory"),
|
||||
"START_RIMO_IMPORT_URL" => $this::getUrl("ADBNetzgebiet/startRimoImport"),
|
||||
"GET_RIMO_IMPORT_STATUS_URL" => $this::getUrl("ADBNetzgebiet/getRimoImportStatus"),
|
||||
"GET_RIMO_IMPORT_LOG_URL" => $this::getUrl("ADBNetzgebiet/getRimoImportLog"),
|
||||
"NETWORK_URL" => $this::getUrl("Network/Index"),
|
||||
"NETWORK_CREATE_URL" => $this::getUrl("Network/add"),
|
||||
"CAMPAIGN_URL" => $this::getUrl("Preordercampaign/edit"),
|
||||
@@ -130,6 +133,193 @@ class ADBNetzgebietController extends mfBaseController {
|
||||
self::returnJson(['success' => true, 'data' => $history]);
|
||||
}
|
||||
|
||||
protected function startRimoImportAction(): void {
|
||||
$id = $_GET['id'] ?? null;
|
||||
if (empty($id)) {
|
||||
self::returnJson(['success' => false, 'message' => "Netzgebiet ID required."]);
|
||||
return;
|
||||
}
|
||||
|
||||
$netzgebiet = ADBNetzgebiet::get($id);
|
||||
if (!$netzgebiet || !$netzgebiet->id) {
|
||||
self::returnJson(['success' => false, 'message' => "Netzgebiet not found."]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos($netzgebiet->source, 'rimo-') !== 0) {
|
||||
self::returnJson(['success' => false, 'message' => "This action is only for RIMO-source Netzgebiete."]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($netzgebiet->source_id)) {
|
||||
self::returnJson(['success' => false, 'message' => "Netzgebiet has no Source ID."]);
|
||||
return;
|
||||
}
|
||||
|
||||
$safeSourceId = preg_replace('/[^a-zA-Z0-9_-]/', '_', $netzgebiet->source_id);
|
||||
$importTempDir = TEMP_DIR . "/ADBNetzgebietRimoImport/";
|
||||
$logDir = $importTempDir . $safeSourceId;
|
||||
$logFile = $logDir . "/import.log";
|
||||
$lockFile = $logDir . "/import.lock";
|
||||
|
||||
if (is_dir($importTempDir)) {
|
||||
foreach (glob($importTempDir . "*") as $dir) {
|
||||
if (is_dir($dir) && (time() - filemtime($dir)) > 86400) {
|
||||
// simple cleanup
|
||||
if (file_exists($dir . "/import.log")) @unlink($dir . "/import.log");
|
||||
if (file_exists($dir . "/import.lock")) @unlink($dir . "/import.lock");
|
||||
@rmdir($dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_dir($logDir)) {
|
||||
mkdir($logDir, 0755, true);
|
||||
}
|
||||
|
||||
if (file_exists($lockFile)) {
|
||||
if ((time() - filemtime($lockFile)) > 3600) { // stale lock for 1h
|
||||
@unlink($lockFile);
|
||||
} else {
|
||||
self::returnJson(['success' => false, 'message' => "Import is already running.", 'status' => 'running']);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (file_exists($logFile) && (time() - filemtime($logFile)) < 900) {
|
||||
$remaining = 900 - (time() - filemtime($logFile));
|
||||
self::returnJson(['success' => false, 'message' => "Please wait before starting another import.", 'status' => 'cooldown', 'remaining' => $remaining]);
|
||||
return;
|
||||
}
|
||||
|
||||
touch($lockFile);
|
||||
|
||||
$projectRoot = dirname(dirname(__DIR__));
|
||||
$scriptRelativePath = 'scripts/adb-rimo-import/rimo-import.php';
|
||||
$scriptFullPath = $projectRoot . '/' . $scriptRelativePath;
|
||||
|
||||
if (!file_exists($scriptFullPath)) {
|
||||
self::returnJson(['success' => false, 'message' => "Import script not found."]);
|
||||
return;
|
||||
}
|
||||
|
||||
$php_executable = "php";
|
||||
$command = "$php_executable $scriptRelativePath " . escapeshellarg($netzgebiet->source_id);
|
||||
|
||||
$bgCommand = 'cd ' . escapeshellarg($projectRoot) . ' && ' . $command . ' > ' . escapeshellarg($logFile) . ' 2>&1 & echo $!';
|
||||
$pid = shell_exec($bgCommand);
|
||||
|
||||
if(empty($pid) || !is_numeric(trim($pid))) {
|
||||
self::returnJson(['success' => false, 'message' => "Failed to start background process."]);
|
||||
return;
|
||||
}
|
||||
|
||||
file_put_contents($lockFile, trim($pid));
|
||||
|
||||
self::returnJson(['success' => true, 'message' => 'RIMO import started.']);
|
||||
}
|
||||
|
||||
protected function getRimoImportStatusAction(): void {
|
||||
$ids = $this->postData['ids'] ?? [];
|
||||
if (empty($ids)) {
|
||||
self::returnJson(['success' => true, 'data' => []]);
|
||||
return;
|
||||
}
|
||||
|
||||
$statuses = [];
|
||||
foreach ($ids as $id) {
|
||||
$netzgebiet = ADBNetzgebiet::get($id);
|
||||
if (!$netzgebiet || !$netzgebiet->id || strpos($netzgebiet->source, 'rimo-') !== 0 || empty($netzgebiet->source_id)) {
|
||||
$statuses[$id] = ['status' => 'not_applicable'];
|
||||
continue;
|
||||
}
|
||||
|
||||
$safeSourceId = preg_replace('/[^a-zA-Z0-9_-]/', '_', $netzgebiet->source_id);
|
||||
$logDir = TEMP_DIR . "/ADBNetzgebietRimoImport/" . $safeSourceId;
|
||||
$logFile = $logDir . "/import.log";
|
||||
$lockFile = $logDir . "/import.lock";
|
||||
|
||||
if (file_exists($lockFile)) {
|
||||
$pid = trim(file_get_contents($lockFile));
|
||||
// Check if process is still running. posix_getpgid returns false if process does not exist.
|
||||
if (is_numeric($pid) && posix_getpgid((int)$pid) !== false) {
|
||||
$statuses[$id] = ['status' => 'running'];
|
||||
} else {
|
||||
// Stale lock file, process is gone.
|
||||
@unlink($lockFile);
|
||||
// Check for cooldown based on log file from the finished process
|
||||
if (file_exists($logFile) && (time() - filemtime($logFile)) < 900) {
|
||||
$statuses[$id] = [
|
||||
'status' => 'cooldown',
|
||||
'remaining' => 900 - (time() - filemtime($logFile))
|
||||
];
|
||||
} else {
|
||||
$statuses[$id] = ['status' => 'idle'];
|
||||
}
|
||||
}
|
||||
} elseif (file_exists($logFile) && (time() - filemtime($logFile)) < 900) {
|
||||
$statuses[$id] = [
|
||||
'status' => 'cooldown',
|
||||
'remaining' => 900 - (time() - filemtime($logFile))
|
||||
];
|
||||
} else {
|
||||
$statuses[$id] = ['status' => 'idle'];
|
||||
}
|
||||
}
|
||||
self::returnJson(['success' => true, 'data' => $statuses]);
|
||||
}
|
||||
|
||||
protected function getRimoImportLogAction(): void {
|
||||
$id = $_GET['id'] ?? null;
|
||||
if (empty($id)) {
|
||||
self::returnJson(['success' => false, 'message' => "Netzgebiet ID required."]);
|
||||
return;
|
||||
}
|
||||
|
||||
$netzgebiet = ADBNetzgebiet::get($id);
|
||||
if (!$netzgebiet || !$netzgebiet->id || empty($netzgebiet->source_id)) {
|
||||
self::returnJson(['success' => false, 'message' => "Netzgebiet not found or not applicable."]);
|
||||
return;
|
||||
}
|
||||
|
||||
$safeSourceId = preg_replace('/[^a-zA-Z0-9_-]/', '_', $netzgebiet->source_id);
|
||||
$logDir = TEMP_DIR . "/ADBNetzgebietRimoImport/" . $safeSourceId;
|
||||
$logFile = $logDir . "/import.log";
|
||||
$lockFile = $logDir . "/import.lock";
|
||||
|
||||
$logContent = "";
|
||||
if (file_exists($logFile)) {
|
||||
$logContent = file_get_contents($logFile);
|
||||
}
|
||||
|
||||
$status = 'idle';
|
||||
if (file_exists($lockFile)) {
|
||||
$pid = trim(file_get_contents($lockFile));
|
||||
if (is_numeric($pid) && posix_getpgid((int)$pid) !== false) {
|
||||
$status = 'running';
|
||||
} else {
|
||||
@unlink($lockFile); // Stale lock, process is gone
|
||||
}
|
||||
}
|
||||
|
||||
if ($status !== 'running') {
|
||||
if (file_exists($logFile) && (time() - filemtime($logFile)) < 900) {
|
||||
$status = 'cooldown';
|
||||
} else {
|
||||
$status = file_exists($logFile) ? 'finished' : 'idle';
|
||||
}
|
||||
}
|
||||
|
||||
self::returnJson([
|
||||
'success' => true,
|
||||
'data' => [
|
||||
'log' => $logContent,
|
||||
'status' => $status,
|
||||
'timestamp' => file_exists($logFile) ? filemtime($logFile) : null
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
// TODO: Implement RIMO API check
|
||||
protected function checkRimoSourceIdAction(): void {
|
||||
self::returnJson(['success' => false, 'message' => "RIMO API check not available."]);
|
||||
|
||||
@@ -133,6 +133,14 @@ const ADBNetzgebiet = {
|
||||
</a>
|
||||
</td>
|
||||
<td class="col-actions">
|
||||
<button
|
||||
v-if="item.netzgebiet.source && item.netzgebiet.source.startsWith('rimo-')"
|
||||
class="icon-btn"
|
||||
@click.prevent="handleRimoImportClick(item)"
|
||||
:title="getImportButtonTitle(item.netzgebiet.id)"
|
||||
:disabled="getImportButtonDisabled(item.netzgebiet.id)">
|
||||
<i class="fa-duotone" :class="getImportButtonIcon(item.netzgebiet.id)"></i>
|
||||
</button>
|
||||
<button class="icon-btn" @click.prevent="openEditModal(item)" title="Bearbeiten"><i class="fa-duotone fa-pen"></i></button>
|
||||
<button class="icon-btn" @click.prevent="copyNetzgebiet(item)" title="Kopieren"><i class="fa-duotone fa-copy"></i></button>
|
||||
<button class="icon-btn" @click.prevent="openHistoryModal(item)" title="Verlauf"><i class="fa-duotone fa-clock-rotate-left"></i></button>
|
||||
@@ -274,6 +282,25 @@ const ADBNetzgebiet = {
|
||||
</div>
|
||||
</div>
|
||||
</tt-dialog>
|
||||
|
||||
<!-- RIMO Import Log Modal -->
|
||||
<tt-dialog :show="showRimoLogModal" :title="rimoLogTitle" size="large" @close="closeRimoLogModal">
|
||||
<div v-if="!rimoLogContent && rimoLogStatus === 'running'" class="table-placeholder compact">
|
||||
<i class="fa-duotone fa-spinner fa-spin"></i>
|
||||
<span>Lade Log...</span>
|
||||
</div>
|
||||
<div v-else-if="!rimoLogContent" class="table-placeholder compact">
|
||||
<i class="fa-duotone fa-file-lines"></i>
|
||||
<span>Kein Log vorhanden.</span>
|
||||
</div>
|
||||
<pre v-else class="log-view" style="white-space: pre-wrap; word-break: break-all; max-height: 60vh; overflow-y: auto; background: #f5f5f5; padding: 10px; border-radius: 5px;">{{ rimoLogContent }}</pre>
|
||||
<template #footer>
|
||||
<div class="footer-status" style="margin-right: auto; font-size: 12px; color: #666;">
|
||||
Status: <strong :style="{color: rimoLogStatus === 'running' ? 'blue' : (rimoLogStatus === 'cooldown' ? 'orange' : 'green')}">{{ rimoLogStatus }}</strong>
|
||||
</div>
|
||||
<button class="ghost-btn" @click="closeRimoLogModal">Schließen</button>
|
||||
</template>
|
||||
</tt-dialog>
|
||||
</div>
|
||||
`,
|
||||
|
||||
@@ -294,6 +321,16 @@ const ADBNetzgebiet = {
|
||||
historyItems: [],
|
||||
historyTitle: 'Verlauf',
|
||||
expandedIds: {},
|
||||
|
||||
// RIMO Import
|
||||
importStatus: {},
|
||||
showRimoLogModal: false,
|
||||
rimoLogContent: '',
|
||||
rimoLogTitle: '',
|
||||
rimoLogStatus: 'idle',
|
||||
rimoLogInterval: null,
|
||||
statusInterval: null,
|
||||
|
||||
freigabeLabels: { interest: 'Interest', provision: 'Provision', order: 'Order', reorder: 'Reorder' },
|
||||
freigabeOptions: [
|
||||
{ key: 'interest', label: 'Interest' },
|
||||
@@ -367,12 +404,21 @@ const ADBNetzgebiet = {
|
||||
filteredNetzgebiete() { if (this.currentPage > this.totalPages) this.currentPage = 1; }
|
||||
},
|
||||
|
||||
async mounted() { await this.fetchNetzgebiete(); },
|
||||
async mounted() {
|
||||
await this.fetchNetzgebiete();
|
||||
this.fetchImportStatus();
|
||||
this.statusInterval = setInterval(this.fetchImportStatus, 15000); // Poll every 15s
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
clearInterval(this.statusInterval);
|
||||
clearInterval(this.rimoLogInterval);
|
||||
},
|
||||
|
||||
methods: {
|
||||
formatConsentName(name) {
|
||||
if (name && name.startsWith('Glasfaserprojekt')) {
|
||||
return name.replace('Glasfaserprojekt', 'Glasfaserprojekt<br>');
|
||||
return name.replace('Glasfaserprojekt', 'Glasfaserprojekt<br />');
|
||||
}
|
||||
return name;
|
||||
},
|
||||
@@ -476,6 +522,96 @@ const ADBNetzgebiet = {
|
||||
} catch { window.notify?.('error', 'Verlauf konnte nicht geladen werden.'); }
|
||||
finally { this.historyLoading = false; }
|
||||
},
|
||||
|
||||
// RIMO Import Methods
|
||||
async fetchImportStatus() {
|
||||
const rimoIds = this.netzgebiete
|
||||
.filter(item => item.netzgebiet.source && item.netzgebiet.source.startsWith('rimo-'))
|
||||
.map(item => item.netzgebiet.id);
|
||||
if (!rimoIds.length) return;
|
||||
try {
|
||||
const response = await axios.post(window.TT_CONFIG.GET_RIMO_IMPORT_STATUS_URL, { ids: rimoIds });
|
||||
if (response.data.success) {
|
||||
this.importStatus = response.data.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Could not fetch RIMO import statuses.", error);
|
||||
}
|
||||
},
|
||||
handleRimoImportClick(item) {
|
||||
const status = this.importStatus[item.netzgebiet.id]?.status || 'idle';
|
||||
if (status === 'running') {
|
||||
this.openRimoLogModal(item);
|
||||
} else if (status === 'cooldown') {
|
||||
const remaining = this.importStatus[item.netzgebiet.id]?.remaining || 0;
|
||||
window.notify?.('info', `Bitte warten Sie noch ${Math.ceil(remaining / 60)} Minuten.`);
|
||||
this.openRimoLogModal(item);
|
||||
} else {
|
||||
this.startRimoImport(item);
|
||||
}
|
||||
},
|
||||
getImportButtonTitle(id) {
|
||||
const status = this.importStatus[id]?.status || 'idle';
|
||||
if (status === 'running') return 'Import-Log anzeigen';
|
||||
if (status === 'cooldown') return 'Manueller RIMO-Import (Abkühlphase)';
|
||||
return 'Manuellen RIMO-Import starten';
|
||||
},
|
||||
getImportButtonDisabled(id) {
|
||||
const status = this.importStatus[id]?.status || 'idle';
|
||||
return false; // Never truly disabled, just changes action
|
||||
},
|
||||
getImportButtonIcon(id) {
|
||||
const status = this.importStatus[id]?.status || 'idle';
|
||||
if (status === 'running') return 'fa-spinner fa-spin';
|
||||
if (status === 'cooldown') return 'fa-hourglass-half';
|
||||
return 'fa-rocket';
|
||||
},
|
||||
async startRimoImport(item) {
|
||||
try {
|
||||
const response = await axios.get(window.TT_CONFIG.START_RIMO_IMPORT_URL + '?id=' + item.netzgebiet.id);
|
||||
if (response.data.success) {
|
||||
window.notify?.('success', 'RIMO Import gestartet.');
|
||||
this.importStatus[item.netzgebiet.id] = { status: 'running' };
|
||||
this.openRimoLogModal(item);
|
||||
} else {
|
||||
window.notify?.('error', response.data.message || 'Import konnte nicht gestartet werden.');
|
||||
}
|
||||
} catch (error) {
|
||||
window.notify?.('error', 'Fehler beim Starten des Imports.');
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
openRimoLogModal(item) {
|
||||
this.rimoLogTitle = `RIMO Import: ${item.netzgebiet.name}`;
|
||||
this.showRimoLogModal = true;
|
||||
this.fetchRimoLog(item); // initial fetch
|
||||
this.rimoLogInterval = setInterval(() => this.fetchRimoLog(item), 3000);
|
||||
},
|
||||
closeRimoLogModal() {
|
||||
this.showRimoLogModal = false;
|
||||
clearInterval(this.rimoLogInterval);
|
||||
this.rimoLogContent = '';
|
||||
this.rimoLogTitle = '';
|
||||
this.rimoLogStatus = 'idle';
|
||||
},
|
||||
async fetchRimoLog(item) {
|
||||
try {
|
||||
const response = await axios.get(window.TT_CONFIG.GET_RIMO_IMPORT_LOG_URL + '?id=' + item.netzgebiet.id);
|
||||
if (response.data.success) {
|
||||
this.rimoLogContent = response.data.data.log;
|
||||
this.rimoLogStatus = response.data.data.status;
|
||||
// If no longer running, stop polling
|
||||
if (this.rimoLogStatus !== 'running') {
|
||||
clearInterval(this.rimoLogInterval);
|
||||
this.fetchImportStatus(); // refresh overall status
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Could not fetch RIMO log.', error);
|
||||
clearInterval(this.rimoLogInterval);
|
||||
}
|
||||
},
|
||||
|
||||
translateAction(action) { return { create: 'Erstellt', update: 'Geändert', delete: 'Gelöscht' }[action] || action; },
|
||||
translateField(field) {
|
||||
return { name: 'Name', extref: 'ExtRef', source: 'Quelle', source_id: 'Source ID',
|
||||
|
||||
@@ -7,7 +7,7 @@ use ADBRimoImport\ADBAddressHelper;
|
||||
//use ADBRimoImport\importer\CitycomImporter;
|
||||
|
||||
//require 'vendor/autoload.php';
|
||||
require("../../config/config.php");
|
||||
require(__DIR__ . "/../../config/config.php");
|
||||
require("ADBAddressHelper/address_helper.php");
|
||||
|
||||
define('FRONKDB_SQLDEBUG', false);
|
||||
|
||||
Reference in New Issue
Block a user