Skip to content

Commit

Permalink
Overhaul site information page (#2661)
Browse files Browse the repository at this point in the history
The site information page `/sites/<id>` is currently somewhat broken,
and does not present much useful information. This PR is the first of
several planned as part of a revitalization of the CDash site feature.
The entire UI was rebuilt with DaisyUI based on information provided via
the GraphQL API. Further improvements remain to be made, including:
- The "claim site" feature should be moved to this page
- Site information should be editable directly from this page
- Aggregate time per project, current site location, and build status
information should be added

Closes #1126

Related to #1984

New UI:

![image](https://github.com/user-attachments/assets/395d7883-469a-470f-a538-b73a4d3a616d)
  • Loading branch information
williamjallen authored Jan 14, 2025
1 parent af3aabb commit 9fd9dcc
Show file tree
Hide file tree
Showing 17 changed files with 709 additions and 514 deletions.
4 changes: 0 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ DB_PASSWORD=secret
# This option is useful if you don't want the CDash's GitHub check to block merging.
#GITHUB_ALWAYS_PASS=false

# API key for maps.google.com.
# Set to a valid value to display maps of sites.
#GOOGLE_MAP_API_KEY=null

# Maximum size of large text fields, in bytes. 0 for unlimited
#LARGE_TEXT_LIMIT=0

Expand Down
230 changes: 3 additions & 227 deletions app/Http/Controllers/SiteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@

use App\Models\Site;
use App\Models\User;
use App\Utils\TestingDay;
use CDash\Database;
use CDash\Model\Project;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\View\View;

final class SiteController extends AbstractController
Expand Down Expand Up @@ -320,231 +317,10 @@ public function editSite(): View|RedirectResponse
->with('xsl_content', generate_XSLT($xml, base_path() . '/app/cdash/public/editSite', true));
}

public function viewSite(int $siteid): View
public function viewSite(Site $site): View
{
$db = Database::getInstance();

$site = Site::findOrFail($siteid);

$currenttime = $_GET['currenttime'] ?? null;
if ($currenttime !== null) {
$currenttime = (int) $currenttime;

// Current timestamp is the beginning of the dashboard and we want the end
$currenttimestamp = Carbon::createFromTimestamp($currenttime + 3600 * 24);
} else {
$currenttimestamp = Carbon::maxValue();
}

$siteinformation = $site->mostRecentInformation($currenttimestamp)->first();

$xml = begin_XML_for_XSLT();

$projectid = (int) ($_GET['project'] ?? 0);

if ($projectid > 0) {
$project = new Project();
$project->Id = $projectid;
$project->Fill();
$xml .= '<backurl>index.php?project=' . urlencode($project->Name);
$date = TestingDay::get($project, gmdate(FMT_DATETIME, $currenttime));
$xml .= '&#38;date=' . $date;
$xml .= '</backurl>';
} else {
$project = null;
$xml .= '<backurl>index.php</backurl>';
}
$xml .= "<title>CDash - {$site->name}</title>";
$xml .= "<menusubtitle>{$site->name}</menusubtitle>";

$xml .= '<dashboard>';
$xml .= '<title>CDash</title>';

$xml .= add_XML_value('baseURL', config('app.url'));
$apikey = config('cdash.google_map_api_key');

$MB = 1048576;

$total_virtual_memory = $siteinformation?->totalvirtualmemory;
if ($total_virtual_memory !== null) {
$total_virtual_memory = getByteValueWithExtension($total_virtual_memory * $MB) . 'iB';
}

$total_physical_memory = $siteinformation?->totalphysicalmemory;
if ($total_physical_memory !== null) {
$total_physical_memory = getByteValueWithExtension($total_physical_memory * $MB) . 'iB';
}

$processor_clock_frequency = $siteinformation?->processorclockfrequency;
if ($processor_clock_frequency !== null) {
$processor_clock_frequency = getByteValueWithExtension($processor_clock_frequency * 10 ** 6, 1000) . 'Hz';
}

$xml .= add_XML_value('googlemapkey', $apikey);
$xml .= '</dashboard>';
$xml .= '<site>';
$xml .= add_XML_value('id', $site->id);
$xml .= add_XML_value('name', $site->name);
$xml .= add_XML_value('description', stripslashes($siteinformation->description ?? ''));
$xml .= add_XML_value('processoris64bits', $siteinformation?->processoris64bits);
$xml .= add_XML_value('processorvendor', $siteinformation?->processorvendor);
$xml .= add_XML_value('processorvendorid', $siteinformation?->processorvendorid);
$xml .= add_XML_value('processorfamilyid', $siteinformation?->processorfamilyid);
$xml .= add_XML_value('processormodelid', $siteinformation?->processormodelid);
$xml .= add_XML_value('processorcachesize', $siteinformation?->processorcachesize);
$xml .= add_XML_value('numberlogicalcpus', $siteinformation?->numberlogicalcpus);
$xml .= add_XML_value('numberphysicalcpus', $siteinformation?->numberphysicalcpus);
$xml .= add_XML_value('totalvirtualmemory', $total_virtual_memory);
$xml .= add_XML_value('totalphysicalmemory', $total_physical_memory);
$xml .= add_XML_value('logicalprocessorsperphysical', $siteinformation?->logicalprocessorsperphysical);
$xml .= add_XML_value('processorclockfrequency', $processor_clock_frequency);
$xml .= add_XML_value('outoforder', $site->outoforder);
if ($project !== null && $project->ShowIPAddresses) {
$xml .= add_XML_value('ip', $site->ip);
$xml .= add_XML_value('latitude', $site->latitude);
$xml .= add_XML_value('longitude', $site->longitude);
}
$xml .= '</site>';

// List the claimers of the site
$siteclaimer = $db->executePrepared('
SELECT u.firstname, u.lastname, u.email
FROM users as u, site2user
WHERE
u.id=site2user.userid
AND site2user.siteid=?
ORDER BY firstname
', [$siteid]);
foreach ($siteclaimer as $sc) {
$xml .= '<claimer>';
$xml .= add_XML_value('firstname', $sc['firstname']);
$xml .= add_XML_value('lastname', $sc['lastname']);
if (isset($_SESSION['cdash'])) {
$xml .= add_XML_value('email', $sc['email']);
}
$xml .= '</claimer>';
}

// Select projects that belong to this site
$displayPage = 0;
$projects = [];
$site2project = $db->executePrepared('
SELECT projectid, max(submittime) AS maxtime
FROM build
WHERE
siteid=?
AND projectid>0
GROUP BY projectid
', [$siteid]);

foreach ($site2project as $project_site) {
$projectid = $project_site['projectid'];

$project = new Project();
$project->Id = $projectid;
$project->Fill();
if (Gate::allows('view-project', $project)) {
$xml .= '<project>';
$xml .= add_XML_value('id', $projectid);
$xml .= add_XML_value('submittime', $project_site['maxtime']);
$xml .= add_XML_value('name', $project->Name);
$xml .= add_XML_value('name_encoded', urlencode($project->Name));
$xml .= '</project>';
$displayPage = 1; // if we have at least a valid project we display the page
$projects[] = $projectid;
}
}

// If the current site as only private projects we check that we have the right
// to view the page
if (!$displayPage) {
abort(403, 'You cannot access this page');
}

// Compute the time for all the projects (faster than individually) average of the week
if (config('database.default') == 'pgsql') {
$timediff = 'EXTRACT(EPOCH FROM (build.submittime - buildupdate.starttime))';
$timestampadd = "NOW()-INTERVAL'167 hours'";
} else {
$timediff = 'TIME_TO_SEC(TIMEDIFF(build.submittime, buildupdate.starttime))';
$timestampadd = 'TIMESTAMPADD(' . qiv('HOUR') . ', -167, NOW())';
}

$testtime = $db->executePrepared("
SELECT projectid, build.name AS buildname, build.type AS buildtype, SUM({$timediff}) AS elapsed
FROM build, buildupdate, build2update
WHERE
build.submittime > {$timestampadd}
AND build2update.buildid = build.id
AND buildupdate.id = build2update.updateid
AND build.siteid = ?
GROUP BY projectid,buildname,buildtype
ORDER BY elapsed
", [$siteid]);

$xml .= '<siteload>';

echo pdo_error();
$totalload = 0;
foreach ($testtime as $tt) {
$projectid = $tt['projectid'];
$project = new Project();
$project->Id = $projectid;
$project->Fill();
if (Gate::allows('view-project', $project)) {
$timespent = round($tt['elapsed'] / 7.0); // average over 7 days
$xml .= '<build>';
$xml .= add_XML_value('name', $tt['buildname']);
$xml .= add_XML_value('project', $project->Name);
$xml .= add_XML_value('type', $tt['buildtype']);
$xml .= add_XML_value('time', $timespent);
$totalload += $timespent;
$xml .= '</build>';
}
}

// Compute the idle time
$idletime = 24 * 3600 - $totalload;
if ($idletime < 0) {
$idletime = 0;
}
$xml .= '<idle>' . $idletime . '</idle>';
$xml .= '</siteload>';

if (isset($_SESSION['cdash'])) {
$xml .= '<user>';
$userid = Auth::id();

// Check if the current user as a role in this project
foreach ($projects as $projectid) {
// TODO: (williamjallen) Optimize this loop to execute a constant number of queries

$user2project = $db->executePrepared('SELECT role FROM user2project WHERE projectid=? and role>0', [$projectid]);
if (count($user2project) > 0) {
$xml .= add_XML_value('sitemanager', '1');

$user2site = $db->executePrepared('SELECT * FROM site2user WHERE siteid=? and userid=?',
[$siteid, $userid]);
if (count($user2site) == 0) {
$xml .= add_XML_value('siteclaimed', '0');
} else {
$xml .= add_XML_value('siteclaimed', '1');
}
break;
}
}

$user = User::where('id', '=', $userid)->first();
$xml .= add_XML_value('id', $userid);
$xml .= add_XML_value('admin', $user->admin);
$xml .= '</user>';
}

$xml .= '</cdash>';

return $this->view('cdash', $site->name)
->with('xsl', true)
->with('xsl_content', generate_XSLT($xml, base_path() . '/app/cdash/public/viewSite', true));
return $this->view('site.view-site', $site->name)
->with('site', $site);
}

/**
Expand Down
Loading

0 comments on commit 9fd9dcc

Please sign in to comment.