From 5e7d6d7e4347a999650be94eb11da5bad73b4986 Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Mon, 13 May 2024 14:39:08 +0900 Subject: [PATCH 1/5] Render classified_sources_count for backend --- lib/diver_down/web/action.rb | 5 ++++- spec/diver_down/web_spec.rb | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/diver_down/web/action.rb b/lib/diver_down/web/action.rb index 9717006..a4124d4 100644 --- a/lib/diver_down/web/action.rb +++ b/lib/diver_down/web/action.rb @@ -33,12 +33,15 @@ def sources end # rubocop:enable Style/HashEachMethods + classified_sources_count = source_names.count { @module_store.include?(_1) } + json( sources: source_names.sort.map do |source_name| { source_name:, } - end + end, + classified_sources_count: ) end diff --git a/spec/diver_down/web_spec.rb b/spec/diver_down/web_spec.rb index b8b6561..8af1349 100644 --- a/spec/diver_down/web_spec.rb +++ b/spec/diver_down/web_spec.rb @@ -294,6 +294,7 @@ def assert_definition_group(definition_group, expected_ids) expect(last_response.status).to eq(200) expect(JSON.parse(last_response.body)).to eq({ 'sources' => [], + 'classified_sources_count' => 0, }) end @@ -304,10 +305,18 @@ def assert_definition_group(definition_group, expected_ids) DiverDown::Definition::Source.new( source_name: 'a.rb' ), + DiverDown::Definition::Source.new( + source_name: 'b.rb' + ), + DiverDown::Definition::Source.new( + source_name: 'c.rb' + ), ] ) store.set(definition) + module_store.set('a.rb', ['A']) + get '/api/sources.json' expect(last_response.status).to eq(200) @@ -316,7 +325,14 @@ def assert_definition_group(definition_group, expected_ids) { 'source_name' => 'a.rb', }, + { + 'source_name' => 'b.rb', + }, + { + 'source_name' => 'c.rb', + }, ], + 'classified_sources_count' => 1, }) end end From 3b3346a9fd69e69a92996274dd9b9a86f967f4ba Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Mon, 13 May 2024 15:13:04 +0900 Subject: [PATCH 2/5] Render classified sources count --- frontend/models/source.ts | 6 ++++++ frontend/pages/Sources/List.tsx | 18 ++++++++++++++---- frontend/repositories/sourceRepository.ts | 22 +++++++++++++++++----- lib/diver_down/web/action.rb | 3 +++ spec/diver_down/web_spec.rb | 3 +++ 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/frontend/models/source.ts b/frontend/models/source.ts index 31828ca..133bd2f 100644 --- a/frontend/models/source.ts +++ b/frontend/models/source.ts @@ -3,6 +3,7 @@ import { Module } from './module' export type Source = { sourceName: string + modules: Module[] } type RelatedDefinition = { @@ -21,3 +22,8 @@ export type SpecificSource = { relatedDefinitions: RelatedDefinition[] reverseDependencies: ReverseDependency[] } + +export type Sources = { + sources: Source[] + classifiedSourcesCount: number +} diff --git a/frontend/pages/Sources/List.tsx b/frontend/pages/Sources/List.tsx index 3205bee..a8bf152 100644 --- a/frontend/pages/Sources/List.tsx +++ b/frontend/pages/Sources/List.tsx @@ -9,27 +9,37 @@ import { spacing } from '@/constants/theme' import { useSources } from '@/repositories/sourceRepository' export const List: FC = () => { - const { sources, isLoading } = useSources() + const { data, isLoading } = useSources() return ( - Sources + Sources {data ? `(classified: ${Math.round(data.classifiedSourcesCount / data.sources.length * 100)}% ${data.classifiedSourcesCount} / ${data.sources.length})` : null}
+ - {sources && sources.length > 0 ? ( + {data && data.sources.length > 0 ? ( - {sources.map((source) => ( + {data.sources.map((source) => ( + ))} diff --git a/frontend/repositories/sourceRepository.ts b/frontend/repositories/sourceRepository.ts index cec6285..70817e5 100644 --- a/frontend/repositories/sourceRepository.ts +++ b/frontend/repositories/sourceRepository.ts @@ -1,21 +1,33 @@ import useSWR from 'swr' import { path } from '@/constants/path' -import { SpecificSource } from '@/models/source' +import { Sources, SpecificSource } from '@/models/source' import { get } from './httpRequest' type SourcesReponse = { - sources: Array<{ source_name: string }> + sources: Array<{ + source_name: string + modules: Array<{ + module_name: string + }> + }> + classified_sources_count: number } export const useSources = () => { - const { data, isLoading } = useSWR(path.api.sources.index(), async () => { + const { data, isLoading } = useSWR(path.api.sources.index(), async () => { const response = await get(path.api.sources.index()) - return response.sources.map((source) => ({ sourceName: source.source_name })) + return { + sources: response.sources.map((source) => ({ + sourceName: source.source_name, + modules: source.modules.map((module) => ({ moduleName: module.module_name })), + })), + classifiedSourcesCount: response.classified_sources_count + } }) - return { sources: data, isLoading } + return { data, isLoading } } type SpecificSourceResponse = { diff --git a/lib/diver_down/web/action.rb b/lib/diver_down/web/action.rb index a4124d4..afbc487 100644 --- a/lib/diver_down/web/action.rb +++ b/lib/diver_down/web/action.rb @@ -39,6 +39,9 @@ def sources sources: source_names.sort.map do |source_name| { source_name:, + modules: @module_store.get(source_name).map do |module_name| + { module_name: } + end, } end, classified_sources_count: diff --git a/spec/diver_down/web_spec.rb b/spec/diver_down/web_spec.rb index 8af1349..d1316da 100644 --- a/spec/diver_down/web_spec.rb +++ b/spec/diver_down/web_spec.rb @@ -324,12 +324,15 @@ def assert_definition_group(definition_group, expected_ids) 'sources' => [ { 'source_name' => 'a.rb', + 'modules' => ['A'], }, { 'source_name' => 'b.rb', + 'modules' => [], }, { 'source_name' => 'c.rb', + 'modules' => [], }, ], 'classified_sources_count' => 1, From ef866e646aa06076cd53d277dcb2e5e53262df1c Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Mon, 13 May 2024 16:30:25 +0900 Subject: [PATCH 3/5] Sort all sources by modules --- frontend/pages/Sources/List.tsx | 80 ++++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/frontend/pages/Sources/List.tsx b/frontend/pages/Sources/List.tsx index a8bf152..b7b9f4b 100644 --- a/frontend/pages/Sources/List.tsx +++ b/frontend/pages/Sources/List.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react' +import { FC, useCallback, useMemo, useState } from 'react' import styled from 'styled-components' import { Link } from '@/components/Link' @@ -7,9 +7,75 @@ import { EmptyTableBody, Heading, Section, Stack, Table, Td, Text, Th } from '@/ import { path } from '@/constants/path' import { spacing } from '@/constants/theme' import { useSources } from '@/repositories/sourceRepository' +import { Sources } from '@/models/source' + +const sortTypes = ['asc', 'desc', 'none'] as const + +type SortTypes = (typeof sortTypes)[number] + +type SortState = { + key: 'sourceName' | 'modules' + sort: SortTypes +} export const List: FC = () => { const { data, isLoading } = useSources() + const [sortState, setSortState] = useState({ key: 'sourceName', sort: 'asc' }) + + const setNextSortType = useCallback( + (key: SortState['key']) => { + setSortState((prev) => { + if (prev.key === key) { + return { + key, + sort: sortTypes[(sortTypes.indexOf(prev.sort) + 1) % sortTypes.length], + } + } else { + return { key, sort: 'asc' } + } + }) + }, + [setSortState], + ) + + const sources: Sources['sources'] = useMemo(() => { + if (!data) { + return null + } + + let sorted = [...data.sources] + + if (sortState.sort === 'none') { + return sorted + } + + const ascString = (a: string, b: string) => { + if (a > b) return 1 + if (a < b) return -1 + return 0 + } + + switch (sortState.key) { + case 'sourceName': { + sorted = sorted.sort((a, b) => ascString(a.sourceName, b.sourceName)) + break + } + case 'modules': { + sorted = sorted.sort((a, b) => + ascString( + a.modules.map((module) => module.moduleName).join('-'), + b.modules.map((module) => module.moduleName).join('-'), + ), + ) + } + } + + if (sortState.sort === 'desc') { + sorted = sorted.reverse() + } + + return sorted + }, [data?.sources, sortState]) return ( @@ -20,13 +86,17 @@ export const List: FC = () => {
Source nameModules
{source.sourceName} + {source.modules.map((module, index) => ( + + mod.moduleName))}> + {module.moduleName} + + + ))} +
- - + + - {data && data.sources.length > 0 ? ( + {sources && sources.length > 0 ? ( - {data.sources.map((source) => ( + {sources.map((source) => (
Source nameModules setNextSortType('sourceName')}> + Source name + setNextSortType('modules')}> + Modules +
{source.sourceName} From 748e65663db304357ae658a1487b5ca2e43510a5 Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Mon, 13 May 2024 16:34:54 +0900 Subject: [PATCH 4/5] DRY sort logic --- frontend/models/source.ts | 35 ++++++++++++++++++ .../DefinitionSources/DefinitionSources.tsx | 34 +---------------- frontend/pages/Sources/List.tsx | 37 ++----------------- 3 files changed, 40 insertions(+), 66 deletions(-) diff --git a/frontend/models/source.ts b/frontend/models/source.ts index 133bd2f..b67a8a3 100644 --- a/frontend/models/source.ts +++ b/frontend/models/source.ts @@ -27,3 +27,38 @@ export type Sources = { sources: Source[] classifiedSourcesCount: number } + +export const sortSources = (sources: Source[], key: 'sourceName' | 'modules', sort: 'none' | 'asc' | 'desc'): Source[] => { + if (sort === 'none') { + return sources + } + + let sorted = [...sources] + + const ascString = (a: string, b: string) => { + if (a > b) return 1 + if (a < b) return -1 + return 0 + } + + switch (key) { + case 'sourceName': { + sorted = sorted.sort((a, b) => ascString(a.sourceName, b.sourceName)) + break + } + case 'modules': { + sorted = sorted.sort((a, b) => + ascString( + a.modules.map((module) => module.moduleName).join('-'), + b.modules.map((module) => module.moduleName).join('-'), + ), + ) + } + } + + if (sort === 'desc') { + sorted = sorted.reverse() + } + + return sorted +} diff --git a/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx b/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx index 36d4b89..0071e02 100644 --- a/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx +++ b/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx @@ -8,6 +8,7 @@ import { color } from '@/constants/theme' import { CombinedDefinition } from '@/models/combinedDefinition' import { SourceModulesComboBox } from '../SourceModulesComboBox' +import { sortSources } from '@/models/source' type Props = { combinedDefinition: CombinedDefinition @@ -44,38 +45,7 @@ export const DefinitionSources: FC = ({ combinedDefinition, mutateCombine ) const sources: CombinedDefinition['sources'] = useMemo(() => { - let sorted = [...combinedDefinition.sources] - - if (sortState.sort === 'none') { - return sorted - } - - const ascString = (a: string, b: string) => { - if (a > b) return 1 - if (a < b) return -1 - return 0 - } - - switch (sortState.key) { - case 'sourceName': { - sorted = sorted.sort((a, b) => ascString(a.sourceName, b.sourceName)) - break - } - case 'modules': { - sorted = sorted.sort((a, b) => - ascString( - a.modules.map((module) => module.moduleName).join('-'), - b.modules.map((module) => module.moduleName).join('-'), - ), - ) - } - } - - if (sortState.sort === 'desc') { - sorted = sorted.reverse() - } - - return sorted + return sortSources(combinedDefinition.sources, sortState.key, sortState.sort) }, [combinedDefinition.sources, sortState]) return ( diff --git a/frontend/pages/Sources/List.tsx b/frontend/pages/Sources/List.tsx index b7b9f4b..12b31dd 100644 --- a/frontend/pages/Sources/List.tsx +++ b/frontend/pages/Sources/List.tsx @@ -7,7 +7,7 @@ import { EmptyTableBody, Heading, Section, Stack, Table, Td, Text, Th } from '@/ import { path } from '@/constants/path' import { spacing } from '@/constants/theme' import { useSources } from '@/repositories/sourceRepository' -import { Sources } from '@/models/source' +import { Sources, sortSources } from '@/models/source' const sortTypes = ['asc', 'desc', 'none'] as const @@ -40,41 +40,10 @@ export const List: FC = () => { const sources: Sources['sources'] = useMemo(() => { if (!data) { - return null + return [] } - let sorted = [...data.sources] - - if (sortState.sort === 'none') { - return sorted - } - - const ascString = (a: string, b: string) => { - if (a > b) return 1 - if (a < b) return -1 - return 0 - } - - switch (sortState.key) { - case 'sourceName': { - sorted = sorted.sort((a, b) => ascString(a.sourceName, b.sourceName)) - break - } - case 'modules': { - sorted = sorted.sort((a, b) => - ascString( - a.modules.map((module) => module.moduleName).join('-'), - b.modules.map((module) => module.moduleName).join('-'), - ), - ) - } - } - - if (sortState.sort === 'desc') { - sorted = sorted.reverse() - } - - return sorted + return sortSources(data.sources, sortState.key, sortState.sort) }, [data?.sources, sortState]) return ( From a4c3dc1b32b0316d2e5c269f385fc22751b03f8e Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Mon, 13 May 2024 16:49:46 +0900 Subject: [PATCH 5/5] fix rspec --- spec/diver_down/web_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/diver_down/web_spec.rb b/spec/diver_down/web_spec.rb index d1316da..24e3fe0 100644 --- a/spec/diver_down/web_spec.rb +++ b/spec/diver_down/web_spec.rb @@ -324,7 +324,7 @@ def assert_definition_group(definition_group, expected_ids) 'sources' => [ { 'source_name' => 'a.rb', - 'modules' => ['A'], + 'modules' => [{ 'module_name' => 'A' }], }, { 'source_name' => 'b.rb',