Skip to content

Commit

Permalink
Merge pull request #1331 from jplag/report-viewer/enchance-table
Browse files Browse the repository at this point in the history
  • Loading branch information
Kr0nox authored Nov 30, 2023
2 parents 9bae5bb + 7ca88f8 commit 04b89d2
Show file tree
Hide file tree
Showing 9 changed files with 177 additions and 62 deletions.
66 changes: 20 additions & 46 deletions report-viewer/src/components/ComparisonsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@
<!-- Body -->
<div class="flex flex-grow flex-col overflow-hidden">
<DynamicScroller v-if="topComparisons.length > 0" :items="comparisonList" :min-item-size="48">
<template v-slot="{ item, index, active }">
<template #default="{ item, index, active }">
<DynamicScrollerItem
:item="item"
:active="active"
:size-dependencies="[
item.firstSubmissionId,
item.secondSubmissionId,
isAnonymous(item.firstSubmissionId),
isAnonymous(item.secondSubmissionId)
store().isAnonymous(item.firstSubmissionId),
store().isAnonymous(item.secondSubmissionId)
]"
:data-index="index"
>
Expand All @@ -60,12 +60,14 @@
'bg-container-secondary-light dark:bg-container-secondary-dark': item.id % 2 == 1
}"
>
<RouterLink
:to="{
name: 'ComparisonView',
params: { firstId: item.firstSubmissionId, secondId: item.secondSubmissionId }
}"
class="flex flex-grow flex-row"
<div
@click="
router.push({
name: 'ComparisonView',
params: { firstId: item.firstSubmissionId, secondId: item.secondSubmissionId }
})
"
class="flex flex-grow cursor-pointer flex-row"
>
<!-- Index in sorted list -->
<div class="tableCellNumber">
Expand All @@ -74,26 +76,8 @@

<!-- Names -->
<div class="tableCellName">
<div
class="break-anywhere w-1/2 px-2"
:class="{ 'blur-[1px]': isAnonymous(item.firstSubmissionId) }"
>
{{
isAnonymous(item.firstSubmissionId)
? 'Hidden'
: displayName(item.firstSubmissionId)
}}
</div>
<div
class="break-anywhere w-1/2 px-2"
:class="{ 'blur-[1px]': isAnonymous(item.secondSubmissionId) }"
>
{{
isAnonymous(item.secondSubmissionId)
? 'Hidden'
: displayName(item.secondSubmissionId)
}}
</div>
<NameElement :id="item.firstSubmissionId" class="h-full w-1/2 px-2" />
<NameElement :id="item.secondSubmissionId" class="h-full w-1/2 px-2" />
</div>

<!-- Similarities -->
Expand All @@ -105,7 +89,7 @@
{{ (item.similarities[MetricType.MAXIMUM] * 100).toFixed(2) }}%
</div>
</div>
</RouterLink>
</div>

<!-- Clusters -->
<div class="tableCellCluster flex !flex-col items-center" v-if="displayClusters">
Expand Down Expand Up @@ -143,6 +127,10 @@
</div>
</DynamicScrollerItem>
</template>

<template #after>
<slot name="footer"></slot>
</template>
</DynamicScroller>
</div>
</div>
Expand All @@ -160,6 +148,8 @@ import { faUserGroup } from '@fortawesome/free-solid-svg-icons'
import { generateColors } from '@/utils/ColorUtils'
import ToolTipComponent from './ToolTipComponent.vue'
import { MetricType, metricToolTips } from '@/model/MetricType'
import NameElement from './NameElement.vue'
import { router } from '@/router'

library.add(faUserGroup)

Expand All @@ -178,22 +168,6 @@ const comparisonList = toRef(props, 'topComparisons')

const displayClusters = props.clusters != undefined

/**
* @param submissionId Id to get name for
* @returns The display name of the submission with the given id.
*/
function displayName(submissionId: string) {
return store().submissionDisplayName(submissionId)
}

/**
* @param id SubmissionId to check
* @returns Whether the name should be hidden.
*/
function isAnonymous(id: string) {
return store().state.anonymous.has(id)
}

let clusterIconColors = [] as Array<string>
if (props.clusters != undefined) {
clusterIconColors = generateColors(props.clusters.length, 0.8, 0.5, 1)
Expand Down
55 changes: 55 additions & 0 deletions report-viewer/src/components/NameElement.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<div class="group relative flex items-center justify-center">
<div
class="break-anywhere"
:class="{ 'blur-[1px]': store().isAnonymous(props.id) && anonymousBlur }"
>
{{ store().isAnonymous(props.id) ? anonymousName : store().submissionDisplayName(id) }}
</div>
<div
class="invisible absolute right-0 top-0 z-10 flex h-full cursor-pointer items-center p-2 delay-0 group-hover:visible group-hover:delay-100"
@click="(event) => changeAnonymous(event)"
>
<FontAwesomeIcon
class="text-gray-500"
:icon="store().isAnonymous(props.id) ? ['fas', 'eye'] : ['fas', 'eye-slash']"
/>
</div>
</div>
</template>

<script setup lang="ts">
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { library } from '@fortawesome/fontawesome-svg-core'
import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons'
import { store } from '@/stores/store'
library.add(faEye)
library.add(faEyeSlash)
const props = defineProps({
id: {
type: String,
required: true
},
anonymousBlur: {
type: Boolean,
required: false,
default: true
},
anonymousName: {
type: String,
required: false,
default: 'Hidden'
}
})
function changeAnonymous(event: Event) {
event.stopPropagation()
if (store().isAnonymous(props.id)) {
store().removeAnonymous([props.id])
} else {
store().addAnonymous([props.id])
}
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,7 @@
import { ref } from 'vue'
import ToolTipComponent from '../ToolTipComponent.vue'
import OptionComponent from './OptionComponent.vue'

type ToolTipLabel = {
displayValue: string
tooltip: string
}
import { type ToolTipLabel } from '@/model/ui/ToolTip'

const props = defineProps({
title: {
Expand Down
2 changes: 1 addition & 1 deletion report-viewer/src/model/MetricType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export enum MetricType {
MAXIMUM = 'MAX'
}

type MetricToolTipData = {
export type MetricToolTipData = {
longName: string
shortName: string
tooltip: string
Expand Down
4 changes: 4 additions & 0 deletions report-viewer/src/model/ui/ToolTip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type ToolTipLabel = {
displayValue: string
tooltip: string
}
1 change: 1 addition & 0 deletions report-viewer/src/stores/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export interface LoadConfiguration {
export interface UIState {
useDarkMode: boolean
comparisonTableSortingMetric: MetricType
comparisonTableClusterSorting: boolean
distributionChartConfig: DistributionChartConfig
}

Expand Down
10 changes: 9 additions & 1 deletion report-viewer/src/stores/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const store = defineStore('store', {
uiState: {
useDarkMode: false,
comparisonTableSortingMetric: MetricType.AVERAGE,
comparisonTableClusterSorting: false,
distributionChartConfig: {
metric: MetricType.AVERAGE,
xScale: 'linear'
Expand All @@ -48,7 +49,7 @@ const store = defineStore('store', {
* @returns the display name of the submission
*/
submissionDisplayName: (state) => (id: string) => {
return state.state.fileIdToDisplayName.get(id)
return state.state.fileIdToDisplayName.get(id) ?? id
},
/**
* @returns the Ids of all submissions
Expand Down Expand Up @@ -77,6 +78,13 @@ const store = defineStore('store', {
: undefined
return index != undefined ? this.state.files[index] : undefined
}
},
/**
* @param id the id to check for
* @returns whether this submission should be anonymised
*/
isAnonymous: (state) => (submissionId: string) => {
return state.state.anonymous.has(submissionId)
}
},
actions: {
Expand Down
14 changes: 13 additions & 1 deletion report-viewer/src/views/ClusterView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,14 @@
</Container>
<Container class="flex max-h-0 min-h-full w-1/3 flex-col space-y-2">
<h2>Comparisons of Cluster Members:</h2>
<ComparisonsTable :topComparisons="comparisons" class="min-h-0 flex-1" />
<ComparisonsTable :topComparisons="comparisons" class="min-h-0 flex-1">
<template #footer v-if="comparisons.length < maxAmountOfComparisonsInCluster">
<p class="w-full pt-1 text-center font-bold">
Not all comparisons inside the cluster are shown. To see more, re-run JPlag with a
higher maximum number argument.
</p>
</template>
</ComparisonsTable>
</Container>
</div>
</div>
Expand Down Expand Up @@ -96,6 +103,11 @@ const clusterListElement: Ref<ClusterListElement> = computed(() => {
}
})
/** The amount of comparisons if every single one was included */
const maxAmountOfComparisonsInCluster = computed(() => {
return props.cluster.members.length ** 2 / 2 - props.cluster.members.length
})
onErrorCaptured((error) => {
redirectOnError(error, 'Error displaying cluster:\n', 'OverviewView', 'Back to overview')
return false
Expand Down
81 changes: 73 additions & 8 deletions report-viewer/src/views/OverviewView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,24 @@
}}
</Button>
</div>
<MetricSelector
<OptionsSelector
title="Sort By:"
:defaultSelected="store().uiState.comparisonTableSortingMetric"
@selection-changed="
(metric: MetricType) => (store().uiState.comparisonTableSortingMetric = metric)
"
:defaultSelected="getSortingMetric()"
:labels="tableSortingOptions"
@selection-changed="(index: number) => changeSortingMetric(index)"
/>
<ComparisonsTable
:clusters="overview.clusters"
:top-comparisons="displayedComparisons"
class="min-h-0 flex-1"
/>
>
<template #footer v-if="overview.topComparisons.length < overview.totalComparisons">
<p class="w-full pt-1 text-center font-bold">
Not all comparisons are shown. To see more, re-run JPlag with a higher maximum number
argument.
</p>
</template>
</ComparisonsTable>
</Container>
</div>
</div>
Expand All @@ -151,14 +157,16 @@ import { store } from '@/stores/store'
import Container from '@/components/ContainerComponent.vue'
import Button from '@/components/ButtonComponent.vue'
import ScrollableComponent from '@/components/ScrollableComponent.vue'
import { MetricType } from '@/model/MetricType'
import { MetricType, metricToolTips } from '@/model/MetricType'
import SearchBarComponent from '@/components/SearchBarComponent.vue'
import TextInformation from '@/components/TextInformation.vue'
import type { ComparisonListElement } from '@/model/ComparisonListElement'
import MetricSelector from '@/components/optionsSelectors/MetricSelector.vue'
import ToolTipComponent from '@/components/ToolTipComponent.vue'
import OptionsSelector from '@/components/optionsSelectors/OptionsSelectorComponent.vue'
import type { Overview } from '@/model/Overview'
import { Overview } from '@/model/Overview'
import type { Cluster } from '@/model/Cluster'
import type { ToolTipLabel } from '@/model/ui/ToolTip'

const props = defineProps({
overview: {
Expand All @@ -168,6 +176,30 @@ const props = defineProps({
})

const searchString = ref('')
const tableSortingMetricOptions = [MetricType.AVERAGE, MetricType.MAXIMUM]
const tableSortingOptions = computed(() => {
const options: (ToolTipLabel | string)[] = tableSortingMetricOptions.map((metric) => {
return {
displayValue: metricToolTips[metric].longName,
tooltip: metricToolTips[metric].tooltip
}
})
options.push('Cluster')
return options
})

function changeSortingMetric(index: number) {
store().uiState.comparisonTableSortingMetric =
index < tableSortingMetricOptions.length ? tableSortingMetricOptions[index] : MetricType.AVERAGE
store().uiState.comparisonTableClusterSorting = tableSortingOptions.value[index] == 'Cluster'
}

function getSortingMetric() {
if (store().uiState.comparisonTableClusterSorting) {
return tableSortingOptions.value.indexOf('Cluster')
}
return tableSortingMetricOptions.indexOf(store().uiState.comparisonTableSortingMetric)
}

/**
* This funtion gets called when the search bar for the compariosn table has been updated.
Expand All @@ -193,12 +225,45 @@ function getFilteredComparisons(comparisons: ComparisonListElement[]) {
})
}

function getClusterIndexFor(id1: string, id2: string) {
let clusterIndex = -1
props.overview.clusters.forEach((c: Cluster, index: number) => {
if (c.members.includes(id1) && c.members.includes(id2) && c.members.length > 2) {
clusterIndex = index
}
})
return clusterIndex
}

function getClusterFor(id1: string, id2: string) {
const index = getClusterIndexFor(id1, id2)
if (index < 0) {
return { averageSimilarity: 0 }
}
return props.overview.clusters[index]
}

function getSortedComparisons(comparisons: ComparisonListElement[]) {
comparisons.sort(
(a, b) =>
b.similarities[store().uiState.comparisonTableSortingMetric] -
a.similarities[store().uiState.comparisonTableSortingMetric]
)

if (store().uiState.comparisonTableClusterSorting) {
comparisons.sort(
(a, b) =>
getClusterIndexFor(b.firstSubmissionId, b.secondSubmissionId) -
getClusterIndexFor(a.firstSubmissionId, a.secondSubmissionId)
)

comparisons.sort(
(a, b) =>
getClusterFor(b.firstSubmissionId, b.secondSubmissionId).averageSimilarity -
getClusterFor(a.firstSubmissionId, a.secondSubmissionId).averageSimilarity
)
}

let index = 0
comparisons.forEach((c) => {
c.sortingPlace = index++
Expand Down

0 comments on commit 04b89d2

Please sign in to comment.