Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add graphs to the dashboard #84

Open
wants to merge 5 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
],
'routes' => [
['name' => 'dashboard#page', 'url' => '/', 'verb' => 'GET'],
['name' => 'dashboard#index', 'url' => '/api/dashboard', 'verb' => 'GET'],
['name' => 'dashboard#getCallStats', 'url' => '/api/dashboard/callstats', 'verb' => 'GET'],
['name' => 'dashboard#getJobStats', 'url' => '/api/dashboard/jobstats', 'verb' => 'GET'],
['name' => 'dashboard#getSyncStats', 'url' => '/api/dashboard/syncstats', 'verb' => 'GET'],
['name' => 'sources#test', 'url' => '/api/source-test/{id}', 'verb' => 'POST'],
['name' => 'sources#logs', 'url' => '/api/sources-logs/{id}', 'verb' => 'GET'],
['name' => 'jobs#run', 'url' => '/api/jobs-test/{id}', 'verb' => 'POST'],
Expand Down
126 changes: 100 additions & 26 deletions lib/Controller/DashboardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,30 @@
use OCA\OpenConnector\Db\EndpointMapper;
use OCA\OpenConnector\Db\JobMapper;
use OCA\OpenConnector\Db\MappingMapper;
use OCA\OpenConnector\Db\CallLogMapper;
use OCA\OpenConnector\Db\JobLogMapper;
use OCA\OpenConnector\Db\SynchronizationContractLogMapper;

/**
* @package OCA\OpenConnector\Controller
*/
class DashboardController extends Controller
{
private $synchronizationMapper;
private $sourceMapper;
private $synchronizationContractMapper;
private $consumerMapper;
private $endpointMapper;
private $jobMapper;
private $mappingMapper;

public function __construct(
$appName,
IRequest $request,
SynchronizationMapper $synchronizationMapper,
SourceMapper $sourceMapper,
SynchronizationContractMapper $synchronizationContractMapper,
ConsumerMapper $consumerMapper,
EndpointMapper $endpointMapper,
JobMapper $jobMapper,
MappingMapper $mappingMapper
private readonly SynchronizationMapper $synchronizationMapper,
private readonly SourceMapper $sourceMapper,
private readonly SynchronizationContractMapper $synchronizationContractMapper,
private readonly ConsumerMapper $consumerMapper,
private readonly EndpointMapper $endpointMapper,
private readonly JobMapper $jobMapper,
private readonly MappingMapper $mappingMapper,
private readonly CallLogMapper $callLogMapper,
private readonly JobLogMapper $jobLogMapper,
private readonly SynchronizationContractLogMapper $synchronizationContractLogMapper
) {
parent::__construct($appName, $request);
$this->synchronizationMapper = $synchronizationMapper;
$this->sourceMapper = $sourceMapper;
$this->synchronizationContractMapper = $synchronizationContractMapper;
$this->consumerMapper = $consumerMapper;
$this->endpointMapper = $endpointMapper;
$this->jobMapper = $jobMapper;
$this->mappingMapper = $mappingMapper;
}

/**
Expand Down Expand Up @@ -82,17 +76,97 @@ public function index(): JSONResponse
{
try {
$results = [
"synchronizations" => $this->synchronizationMapper->getTotalCallCount(),
"sources" => $this->sourceMapper->getTotalCallCount(),
"mappings" => $this->mappingMapper->getTotalCallCount(),
"synchronizations" => $this->synchronizationMapper->getTotalCallCount(),
"synchronizationContracts" => $this->synchronizationContractMapper->getTotalCallCount(),
"consumers" => $this->consumerMapper->getTotalCallCount(),
"endpoints" => $this->endpointMapper->getTotalCallCount(),
"jobs" => $this->jobMapper->getTotalCallCount(),
"mappings" => $this->mappingMapper->getTotalCallCount()
"endpoints" => $this->endpointMapper->getTotalCallCount()
];
return new JSONResponse($results);
} catch (\Exception $e) {
return new JSONResponse(['error' => $e->getMessage()], 500);
}
}

/**
* Get call statistics for the dashboard
*
* @NoAdminRequired
* @NoCSRFRequired
* @param string|null $from Start date in ISO format
* @param string|null $to End date in ISO format
* @return JSONResponse
*/
public function getCallStats(?string $from = null, ?string $to = null): JSONResponse
{
try {
$fromDate = $from ? new \DateTime($from) : (new \DateTime())->modify('-7 days');
$toDate = $to ? new \DateTime($to) : new \DateTime();

$dailyStats = $this->callLogMapper->getCallStatsByDateRange($fromDate, $toDate);
$hourlyStats = $this->callLogMapper->getCallStatsByHourRange($fromDate, $toDate);

return new JSONResponse([
'daily' => $dailyStats,
'hourly' => $hourlyStats
]);
} catch (\Exception $e) {
return new JSONResponse(['error' => $e->getMessage()], 500);
}
}

/**
* Get job statistics for the dashboard
*
* @NoAdminRequired
* @NoCSRFRequired
* @param string|null $from Start date in ISO format
* @param string|null $to End date in ISO format
* @return JSONResponse
*/
public function getJobStats(?string $from = null, ?string $to = null): JSONResponse
{
try {
$fromDate = $from ? new \DateTime($from) : (new \DateTime())->modify('-7 days');
$toDate = $to ? new \DateTime($to) : new \DateTime();

$dailyStats = $this->jobLogMapper->getJobStatsByDateRange($fromDate, $toDate);
$hourlyStats = $this->jobLogMapper->getJobStatsByHourRange($fromDate, $toDate);

return new JSONResponse([
'daily' => $dailyStats,
'hourly' => $hourlyStats
]);
} catch (\Exception $e) {
return new JSONResponse(['error' => $e->getMessage()], 500);
}
}

/**
* Get synchronization statistics for the dashboard
*
* @NoAdminRequired
* @NoCSRFRequired
* @param string|null $from Start date in ISO format
* @param string|null $to End date in ISO format
* @return JSONResponse
*/
public function getSyncStats(?string $from = null, ?string $to = null): JSONResponse
{
try {
$fromDate = $from ? new \DateTime($from) : (new \DateTime())->modify('-7 days');
$toDate = $to ? new \DateTime($to) : new \DateTime();

$dailyStats = $this->synchronizationContractLogMapper->getSyncStatsByDateRange($fromDate, $toDate);
$hourlyStats = $this->synchronizationContractLogMapper->getSyncStatsByHourRange($fromDate, $toDate);

return new JSONResponse([
'daily' => $dailyStats,
'hourly' => $hourlyStats
]);
} catch (\Exception $e) {
return new JSONResponse(['error' => $e->getMessage()], 500);
}
}
}
88 changes: 88 additions & 0 deletions lib/Db/CallLogMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,92 @@ public function getLastCallLog(): ?CallLog
return null;
}
}

/**
* Get call statistics grouped by date for a specific date range
*
* @param \DateTime $from Start date
* @param \DateTime $to End date
* @return array Array of daily statistics with success and error counts
*/
public function getCallStatsByDateRange(\DateTime $from, \DateTime $to): array
{
$qb = $this->db->getQueryBuilder();

// Get the actual data from database
$qb->select(
$qb->createFunction('DATE(created) as date'),
$qb->createFunction('SUM(CASE WHEN status_code >= 200 AND status_code < 300 THEN 1 ELSE 0 END) as success'),
$qb->createFunction('SUM(CASE WHEN status_code < 200 OR status_code >= 300 THEN 1 ELSE 0 END) as error')
)
->from('openconnector_call_logs')
->where($qb->expr()->gte('created', $qb->createNamedParameter($from->format('Y-m-d H:i:s'))))
->andWhere($qb->expr()->lte('created', $qb->createNamedParameter($to->format('Y-m-d H:i:s'))))
->groupBy('date')
->orderBy('date', 'ASC');

$result = $qb->execute();
$stats = [];

// Create DatePeriod to iterate through all dates
$period = new \DatePeriod(
$from,
new \DateInterval('P1D'),
$to->modify('+1 day')
);

// Initialize all dates with zero values
foreach ($period as $date) {
$dateStr = $date->format('Y-m-d');
$stats[$dateStr] = [
'success' => 0,
'error' => 0
];
}

// Fill in actual values where they exist
while ($row = $result->fetch()) {
$stats[$row['date']] = [
'success' => (int)$row['success'],
'error' => (int)$row['error']
];
}

return $stats;
}

/**
* Get call statistics grouped by hour for a specific date range
*
* @param \DateTime $from Start date
* @param \DateTime $to End date
* @return array Array of hourly statistics with success and error counts
*/
public function getCallStatsByHourRange(\DateTime $from, \DateTime $to): array
{
$qb = $this->db->getQueryBuilder();

$qb->select(
$qb->createFunction('HOUR(created) as hour'),
$qb->createFunction('SUM(CASE WHEN status_code >= 200 AND status_code < 300 THEN 1 ELSE 0 END) as success'),
$qb->createFunction('SUM(CASE WHEN status_code < 200 OR status_code >= 300 THEN 1 ELSE 0 END) as error')
)
->from('openconnector_call_logs')
->where($qb->expr()->gte('created', $qb->createNamedParameter($from->format('Y-m-d H:i:s'))))
->andWhere($qb->expr()->lte('created', $qb->createNamedParameter($to->format('Y-m-d H:i:s'))))
->groupBy('hour')
->orderBy('hour', 'ASC');

$result = $qb->execute();
$stats = [];

while ($row = $result->fetch()) {
$stats[$row['hour']] = [
'success' => (int)$row['success'],
'error' => (int)$row['error']
];
}

return $stats;
}
}
97 changes: 97 additions & 0 deletions lib/Db/JobLogMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,101 @@ public function getLastCallLog(): ?JobLog
return null;
}
}

/**
* Get job statistics grouped by date for a specific date range
*
* @param \DateTime $from Start date
* @param \DateTime $to End date
* @return array Array of daily statistics with counts per log level
*/
public function getJobStatsByDateRange(\DateTime $from, \DateTime $to): array
{
$qb = $this->db->getQueryBuilder();

$qb->select(
$qb->createFunction('DATE(created) as date'),
$qb->createFunction('SUM(CASE WHEN level = \'INFO\' THEN 1 ELSE 0 END) as info'),
$qb->createFunction('SUM(CASE WHEN level = \'WARNING\' THEN 1 ELSE 0 END) as warning'),
$qb->createFunction('SUM(CASE WHEN level = \'ERROR\' THEN 1 ELSE 0 END) as error'),
$qb->createFunction('SUM(CASE WHEN level = \'DEBUG\' THEN 1 ELSE 0 END) as debug')
)
->from('openconnector_job_logs')
->where($qb->expr()->gte('created', $qb->createNamedParameter($from->format('Y-m-d H:i:s'))))
->andWhere($qb->expr()->lte('created', $qb->createNamedParameter($to->format('Y-m-d H:i:s'))))
->groupBy('date')
->orderBy('date', 'ASC');

$result = $qb->execute();
$stats = [];

// Create DatePeriod to iterate through all dates
$period = new \DatePeriod(
$from,
new \DateInterval('P1D'),
$to->modify('+1 day')
);

// Initialize all dates with zero values
foreach ($period as $date) {
$dateStr = $date->format('Y-m-d');
$stats[$dateStr] = [
'info' => 0,
'warning' => 0,
'error' => 0,
'debug' => 0
];
}

// Fill in actual values where they exist
while ($row = $result->fetch()) {
$stats[$row['date']] = [
'info' => (int)$row['info'],
'warning' => (int)$row['warning'],
'error' => (int)$row['error'],
'debug' => (int)$row['debug']
];
}

return $stats;
}

/**
* Get job statistics grouped by hour for a specific date range
*
* @param \DateTime $from Start date
* @param \DateTime $to End date
* @return array Array of hourly statistics with counts per log level
*/
public function getJobStatsByHourRange(\DateTime $from, \DateTime $to): array
{
$qb = $this->db->getQueryBuilder();

$qb->select(
$qb->createFunction('HOUR(created) as hour'),
$qb->createFunction('SUM(CASE WHEN level = \'INFO\' THEN 1 ELSE 0 END) as info'),
$qb->createFunction('SUM(CASE WHEN level = \'WARNING\' THEN 1 ELSE 0 END) as warning'),
$qb->createFunction('SUM(CASE WHEN level = \'ERROR\' THEN 1 ELSE 0 END) as error'),
$qb->createFunction('SUM(CASE WHEN level = \'DEBUG\' THEN 1 ELSE 0 END) as debug')
)
->from('openconnector_job_logs')
->where($qb->expr()->gte('created', $qb->createNamedParameter($from->format('Y-m-d H:i:s'))))
->andWhere($qb->expr()->lte('created', $qb->createNamedParameter($to->format('Y-m-d H:i:s'))))
->groupBy('hour')
->orderBy('hour', 'ASC');

$result = $qb->execute();
$stats = [];

while ($row = $result->fetch()) {
$stats[$row['hour']] = [
'info' => (int)$row['info'],
'warning' => (int)$row['warning'],
'error' => (int)$row['error'],
'debug' => (int)$row['debug']
];
}

return $stats;
}
}
Loading
Loading