Skip to content

Commit

Permalink
Make report metadata live update
Browse files Browse the repository at this point in the history
  • Loading branch information
kraih committed Jan 2, 2024
1 parent 8d5b22a commit 4dc8647
Show file tree
Hide file tree
Showing 7 changed files with 545 additions and 491 deletions.
9 changes: 9 additions & 0 deletions assets/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import KnownProducts from './vue/KnownProducts.vue';
import OpenReviews from './vue/OpenReviews.vue';
import ProductReviews from './vue/ProductReviews.vue';
import RecentReviews from './vue/RecentReviews.vue';
import ReportMetadata from './vue/ReportMetadata.vue';
import ReviewSearch from './vue/ReviewSearch.vue';
import $ from 'jquery';
import {createApp} from 'vue';
Expand Down Expand Up @@ -54,6 +55,14 @@ window.cavil = {
createApp(RecentReviews).mount('#recent-reviews');
},

setupReportMetadata(pkgId, hasManagerRole, hasAdminRole) {
const app = createApp(ReportMetadata);
app.config.globalProperties.pkgId = pkgId;
app.config.globalProperties.hasManagerRole = hasManagerRole;
app.config.globalProperties.hasAdminRole = hasAdminRole;
app.mount('#report-metadata');
},

setupReviewSearch(pkg) {
const app = createApp(ReviewSearch);
app.config.globalProperties.currentPackage = pkg;
Expand Down
369 changes: 369 additions & 0 deletions assets/vue/ReportMetadata.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,369 @@
<template>
<div v-if="pkgName === null"><i class="fas fa-sync fa-spin"></i> Loading package information...</div>
<div v-else>
<div class="float-right format">
<i class="fab fa-suse" v-if="pkgType === 'spec'"></i>
<i class="fas fa-kiwi-bird" v-else-if="pkgType === 'kiwi'"></i>
<i class="fab fa-docker" v-else-if="pkgType === 'docker'"></i>
<i class="fas fa-dharmachakra" v-else-if="pkgType === 'helm'"></i>
<i class="far fa-question-circle" v-else></i>
</div>
<h2 v-if="pkgName !== null">
<a :href="searchUrl" target="_blank">{{ pkgName }}</a>
</h2>
<table class="table borderless novertpad">
<tbody>
<tr v-if="pkgLicense !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-box"></i>
</th>
<th class="fit text-left noleftpad" scope="row">License:</th>
<td id="pkg-license">
{{ pkgLicense.name }}
<small v-if="pkgLicense.spdx === false">(not SPDX)</small>
</td>
</tr>
<tr v-if="pkgFiles.length > 0">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-cubes"></i>
</th>
<th class="fit text-left noleftpad" scope="row">Package Files:</th>
<td id="num-spec-files">
<a v-if="actions.length === 1" href="#spec-files" data-toggle="collapse">1 file</a>
<a v-else href="#spec-files" data-toggle="collapse">{{ pkgFiles.length }} files</a>
</td>
</tr>
<tr v-if="actions.length > 0">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-directions"></i>
</th>
<th class="fit text-left noleftpad" scope="row">Actions:</th>
<td>
<a v-if="actions.length === 1" href="#actions" data-toggle="collapse">1 related review</a>
<a v-else href="#actions" data-toggle="collapse">{{ actions.length }} related reviews</a>
</td>
</tr>
<tr v-if="history.length > 0">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-history"></i>
</th>
<th class="fit text-left noleftpad" scope="row">History:</th>
<td>
<a v-if="history.length === 1" href="#history" data-toggle="collapse">1 other review</a>
<a v-else href="#history" data-toggle="collapse">{{ history.length }} other reviews</a>
</td>
</tr>
<tr v-if="externalLink !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-external-link-alt"></i>
</th>
<th class="fit text-left noleftpad" scope="row">External Link:</th>
<td v-html="externalLink"></td>
</tr>
<tr v-if="requestsHtml !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-link"></i>
</th>
<th class="fit text-left noleftpad" scope="row">Requests:</th>
<td v-html="requestsHtml"></td>
</tr>
<tr v-if="productsHtml !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-shopping-bag"></i>
</th>
<th class="fit text-left noleftpad" scope="row">Products:</th>
<td v-html="productsHtml"></td>
</tr>
<tr v-if="pkgVersion !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-code-branch"></i>
</th>
<th class="fit text-left noleftpad" scope="row">Version:</th>
<td id="pkg-version">{{ pkgVersion }}</td>
</tr>
<tr v-if="pkgSummary !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-edit"></i>
</th>
<th class="fit text-left noleftpad" scope="row">Summary:</th>
<td id="pkg-summary">{{ pkgSummary }}</td>
</tr>
<tr v-if="pkgGroup !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-users"></i>
</th>
<th class="fit text-left noleftpad" scope="row">Group:</th>
<td id="pkg-group">{{ pkgGroup }}</td>
</tr>
<tr v-if="pkgUrl !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-link"></i>
</th>
<th class="fit text-left noleftpad" scope="row">URL:</th>
<td id="pkg-url">
<a :href="pkgUrl" target="_blank">{{ pkgUrl }}</a>
</td>
</tr>
<tr>
<th class="fit text-left noleftpad" scope="row">
<i class="far fa-chart-bar"></i>
</th>
<th class="fit text-left noleftpad" scope="row">SPDX Report:</th>
<td>
<a :href="spdxUrl" target="_blank">
<span v-if="hasSpdxReport === true">available</span>
<span v-else>not yet generated</span>
</a>
</td>
</tr>
<tr v-if="pkgShortname !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="far fa-file"></i>
</th>
<th class="fit text-left noleftpad" scope="row">Shortname:</th>
<td id="pkg-shortname">{{ pkgShortname }}</td>
</tr>
<tr v-if="created !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="far fa-plus-square"></i>
</th>
<th class="fit text-left noleftpad" scope="row">Created:</th>
<td class="from-now">{{ created }}</td>
</tr>
<tr v-if="reviewed !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-search"></i>
</th>
<th class="fit text-left noleftpad" scope="row">Reviewed:</th>
<td class="from-now">{{ reviewed }}</td>
</tr>
<tr v-if="reviewingUser !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-user"></i>
</th>
<th class="fit text-left noleftpad" scope="row">Reviewing User:</th>
<td>{{ reviewingUser }}</td>
</tr>
<tr v-if="state !== null">
<th class="fit text-left noleftpad" scope="row">
<i class="fas fa-balance-scale"></i>
</th>
<th class="fit text-left noleftpad" scope="row">State:</th>
<td id="pkg-state">{{ state }}</td>
</tr>
</tbody>
</table>
<div v-if="actions.length > 0" id="actions" class="collapse">
<table class="table table-striped">
<tbody>
<tr v-for="action in actions" :key="action.id">
<td>{{ prev.name }}</td>
<td>{{ prev.result }}</td>
<td>{{ prev.state }}</td>
<td>{{ prev.reviewing_user }}</td>
<td class="text-right">
<a :href="prev.actionUrl" target="_blank">{{ prev.created }}</a>
</td>
</tr>
</tbody>
</table>
</div>
<div v-if="history.length > 0" id="history" class="collapse">
<table class="table table-striped">
<tbody>
<tr v-for="prev in history" :key="prev.id">
<td v-html="prev.externalLink"></td>
<td>{{ prev.result }}</td>
<td>{{ prev.state }}</td>
<td>{{ prev.reviewing_user }}</td>
<td class="text-right">
<a :href="prev.reportUrl" target="_blank">{{ prev.created }}</a>
</td>
</tr>
</tbody>
</table>
</div>
<div v-if="pkgFiles.length > 0" id="spec-files" class="collapse">
<div class="alert alert-secondary">
<table class="table borderless novertpad">
<tbody>
<tr v-for="file in pkgFiles" :key="file.file">
<td class="noleftpad">
<table class="table borderless novertpad">
<tr>
<th class="fit text-left noleftpad" colspan="2"><i class="fas fa-file-alt"></i> {{ file.file }}</th>
</tr>
<tr v-if="file.licenses !== null">
<td class="fit text-left noleftpad">Licenses:</td>
<td>{{ file.licenses }}</td>
</tr>
<tr v-if="file.version !== null">
<td class="fit text-left noleftpad">Version:</td>
<td>{{ file.version }}</td>
</tr>
<tr v-if="file.summary !== null">
<td class="fit text-left noleftpad">Summary:</td>
<td>{{ file.summary }}</td>
</tr>
<tr v-if="file.group !== null">
<td class="fit text-left noleftpad">Group:</td>
<td>{{ file.group }}</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div v-if="errors.length > 0" id="spec-errors" class="alert alert-danger">
<p>Package file errors:</p>
<ul>
<li v-for="error in errors" :key="error">{{ error }}</li>
</ul>
</div>
<div v-if="warnings.length > 0" id="spec-warnings" class="alert alert-warning">
<p>Package file warnings:</p>
<ul>
<li v-for="warning in warnings" :key="warning">{{ warning }}</li>
</ul>
</div>
<div v-if="hasAdminRole === true" class="row">
<form :action="reviewUrl" method="POST" class="container" id="pkg-review">
<div class="form-group">
<label for="comment">Comment:</label>
<textarea v-model="result" name="comment" rows="10" class="form-control"></textarea>
</div>
<input class="btn btn-success" id="correct" name="correct" type="submit" value="Checked" />&nbsp;
<input class="btn btn-warning" id="acceptable" name="acceptable" type="submit" value="Good Enough" />&nbsp;
<input class="btn btn-danger" id="unacceptable" name="unacceptable" type="submit" value="Unacceptable" />
</form>
</div>
<div v-else-if="hasManagerRole === true" class="row">
<form :action="reviewUrl" method="POST" class="container" id="pkg-review">
<div class="form-group">
<label for="comment">Comment:</label>
<textarea v-model="result" name="comment" rows="10" class="form-control"></textarea>
</div>
<input class="btn btn-warning" id="acceptable" name="acceptable" type="submit" value="Good Enough" />
</form>
</div>
<div v-else class="row">
<form :action="reviewUrl" method="POST" class="container" id="pkg-review">
<div class="form-group">
<label for="comment">Comment:</label>
<textarea v-model="result" name="comment" rows="10" class="form-control" disabled></textarea>
</div>
</form>
</div>
<div
v-if="copiedFiles['%doc'] !== null || copiedFiles['%license'] !== null"
class="alert alert-secondary top-buffer"
>
<p v-if="copiedFiles['%doc'] !== null">
<b>Files copied as %doc:</b>
{{ copiedFiles['%doc'] }}
</p>
<p v-if="copiedFiles['%license'] !== null">
<b>Files copied as %license:</b>
{{ copiedFiles['%license'] }}
</p>
</div>
</div>
</template>

<script>
import {externalLink, productLink} from './helpers/links.js';
import Refresh from './mixins/refresh.js';
import moment from 'moment';
export default {
name: 'ReportMetadata',
mixins: [Refresh],
data() {
return {
copiedFiles: {'%doc': null, '%license': null},
created: null,
errors: [],
externalLink: null,
hasSpdxReport: false,
history: [],
pkgFiles: [],
pkgLicense: null,
pkgName: null,
pkgShortname: null,
pkgSummary: null,
pkgType: null,
pkgUrl: null,
pkgVersion: null,
productsHtml: null,
refreshDelay: 30000,
refreshUrl: `/reviews/meta/${this.pkgId}`,
requestsHtml: null,
result: 'Reviewed ok',
reviewed: null,
reviewingUser: null,
reviewUrl: `/reviews/review_package/${this.pkgId}`,
searchUrl: null,
spdxUrl: `/spdx/${this.pkgId}`,
state: null,
warnings: []
};
},
methods: {
refreshData(data) {
const copiedFiles = data.copied_files;
if (copiedFiles['%doc'].length > 0) this.copiedFiles['%doc'] = copiedFiles['%doc'].join(' ');
if (copiedFiles['%license'].length > 0) this.copiedFiles['%license'] = copiedFiles['%license'].join(' ');
this.created = moment(data.created * 1000).fromNow();
this.errors = data.errors;
this.externalLink = externalLink({external_link: data.external_link});
this.hasSpdxReport = data.has_spdx_report;
this.actions = data.actions;
for (const action of this.actions) {
action.created = moment(action.created * 1000).fromNow();
action.actionUrl = `/reviews/details/${action.id}`;
}
this.history = data.history;
for (const prev of this.history) {
prev.created = moment(prev.created * 1000).fromNow();
prev.externalLink = externalLink({external_link: prev.external_link});
prev.reportUrl = `/reviews/details/${prev.id}`;
}
this.pkgFiles = data.package_files;
for (const file of this.pkgFiles) {
file.licenses = file.licenses.length > 0 ? file.licenses.join(' ') : null;
}
this.pkgGroup = data.package_group;
this.pkgLicense = data.package_license;
this.pkgName = data.package_name;
this.pkgShortname = data.package_shortname;
this.pkgSummary = data.package_summary;
this.pkgType = data.package_type;
this.pkgUrl = data.package_url;
this.pkgVersion = data.package_version;
if (data.products.length > 0) {
this.productsHtml = data.products.map(name => productLink({name})).join(', ');
}
if (data.requests.length > 0) {
this.requestsHtml = data.requests.map(req => externalLink({externalLink: req})).join(', ');
}
if (data.reviewed !== null) this.reviewed = moment(data.reviewed * 1000).fromNow();
this.reviewingUser = data.reviewing_user;
this.searchUrl = `/search?q=${this.pkgName}`;
// Make sure not to reset the comment field in the middle of a review (unless someone else changed the state)
if (data.state !== this.state) this.result = data.result ?? 'Reviewed ok';
this.state = data.state;
this.warnings = data.warnings;
}
}
};
</script>
Loading

0 comments on commit 4dc8647

Please sign in to comment.