Files
controls-web/controls-rework/includes/log_query.php
2026-02-17 09:29:34 -06:00

151 lines
4.6 KiB
PHP

<?php // phpcs:ignoreFile
/**
* Query logging helper for NL2SQL few-shot seeding.
*
* Usage:
* require_once __DIR__ . '/includes/log_query.php';
* logControlsQuery($userQuery, $sqlTemplate, $params, $sourceForm, $executionMs, $rowCount);
*/
// Logging database connection (separate from historian)
function getLoggingPdo(): ?PDO
{
static $pdo = null;
if ($pdo !== null) {
return $pdo;
}
$servername = '192.168.0.16';
$username = 'lasucaai';
$password = 'is413#dfslw';
$dbname = 'lasucaai';
try {
$pdo = new PDO(
"sqlsrv:Server=$servername;Database=$dbname",
$username,
$password,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
);
return $pdo;
} catch (PDOException $e) {
error_log('getLoggingPdo connection error: ' . $e->getMessage());
return null;
}
}
/**
* Log a query to dbo.controls_query_log for NL2SQL training.
*
* @param string $userQuery Natural-language description of the search.
* @param string $sqlTemplate Parameterized SQL with placeholders.
* @param array|null $params Associative array of parameter values.
* @param string|null $sourceForm Originating form identifier.
* @param int|null $executionMs Query runtime in milliseconds.
* @param int|null $rowCount Number of rows returned.
* @return bool True on success, false on failure.
*/
function logControlsQuery(
string $userQuery,
string $sqlTemplate,
?array $params = null,
?string $sourceForm = null,
?int $executionMs = null,
?int $rowCount = null
): bool {
// Silently skip if inputs are empty
if (trim($userQuery) === '' || trim($sqlTemplate) === '') {
return false;
}
$pdo = getLoggingPdo();
if ($pdo === null) {
return false;
}
$sql = "
INSERT INTO dbo.controls_query_log
(user_query, sql_template, params, source_form, user_session, execution_ms, row_count)
VALUES
(:user_query, :sql_template, :params, :source_form, :user_session, :execution_ms, :row_count)
";
try {
$stmt = $pdo->prepare($sql);
$result = $stmt->execute([
':user_query' => mb_substr($userQuery, 0, 1000),
':sql_template' => $sqlTemplate,
':params' => $params !== null ? json_encode($params, JSON_UNESCAPED_UNICODE) : null,
':source_form' => $sourceForm,
':user_session' => session_id() ?: null,
':execution_ms' => $executionMs,
':row_count' => $rowCount,
]);
return $result;
} catch (Throwable $e) {
return false;
}
}
/**
* Build a natural-language description from form inputs.
*
* @param string $searchType 'single_name' or 'multiple_names'.
* @param string $searchName Single tag name (if applicable).
* @param array $selectedTags Array of tag names for multi-tag search.
* @param string $startDate Start datetime string.
* @param string $endDate End datetime string.
* @param int $limit Row limit.
* @param int $timeInterval Sampling interval in seconds.
* @return string Human-readable query description.
*/
function buildUserQueryDescription(
string $searchType,
string $searchName,
array $selectedTags,
string $startDate,
string $endDate,
int $limit,
int $timeInterval
): string {
$parts = [];
// Tag(s)
if ($searchType === 'single_name' && $searchName !== '') {
$parts[] = "Show data for tag \"{$searchName}\"";
} elseif ($searchType === 'multiple_names' && !empty($selectedTags)) {
$tagList = implode(', ', array_map(fn($t) => "\"{$t}\"", $selectedTags));
$parts[] = "Compare tags {$tagList}";
} else {
return '';
}
// Date range
if ($startDate !== '' && $endDate !== '') {
$parts[] = "from {$startDate} to {$endDate}";
} elseif ($startDate !== '') {
$parts[] = "starting from {$startDate}";
} elseif ($endDate !== '') {
$parts[] = "up until {$endDate}";
}
// Sampling
if ($timeInterval > 1) {
if ($timeInterval >= 60) {
$minutes = (int) ($timeInterval / 60);
$parts[] = "sampled every {$minutes} minute" . ($minutes > 1 ? 's' : '');
} else {
$parts[] = "sampled every {$timeInterval} seconds";
}
}
// Limit
$parts[] = "limit {$limit} rows";
return implode(' ', $parts);
}