Skip to content

Commit dfe8980

Browse files
authored
Merge pull request #12 from hdpriest-ui/change-package-view-interface
Updates to web viewer:
2 parents 400c58b + 50f592b commit dfe8980

File tree

3 files changed

+227
-47
lines changed

3 files changed

+227
-47
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ These files should be located in the data directory or mounted as volumes.
130130

131131
1. **Build the Docker image:**
132132
```sh
133-
docker build -t icrn-kernel-webserver:latest -f web/Dockerfile .
133+
docker build -t icrn-kernel-webserver:latest -f web/Dockerfile web/
134134
```
135135

136136
2. **Run the container with kernel data:**

web/Dockerfile

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ RUN apt-get update && \
99
WORKDIR /app
1010

1111
# Copy requirements and install Python dependencies
12-
COPY web/requirements.txt .
12+
COPY requirements.txt .
1313
RUN pip install --no-cache-dir -r requirements.txt
1414

1515
# Copy application files
16-
COPY web/kernel_service.py .
17-
COPY web/nginx.conf /etc/nginx/nginx.conf
18-
COPY web/start.sh /app/start.sh
16+
COPY kernel_service.py .
17+
COPY nginx.conf /etc/nginx/nginx.conf
18+
COPY start.sh /app/start.sh
1919

2020
# Copy static files
21-
COPY web/static/ /app/static/
21+
COPY static/ /app/static/
2222

2323
# Create data directory and fix line endings
2424
RUN mkdir -p /app/data && \

web/static/index.html

Lines changed: 221 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,11 @@ <h5 class="mb-0">Search Packages</h5>
386386
let filteredPackages = [];
387387
let sortColumn = -1;
388388
let sortDirection = 1; // 1 for ascending, -1 for descending
389+
let packageSearchRows = []; // Store package search results for sorting
390+
let packageSearchSortColumn = -1;
391+
let packageSearchSortDirection = 1;
392+
let packageSearchTotalMatches = 0; // Store total package matches
393+
let packageSearchQuery = ''; // Store search query
389394
const API_BASE = '/api';
390395

391396
// Initialize on page load
@@ -768,64 +773,239 @@ <h5 class="mb-0">Search Packages</h5>
768773
<i class="bi bi-info-circle"></i> No packages found matching "${escapeHtml(data.query)}"
769774
</div>
770775
`;
776+
packageSearchRows = [];
777+
packageSearchTotalMatches = 0;
778+
packageSearchQuery = data.query || '';
771779
return;
772780
}
773781

774-
let html = `
775-
<div class="mb-3">
776-
<strong>Found ${data.total_matches} package(s) matching "${escapeHtml(data.query)}"</strong>
777-
</div>
778-
`;
782+
// Store total matches and query
783+
packageSearchTotalMatches = data.total_matches || 0;
784+
packageSearchQuery = data.query || '';
779785

786+
// Flatten the data: create one row per package-kernel combination
787+
packageSearchRows = [];
780788
data.packages.forEach(pkg => {
781-
html += `
782-
<div class="card mb-3">
783-
<div class="card-header d-flex justify-content-between align-items-center">
784-
<h6 class="mb-0">
785-
<i class="bi bi-box-seam"></i> ${escapeHtml(pkg.name)}
786-
</h6>
787-
<span class="badge bg-secondary">${pkg.kernel_count} kernel(s)</span>
788-
</div>
789-
<div class="card-body">
790-
<h6 class="mb-2">Available in the following kernels:</h6>
791-
<div class="table-responsive">
792-
<table class="table table-sm table-hover">
793-
<thead>
794-
<tr>
795-
<th>Language</th>
796-
<th>Kernel Name</th>
797-
<th>Version</th>
798-
<th>Package Version</th>
799-
</tr>
800-
</thead>
801-
<tbody>
802-
`;
803-
804789
if (pkg.kernels && pkg.kernels.length > 0) {
805790
pkg.kernels.forEach(kernel => {
806-
html += `
791+
packageSearchRows.push({
792+
packageName: pkg.name,
793+
packageVersion: kernel.package_version || 'N/A',
794+
kernelName: kernel.kernel_name || 'N/A',
795+
kernelVersion: kernel.kernel_version || 'N/A',
796+
language: kernel.kernel_language || kernel.language || 'N/A'
797+
});
798+
});
799+
} else {
800+
// If no kernels, still show the package
801+
packageSearchRows.push({
802+
packageName: pkg.name,
803+
packageVersion: 'N/A',
804+
kernelName: 'N/A',
805+
kernelVersion: 'N/A',
806+
language: 'N/A'
807+
});
808+
}
809+
});
810+
811+
// Reset sort state
812+
packageSearchSortColumn = -1;
813+
packageSearchSortDirection = 1;
814+
815+
// Render the table
816+
renderPackageSearchTable();
817+
}
818+
819+
// Render package search results table
820+
function renderPackageSearchTable() {
821+
const resultsDiv = document.getElementById('packageSearchResults');
822+
823+
if (packageSearchRows.length === 0) {
824+
resultsDiv.innerHTML = '<p class="text-muted text-center">No results to display</p>';
825+
return;
826+
}
827+
828+
let html = `
829+
<div class="mb-3">
830+
<strong>Found ${packageSearchTotalMatches} package(s) matching "${escapeHtml(packageSearchQuery)}" (${packageSearchRows.length} total result(s))</strong>
831+
</div>
832+
<div class="table-responsive">
833+
<table class="table table-striped table-hover" id="packageSearchTable">
834+
<thead>
807835
<tr>
808-
<td><span class="badge bg-info">${escapeHtml(kernel.language || 'N/A')}</span></td>
809-
<td>${escapeHtml(kernel.kernel_name || 'N/A')}</td>
810-
<td><span class="badge bg-secondary">${escapeHtml(kernel.kernel_version || 'N/A')}</span></td>
811-
<td>${escapeHtml(kernel.package_version || 'N/A')}</td>
836+
<th class="sortable" onclick="sortPackageSearchTable(0)">
837+
Package Name
838+
<i class="bi bi-arrow-down-up sort-icon" id="packageSortIcon0"></i>
839+
</th>
840+
<th class="sortable" onclick="sortPackageSearchTable(1)">
841+
Package Version
842+
<i class="bi bi-arrow-down-up sort-icon" id="packageSortIcon1"></i>
843+
</th>
844+
<th class="sortable" onclick="sortPackageSearchTable(2)">
845+
Kernel Name
846+
<i class="bi bi-arrow-down-up sort-icon" id="packageSortIcon2"></i>
847+
</th>
848+
<th class="sortable" onclick="sortPackageSearchTable(3)">
849+
Kernel Version
850+
<i class="bi bi-arrow-down-up sort-icon" id="packageSortIcon3"></i>
851+
</th>
852+
<th class="sortable" onclick="sortPackageSearchTable(4)">
853+
Language
854+
<i class="bi bi-arrow-down-up sort-icon" id="packageSortIcon4"></i>
855+
</th>
812856
</tr>
813-
`;
814-
});
857+
</thead>
858+
<tbody>
859+
`;
860+
861+
packageSearchRows.forEach((row, index) => {
862+
// Create clickable kernel name link if kernel info is available
863+
let kernelNameCell;
864+
if (row.kernelName !== 'N/A' && row.kernelVersion !== 'N/A' && row.language !== 'N/A') {
865+
kernelNameCell = `<a href="#" class="kernel-link" data-language="${escapeHtml(row.language)}" data-kernel-name="${escapeHtml(row.kernelName)}" data-kernel-version="${escapeHtml(row.kernelVersion)}" style="color: var(--illinois-blue); text-decoration: none; font-weight: 500; cursor: pointer;">${escapeHtml(row.kernelName)}</a>`;
815866
} else {
816-
html += '<tr><td colspan="4" class="text-muted text-center">No kernel information available</td></tr>';
867+
kernelNameCell = escapeHtml(row.kernelName);
817868
}
818869

819870
html += `
820-
</tbody>
821-
</table>
822-
</div>
823-
</div>
824-
</div>
871+
<tr>
872+
<td>${escapeHtml(row.packageName)}</td>
873+
<td>${escapeHtml(row.packageVersion)}</td>
874+
<td>${kernelNameCell}</td>
875+
<td>${escapeHtml(row.kernelVersion)}</td>
876+
<td><span class="badge bg-info">${escapeHtml(row.language)}</span></td>
877+
</tr>
825878
`;
826879
});
827880

881+
html += `
882+
</tbody>
883+
</table>
884+
</div>
885+
`;
886+
828887
resultsDiv.innerHTML = html;
888+
889+
// Update sort icons
890+
for (let i = 0; i < 5; i++) {
891+
const icon = document.getElementById(`packageSortIcon${i}`);
892+
if (icon) {
893+
if (packageSearchSortColumn === i) {
894+
if (packageSearchSortDirection === 1) {
895+
icon.className = 'bi bi-arrow-down sort-icon active';
896+
} else {
897+
icon.className = 'bi bi-arrow-up sort-icon active';
898+
}
899+
} else {
900+
icon.className = 'bi bi-arrow-down-up sort-icon';
901+
}
902+
}
903+
}
904+
905+
// Attach event listeners to kernel links
906+
resultsDiv.querySelectorAll('.kernel-link').forEach(link => {
907+
link.addEventListener('click', function(e) {
908+
e.preventDefault();
909+
const language = this.getAttribute('data-language');
910+
const kernelName = this.getAttribute('data-kernel-name');
911+
const kernelVersion = this.getAttribute('data-kernel-version');
912+
navigateToKernel(language, kernelName, kernelVersion);
913+
});
914+
});
915+
}
916+
917+
// Sort package search table
918+
function sortPackageSearchTable(columnIndex) {
919+
// Reset all sort icons
920+
for (let i = 0; i < 5; i++) {
921+
const icon = document.getElementById(`packageSortIcon${i}`);
922+
if (icon) {
923+
icon.className = 'bi bi-arrow-down-up sort-icon';
924+
}
925+
}
926+
927+
// If clicking same column, reverse direction
928+
if (packageSearchSortColumn === columnIndex) {
929+
packageSearchSortDirection *= -1;
930+
} else {
931+
packageSearchSortColumn = columnIndex;
932+
packageSearchSortDirection = 1;
933+
}
934+
935+
// Update sort icon
936+
const icon = document.getElementById(`packageSortIcon${columnIndex}`);
937+
if (icon) {
938+
if (packageSearchSortDirection === 1) {
939+
icon.className = 'bi bi-arrow-down sort-icon active';
940+
} else {
941+
icon.className = 'bi bi-arrow-up sort-icon active';
942+
}
943+
}
944+
945+
// Sort rows
946+
packageSearchRows.sort((a, b) => {
947+
let aVal, bVal;
948+
switch(columnIndex) {
949+
case 0: // Package Name
950+
aVal = a.packageName || '';
951+
bVal = b.packageName || '';
952+
break;
953+
case 1: // Package Version
954+
aVal = a.packageVersion || '';
955+
bVal = b.packageVersion || '';
956+
break;
957+
case 2: // Kernel Name
958+
aVal = a.kernelName || '';
959+
bVal = b.kernelName || '';
960+
break;
961+
case 3: // Kernel Version
962+
aVal = a.kernelVersion || '';
963+
bVal = b.kernelVersion || '';
964+
break;
965+
case 4: // Language
966+
aVal = a.language || '';
967+
bVal = b.language || '';
968+
break;
969+
}
970+
971+
if (aVal < bVal) return -1 * packageSearchSortDirection;
972+
if (aVal > bVal) return 1 * packageSearchSortDirection;
973+
return 0;
974+
});
975+
976+
// Re-render the table
977+
renderPackageSearchTable();
978+
}
979+
980+
// Function to navigate to kernel view
981+
async function navigateToKernel(language, kernelName, version) {
982+
// Switch to kernels view
983+
showView('kernels');
984+
985+
// Set the language if needed
986+
const languageSelect = document.getElementById('languageSelect');
987+
if (languageSelect.value !== language) {
988+
languageSelect.value = language;
989+
currentLanguage = language;
990+
// Load kernels for the new language
991+
await loadKernels(language);
992+
}
993+
994+
// Find the kernel item in the list to highlight it
995+
const kernelItems = document.querySelectorAll('.kernel-item');
996+
let kernelElement = null;
997+
998+
kernelItems.forEach(item => {
999+
const itemKernelName = item.getAttribute('data-kernel-name');
1000+
const itemVersion = item.getAttribute('data-version');
1001+
1002+
if (itemKernelName === kernelName && itemVersion === version) {
1003+
kernelElement = item;
1004+
}
1005+
});
1006+
1007+
// Select the kernel (with or without element for highlighting)
1008+
await selectKernel(language, kernelName, version, kernelElement);
8291009
}
8301010
</script>
8311011
</body>

0 commit comments

Comments
 (0)