Skip to content

Commit

Permalink
Merge pull request #1891 from jplag/report-viewer/file-sorting
Browse files Browse the repository at this point in the history
Add file sorting
  • Loading branch information
tsaglam authored Aug 2, 2024
2 parents d9b000d + 4e32d90 commit a0a6233
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 9 deletions.
88 changes: 80 additions & 8 deletions report-viewer/src/components/fileDisplaying/FilesContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@
</div>

<ScrollableComponent class="flex-grow" ref="scrollContainer">
<VueDraggableNext>
<VueDraggableNext @update="emitFileMoving()">
<CodePanel
v-for="(file, index) in files"
:key="index"
v-for="file in sortedFiles"
:key="file.fileName"
ref="codePanels"
:file="file"
:matches="
!matches.get(file.fileName) ? [] : (matches.get(file.fileName) as MatchInSingleFile[])
"
:matches="matchesPerFile[file.fileName]"
:highlight-language="highlightLanguage"
@match-selected="(match: Match) => $emit('matchSelected', match)"
class="mt-1 first:mt-0"
Expand All @@ -48,6 +46,8 @@ import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { faCompressAlt } from '@fortawesome/free-solid-svg-icons'
import { library } from '@fortawesome/fontawesome-svg-core'
import type { Language } from '@/model/Language'
import { FileSortingOptions } from '@/model/ui/FileSortingOptions'
import { store } from '@/stores/store'
import type { BaseCodeMatch } from '@/model/BaseCodeReport'
import type { Match } from '@/model/Match'

Expand Down Expand Up @@ -91,7 +91,78 @@ const props = defineProps({
}
})

defineEmits(['matchSelected'])
const emit = defineEmits(['matchSelected', 'filesMoved'])

const matchesPerFile = computed(() => {
const matches: Record<string, MatchInSingleFile[]> = {}
for (const file of props.files) {
matches[file.fileName] = !props.matches.get(file.fileName)
? []
: (props.matches.get(file.fileName) as MatchInSingleFile[])
}
return matches
})

const sortedFiles: Ref<SubmissionFile[]> = ref([])
sortFiles(store().uiState.fileSorting ?? FileSortingOptions.ALPHABETICAL)

function sortFiles(fileSorting: FileSortingOptions) {
switch (fileSorting) {
case FileSortingOptions.ALPHABETICAL: {
sortedFiles.value = Array.from(props.files).sort((a, b) =>
a.fileName.localeCompare(b.fileName)
)
break
}

case FileSortingOptions.MATCH_SIZE: {
const largestMatch: Record<string, number> = {}
for (const file of props.files) {
largestMatch[file.fileName] = Math.max(
...matchesPerFile.value[file.fileName].map((match) => match.match.tokens)
)
}
sortedFiles.value = Array.from(props.files).sort(
(a, b) => largestMatch[b.fileName] - largestMatch[a.fileName]
)
break
}

case FileSortingOptions.MATCH_COUNT: {
const matchCount: Record<string, number> = {}
for (const file of props.files) {
matchCount[file.fileName] = matchesPerFile.value[file.fileName].length
}
sortedFiles.value = Array.from(props.files).sort(
(a, b) => matchCount[b.fileName] - matchCount[a.fileName]
)
break
}

case FileSortingOptions.MATCH_COVERAGE: {
const matchCoverage: Record<string, number> = {}
for (const file of props.files) {
const matches = matchesPerFile.value[file.fileName]
const totalTokens = matches.reduce((acc, match) => acc + match.match.tokens, 0)
matchCoverage[file.fileName] =
totalTokens / (file.tokenCount > 0 ? file.tokenCount : Infinity)
}
sortedFiles.value = Array.from(props.files).sort(
(a, b) => matchCoverage[b.fileName] - matchCoverage[a.fileName]
)
break
}
}
}

const shouldEmitFileMoving = ref(true)

function emitFileMoving() {
if (!shouldEmitFileMoving.value) {
return
}
emit('filesMoved')
}

const codePanels: Ref<(typeof CodePanel)[]> = ref([])
const scrollContainer: Ref<typeof ScrollableComponent | null> = ref(null)
Expand Down Expand Up @@ -132,6 +203,7 @@ function collapseAll() {
}

defineExpose({
scrollTo
scrollTo,
sortFiles
})
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,8 @@ function select(index: number) {
emit('selectionChanged', index)
selected.value = index
}

defineExpose({
select
})
</script>
27 changes: 27 additions & 0 deletions report-viewer/src/model/ui/FileSortingOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { ToolTipLabel } from './ToolTip'

export enum FileSortingOptions {
ALPHABETICAL,
MATCH_COVERAGE,
MATCH_COUNT,
MATCH_SIZE
}

export const fileSortingTooltips: Record<FileSortingOptions, ToolTipLabel> = {
[FileSortingOptions.ALPHABETICAL]: {
displayValue: 'Alphabetical',
tooltip: 'Sort files alphabetically, similar to package structure.'
},
[FileSortingOptions.MATCH_COVERAGE]: {
displayValue: 'Match Coverage',
tooltip: 'Sort files by the percentage of tokens included in a match.'
},
[FileSortingOptions.MATCH_COUNT]: {
displayValue: 'Match Count',
tooltip: 'Sort files by the number of matches found.'
},
[FileSortingOptions.MATCH_SIZE]: {
displayValue: 'Match Size',
tooltip: 'Sort files by match size, with the largest matches at the top.'
}
}
2 changes: 2 additions & 0 deletions report-viewer/src/stores/state.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { SubmissionFile } from '@/model/File'
import type { MetricType } from '@/model/MetricType'
import type { DistributionChartConfig } from '@/model/ui/DistributionChartConfig'
import type { FileSortingOptions } from '@/model/ui/FileSortingOptions'

/**
* Local store. Stores the state of the application.
Expand Down Expand Up @@ -49,4 +50,5 @@ export interface UIState {
comparisonTableSortingMetric: MetricType
comparisonTableClusterSorting: boolean
distributionChartConfig: DistributionChartConfig
fileSorting: FileSortingOptions
}
4 changes: 3 additions & 1 deletion report-viewer/src/stores/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { defineStore } from 'pinia'
import type { State, UIState } from './state'
import { MetricType } from '@/model/MetricType'
import type { SubmissionFile, File } from '@/model/File'
import { FileSortingOptions } from '@/model/ui/FileSortingOptions'

/**
* The store is a global state management system. It is used to store the state of the application.
Expand Down Expand Up @@ -31,7 +32,8 @@ const store = defineStore('store', {
metric: MetricType.AVERAGE,
xScale: 'linear',
bucketCount: 10
}
},
fileSorting: FileSortingOptions.ALPHABETICAL
}
}),
getters: {
Expand Down
38 changes: 38 additions & 0 deletions report-viewer/src/views/ComparisonView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@
:basecode-in-second="secondBaseCodeMatches"
@match-selected="showMatch"
/>
<OptionsSelectorComponent
class="mt-2"
ref="sortingOptionSelector"
title="File Sorting:"
:labels="sortingOptions.map((o) => fileSortingTooltips[o])"
@selectionChanged="(index: number) => changeFileSorting(index)"
:default-selected="sortingOptions.indexOf(store().uiState.fileSorting)"
/>
</Container>
</div>
<div ref="styleholder"></div>
Expand All @@ -87,6 +95,7 @@
:highlight-language="language"
:base-code-matches="firstBaseCodeMatches"
@match-selected="showMatchInSecond"
@files-moved="filesMoved()"
class="max-h-0 min-h-full flex-1 overflow-hidden print:max-h-none print:overflow-y-visible"
/>
<FilesContainer
Expand All @@ -97,6 +106,7 @@
:highlight-language="language"
:base-code-matches="secondBaseCodeMatches"
@match-selected="showMatchInFirst"
@files-moved="filesMoved()"
class="max-h-0 min-h-full flex-1 overflow-hidden print:max-h-none print:overflow-y-visible"
/>
</div>
Expand All @@ -122,6 +132,8 @@ import { MetricType } from '@/model/MetricType'
import { Comparison } from '@/model/Comparison'
import { redirectOnError } from '@/router'
import ToolTipComponent from '@/components/ToolTipComponent.vue'
import { FileSortingOptions, fileSortingTooltips } from '@/model/ui/FileSortingOptions'
import OptionsSelectorComponent from '@/components/optionsSelectors/OptionsSelectorComponent.vue'
import type { BaseCodeMatch } from '@/model/BaseCodeReport'

library.add(faPrint)
Expand Down Expand Up @@ -178,6 +190,32 @@ function showMatch(match: Match) {
showMatchInSecond(match)
}

const sortingOptions = [
FileSortingOptions.ALPHABETICAL,
FileSortingOptions.MATCH_COVERAGE,
FileSortingOptions.MATCH_COUNT,
FileSortingOptions.MATCH_SIZE
]
const movedAfterSorting = ref(false)
const sortingOptionSelector: Ref<typeof OptionsSelectorComponent | null> = ref(null)

function changeFileSorting(index: number) {
movedAfterSorting.value = false
if (index < 0) {
return
}
store().uiState.fileSorting = sortingOptions[index]
panel1.value?.sortFiles(store().uiState.fileSorting)
panel2.value?.sortFiles(store().uiState.fileSorting)
}

function filesMoved() {
movedAfterSorting.value = true
if (sortingOptionSelector.value) {
sortingOptionSelector.value.select(-2)
}
}

function print() {
window.print()
}
Expand Down

0 comments on commit a0a6233

Please sign in to comment.