From de35d5d4f2ba2195a943b99ae0de7543856f2122 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 24 Nov 2021 13:00:23 +0900 Subject: [PATCH 01/74] =?UTF-8?q?fix:=20=E3=83=AD=E3=83=BC=E3=82=AB?= =?UTF-8?q?=E3=83=AB=E7=92=B0=E5=A2=83=E3=81=A7Flask=E3=81=AB=E3=83=AA?= =?UTF-8?q?=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88=E3=81=8C=E5=B1=8A=E3=81=8B?= =?UTF-8?q?=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/webpack.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/webpack.config.js b/node/webpack.config.js index 575e51d5..a3fe7a12 100644 --- a/node/webpack.config.js +++ b/node/webpack.config.js @@ -14,7 +14,7 @@ module.exports = (env, args) => { let FIREBASE_CONFIG switch (args.mode) { case 'development': - API_ENDPOINT = 'http://localhost/api/v1' + API_ENDPOINT = 'http://127.0.0.1:5000/api/v1' FIREBASE_CONFIG = { apiKey: "AIzaSyB3GMmRd9JWGVvDlEtgpemtYZPo-WRkNpc", authDomain: "fabled-alchemy-246207.firebaseapp.com", @@ -38,7 +38,7 @@ module.exports = (env, args) => { } break default: - API_ENDPOINT = 'http://localhost/api/v1' + API_ENDPOINT = 'http://127.0.0.1:5000/api/v1' FIREBASE_CONFIG = { apiKey: "", authDomain: "", From b098dbece09d91e9f17da1500962f09bcb110118 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 24 Nov 2021 17:59:38 +0900 Subject: [PATCH 02/74] =?UTF-8?q?perf:=20=E3=82=B0=E3=83=A9=E3=83=95?= =?UTF-8?q?=E3=81=AE=E6=8F=8F=E7=94=BB=E4=B8=AD=E3=81=AB=E5=86=8D=E6=8F=8F?= =?UTF-8?q?=E7=94=BB=E3=82=92=E8=A1=8C=E3=82=8F=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 67 +++++++++---------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index f1f9f534..22cc4902 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -577,58 +577,53 @@ const ClassStructure: React.FC = (props) => { GraphRepository.setSearching() GraphRepository.setArrowHead() - const [nonNullWidth, nonNullHeight, nonNullDiameter] = [ - width || 0, - height || 0, - circleDiameter || 1, - ] - GraphRepository.initialRootCircleSize = nonNullDiameter - - onResize(nonNullWidth, nonNullHeight, nonNullDiameter) - - GraphRepository.manuallyZoomed = false - update(detail, true) - if (isIE11) { setInterval(GraphRepository.forceRedrawLines, 10) } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [classes, nodes]) + }, [isIE11, classes, nodes]) const oldPropsRef = useRef({ oldWidth: width, oldHeight: height }) const mounted = React.useRef(false) + const updating = React.useRef(false) React.useEffect(() => { + if (updating.current) { + return + } + if (mounted.current) { - const [nonNullWidth, nonNullHeight, nonNullDiameter] = [ - width || 0, - height || 0, - circleDiameter || 1, - ] const { oldWidth, oldHeight } = oldPropsRef.current - if (width !== oldWidth || height !== oldHeight) { - onResize(nonNullWidth, nonNullHeight, nonNullDiameter) + onResize(width ?? 0, height ?? 0, circleDiameter ?? 1) } const { current: oldDetail } = oldDetailStateRef - if (classes) { - if ( - width !== oldWidth || - height !== oldHeight || - detail.focusingCircleKey !== oldDetail.focusingCircleKey || - detail.showRightHand !== oldDetail.showRightHand || - detail.showLeftHand !== oldDetail.showLeftHand || - detail.showingRelation !== oldDetail.showingRelation || - detail.propertyClass.domain !== oldDetail.propertyClass.domain || - detail.propertyClass.range !== oldDetail.propertyClass.range || - detail.searchingURI !== oldDetail.searchingURI - ) { - GraphRepository.manuallyZoomed = false - update(detail, true) - } + if ( + classes || + width !== oldWidth || + height !== oldHeight || + detail.focusingCircleKey !== oldDetail.focusingCircleKey || + detail.showRightHand !== oldDetail.showRightHand || + detail.showLeftHand !== oldDetail.showLeftHand || + detail.showingRelation !== oldDetail.showingRelation || + detail.propertyClass.domain !== oldDetail.propertyClass.domain || + detail.propertyClass.range !== oldDetail.propertyClass.range || + detail.searchingURI !== oldDetail.searchingURI + ) { + updating.current = true + GraphRepository.manuallyZoomed = false + update(detail, true) + updating.current = false } } else { mounted.current = true + + GraphRepository.initialRootCircleSize = circleDiameter ?? 1 + onResize(width ?? 0, height ?? 0, circleDiameter ?? 1) + + updating.current = true + GraphRepository.manuallyZoomed = false + update(detail, true) + updating.current = false } oldPropsRef.current = { oldWidth: width, oldHeight: height } From f9454d21e351650a61fc12c1c7a9ebebf7173765 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 24 Nov 2021 14:51:09 +0900 Subject: [PATCH 03/74] =?UTF-8?q?feat:=20=E3=82=AF=E3=83=A9=E3=82=B9?= =?UTF-8?q?=E3=81=AE=E3=83=A9=E3=83=99=E3=83=AB=E3=81=8CURI=E3=81=AE?= =?UTF-8?q?=E3=81=A8=E3=81=8D=E3=80=81=E3=83=A9=E3=83=99=E3=83=AB=E3=82=92?= =?UTF-8?q?=E7=9C=81=E7=95=A5=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/utils/index.ts | 28 ++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/node/src/ts/visualizer/utils/index.ts b/node/src/ts/visualizer/utils/index.ts index 5eb4abc6..e8706e95 100644 --- a/node/src/ts/visualizer/utils/index.ts +++ b/node/src/ts/visualizer/utils/index.ts @@ -2,18 +2,36 @@ import { useLocation } from 'react-router-dom' import locales from '../locales' import { Classes } from '../types/class' +const omitUri = (uri: string) => { + // Do not allow endwith '#' or '/' because fragment or path will be empty. + const uriWithoutEndDelim = uri.replace(/[#,/]$/, '') + + if (uriWithoutEndDelim.lastIndexOf('#') > -1) { + const fragment = uri.split('#').slice(-1)[0] + return fragment + } + + const uriWithoutScheme = uriWithoutEndDelim.split('://').slice(-1)[0] + if (uriWithoutScheme.lastIndexOf('/') > -1) { + const path = uriWithoutScheme.split('/').slice(-1)[0] + return path + } + + return uri +} + export const getPreferredLabel = ( uri: string, classes: Classes, locale: string ): string => { - const detail = classes[uri] - const labels = detail?.label + const labels = classes[uri]?.label if (!labels) { - return uri + return omitUri(uri) } - const label = labels[locale] || labels.en || labels[''] || null - return label || uri + + const label = labels[locale] ?? labels.en ?? labels[''] ?? undefined + return label ?? omitUri(uri) } export const getLocaleShortString = (): string => { From 16b16318a35d6672fb98d5e352cf07dacec48a70 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 24 Nov 2021 19:53:56 +0900 Subject: [PATCH 04/74] =?UTF-8?q?fix:=20=E3=82=A2=E3=83=8B=E3=83=A1?= =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E6=99=82=E3=81=AB=E3=83=A9?= =?UTF-8?q?=E3=83=99=E3=83=AB=E3=81=AE=E5=BC=B7=E8=AA=BF=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=8C=E8=A1=8C=E3=82=8F=E3=82=8C=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ts/visualizer/utils/GraphRepository.ts | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index c410a0c8..95a4c645 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -599,20 +599,22 @@ class GraphRepository { // Firefoxはdisplay:noneな要素にgetBoundingClientRectできない const { scale } = this - const visibilityTexts = ctx.gtexts - .style('visibility', 'hidden') - .style('opacity', 0) - ?.filter((d) => { - return ( - this.targetKey === d.data.key || - !d.children || - !(d.children[0].data.key in this.visibleNodesSet) || - d.r * scale <= SHOW_TEXT_MAX_CIRCLE_DIAMETER - ) - }) - .filter((d) => this.isShowNodeText(d)) + // ctxがd3.Transitionを返すときにvisibilityTexts.dataがundefinedになるので、アクセサを呼び分ける + const filterVisibilityTexts = (accessor: SVGElementsAccessor) => + accessor.gtexts + ?.filter((d) => { + return ( + this.targetKey === d.data.key || + !d.children || + !(d.children[0].data.key in this.visibleNodesSet) || + d.r * scale <= SHOW_TEXT_MAX_CIRCLE_DIAMETER + ) + }) + .filter((d) => this.isShowNodeText(d)) + + ctx.gtexts.style('visibility', 'hidden').style('opacity', 0) - visibilityTexts + filterVisibilityTexts(ctx) .attr('class', '') .style('visibility', 'visible') .style('opacity', 1) @@ -620,6 +622,7 @@ class GraphRepository { .selectAll('tspan') .attr('x', 0) + const visibilityTexts = filterVisibilityTexts(this.svgAccessor) if (typeof visibilityTexts?.data === 'function') { // Leafノードでない && クラス単位で最上位にあたるノードを強調表示 const data = visibilityTexts.data() @@ -638,9 +641,9 @@ class GraphRepository { (acc, d) => acc.concat([getUpperParent(d)]), [] ) - const upperParentUris: string[] = _.uniq(_.map(upperParents, 'data.uri')) + const upperParentUris = new Set(upperParents.map((v) => v.data.uri)) visibilityTexts - .filter((d) => !!d.children && upperParentUris.includes(d.data.uri)) + .filter((d) => !!d.children && upperParentUris.has(d.data.uri)) .attr('class', 'emphasized-class') } From ad64df73874aa490ba824036889f9a91646e2716 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Thu, 25 Nov 2021 12:16:05 +0900 Subject: [PATCH 05/74] =?UTF-8?q?fix:=20=E3=82=A2=E3=83=8B=E3=83=A1?= =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E6=99=82=E3=81=AB=E3=83=A9?= =?UTF-8?q?=E3=83=99=E3=83=AB=E3=81=AE=E5=BC=B7=E8=AA=BF=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=8C=E3=82=AF=E3=83=AA=E3=82=A2=E3=81=95=E3=82=8C=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/utils/GraphRepository.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index 95a4c645..c767d6b3 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -613,8 +613,9 @@ class GraphRepository { .filter((d) => this.isShowNodeText(d)) ctx.gtexts.style('visibility', 'hidden').style('opacity', 0) + const visibilityTextsCtx = filterVisibilityTexts(ctx) - filterVisibilityTexts(ctx) + visibilityTextsCtx .attr('class', '') .style('visibility', 'visible') .style('opacity', 1) @@ -642,8 +643,13 @@ class GraphRepository { [] ) const upperParentUris = new Set(upperParents.map((v) => v.data.uri)) - visibilityTexts - .filter((d) => !!d.children && upperParentUris.has(d.data.uri)) + visibilityTextsCtx + .filter( + (d) => + this.targetKey !== d.data.key && + !!d.children && + upperParentUris.has(d.data.uri) + ) .attr('class', 'emphasized-class') } From 63ad1741d680ef5dcbd1b7c2237bbb5c62088b1c Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 24 Nov 2021 13:43:15 +0900 Subject: [PATCH 06/74] =?UTF-8?q?style:=20=E3=83=A9=E3=83=99=E3=83=AB?= =?UTF-8?q?=E3=81=AE=E5=BC=B7=E8=AA=BF=E8=A1=A8=E7=A4=BA=E3=82=92=E9=BB=92?= =?UTF-8?q?=E5=A4=AA=E5=AD=97=E3=81=8B=E3=82=89=E7=99=BD=E5=A4=AA=E5=AD=97?= =?UTF-8?q?=E3=81=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/style/style.scss | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/node/src/style/style.scss b/node/src/style/style.scss index 5b9f00c4..d1e83b4c 100644 --- a/node/src/style/style.scss +++ b/node/src/style/style.scss @@ -620,15 +620,15 @@ body { height: 20px; overflow: hidden; tspan { - fill: #444; + fill: $white-color; font-size: 18px; font-weight: bold; line-height: 1.8; text-shadow: - 0 0 2px $white-color, 0 0 2px $white-color, 0 0 2px $white-color, 0 0 2px $white-color, - 0 0 2px $white-color, 0 0 2px $white-color, 0 0 2px $white-color, 0 0 2px $white-color, - 0 0 2px $white-color, 0 0 2px $white-color, 0 0 2px $white-color, 0 0 2px $white-color, - 0 0 2px $white-color, 0 0 2px $white-color, 0 0 2px $white-color, 0 0 2px $white-color; + 0 0 2px #444, 0 0 2px #444, 0 0 2px #444, 0 0 2px #444, + 0 0 2px #444, 0 0 2px #444, 0 0 2px #444, 0 0 2px #444, + 0 0 2px #444, 0 0 2px #444, 0 0 2px #444, 0 0 2px #444, + 0 0 2px #444, 0 0 2px #444, 0 0 2px #444, 0 0 2px #444; } } } From ca7e65d21de914b3e883a707c4389fa5a3698461 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Fri, 26 Nov 2021 11:46:02 +0900 Subject: [PATCH 07/74] =?UTF-8?q?feat:=20=E3=83=8F=E3=82=A4=E3=83=A9?= =?UTF-8?q?=E3=82=A4=E3=83=88=E3=81=95=E3=82=8C=E3=81=9F=E3=82=AF=E3=83=A9?= =?UTF-8?q?=E3=82=B9=E4=BB=A5=E5=A4=96=E3=81=AE=E3=83=A9=E3=83=99=E3=83=AB?= =?UTF-8?q?=E3=82=92=E7=9B=AE=E7=AB=8B=E3=81=9F=E3=81=AA=E3=81=8F=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 23 ++++++++++++++----- .../ts/visualizer/utils/GraphRepository.ts | 13 ++++++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 22cc4902..65ef14e4 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -355,7 +355,12 @@ const ClassStructure: React.FC = (props) => { ) const showCircles = React.useCallback( - (circles: NodeType[], animate: boolean, updateScale: boolean = true) => { + ( + circles: NodeType[], + animate: boolean, + updateScale: boolean = true, + transparentLabel: boolean = false + ) => { if (circles.length === 0) { return } @@ -364,6 +369,8 @@ const ClassStructure: React.FC = (props) => { GraphRepository.calcCircleScale(circles) GraphRepository.updateScale() } + + GraphRepository.transparentLabel = transparentLabel if (animate) { GraphRepository.updatePositionWithAnimate() } else { @@ -488,19 +495,23 @@ const ClassStructure: React.FC = (props) => { if (showingRelation) { showCircles( focus(focusingCircleKey, true, true, showingRelation), - animate + animate, + true, + true ) return } if (showRightHand || showLeftHand) { showCircles( focus(focusingCircleKey, showRightHand, showLeftHand), - animate + animate, + true, + true ) return } - showCircles(focus(focusingCircleKey), animate) + showCircles(focus(focusingCircleKey), animate, true, true) return } if (domain || range) { @@ -527,7 +538,7 @@ const ClassStructure: React.FC = (props) => { GraphRepository.updateSelfLines([object]) } } - showCircles(showPropertyClass(domain, range), animate) + showCircles(showPropertyClass(domain, range), animate, true, true) return } if (searchingURI) { @@ -542,7 +553,7 @@ const ClassStructure: React.FC = (props) => { ) return } - showCircles(matchedNodes, animate) + showCircles(matchedNodes, animate, true, true) return } diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index c767d6b3..3399628f 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -98,6 +98,8 @@ class GraphRepository { ignoreEvent: Boolean + transparentLabel: boolean + pos: { top: number bottom: number @@ -147,6 +149,7 @@ class GraphRepository { this.zoom = undefined this.timer = undefined this.ignoreEvent = false + this.transparentLabel = false } // public accessor @@ -348,6 +351,7 @@ class GraphRepository { this.manuallyZoomed = true this.scale = event.transform.k this.translate = [event.transform.x, event.transform.y] + this.transparentLabel = false this.updateScale() this.updatePosition() @@ -597,7 +601,7 @@ class GraphRepository { // texts // Firefoxはdisplay:noneな要素にgetBoundingClientRectできない - const { scale } = this + const { scale, transparentLabel } = this // ctxがd3.Transitionを返すときにvisibilityTexts.dataがundefinedになるので、アクセサを呼び分ける const filterVisibilityTexts = (accessor: SVGElementsAccessor) => @@ -613,12 +617,15 @@ class GraphRepository { .filter((d) => this.isShowNodeText(d)) ctx.gtexts.style('visibility', 'hidden').style('opacity', 0) - const visibilityTextsCtx = filterVisibilityTexts(ctx) + const visibilityTextsCtx = filterVisibilityTexts(ctx) + const labelOpacity = transparentLabel ? 0.6 : 1 + const shouldBeOpaqueLabel = (d: NodeType) => + d.data.key === this.targetKey || this.urisToHighlight.includes(d.data.uri) visibilityTextsCtx .attr('class', '') .style('visibility', 'visible') - .style('opacity', 1) + .style('opacity', (d) => (shouldBeOpaqueLabel(d) ? 1 : labelOpacity)) .attr('transform', (d) => `translate(${this.x(d.x)}, ${this.textY(d)})`) .selectAll('tspan') .attr('x', 0) From 42870ad95c5724820a40f857f268ffbc18032bd9 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 24 Nov 2021 17:29:32 +0900 Subject: [PATCH 08/74] =?UTF-8?q?fix:=20=E8=A1=A8=E7=A4=BA=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=81=AA=E3=81=84=E3=83=A9=E3=83=99=E3=83=AB=E3=81=8C?= =?UTF-8?q?=E3=81=82=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/constants/ClassStructure.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/ts/visualizer/constants/ClassStructure.ts b/node/src/ts/visualizer/constants/ClassStructure.ts index 956e8cfd..5789b46d 100644 --- a/node/src/ts/visualizer/constants/ClassStructure.ts +++ b/node/src/ts/visualizer/constants/ClassStructure.ts @@ -1,6 +1,6 @@ export const FOCUSING_PADDING = 50 export const SHOW_TEXT_MIN_CIRCLE_DIAMETER = 60 -export const SHOW_TEXT_MAX_CIRCLE_DIAMETER = SHOW_TEXT_MIN_CIRCLE_DIAMETER * 4 +export const SHOW_TEXT_MAX_CIRCLE_DIAMETER = SHOW_TEXT_MIN_CIRCLE_DIAMETER * 6 export const HIGHLIGHTING_MIN_SIZE = 17 From 32fc7c2624cbf37525a0c9d910e44f742184761c Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Mon, 29 Nov 2021 14:12:02 +0900 Subject: [PATCH 09/74] =?UTF-8?q?feat:=20=E3=82=AF=E3=83=A9=E3=82=B9?= =?UTF-8?q?=E3=81=AB=E3=82=AB=E3=83=BC=E3=82=BD=E3=83=AB=E3=82=92=E5=BD=93?= =?UTF-8?q?=E3=81=A6=E3=81=9F=E9=9A=9B=E3=81=AB=E3=83=84=E3=83=BC=E3=83=AB?= =?UTF-8?q?=E3=83=81=E3=83=83=E3=83=97=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/style/style.scss | 37 +++++++++++-- .../visualizer/components/ClassStructure.tsx | 2 +- node/src/ts/visualizer/components/Tooltip.tsx | 55 +++++++++++++------ .../ts/visualizer/utils/GraphRepository.ts | 7 +-- 4 files changed, 72 insertions(+), 29 deletions(-) diff --git a/node/src/style/style.scss b/node/src/style/style.scss index d1e83b4c..db8369c2 100644 --- a/node/src/style/style.scss +++ b/node/src/style/style.scss @@ -1392,19 +1392,46 @@ body { font-size: 14px; z-index: 5; - p, li { - margin-top: 8px; - list-style-type: none; + > * { + margin-bottom: 12px; + :last-child { + margin-bottom: 0; + } } - .subject { - margin-top: 16px; + .detail, .uri { + display: flex; + flex-direction: row; + align-items: center; + h4 { + margin-right: 8px; + } + } + + .subject, .object { + display: flex; + flex-direction: column; + align-items: flex-start; + li { + margin-top: 8px; + list-style-type: none; + display: flex; + flex-direction: row; + align-items: center; + } } .object { color: #a567a7; } + .detail h4 { + font-weight: bold; + } + :not(.detail) h4 { + @extend .legend-label; + } + .arrow { position: absolute; left: calc(50% - 25px); diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 65ef14e4..2b74fdcf 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -141,7 +141,7 @@ const ClassStructure: React.FC = (props) => { const handleShowTooltip: SVGEventHandlerType = React.useCallback( (event?: React.MouseEvent, d?: NodeType) => { - if (!d || !event || GraphRepository.isShowNodeText(d)) { + if (!d || !event) { return } diff --git a/node/src/ts/visualizer/components/Tooltip.tsx b/node/src/ts/visualizer/components/Tooltip.tsx index 34a6caa9..1d19f89f 100644 --- a/node/src/ts/visualizer/components/Tooltip.tsx +++ b/node/src/ts/visualizer/components/Tooltip.tsx @@ -1,8 +1,10 @@ -import React, { useEffect, useRef, useState } from 'react' +import React, { useEffect, useRef, useState, useMemo } from 'react' import { useSelector } from 'react-redux' +import { useIntl } from 'react-intl' import { RootState } from '../reducers' import { Classes } from '../types/class' import SubjectDetail from './SubjectDetail' +import { getPreferredLabel } from '../utils' type TooltipProps = { classes: Classes @@ -59,22 +61,41 @@ const Tooltip: React.FC = (props) => { } }, [classes, pos, uri]) - return ( -
-

URI

-

{uri}

- -
-
- ) + const intl = useIntl() + const { x, y, visible, isOnBottom } = state + const tooltipElement = useMemo(() => { + if (!uri) { + return null + } + + const detail = classes[uri] + const entities = detail?.entities + const preferredLabel = getPreferredLabel(uri, classes, intl.locale) + return ( +
+
+

{preferredLabel}

+

{entities !== undefined ? `(${entities} entities)` : null}

+
+
+

URI

+

{uri}

+
+ +
+
+ ) + }, [uri, intl.locale, classes, x, y, visible, isOnBottom]) + + return tooltipElement } export default Tooltip diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index 3399628f..f3b05e35 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -1015,12 +1015,7 @@ class GraphRepository { handleHideTooltip: SVGEventHandlerType ) { this.circles - ?.on('mouseenter', null) - .on('mousemove', null) - .on('wheel', null) - .on('mouseleave', null) - .filter((d) => this.urisToHighlight.includes(d.data.uri)) - .on('mouseenter', handleShowTooltip) + ?.on('mouseenter', handleShowTooltip) .on('mousemove', handleShowTooltip) .on('wheel', handleShowTooltip) .on('mouseleave', handleHideTooltip) From 233e656fec3f5ab73cf254c208f9dd2a684a9e9d Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Mon, 29 Nov 2021 14:30:02 +0900 Subject: [PATCH 10/74] =?UTF-8?q?fix:=20=E3=83=84=E3=83=BC=E3=83=AB?= =?UTF-8?q?=E3=83=81=E3=83=83=E3=83=97=E3=81=AEonBottom=E3=81=AE=E5=88=A4?= =?UTF-8?q?=E5=AE=9A=E3=81=8C=E3=81=8A=E3=81=8B=E3=81=97=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/style/style.scss | 2 +- node/src/ts/visualizer/components/Tooltip.tsx | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/node/src/style/style.scss b/node/src/style/style.scss index db8369c2..043a30d9 100644 --- a/node/src/style/style.scss +++ b/node/src/style/style.scss @@ -1446,7 +1446,7 @@ body { } &.downward { - bottom: -25px; + bottom: -35px; border-width: 25px 25px 0 25px; } } diff --git a/node/src/ts/visualizer/components/Tooltip.tsx b/node/src/ts/visualizer/components/Tooltip.tsx index 1d19f89f..1e4d8567 100644 --- a/node/src/ts/visualizer/components/Tooltip.tsx +++ b/node/src/ts/visualizer/components/Tooltip.tsx @@ -40,14 +40,17 @@ const Tooltip: React.FC = (props) => { const tooltip = tooltipRef.current?.getBoundingClientRect() if (tooltip && pos) { - const onBottom = pos.bottom < tooltip.height + const dbclsHeaderHeight = 24 + 8 const arrowSize = 25 + const topMarginRequired = + tooltip.height + arrowSize + dbclsHeaderHeight + const onBottom = pos.top < topMarginRequired setState({ x: (pos.left + pos.right - tooltip.width) / 2, y: onBottom ? pos.bottom + arrowSize - : pos.top - tooltip.height - arrowSize, + : pos.top - (tooltip.height + arrowSize), visible: true, isOnBottom: onBottom, }) From eec061782e07f8d9377facf695f0b2d1c4ee6cae Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Mon, 29 Nov 2021 15:18:55 +0900 Subject: [PATCH 11/74] =?UTF-8?q?refactor:=20=E3=83=84=E3=83=BC=E3=83=AB?= =?UTF-8?q?=E3=83=81=E3=83=83=E3=83=97=E3=81=AE=E8=A1=A8=E7=A4=BA=E5=88=A4?= =?UTF-8?q?=E5=AE=9A=E3=81=AE=E3=83=8D=E3=82=B9=E3=83=88=E3=82=92=E6=B8=9B?= =?UTF-8?q?=E3=82=89=E3=81=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/components/Tooltip.tsx | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/node/src/ts/visualizer/components/Tooltip.tsx b/node/src/ts/visualizer/components/Tooltip.tsx index 1e4d8567..0af06d50 100644 --- a/node/src/ts/visualizer/components/Tooltip.tsx +++ b/node/src/ts/visualizer/components/Tooltip.tsx @@ -32,33 +32,31 @@ const Tooltip: React.FC = (props) => { useEffect(() => { if (mounted.current) { const { uri: oldUri } = oldTooltipStateRef.current + if (uri === oldUri) { + return + } - if (uri !== oldUri) { - setState({ ...state, visible: false }) - - if (uri && !state.visible) { - const tooltip = tooltipRef.current?.getBoundingClientRect() - - if (tooltip && pos) { - const dbclsHeaderHeight = 24 + 8 - const arrowSize = 25 - const topMarginRequired = - tooltip.height + arrowSize + dbclsHeaderHeight - const onBottom = pos.top < topMarginRequired + setState({ ...state, visible: false }) + oldTooltipStateRef.current = { uri } - setState({ - x: (pos.left + pos.right - tooltip.width) / 2, - y: onBottom - ? pos.bottom + arrowSize - : pos.top - (tooltip.height + arrowSize), - visible: true, - isOnBottom: onBottom, - }) - } - } + const tooltip = tooltipRef.current?.getBoundingClientRect() + if (!uri || !tooltip || !pos) { + return } - oldTooltipStateRef.current = { uri } + const dbclsHeaderHeight = 24 + 8 + const arrowSize = 25 + const topMarginRequired = tooltip.height + arrowSize + dbclsHeaderHeight + const onBottom = pos.top < topMarginRequired + + setState({ + x: (pos.left + pos.right - tooltip.width) / 2, + y: onBottom + ? pos.bottom + arrowSize + : pos.top - (tooltip.height + arrowSize), + visible: true, + isOnBottom: onBottom, + }) } else { mounted.current = true } From 1416393fd78e854cab98cf284300f2509e02eac5 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Fri, 26 Nov 2021 15:45:19 +0900 Subject: [PATCH 12/74] =?UTF-8?q?refactor:=20=E3=82=AF=E3=83=A9=E3=82=B9?= =?UTF-8?q?=E9=96=93=E9=96=A2=E4=BF=82=E3=81=AE=E7=9F=A2=E5=8D=B0=E5=91=A8?= =?UTF-8?q?=E3=82=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ts/visualizer/utils/GraphRepository.ts | 137 +++++++----------- 1 file changed, 55 insertions(+), 82 deletions(-) diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index f3b05e35..f2de2296 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -683,102 +683,75 @@ class GraphRepository { // lines // const { paths } = this + const makeArrowLineToCenter = (from: NodeType, to: NodeType) => { + // 中心から中心を指す + const fromX = this.x(from.x) + const fromY = this.y(from.y) + const toX = this.x(to.x) + const toY = this.y(to.y) + return `M ${fromX},${fromY} ${toX},${toY}` + } + + const makeArrowLineToSide = ( + from: NodeType, + to: NodeType, + calculatedDistance?: number + ) => { + const dist = calculatedDistance ?? distance(from.x, from.y, to.x, to.y) + + // 辺から辺を指す + const cutFrom = (dist - from.r) / dist + const fromX = this.x((from.x - to.x) * cutFrom + to.x) + const fromY = this.y((from.y - to.y) * cutFrom + to.y) + const cutTo = (dist - to.r) / dist + const toX = this.x((to.x - from.x) * cutTo + from.x) + const toY = this.y((to.y - from.y) * cutTo + from.y) + return `M ${fromX},${fromY} ${toX},${toY}` + } + const minSpaceBetweenCircles = (10 / scale) * 2 - ctx.paths.rightHand.attr('d', (d) => { - const dist = distance(f.x, f.y, d.x, d.y) + const makeArrowLine = (from: NodeType, to: NodeType) => { + const dist = distance(from.x, from.y, to.x, to.y) + // 小数点精度の問題か何かでまれに誤って判定されることがあったので余白を設ける - if (dist < f.r + d.r + minSpaceBetweenCircles) { - // 中心から中心を指す - return [ - `M ${this.x(f.x)},${this.y(f.y)}`, - `${this.x(d.x)},${this.y(d.y)}`, - ].join(' ') + const shouldPointToCenter = dist < from.r + to.r + minSpaceBetweenCircles + if (shouldPointToCenter) { + return makeArrowLineToCenter(from, to) } - // 辺から辺を指す - const cut1 = (dist - f.r) / dist - const cut2 = (dist - d.r) / dist - return [ - `M ${this.x((f.x - d.x) * cut1 + d.x)},${this.y( - (f.y - d.y) * cut1 + d.y - )}`, - `${this.x((d.x - f.x) * cut2 + f.x)},${this.y( - (d.y - f.y) * cut2 + f.y - )}`, - ].join(' ') - }) + return makeArrowLineToSide(from, to, dist) + } + + ctx.paths.rightHand.attr('d', (d) => { + return makeArrowLine(f, d) + }) ctx.paths.leftHand.attr('d', (d) => { d.data.pointToCenter = true - const dist = distance(f.x, f.y, d.x, d.y) - if (dist < f.r + d.r + minSpaceBetweenCircles) { - return [ - `M ${this.x(d.x)},${this.y(d.y)}`, - `${this.x(f.x)},${this.y(f.y)}`, - ].join(' ') - } - const cut1 = (dist - d.r) / dist - const cut2 = (dist - f.r) / dist - return [ - `M ${this.x((d.x - f.x) * cut1 + f.x)},${this.y( - (d.y - f.y) * cut1 + f.y - )}`, - `${this.x((f.x - d.x) * cut2 + d.x)},${this.y( - (f.y - d.y) * cut2 + d.y - )}`, - ].join(' ') + return makeArrowLine(d, f) }) - ctx.paths.both.attr('d', (d) => { d.data.pointToCenter = true - const dist = distance(f.x, f.y, d.x, d.y) - if (dist < f.r + d.r + minSpaceBetweenCircles) { - return [ - `M ${this.x(f.x)},${this.y(f.y)}`, - `${this.x(d.x)},${this.y(d.y)}`, - ].join(' ') - } - const cut1 = (dist - f.r) / dist - const cut2 = (dist - d.r) / dist - return [ - `M ${this.x((f.x - d.x) * cut1 + d.x)},${this.y( - (f.y - d.y) * cut1 + d.y - )}`, - `${this.x((d.x - f.x) * cut2 + f.x)},${this.y( - (d.y - f.y) * cut2 + f.y - )}`, - ].join(' ') + return makeArrowLine(f, d) + }) + ctx.paths.same.attr('d', (d) => { + return makeArrowLineToSide(f, d) }) - ctx.paths.self.attr('d', (d) => { + const makeArrowLineToSelf = (node: NodeType) => { + // 4時から11時の方向を指す + const lineR = this.r(node.r * 1.1) + const nodeR = node.r * 1.02 const fourOclock = Math.PI / 6 + const fromX = this.x(node.x + nodeR * Math.cos(fourOclock)) + const fromY = this.y(node.y + nodeR * Math.sin(fourOclock)) const elevenOclock = (-Math.PI * 4) / 6 - const r = d.r * 1.02 - const r2 = this.r(f.r * 1.1) - return [ - `M ${this.x(d.x + r * Math.cos(fourOclock))},${this.y( - d.y + r * Math.sin(fourOclock) - )}`, - `A ${r2},${r2}`, - '0', - '1,0', - `${this.x(d.x + r * Math.cos(elevenOclock))},${this.y( - d.y + r * Math.sin(elevenOclock) - )}`, - ].join(' ') - }) + const toX = this.x(node.x + nodeR * Math.cos(elevenOclock)) + const toY = this.y(node.y + nodeR * Math.sin(elevenOclock)) + return `M ${fromX},${fromY} A ${lineR},${lineR} 0,1,0 ${toX},${toY}` + } - ctx.paths.same.attr('d', (d) => { - const dist = distance(f.x, f.y, d.x, d.y) - const cut1 = (dist - f.r) / dist - const cut2 = (dist - d.r) / dist - return [ - `M ${this.x((f.x - d.x) * cut1 + d.x)},${this.y( - (f.y - d.y) * cut1 + d.y - )}`, - `${this.x((d.x - f.x) * cut2 + f.x)},${this.y( - (d.y - f.y) * cut2 + f.y - )}`, - ].join(' ') + ctx.paths.self.attr('d', (d) => { + return makeArrowLineToSelf(d) }) } From 85b2f0806ab134dfe6f6628159f7d916f344e91d Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Fri, 26 Nov 2021 18:31:03 +0900 Subject: [PATCH 13/74] =?UTF-8?q?feat:=20=E3=82=AF=E3=83=A9=E3=82=B9?= =?UTF-8?q?=E9=96=93=E9=96=A2=E4=BF=82=E3=81=AE=E7=9F=A2=E5=8D=B0=E3=81=AB?= =?UTF-8?q?=E7=AF=80=E3=82=92=E6=8F=8F=E7=94=BB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 31 ++--- .../ts/visualizer/utils/GraphRepository.ts | 109 +++++++++++++++--- .../visualizer/utils/SVGElementsAccessor.ts | 4 +- 3 files changed, 104 insertions(+), 40 deletions(-) diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 2b74fdcf..1dd9ae1c 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -255,25 +255,16 @@ const ClassStructure: React.FC = (props) => { if (!event || !d) return const targetElement = event.currentTarget - - let [x1, y1] = [0, 0] - let [x2, y2] = [0, 0] - if (targetElement?.getAttribute('class')?.includes('self-line')) { - x1 = GraphRepository.x(d.x) - y1 = GraphRepository.y(d.y) - x2 = GraphRepository.x(d.x) - y2 = GraphRepository.y(d.y) - } else { - // eslint-disable-next-line no-extra-semi - ;[[x1, y1], [x2, y2]] = targetElement - ?.getAttribute('d') - ?.split(' ') - .slice(1, 3) - .map((xy) => xy.split(',').map(Number)) || [ - [0, 0], - [0, 0], - ] - } + const isSelfLine = !!targetElement + ?.getAttribute('class') + ?.includes('self-line') + const [x, y] = isSelfLine + ? [GraphRepository.x(d.x), GraphRepository.y(d.y)] + : targetElement + ?.getAttribute('d') + ?.split(' ')[1] + .split(',') + .map((v) => Number(v)) ?? [0, 0] const predicates: string[] = [] if (showRhs && targetClassDetail.rhs) { @@ -304,7 +295,7 @@ const ClassStructure: React.FC = (props) => { const predicateMessage = intl.formatMessage({ id: 'classStructure.text.predicate', }) - GraphRepository.addPopup(x1, y1, x2, y2, predicates, predicateMessage) + GraphRepository.addPopup(x, y, predicates, predicateMessage) GraphRepository.updatePosition() } diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index f2de2296..407e3c3a 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -21,6 +21,8 @@ import { import { getPreferredLabel, isIE11 } from '.' import SVGElementsAccessor from './SVGElementsAccessor' +export type Point = { x: number; y: number } + export type NodeType = d3.HierarchyCircularNode export type SVGGElementType = d3.Selection< SVGGElement, @@ -35,7 +37,6 @@ export type SVGEventHandlerType = ( type HTMLElementType = d3.Selection type ScaleLinearType = d3.ScaleLinear type ZoomType = d3.ZoomBehavior -export type PopupDataType = { x1: number; x2: number; y1: number; y2: number } const distance = (x1: number, y1: number, x2: number, y2: number) => { return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) @@ -45,6 +46,60 @@ const calcMoveCenter = (diameter: number, scale: number) => { return (diameter * scale) / 2 - diameter / 2 } +// 2点と半径から円の中心座標を求める (2点ある) +const calcCenterFromPoints = ( + x1: number, + y1: number, + x2: number, + y2: number, + r: number +) => { + const x3 = (x1 + x2) / 2 + const y3 = (y1 + y2) / 2 + const r2 = r ** 2 + const l1 = (x2 - x3) ** 2 + (y2 - y3) ** 2 + + if (r2 < l1) { + throw Error('Cannot calculate center of circle.') + } + + const d = Math.sqrt(r2 / l1 - 1.0) + const dx = d * (y2 - y3) + const dy = d * (x2 - x3) + + const p1 = [x3 + dx, y3 - dy] + const p2 = [x3 - dx, y3 + dy] + + return [p1, p2].sort((a, b) => b[0] - a[0] || b[1] - a[1]) +} + +// 円の中心座標と円周上の2点から中心角を求める +const calcCenterAngleFromPoints = ( + r: number, + x1: number, + y1: number, + x2: number, + y2: number +) => { + const theta = 2 * Math.asin(distance(x1, y1, x2, y2) / (2 * r)) + return theta +} + +// 基点を中心に座標を反時計回りにθ度回転させる +const rotateCoordinate = ( + x: number, + y: number, + refX: number, + refY: number, + theta: number +) => { + const adjustedX = x - refX + const adjustedY = y - refY + const roratedX = Math.cos(theta) * adjustedX + -Math.sin(theta) * adjustedY + const roratedY = Math.sin(theta) * adjustedX + Math.cos(theta) * adjustedY + return [roratedX + refX, roratedY + refY] +} + const textBeforeEdgePolyfill = ( element: SVGTSpanElement, cond: boolean | undefined @@ -284,7 +339,7 @@ class GraphRepository { } get popups() { - return this.svg?.selectAll('.popup') + return this.svg?.selectAll('.popup') } // accesor modoki @@ -495,15 +550,8 @@ class GraphRepository { .on('click', handleClick) } - addPopup( - x1: number, - y1: number, - x2: number, - y2: number, - predicates: string[], - message: string - ) { - const popup = this.popups?.data([{ x1, y1, x2, y2 }]) + addPopup(x: number, y: number, predicates: string[], message: string) { + const popup = this.popups?.data([{ x, y }]) const div = popup ?.enter() .append('foreignObject') @@ -674,22 +722,32 @@ class GraphRepository { .attr('y', (d) => (d.data.isLabelOnTop ? 0 : -imageSize / 2)) // popup - this.popups - ?.attr('x', ({ x1, x2 }) => (x1 + x2) / 2) - .attr('y', ({ y1, y2 }) => (y1 + y2) / 2) + this.popups?.attr('x', ({ x }) => x).attr('y', ({ y }) => y) const f = this.targetNode if (!f) return // lines // const { paths } = this + const makeArrowNode = (x: number, y: number) => { + const r = 3.5 + const moveToStart = `M${x - r},${y}` + const drawUppperHalf = `A${r},${r} 0,1,0 ${x + r},${y}` + const drawBottomHalf = `A${r},${r} 0,1,0 ${x - r},${y}` + const moveToOrigin = `M${x},${y}` + return `${moveToStart} ${drawUppperHalf} ${drawBottomHalf} ${moveToOrigin}` + } + const makeArrowLineToCenter = (from: NodeType, to: NodeType) => { // 中心から中心を指す const fromX = this.x(from.x) const fromY = this.y(from.y) const toX = this.x(to.x) const toY = this.y(to.y) - return `M ${fromX},${fromY} ${toX},${toY}` + const midX = (fromX + toX) / 2 + const midY = (fromY + toY) / 2 + const drawNode = makeArrowNode(midX, midY) + return `M${fromX},${fromY} ${midX},${midY} ${drawNode} ${toX},${toY}` } const makeArrowLineToSide = ( @@ -706,7 +764,10 @@ class GraphRepository { const cutTo = (dist - to.r) / dist const toX = this.x((to.x - from.x) * cutTo + from.x) const toY = this.y((to.y - from.y) * cutTo + from.y) - return `M ${fromX},${fromY} ${toX},${toY}` + const midX = (fromX + toX) / 2 + const midY = (fromY + toY) / 2 + const drawNode = makeArrowNode(midX, midY) + return `M${fromX},${fromY} ${midX},${midY} ${drawNode} ${toX},${toY}` } const minSpaceBetweenCircles = (10 / scale) * 2 @@ -739,7 +800,6 @@ class GraphRepository { const makeArrowLineToSelf = (node: NodeType) => { // 4時から11時の方向を指す - const lineR = this.r(node.r * 1.1) const nodeR = node.r * 1.02 const fourOclock = Math.PI / 6 const fromX = this.x(node.x + nodeR * Math.cos(fourOclock)) @@ -747,7 +807,20 @@ class GraphRepository { const elevenOclock = (-Math.PI * 4) / 6 const toX = this.x(node.x + nodeR * Math.cos(elevenOclock)) const toY = this.y(node.y + nodeR * Math.sin(elevenOclock)) - return `M ${fromX},${fromY} A ${lineR},${lineR} 0,1,0 ${toX},${toY}` + + // 節を描画するために矢印の中点を求める + const lineR = this.r(node.r * 1.1) + const lineC = calcCenterFromPoints(fromX, fromY, toX, toY, lineR)[0] + const angle = calcCenterAngleFromPoints(lineR, fromX, fromY, toX, toY) + const theta = angle / 2 + Math.PI + const lineMid = rotateCoordinate(fromX, fromY, lineC[0], lineC[1], theta) + + const moveToStart = `M${fromX},${fromY}` + const drawUppperHalf = `A${lineR},${lineR} 0,0,0 ${lineMid[0]},${lineMid[1]}` + const drawNode = makeArrowNode(lineMid[0], lineMid[1]) + const drawBottomHalf = `A${lineR},${lineR} 0,0,0 ${toX},${toY}` + + return `${moveToStart} ${drawUppperHalf} ${drawNode} ${drawBottomHalf}` } ctx.paths.self.attr('d', (d) => { diff --git a/node/src/ts/visualizer/utils/SVGElementsAccessor.ts b/node/src/ts/visualizer/utils/SVGElementsAccessor.ts index 16471aad..c4f18c5c 100644 --- a/node/src/ts/visualizer/utils/SVGElementsAccessor.ts +++ b/node/src/ts/visualizer/utils/SVGElementsAccessor.ts @@ -1,4 +1,4 @@ -import { NodeType, SVGGElementType, PopupDataType } from './GraphRepository' +import { NodeType, SVGGElementType, Point } from './GraphRepository' export default class SVGElementsAccessor { ctx: SVGGElementType @@ -39,6 +39,6 @@ export default class SVGElementsAccessor { } get popups() { - return this.ctx.selectAll('.popup') + return this.ctx.selectAll('.popup') } } From ef4d0426bde241e6e506ca3ac19e5ba898226d3b Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Tue, 30 Nov 2021 15:58:25 +0900 Subject: [PATCH 14/74] =?UTF-8?q?chore:=20Yasgui=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/default.conf | 4 ++++ node/nginx/nginx.conf | 5 +++++ node/src/html/yasgui.html | 16 ++++++++++++++++ node/webpack.config.js | 1 + 4 files changed, 26 insertions(+) create mode 100644 node/src/html/yasgui.html diff --git a/docker/default.conf b/docker/default.conf index 62acd256..f6cc8701 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -15,4 +15,8 @@ server { location / { try_files $uri /index.html; } + + location /yasgui { + try_files $uri /yasgui.html; + } } diff --git a/node/nginx/nginx.conf b/node/nginx/nginx.conf index 4c3d28ae..d0b0ab42 100644 --- a/node/nginx/nginx.conf +++ b/node/nginx/nginx.conf @@ -74,5 +74,10 @@ http { try_files $uri /index.html; add_header Cache-Control no-cache; } + + location /yasgui { + try_files $uri /yasgui.html; + add_header Cache-Control no-cache; + } } } diff --git a/node/src/html/yasgui.html b/node/src/html/yasgui.html new file mode 100644 index 00000000..d677fb2c --- /dev/null +++ b/node/src/html/yasgui.html @@ -0,0 +1,16 @@ + + + + + + Yasgui + + + + +
+ + + diff --git a/node/webpack.config.js b/node/webpack.config.js index a3fe7a12..24a45b5b 100644 --- a/node/webpack.config.js +++ b/node/webpack.config.js @@ -124,6 +124,7 @@ module.exports = (env, args) => { new CopyPlugin([ { from: 'src/images', to: 'static/images' }, { from: 'src/blacklists', to: 'static/blacklists' }, + { from: 'src/html/yasgui.html', to: 'yasgui.html'} ]), new MiniCssExtractPlugin({ filename: 'static/css/[name].css', From 2370eee7e1f92c3fde92359fa79bf55321c10d0c Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Tue, 30 Nov 2021 17:10:06 +0900 Subject: [PATCH 15/74] =?UTF-8?q?refactor:=20Visualizer=E3=81=AEindex.tsx?= =?UTF-8?q?=E5=91=A8=E3=82=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/actions/visualize.ts | 2 +- node/src/ts/components/Visualize.tsx | 3 +- node/src/ts/visualizer/index.tsx | 116 +++++++++----------------- node/src/ts/visualizer/types/index.ts | 31 +++++++ node/src/ts/visualizer/utils/node.ts | 7 ++ 5 files changed, 82 insertions(+), 77 deletions(-) create mode 100644 node/src/ts/visualizer/types/index.ts diff --git a/node/src/ts/actions/visualize.ts b/node/src/ts/actions/visualize.ts index 470d189a..117749fa 100644 --- a/node/src/ts/actions/visualize.ts +++ b/node/src/ts/actions/visualize.ts @@ -1,5 +1,5 @@ import { Action } from 'redux' -import { Content } from '../visualizer' +import { Content } from '../visualizer/types' export enum VisualizeActionNames { GET_DATA_SET = 'visualize/get-data-set', diff --git a/node/src/ts/components/Visualize.tsx b/node/src/ts/components/Visualize.tsx index 68f006a4..f5e4293f 100644 --- a/node/src/ts/components/Visualize.tsx +++ b/node/src/ts/components/Visualize.tsx @@ -1,7 +1,8 @@ import * as React from 'react' import { GetDataSetAction, VisualizedDataSet } from '../actions/visualize' -import Visualizer, { Content } from '../visualizer' +import Visualizer from '../visualizer' +import { Content } from '../visualizer/types' interface Props { match: { diff --git a/node/src/ts/visualizer/index.tsx b/node/src/ts/visualizer/index.tsx index 0ef92c63..7fec5abe 100644 --- a/node/src/ts/visualizer/index.tsx +++ b/node/src/ts/visualizer/index.tsx @@ -18,9 +18,6 @@ import { import _ from 'lodash' import { useHistory } from 'react-router-dom' import { getLocaleMessages, getLocaleShortString, useQuery } from './utils' -import { Property } from './types/property' -import { Prefixes } from './types/prefix' -import { Classes } from './types/class' import { Structure } from './types/structure' import Prefix from './components/Prefix' import Tooltip from './components/Tooltip' @@ -35,24 +32,8 @@ import ApiClient from '../ApiClient' import { useDBCLSFooter } from '../useDBCLSFooter' import { RootState } from './reducers' import { FilterAction } from './actions/filter' - -declare global { - interface Document { - documentMode?: number - } - - interface Navigator { - userLanguage?: string - browserLanguage?: string - } -} - -export type AppState = { - structure: Structure[] - classes: Classes - properties: Property[] - prefixes: Prefixes -} +import { flattenStructure } from './utils/node' +import { AppState, Content } from './types' const initialAppState: AppState = { structure: [], @@ -61,7 +42,7 @@ const initialAppState: AppState = { prefixes: {}, } -const filterContent = ( +const filterStateDestructive = ( state: AppState, condition: (uri: string) => boolean ) => { @@ -103,26 +84,18 @@ const filterContent = ( }) } -const isEmptyContent = (content: AppState) => { +const isEmptyState = (state: AppState) => { if ( - content.structure.length === 0 && - content.properties.length === 0 && - Object.keys(content.classes).length === 0 && - Object.keys(content.prefixes).length === 0 + state.structure.length === 0 && + state.properties.length === 0 && + Object.keys(state.classes).length === 0 && + Object.keys(state.prefixes).length === 0 ) { return true } return false } -export type Content = { - inheritance_structure: Structure[] - classes: Classes - properties: Property[] - prefixes: Prefixes - meta_data: any -} - type AppProps = { content: Content } @@ -140,7 +113,6 @@ const App: React.FC = (props) => { const [rawState, setRawState] = useState(initialAppState) const [state, setState] = useState(initialAppState) - // utility const getReferenceURL = useCallback( (uri: string | null) => { if (uri === null) { @@ -155,15 +127,16 @@ const App: React.FC = (props) => { }, [state.prefixes] ) + useEffect(() => { - const preferredContent = { + const nextState = { structure: content.inheritance_structure, classes: content.classes, properties: content.properties, prefixes: content.prefixes, } - if (isEmptyContent(preferredContent) || !isEmptyContent(rawState)) { + if (isEmptyState(nextState) || !isEmptyState(rawState)) { return } @@ -174,24 +147,23 @@ const App: React.FC = (props) => { ApiClient.checkHealthy().then((res) => { if (res.data.ok) { - // set blacklist Blacklist.configre({ classes: '/static/blacklists/bcl.txt', prefixes: '/static/blacklists/bpr.txt', }) - // filter content const existsInBlacklist = (uri: string) => - Blacklist.has(uri, preferredContent.prefixes) - filterContent(preferredContent, existsInBlacklist) - setRawState(preferredContent) + Blacklist.has(uri, content.prefixes) + filterStateDestructive(nextState, existsInBlacklist) + + setRawState(nextState) } }) }, [content, rawState]) // eslint-disable-line react-hooks/exhaustive-deps const { lowerLimitOfClassEntities } = useSelector(selector) useEffect(() => { - if (isEmptyContent(rawState)) { + if (isEmptyState(rawState)) { return } @@ -208,46 +180,34 @@ const App: React.FC = (props) => { search: `?lower_limit=${lowerLimitOfClassEntities}`, }) - const flattenChildren = (elem: Structure): Structure[] => { - if (elem.children === undefined) { - return [elem] - } - return _.flatMap(elem.children, flattenChildren).concat([elem]) - } - const urisToHide = rawState.structure - .flatMap((elem) => flattenChildren(elem)) - .filter((elem) => { - const classDetail = rawState.classes[elem.uri] - return ( - elem.children === undefined && - (classDetail === undefined || - classDetail.entities === undefined || - classDetail.entities < lowerLimitOfClassEntities) - ) + .flatMap((e) => flattenStructure(e)) + .filter((e) => { + const detail = rawState.classes[e.uri] + const hasNoChildren = e.children === undefined + const entityUndefined = !detail || detail.entities === undefined + const lessThanLimit = (detail.entities ?? 0) < lowerLimitOfClassEntities + return hasNoChildren && (entityUndefined || lessThanLimit) }) - .reduce<{ [key: string]: true }>((prev, cur) => { - // eslint-disable-next-line no-param-reassign - prev[cur.uri] = true - return prev - }, {}) + .reduce((prev, cur) => prev.add(cur.uri), new Set()) - const shouldHideElement = (uri: string) => urisToHide[uri] + const shouldHideElement = (uri: string) => urisToHide.has(uri) const nextState = _.cloneDeep(rawState) - filterContent(nextState, shouldHideElement) + filterStateDestructive(nextState, shouldHideElement) setState(nextState) }, [rawState, lowerLimitOfClassEntities]) + const { structure, classes, properties, prefixes } = state return (
- - - + + +
- - + +
- +
) } @@ -270,8 +230,14 @@ const Visualizer: React.FC = (props) => { }, []) const footerElement = useMemo(() => { - // eslint-disable-next-line react/no-danger - return
+ return ( +
+ ) }, [copyElement]) return ( diff --git a/node/src/ts/visualizer/types/index.ts b/node/src/ts/visualizer/types/index.ts new file mode 100644 index 00000000..471ca161 --- /dev/null +++ b/node/src/ts/visualizer/types/index.ts @@ -0,0 +1,31 @@ +/* eslint-disable camelcase */ +import { Classes } from './class' +import { Prefixes } from './prefix' +import { Property } from './property' +import { Structure } from './structure' + +declare global { + interface Document { + documentMode?: number + } + + interface Navigator { + userLanguage?: string + browserLanguage?: string + } +} + +export type AppState = { + structure: Structure[] + classes: Classes + properties: Property[] + prefixes: Prefixes +} + +export type Content = { + inheritance_structure: Structure[] + classes: Classes + properties: Property[] + prefixes: Prefixes + meta_data: any +} diff --git a/node/src/ts/visualizer/utils/node.ts b/node/src/ts/visualizer/utils/node.ts index e8826698..6329af98 100644 --- a/node/src/ts/visualizer/utils/node.ts +++ b/node/src/ts/visualizer/utils/node.ts @@ -2,6 +2,13 @@ import _ from 'lodash' import { Structure, NodeStructure } from '../types/structure' import { NodeType } from './GraphRepository' +export const flattenStructure = (elem: Structure): Structure[] => { + if (elem.children === undefined) { + return [elem] + } + return _.flatMap(elem.children, flattenStructure).concat([elem]) +} + const falsyStructure: NodeStructure = { key: 0, originalR: 0, From 4016c6a3882f520e116c80c6c8c88422fe548b2a Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Tue, 30 Nov 2021 17:24:48 +0900 Subject: [PATCH 16/74] =?UTF-8?q?chore:=20Visualizer=E3=81=A7metadata?= =?UTF-8?q?=E3=82=92=E4=BF=9D=E6=8C=81=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/components/Visualize.tsx | 8 +++++++- node/src/ts/visualizer/index.tsx | 3 +++ node/src/ts/visualizer/types/index.ts | 3 ++- node/src/ts/visualizer/types/metadata.ts | 9 +++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 node/src/ts/visualizer/types/metadata.ts diff --git a/node/src/ts/components/Visualize.tsx b/node/src/ts/components/Visualize.tsx index f5e4293f..6f3b33f1 100644 --- a/node/src/ts/components/Visualize.tsx +++ b/node/src/ts/components/Visualize.tsx @@ -21,7 +21,13 @@ const Visualize = (props: Props) => { classes: {}, properties: [], prefixes: {}, - meta_data: {}, + meta_data: { + properties: 0, + triples: 0, + classes: 0, + endpoint: '', + crawl_date: '', + }, }) React.useEffect(() => { diff --git a/node/src/ts/visualizer/index.tsx b/node/src/ts/visualizer/index.tsx index 7fec5abe..66d52fa3 100644 --- a/node/src/ts/visualizer/index.tsx +++ b/node/src/ts/visualizer/index.tsx @@ -19,6 +19,7 @@ import _ from 'lodash' import { useHistory } from 'react-router-dom' import { getLocaleMessages, getLocaleShortString, useQuery } from './utils' import { Structure } from './types/structure' +import { Metadata } from './types/metadata' import Prefix from './components/Prefix' import Tooltip from './components/Tooltip' import SearchBox from './components/SearchBox' @@ -112,6 +113,7 @@ const App: React.FC = (props) => { const [rawState, setRawState] = useState(initialAppState) const [state, setState] = useState(initialAppState) + const [metadata, setMetadata] = useState(null) const getReferenceURL = useCallback( (uri: string | null) => { @@ -157,6 +159,7 @@ const App: React.FC = (props) => { filterStateDestructive(nextState, existsInBlacklist) setRawState(nextState) + setMetadata(content.meta_data) } }) }, [content, rawState]) // eslint-disable-line react-hooks/exhaustive-deps diff --git a/node/src/ts/visualizer/types/index.ts b/node/src/ts/visualizer/types/index.ts index 471ca161..4959827a 100644 --- a/node/src/ts/visualizer/types/index.ts +++ b/node/src/ts/visualizer/types/index.ts @@ -1,5 +1,6 @@ /* eslint-disable camelcase */ import { Classes } from './class' +import { Metadata } from './metadata' import { Prefixes } from './prefix' import { Property } from './property' import { Structure } from './structure' @@ -27,5 +28,5 @@ export type Content = { classes: Classes properties: Property[] prefixes: Prefixes - meta_data: any + meta_data: Metadata } diff --git a/node/src/ts/visualizer/types/metadata.ts b/node/src/ts/visualizer/types/metadata.ts new file mode 100644 index 00000000..1bf94508 --- /dev/null +++ b/node/src/ts/visualizer/types/metadata.ts @@ -0,0 +1,9 @@ +/* eslint-disable camelcase */ + +export type Metadata = { + properties: number + triples: number + classes: number + endpoint: string + crawl_date: string +} From 61871125d2740aa8621b75a19da5a4c22149eeaa Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Tue, 30 Nov 2021 19:45:25 +0900 Subject: [PATCH 17/74] =?UTF-8?q?feat:=20=E3=83=80=E3=83=96=E3=83=AB?= =?UTF-8?q?=E3=82=AF=E3=83=AA=E3=83=83=E3=82=AF=E3=81=97=E3=81=9F=E3=82=AF?= =?UTF-8?q?=E3=83=A9=E3=82=B9=E3=81=AB=E5=B1=9E=E3=81=99=E3=82=8B=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=82=B9=E3=82=BF=E3=83=B3=E3=82=B9=E3=82=92=E5=88=97?= =?UTF-8?q?=E6=8C=99=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 58 ++++++++++++++++--- node/src/ts/visualizer/components/Graph.tsx | 7 ++- .../ts/visualizer/hooks/useDblClickHandler.ts | 35 +++++++++++ node/src/ts/visualizer/index.tsx | 7 ++- .../ts/visualizer/utils/GraphRepository.ts | 17 +++--- 5 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 node/src/ts/visualizer/hooks/useDblClickHandler.ts diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 1dd9ae1c..d001bcbd 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -15,6 +15,8 @@ import { getChildrenRecursive } from '../utils/node' import { ClassNames } from '../constants/ClassStructure' import { TooltipAction } from '../actions/tooltip' +import { Metadata } from '../types/metadata' +import useDblClickHandler from '../hooks/useDblClickHandler' function decideNormalClass( d: NodeType, @@ -115,12 +117,22 @@ type ClassStructureProps = { nodes: NodeType[] width: number | null height: number | null + metadata: Metadata | null + getReferenceURL: (uri: string | null) => string | null } const selector = ({ detail }: RootState) => detail const ClassStructure: React.FC = (props) => { - const { classes, circleDiameter, nodes, width, height } = props + const { + classes, + metadata, + circleDiameter, + nodes, + width, + height, + getReferenceURL, + } = props const dispatch = useDispatch() const intl = useIntl() @@ -128,7 +140,7 @@ const ClassStructure: React.FC = (props) => { dispatch(DetailAction.showTree()) }, [dispatch]) - const handleClickClass: SVGEventHandlerType = React.useCallback( + const handleSingleClickClass = React.useCallback( (event?: React.MouseEvent, d?: NodeType) => { if (!d || !event || event.defaultPrevented) { return @@ -138,6 +150,36 @@ const ClassStructure: React.FC = (props) => { }, [dispatch] ) + const handleDoubleClickClass = React.useCallback( + (event?: React.MouseEvent, d?: NodeType) => { + const refUri = d ? getReferenceURL(d.data.uri) : '' + if (!refUri || !event) { + return + } + + const query = ` + SELECT ?i + WHERE { + ?i a <${refUri}> . + } + LIMIT 20 + `.replace(/^\n|\s+$|^ {8}/gm, '') + + const params = new URLSearchParams() + params.append('endpoint', metadata?.endpoint ?? '') + params.append('query', query) + window.open( + `/yasgui?${params.toString()}`, + '_brank', + 'noopener,noreferrer' + ) + }, + [metadata?.endpoint] + ) + const [handleMouseDownClass] = useDblClickHandler( + handleDoubleClickClass, + handleSingleClickClass + ) const handleShowTooltip: SVGEventHandlerType = React.useCallback( (event?: React.MouseEvent, d?: NodeType) => { @@ -311,7 +353,7 @@ const ClassStructure: React.FC = (props) => { GraphRepository.avoidColidedLabel() - GraphRepository.showNodes(visibleNodes, handleClickClass, intl.locale) + GraphRepository.showNodes(visibleNodes, handleMouseDownClass, intl.locale) const decideClass = (d: NodeType) => { if (_.includes(both, d.data.uri)) { @@ -342,7 +384,7 @@ const ClassStructure: React.FC = (props) => { return [target].concat(rhsNodes, lhsNodes, bothNodes) }, - [classes, handleClickClass, handleClickTreeImg, intl] + [classes, handleMouseDownClass, handleClickTreeImg, intl] ) const showCircles = React.useCallback( @@ -393,7 +435,7 @@ const ClassStructure: React.FC = (props) => { const visibleNodesSet = getNodeSet(visibleNodes) GraphRepository.visibleNodesSet = visibleNodesSet - GraphRepository.showNodes(visibleNodes, handleClickClass, intl.locale) + GraphRepository.showNodes(visibleNodes, handleMouseDownClass, intl.locale) GraphRepository.avoidColidedLabel() let decideClass @@ -433,7 +475,7 @@ const ClassStructure: React.FC = (props) => { GraphRepository.addClass(visibleNodes, decideClass) return _.union(domainNodes, rangeNodes, focusRootNodes) }, - [handleClickClass, intl.locale] + [handleMouseDownClass, intl.locale] ) const search = React.useCallback( @@ -449,7 +491,7 @@ const ClassStructure: React.FC = (props) => { const visibleNodesSet = getNodeSet(visibleNodes) GraphRepository.visibleNodesSet = visibleNodesSet - GraphRepository.showNodes(visibleNodes, handleClickClass, intl.locale) + GraphRepository.showNodes(visibleNodes, handleMouseDownClass, intl.locale) GraphRepository.avoidColidedLabel() const decideClass = (d: NodeType) => { @@ -462,7 +504,7 @@ const ClassStructure: React.FC = (props) => { GraphRepository.addClass(visibleNodes, decideClass) return matchedNodes }, - [handleClickClass, intl.locale] + [handleMouseDownClass, intl.locale] ) const detail = useSelector(selector) diff --git a/node/src/ts/visualizer/components/Graph.tsx b/node/src/ts/visualizer/components/Graph.tsx index 932cf7b2..3d253480 100644 --- a/node/src/ts/visualizer/components/Graph.tsx +++ b/node/src/ts/visualizer/components/Graph.tsx @@ -15,10 +15,13 @@ import ClassStructure from './ClassStructure' import { Tree } from './Tree' import LoadingSpinner from './LoadingSpinner' import Filter from './Filter' +import { Metadata } from '../types/metadata' type GraphProps = { classes: Classes structure: Structure[] + metadata: Metadata | null + getReferenceURL: (uri: string | null) => string | null } const selector = ({ @@ -92,7 +95,7 @@ const avoidStackedCircle = ( } const Graph: React.FC = (props) => { - const { classes, structure } = props + const { classes, structure, metadata, getReferenceURL } = props const { circleDiameter, svgWidth, svgHeight, showTree } = useSelector( selector ) @@ -144,9 +147,11 @@ const Graph: React.FC = (props) => { )} diff --git a/node/src/ts/visualizer/hooks/useDblClickHandler.ts b/node/src/ts/visualizer/hooks/useDblClickHandler.ts new file mode 100644 index 00000000..e46b0bb7 --- /dev/null +++ b/node/src/ts/visualizer/hooks/useDblClickHandler.ts @@ -0,0 +1,35 @@ +import React, { useCallback, useRef } from 'react' +import { NodeType, SVGEventHandlerType } from '../utils/GraphRepository' + +type TimeoutId = ReturnType + +const useDblClickHandler = ( + doubleClickHandler: SVGEventHandlerType, + singleClickHandler?: SVGEventHandlerType, + duration: number = 150 +) => { + const timeoutRef = useRef(undefined) + const handleMouseDown: SVGEventHandlerType = useCallback( + (e?: React.MouseEvent, d?: NodeType) => { + const clicked = timeoutRef.current !== undefined + if (clicked) { + e?.preventDefault() + clearTimeout(timeoutRef.current!) + + doubleClickHandler(e, d) + timeoutRef.current = undefined + } + + const clickEventId = setTimeout(() => { + singleClickHandler?.(e, d) + timeoutRef.current = undefined + }, duration) + + timeoutRef.current = clickEventId + }, + [doubleClickHandler, singleClickHandler] + ) + return [handleMouseDown] +} + +export default useDblClickHandler diff --git a/node/src/ts/visualizer/index.tsx b/node/src/ts/visualizer/index.tsx index 66d52fa3..593a9400 100644 --- a/node/src/ts/visualizer/index.tsx +++ b/node/src/ts/visualizer/index.tsx @@ -204,7 +204,12 @@ const App: React.FC = (props) => { return (
- +
diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index 407e3c3a..2515a5a4 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -876,24 +876,27 @@ class GraphRepository { showNodes( nodes: NodeType[], - handleClickClass: SVGEventHandlerType, + handleMouseDownClass: SVGEventHandlerType, locale: string ) { - this.showCircleNodes(nodes, handleClickClass) + this.showCircleNodes(nodes, handleMouseDownClass) this.updateScale() - this.showTextNodes(nodes, handleClickClass, locale) + this.showTextNodes(nodes, handleMouseDownClass, locale) this.updatePosition() } - showCircleNodes(nodes: NodeType[], handleClickClass: SVGEventHandlerType) { + showCircleNodes( + nodes: NodeType[], + handleMouseDownClass: SVGEventHandlerType + ) { const circles = this.circles?.data(nodes, nodeKeyFn) - circles?.enter().append('svg:circle').on('click', handleClickClass) + circles?.enter().append('svg:circle').on('mousedown', handleMouseDownClass) circles?.exit().remove() } showTextNodes( nodes: NodeType[], - handleClickClass: SVGEventHandlerType, + handleMouseDownClass: SVGEventHandlerType, locale: string ) { const { classes } = this @@ -909,7 +912,7 @@ class GraphRepository { .attr('transform', (d) => `translate(${this.x(d.x)}, ${this.textY(d)})`) const texts = textAndButton ?.append('svg:text') - .on('click', handleClickClass) + .on('mousedown', handleMouseDownClass) .attr('y', (d) => (d.data.isLabelOnTop && isIE11 ? '1em' : 0)) // IE11はdominant-baselineをサポートしない From 0c80d1f35509416bd03fee36124e59452e30b519 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 1 Dec 2021 14:06:39 +0900 Subject: [PATCH 18/74] =?UTF-8?q?refactor:=20=E7=9F=A2=E5=8D=B0=E3=81=AE?= =?UTF-8?q?=E3=83=9E=E3=82=A6=E3=82=B9=E3=82=AA=E3=83=BC=E3=83=90=E3=83=BC?= =?UTF-8?q?=E3=81=AE=E5=87=A6=E7=90=86=E3=81=82=E3=81=9F=E3=82=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index d001bcbd..b1995c37 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -290,6 +290,31 @@ const ClassStructure: React.FC = (props) => { GraphRepository.addTreeImg(targetKey, handleClickTreeImg) } + const getPredicates = (d: NodeType) => { + const detail = targetClassDetail + const rhsProps = + showRhs && detail.rhs + ? detail.rhs + .filter((r) => + relation + ? relation[0] === r[0] && relation[1] === r[1] + : r[1] === d.data.uri + ) + .map((r) => r[0]) + : [] + const lhsProps = + showLhs && detail.lhs + ? detail.lhs + .filter((r) => + relation + ? relation[0] === r[0] && relation[1] === r[1] + : r[0] === d.data.uri + ) + .map((r) => r[1]) + : [] + return [...rhsProps, ...lhsProps] + } + function arrowMouseover( event?: React.MouseEvent, d?: NodeType @@ -308,31 +333,7 @@ const ClassStructure: React.FC = (props) => { .split(',') .map((v) => Number(v)) ?? [0, 0] - const predicates: string[] = [] - if (showRhs && targetClassDetail.rhs) { - Array.prototype.push.apply( - predicates, - targetClassDetail.rhs - .filter((r) => - relation - ? relation[0] === r[0] && relation[1] === r[1] - : r[1] === d.data.uri - ) - .map((r) => r[0]) - ) - } - if (showLhs && targetClassDetail.lhs) { - Array.prototype.push.apply( - predicates, - targetClassDetail.lhs - .filter((r) => - relation - ? relation[0] === r[0] && relation[1] === r[1] - : r[0] === d.data.uri - ) - .map((r) => r[1]) - ) - } + const predicates = getPredicates(d) const predicateMessage = intl.formatMessage({ id: 'classStructure.text.predicate', From 2392fd18def4d75e9802798441718fb0cf9da9e6 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 1 Dec 2021 16:49:15 +0900 Subject: [PATCH 19/74] =?UTF-8?q?feat:=20=E3=83=80=E3=83=96=E3=83=AB?= =?UTF-8?q?=E3=82=AF=E3=83=AA=E3=83=83=E3=82=AF=E3=81=97=E3=81=9F=E7=9F=A2?= =?UTF-8?q?=E5=8D=B0=E3=81=AE=E3=82=AF=E3=83=A9=E3=82=B9=E9=96=93=E3=81=AB?= =?UTF-8?q?=E5=B1=9E=E3=81=99=E3=82=8B=E3=82=A4=E3=83=B3=E3=82=B9=E3=82=BF?= =?UTF-8?q?=E3=83=B3=E3=82=B9=E3=81=AE=E9=96=A2=E4=BF=82=E3=82=92=E5=88=97?= =?UTF-8?q?=E6=8C=99=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 59 ++++++++++++++++++- .../ts/visualizer/utils/GraphRepository.ts | 8 ++- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index b1995c37..b4e3217f 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -347,10 +347,65 @@ const ClassStructure: React.FC = (props) => { GraphRepository.removePopup() } - GraphRepository.addArrowLineEvent(arrowMouseover, arrowMouseout) + const arrowDblClick = ( + event?: React.MouseEvent, + d?: NodeType + ) => { + if (!event || !d) return + + const focusingUri = getReferenceURL(target.data.uri)! + const targetUri = getReferenceURL(d.data.uri)! + const predicateUris = getPredicates(d).map((p) => getReferenceURL(p)!) + + const targetElement = event.currentTarget + const pathClass = targetElement?.getAttribute('class') + const isLeftLine = pathClass?.includes('left-line') + const isSelfLine = pathClass?.includes('self-line') + + const createTripe = (): [string, string[], string] => { + if (isLeftLine) { + return [targetUri, predicateUris, focusingUri] + } + if (isSelfLine) { + return [targetUri, predicateUris, targetUri] + } + return [focusingUri, predicateUris, targetUri] + } + + const [sbj, prds, obj] = createTripe() + const query = ` + SELECT ?sbj ?obj + WHERE { + ?sbj ${prds.map((p) => `<${p}>`).join('|')} ?obj . + ?sbj a <${sbj}> . + ?obj a <${obj}> . + } + LIMIT 20 + `.replace(/^\n|\s+$|^ {10}/gm, '') + + const params = new URLSearchParams() + params.append('endpoint', metadata?.endpoint ?? '') + params.append('query', query) + window.open( + `/yasgui?${params.toString()}`, + '_brank', + 'noopener,noreferrer' + ) + } + + GraphRepository.addArrowLineEvent( + arrowMouseover, + arrowMouseout, + arrowDblClick + ) const selfPath: NodeType[] = isOneself ? [target] : [] - GraphRepository.updateSelfLines(selfPath, arrowMouseover, arrowMouseout) + GraphRepository.updateSelfLines( + selfPath, + arrowMouseover, + arrowMouseout, + arrowDblClick + ) GraphRepository.avoidColidedLabel() diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index 2515a5a4..aee661cf 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -522,7 +522,8 @@ class GraphRepository { updateSelfLines( nodes: NodeType[], handleMouseOver: SVGEventHandlerType = () => {}, - handleMouseOut: SVGEventHandlerType = () => {} + handleMouseOut: SVGEventHandlerType = () => {}, + handleDblClick: SVGEventHandlerType = () => {} ) { const selfLines = this.paths.self?.data(nodes, nodeKeyFn) selfLines @@ -532,6 +533,7 @@ class GraphRepository { .attr('marker-end', 'url(#arrow-head)') .on('mouseover', handleMouseOver) .on('mouseout', handleMouseOut) + .on('dblclick', handleDblClick) selfLines?.exit().remove() } @@ -830,12 +832,14 @@ class GraphRepository { addArrowLineEvent( handleMouseOver: SVGEventHandlerType, - handleMouseOut: SVGEventHandlerType + handleMouseOut: SVGEventHandlerType, + handleDblClick: SVGEventHandlerType ) { this.svg ?.selectAll('.arrow-line-base') .on('mouseover', handleMouseOver) .on('mouseout', handleMouseOut) + .on('dblclick', handleDblClick) } avoidColidedLabel() { From 88c21e9fd0a04bbc186111fe552a16d79e1f4704 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 1 Dec 2021 17:27:06 +0900 Subject: [PATCH 20/74] =?UTF-8?q?refactor:=20GraphRepository=E3=81=ABlocal?= =?UTF-8?q?e=E3=82=92=E6=8C=81=E3=81=9F=E3=81=9B=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 13 +++++++------ .../ts/visualizer/utils/GraphRepository.ts | 19 +++++++------------ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index b4e3217f..1d352ba4 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -409,7 +409,7 @@ const ClassStructure: React.FC = (props) => { GraphRepository.avoidColidedLabel() - GraphRepository.showNodes(visibleNodes, handleMouseDownClass, intl.locale) + GraphRepository.showNodes(visibleNodes, handleMouseDownClass) const decideClass = (d: NodeType) => { if (_.includes(both, d.data.uri)) { @@ -491,7 +491,7 @@ const ClassStructure: React.FC = (props) => { const visibleNodesSet = getNodeSet(visibleNodes) GraphRepository.visibleNodesSet = visibleNodesSet - GraphRepository.showNodes(visibleNodes, handleMouseDownClass, intl.locale) + GraphRepository.showNodes(visibleNodes, handleMouseDownClass) GraphRepository.avoidColidedLabel() let decideClass @@ -531,7 +531,7 @@ const ClassStructure: React.FC = (props) => { GraphRepository.addClass(visibleNodes, decideClass) return _.union(domainNodes, rangeNodes, focusRootNodes) }, - [handleMouseDownClass, intl.locale] + [handleMouseDownClass] ) const search = React.useCallback( @@ -547,7 +547,7 @@ const ClassStructure: React.FC = (props) => { const visibleNodesSet = getNodeSet(visibleNodes) GraphRepository.visibleNodesSet = visibleNodesSet - GraphRepository.showNodes(visibleNodes, handleMouseDownClass, intl.locale) + GraphRepository.showNodes(visibleNodes, handleMouseDownClass) GraphRepository.avoidColidedLabel() const decideClass = (d: NodeType) => { @@ -560,7 +560,7 @@ const ClassStructure: React.FC = (props) => { GraphRepository.addClass(visibleNodes, decideClass) return matchedNodes }, - [handleMouseDownClass, intl.locale] + [handleMouseDownClass] ) const detail = useSelector(selector) @@ -668,6 +668,7 @@ const ClassStructure: React.FC = (props) => { ) React.useEffect(() => { + GraphRepository.locale = intl.locale GraphRepository.classes = classes GraphRepository.updateNode(nodes) GraphRepository.removeCircles() @@ -680,7 +681,7 @@ const ClassStructure: React.FC = (props) => { if (isIE11) { setInterval(GraphRepository.forceRedrawLines, 10) } - }, [isIE11, classes, nodes]) + }, [isIE11, classes, nodes, intl.locale]) const oldPropsRef = useRef({ oldWidth: width, oldHeight: height }) const mounted = React.useRef(false) diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index aee661cf..32909802 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -155,6 +155,8 @@ class GraphRepository { transparentLabel: boolean + locale: string + pos: { top: number bottom: number @@ -205,6 +207,7 @@ class GraphRepository { this.timer = undefined this.ignoreEvent = false this.transparentLabel = false + this.locale = 'en' } // public accessor @@ -878,14 +881,10 @@ class GraphRepository { }) } - showNodes( - nodes: NodeType[], - handleMouseDownClass: SVGEventHandlerType, - locale: string - ) { + showNodes(nodes: NodeType[], handleMouseDownClass: SVGEventHandlerType) { this.showCircleNodes(nodes, handleMouseDownClass) this.updateScale() - this.showTextNodes(nodes, handleMouseDownClass, locale) + this.showTextNodes(nodes, handleMouseDownClass) this.updatePosition() } @@ -898,12 +897,8 @@ class GraphRepository { circles?.exit().remove() } - showTextNodes( - nodes: NodeType[], - handleMouseDownClass: SVGEventHandlerType, - locale: string - ) { - const { classes } = this + showTextNodes(nodes: NodeType[], handleMouseDownClass: SVGEventHandlerType) { + const { classes, locale } = this const gtexts = this.gtexts?.data( _.sortBy(nodes, ({ depth }) => depth * -1), From 8167bfb89ec44e823bea9a590faaa43679c53cd3 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 1 Dec 2021 17:36:04 +0900 Subject: [PATCH 21/74] =?UTF-8?q?refactor:=20GraphRepository=E3=81=AE?= =?UTF-8?q?=E3=83=97=E3=83=AD=E3=83=91=E3=83=86=E3=82=A3=E3=82=92=E6=95=B4?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ts/visualizer/utils/GraphRepository.ts | 54 ++++--------------- 1 file changed, 11 insertions(+), 43 deletions(-) diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index 32909802..b164230b 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -34,7 +34,6 @@ export type SVGEventHandlerType = ( e?: React.MouseEvent, d?: NodeType ) => void -type HTMLElementType = d3.Selection type ScaleLinearType = d3.ScaleLinear type ZoomType = d3.ZoomBehavior @@ -115,20 +114,10 @@ const textBeforeEdgePolyfill = ( } class GraphRepository { - // private instance field private _nodes: NodeType[] private _svg: SVGGElementType | null - private elements: { - shadow: HTMLElementType | null - searching: HTMLElementType | null - both: HTMLElementType | null - arrowHead: HTMLElementType | null - } - - // public instance field - classes: Classes manuallyZoomed: boolean @@ -179,12 +168,6 @@ class GraphRepository { constructor() { this._nodes = [] this._svg = null - this.elements = { - shadow: null, - searching: null, - both: null, - arrowHead: null, - } this.classes = {} @@ -237,57 +220,46 @@ class GraphRepository { } setShadow() { - this.elements.shadow = d3.select('#shadow') + const shadow = this.svg?.select('#shadow') - this.shadow + shadow ?.append('feGaussianBlur') .attr('in', 'SourceAlpha') .attr('result', 'blur') .attr('stdDeviation', 5) - this.shadow - ?.append('feBlend') - .attr('in', 'SourceGraphic') - .attr('mode', 'normal') - } - - get shadow() { - return this.elements.shadow + shadow?.append('feBlend').attr('in', 'SourceGraphic').attr('mode', 'normal') } setSearching() { - this.elements.searching = d3.select('#searching') + const searching = this.svg?.select('#searching') - this.searching + searching ?.append('feGaussianBlur') .attr('stdDeviation', 9.5) .attr('in', 'SourceAlpha') - this.searching + searching ?.append('feOffset') .attr('dx', 0.5) .attr('dy', 0.5) .attr('result', 'offsetblur') - this.searching + searching ?.append('feFlood') .attr('flood-color', '#FF4F20') .attr('flood-opacity', 0.76) - this.searching + searching ?.append('feComposite') .attr('in2', 'offsetblur') .attr('operator', 'in') - const merge = this.searching?.append('feMerge') + const merge = searching?.append('feMerge') merge?.append('feMergeNode') merge?.append('feMergeNode').attr('in', 'SourceGraphic') } - get searching() { - return this.elements.searching - } - setArrowHead() { - this.elements.arrowHead = d3.select('#arrow-head') + const arrowHead = this.svg?.select('#arrow-head') - this.arrowHead + arrowHead ?.attr('orient', 'auto-start-reverse') .attr('markerUnits', 'strokeWidth') .attr('markerWidth', '10') @@ -297,10 +269,6 @@ class GraphRepository { .attr('viewBox', '0 0 10 10') } - get arrowHead() { - return this.elements.arrowHead - } - // custom accessor get topLevelNodes() { return this._nodes.filter((d) => d.depth <= 2) From d00961c70f2d3f7d48e31fe55fc743527a179194 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 1 Dec 2021 14:49:50 +0900 Subject: [PATCH 22/74] =?UTF-8?q?fix:=20prefix=E3=81=AE=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=BC=E3=83=9E=E3=83=83=E3=83=88=E3=81=8Crdf=E5=9F=BA?= =?UTF-8?q?=E6=BA=96=E3=81=A7=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/components/Prefix.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/ts/visualizer/components/Prefix.tsx b/node/src/ts/visualizer/components/Prefix.tsx index d42f39a2..a067fc71 100644 --- a/node/src/ts/visualizer/components/Prefix.tsx +++ b/node/src/ts/visualizer/components/Prefix.tsx @@ -32,7 +32,7 @@ const Prefix: React.FC = (props) => { .sort() .map((key, idx) => [
- @prefix : {key} + @prefix {key}:
,
< From c388d50a578bf711c221a526072a847257fb9e71 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 1 Dec 2021 18:54:28 +0900 Subject: [PATCH 23/74] =?UTF-8?q?fix:=20=E3=82=AF=E3=83=A9=E3=82=B9?= =?UTF-8?q?=E3=81=AE=E4=B8=8A=E4=B8=8B=E3=81=AB=E4=BD=99=E7=99=BD=E3=81=8C?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=A0=B4=E5=90=88=E3=80=81=E3=83=84=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E3=83=81=E3=83=83=E3=83=97=E3=81=8C=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E5=A4=96=E3=81=AB=E6=8F=8F=E7=94=BB=E3=81=95=E3=82=8C=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/style/style.scss | 4 ++ node/src/ts/visualizer/components/Tooltip.tsx | 56 +++++++++++++------ 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/node/src/style/style.scss b/node/src/style/style.scss index 043a30d9..a7fd2993 100644 --- a/node/src/style/style.scss +++ b/node/src/style/style.scss @@ -1449,6 +1449,10 @@ body { bottom: -35px; border-width: 25px 25px 0 25px; } + + &.none { + display: none; + } } } diff --git a/node/src/ts/visualizer/components/Tooltip.tsx b/node/src/ts/visualizer/components/Tooltip.tsx index 0af06d50..13a285d6 100644 --- a/node/src/ts/visualizer/components/Tooltip.tsx +++ b/node/src/ts/visualizer/components/Tooltip.tsx @@ -15,6 +15,8 @@ const selector = ({ tooltip: { pos, uri } }: RootState) => ({ uri, }) +type ArrowType = 'upward' | 'downward' | 'none' + const Tooltip: React.FC = (props) => { const { classes } = props const { pos, uri } = useSelector(selector) @@ -23,7 +25,7 @@ const Tooltip: React.FC = (props) => { x: 0, y: 0, visible: false, - isOnBottom: false, + arrowType: 'none' as ArrowType, }) const mounted = useRef(false) @@ -40,30 +42,50 @@ const Tooltip: React.FC = (props) => { oldTooltipStateRef.current = { uri } const tooltip = tooltipRef.current?.getBoundingClientRect() - if (!uri || !tooltip || !pos) { + const boundary = document + .getElementById('classes-structure') + ?.getBoundingClientRect() + if (!uri || !tooltip || !pos || !boundary) { return } - const dbclsHeaderHeight = 24 + 8 const arrowSize = 25 - const topMarginRequired = tooltip.height + arrowSize + dbclsHeaderHeight - const onBottom = pos.top < topMarginRequired - - setState({ - x: (pos.left + pos.right - tooltip.width) / 2, - y: onBottom - ? pos.bottom + arrowSize - : pos.top - (tooltip.height + arrowSize), - visible: true, - isOnBottom: onBottom, - }) + const [width, height] = [tooltip.width, tooltip.height + arrowSize] + + const canPlaceOnTop = boundary.top <= pos.top - height + const canPlaceOnBottom = pos.bottom + height <= boundary.bottom + + const x = (pos.left + pos.right - width) / 2 + const outOfLeftBoundary = x < boundary.left + const outOfRightBoundary = boundary.right < x + width + + if ( + (canPlaceOnTop || canPlaceOnBottom) && + !outOfLeftBoundary && + !outOfRightBoundary + ) { + setState({ + x, + y: canPlaceOnTop ? pos.top - height : pos.bottom + arrowSize, + visible: true, + arrowType: canPlaceOnTop ? 'downward' : 'upward', + }) + } else { + const margin = 16 + setState({ + x: boundary.left + margin, + y: boundary.bottom - (tooltip.height + margin), + visible: true, + arrowType: 'none', + }) + } } else { mounted.current = true } }, [classes, pos, uri]) const intl = useIntl() - const { x, y, visible, isOnBottom } = state + const { x, y, visible, arrowType } = state const tooltipElement = useMemo(() => { if (!uri) { return null @@ -91,10 +113,10 @@ const Tooltip: React.FC = (props) => {

{uri}

-
+
) - }, [uri, intl.locale, classes, x, y, visible, isOnBottom]) + }, [uri, intl.locale, classes, x, y, visible, arrowType]) return tooltipElement } From b189b79fc4d6c82c45b4c7f93cab2c7e50fc3eed Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 1 Dec 2021 20:08:35 +0900 Subject: [PATCH 24/74] =?UTF-8?q?fix:=20prefix=E3=81=8C=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=81=A8=E3=81=8D=E3=81=AB=E3=83=96=E3=83=A9=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=83=AA=E3=82=B9=E3=83=88=E3=81=AE=E3=83=81=E3=82=A7=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=81=8C=E5=8A=B9=E3=81=8B=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/utils/BlackList.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/node/src/ts/visualizer/utils/BlackList.ts b/node/src/ts/visualizer/utils/BlackList.ts index 17e790df..68cc0ccd 100644 --- a/node/src/ts/visualizer/utils/BlackList.ts +++ b/node/src/ts/visualizer/utils/BlackList.ts @@ -15,16 +15,12 @@ class Blacklist { } has(uri: string, prefixes: Prefixes) { - const [prefixKey, className] = uri.split(':') - const prefix = prefixes[prefixKey] + const [shorthand, className] = uri.split(':') + const longhand = prefixes[shorthand] + const rawUri = longhand ? `${longhand}${className}` : uri return ( - prefix !== undefined && - (('classes' in this.blacklist && - this.blacklist.classes.includes(`${prefix}${className}`)) || - ('prefixes' in this.blacklist && - this.blacklist.prefixes.some( - (item) => prefix === item || prefix.startsWith(item) - ))) + this.blacklist.classes.includes(rawUri) || + this.blacklist.prefixes.some((item) => rawUri.indexOf(item) > -1) ) } } From 4207c0ac91d8b6972bd14a4de26b28fb9e491ce5 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 1 Dec 2021 19:26:37 +0900 Subject: [PATCH 25/74] =?UTF-8?q?fix:=20number=20||=20number=20=E3=81=A70?= =?UTF-8?q?=E3=82=92=E7=84=A1=E8=A6=96=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/components/Graph.tsx | 6 ++--- node/src/ts/visualizer/components/Tree.tsx | 10 ++++----- node/src/ts/visualizer/reducers/search.ts | 2 +- .../ts/visualizer/utils/GraphRepository.ts | 10 ++++----- .../src/ts/visualizer/utils/TreeRepository.ts | 22 +++++++++---------- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/node/src/ts/visualizer/components/Graph.tsx b/node/src/ts/visualizer/components/Graph.tsx index 3d253480..5b6eb045 100644 --- a/node/src/ts/visualizer/components/Graph.tsx +++ b/node/src/ts/visualizer/components/Graph.tsx @@ -117,13 +117,13 @@ const Graph: React.FC = (props) => { React.useEffect(() => { if (circleDiameter && structure.length > 0 && classes && structure) { - const diameter = circleDiameter || 0 + const diameter = circleDiameter || 1 const pack = (data: NodeStructure) => { return d3.pack().size([diameter, diameter])( d3 .hierarchy(data) - .sum((d) => classes[d.uri]?.entities || 1) - .sort((a, b) => (a.value || 1) - (b.value || 1)) + .sum((d) => classes[d.uri]?.entities || 0.5) // entityが1かfalsyかで差をつける + .sort((a, b) => (a.value ?? 0) - (b.value ?? 0)) ) } const root: NodeStructure = createNodeStructure(structure) diff --git a/node/src/ts/visualizer/components/Tree.tsx b/node/src/ts/visualizer/components/Tree.tsx index 9e1a60cf..c908163c 100644 --- a/node/src/ts/visualizer/components/Tree.tsx +++ b/node/src/ts/visualizer/components/Tree.tsx @@ -232,12 +232,12 @@ export const Tree: React.FC = (props) => { return treeNodes.concat( multipleInheritanceNodes.map((superClass, i) => ({ ...superClass, - depth: focusingNode.parent?.depth || 0, + depth: focusingNode.parent?.depth ?? 0, data: { ...superClass.data, isMultipleInheritanceSource: true, oldTreeY: superClass.data.treeY, - treeY: (focusingNode.data?.treeY || 0) + (Margin.Y / 2) * (i + 1), + treeY: (focusingNode.data?.treeY ?? 0) + (Margin.Y / 2) * (i + 1), }, })) ) @@ -343,7 +343,7 @@ export const Tree: React.FC = (props) => { const rect = element.firstChild?.parentElement?.getBoundingClientRect() return rect ? rect[prop] : 0 } - return _.max(_.map(children, (child) => getGSize(child, prop))) || 0 + return _.max(_.map(children, (child) => getGSize(child, prop))) ?? 0 }, [] ) @@ -370,7 +370,7 @@ export const Tree: React.FC = (props) => { const treeNodesRef = React.useRef([]) const oldTreeNodesRef = React.useRef([]) React.useEffect(() => { - TreeRepository.setFocusingNodes(focusingCircleKey || 0) + TreeRepository.setFocusingNodes(focusingCircleKey ?? 0) TreeRepository.setSvg() TreeRepository.setFilters() @@ -402,7 +402,7 @@ export const Tree: React.FC = (props) => { let nextState: TreeState = state if (focusingURI !== prevFocusingURI) { - TreeRepository.setFocusingNodes(focusingCircleKey || 0) + TreeRepository.setFocusingNodes(focusingCircleKey ?? 0) const { hiddenUris } = state const newHiddenUris = hiddenUris.filter((uri) => uri !== focusingURI) diff --git a/node/src/ts/visualizer/reducers/search.ts b/node/src/ts/visualizer/reducers/search.ts index 9e4013c1..5f870d75 100644 --- a/node/src/ts/visualizer/reducers/search.ts +++ b/node/src/ts/visualizer/reducers/search.ts @@ -73,7 +73,7 @@ export default function search( return _.orderBy( candidates, - ({ entities }) => entities || Infinity * -1, + ({ entities }) => entities ?? Infinity * -1, ['desc'] ) } diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index b164230b..1370ba6c 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -326,15 +326,15 @@ class GraphRepository { } x(x: number) { - return (this.XLinear?.(x) || 0) + this.coordinate[0] + return (this.XLinear?.(x) ?? 0) + this.coordinate[0] } y(y: number) { - return (this.YLinear?.(y) || 0) + this.coordinate[1] + return (this.YLinear?.(y) ?? 0) + this.coordinate[1] } textY(d: NodeType) { - return this.y(d.y + (d.data.labelY || 0)) + return this.y(d.y + (d.data.labelY ?? 0)) } r(r: number) { @@ -690,7 +690,7 @@ class GraphRepository { const textRect = g[i].parentElement ?.getElementsByTagName('text')[0] .getBoundingClientRect() - return (textRect?.width || 0) / 2 + imageSize + return (textRect?.width ?? 0) / 2 + imageSize }) .attr('y', (d) => (d.data.isLabelOnTop ? 0 : -imageSize / 2)) @@ -844,7 +844,7 @@ class GraphRepository { .map((child) => child.data.labelY) ) - node.data.labelY = (maxYInChildren || 0) + scale * -60 + node.data.labelY = (maxYInChildren ?? 0) + scale * -60 } }) } diff --git a/node/src/ts/visualizer/utils/TreeRepository.ts b/node/src/ts/visualizer/utils/TreeRepository.ts index eeb27ef2..efbaec14 100644 --- a/node/src/ts/visualizer/utils/TreeRepository.ts +++ b/node/src/ts/visualizer/utils/TreeRepository.ts @@ -33,9 +33,9 @@ export const shouldShowDisplayButton = ( isLinealChildren(node, focusingNode)) export const getTranslateX = (d: NodeType) => d.depth * Margin.X export const getTranslateY = (d: NodeType) => - (d.data?.treeY || 0) + 35 + 12 * 2 + 6 + 20 + 8 + (d.data?.treeY ?? 0) + 35 + 12 * 2 + 6 + 20 + 8 export const getTranslateOldY = (d: NodeType) => - (d.data?.oldTreeY || 0) + 35 + 12 * 2 + 6 + 20 + 8 + (d.data?.oldTreeY ?? 0) + 35 + 12 * 2 + 6 + 20 + 8 export const getFontSize = (e: SVGTextElement) => Number(e.getAttribute('font-size') || '1') @@ -391,7 +391,7 @@ class TreeRepository { ) .each((d, i, g) => { const updateProgress = (progress: number) => { - const delta = (d.data.treeY || 0) - (d.data.oldTreeY || 0) + const delta = (d.data.treeY ?? 0) - (d.data.oldTreeY ?? 0) g[i].style.transform = `translate(${getTranslateX(d)}px, ${ getTranslateOldY(d) + progress * delta }px)` @@ -473,12 +473,12 @@ class TreeRepository { datum.data.delta > 0 ? (progress: number) => { group[index].style.strokeDashoffset = String( - (datum.data.delta || 0) * (1 - progress) + (datum.data.delta ?? 0) * (1 - progress) ) } : (progress: number) => { group[index].style.strokeDashoffset = String( - (datum.data.delta || 0) * -1 * progress + (datum.data.delta ?? 0) * -1 * progress ) } this.animationFuncMap.set(group[index], updateProgress) @@ -501,7 +501,7 @@ class TreeRepository { const lastChild = _.last(d.children) if (lastChild?.data.oldTreeY !== undefined) { d.data.delta = - (lastChild.data.treeY || 0) - (lastChild.data.oldTreeY || 0) + (lastChild.data.treeY ?? 0) - (lastChild.data.oldTreeY ?? 0) } } }) @@ -509,8 +509,8 @@ class TreeRepository { const lastChild = _.last(d.children) const y = d.data.delta !== undefined && d.data.delta < 0 - ? (lastChild?.data.oldTreeY || 0) - (d.data.treeY || 0) - : (lastChild?.data.treeY || 0) - (d.data.treeY || 0) + ? (lastChild?.data.oldTreeY ?? 0) - (d.data.treeY ?? 0) + : (lastChild?.data.treeY ?? 0) - (d.data.treeY ?? 0) return `M 0 0 H ${Margin.X / 2} V ${y}` }) .attr('stroke-dasharray', getDatumTotalLength) @@ -583,7 +583,7 @@ class TreeRepository { const shouldBridgeMultipleInheritance = multipleInheritanceUris.length > 0 && (_.max(focusingNode.parent?.children?.map((d) => d.data.treeY)) || [0]) > - (focusingNode.data.treeY || 0) + (focusingNode.data.treeY ?? 0) if (multipleInheritanceUris.length > 0) { const target = svgg?.filter( @@ -655,7 +655,7 @@ class TreeRepository { const linealAscendant = children?.find((d) => isLinealAscendant(d)) const childFocusingY = linealAscendant?.data.treeY - const y = (childFocusingY || 0) - (treeY || 0) + const y = (childFocusingY ?? 0) - (treeY ?? 0) focusingNodePositions.push({ depth, shouldPolygonal: !!children && children.length > 1 && y > 0, @@ -819,7 +819,7 @@ class TreeRepository { ) { const elapsed = window.performance.now() - start if (elapsed > ANIMATION_DURATION) { - this.finishAnimation(selection, this.animationKeyMap.get(selection) || 0) + this.finishAnimation(selection, this.animationKeyMap.get(selection) ?? 0) this.animationKeyMap.delete(selection) completion() } else { From 616adb9f04967bf20556e6d9df2668e977d12518 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Thu, 2 Dec 2021 13:43:39 +0900 Subject: [PATCH 26/74] =?UTF-8?q?fix:=20URI=E3=81=AB=E5=AF=BE=E5=BF=9C?= =?UTF-8?q?=E3=81=99=E3=82=8Bcircle=E3=81=8C=E3=81=AA=E3=81=84=E3=81=A8?= =?UTF-8?q?=E3=81=8D=E3=81=AB=E6=84=8F=E5=9B=B3=E3=81=97=E3=81=AA=E3=81=84?= =?UTF-8?q?=E6=8C=99=E5=8B=95=E3=81=A8=E3=81=AA=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 1d352ba4..8c4befa3 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -450,11 +450,7 @@ const ClassStructure: React.FC = (props) => { updateScale: boolean = true, transparentLabel: boolean = false ) => { - if (circles.length === 0) { - return - } - - if (updateScale) { + if (updateScale && circles.length > 0) { GraphRepository.calcCircleScale(circles) GraphRepository.updateScale() } @@ -604,7 +600,6 @@ const ClassStructure: React.FC = (props) => { return } if (domain || range) { - focus(0) const subject = GraphRepository.findUriNode(domain) const object = GraphRepository.findUriNode(range) GraphRepository.targetKey = subject ? subject.data.key : null @@ -627,11 +622,14 @@ const ClassStructure: React.FC = (props) => { GraphRepository.updateSelfLines([object]) } } - showCircles(showPropertyClass(domain, range), animate, true, true) - return + + const targetNodes = showPropertyClass(domain, range) + if (targetNodes.length > 0) { + showCircles(targetNodes, animate, true, true) + return + } } if (searchingURI) { - focus(0) const matchedNodes = search(searchingURI) if (matchedNodes.length === 1) { dispatch( @@ -642,8 +640,11 @@ const ClassStructure: React.FC = (props) => { ) return } - showCircles(matchedNodes, animate, true, true) - return + + if (matchedNodes.length > 0) { + showCircles(matchedNodes, animate, true, true) + return + } } showCircles(focus(0), animate) From 521cbc4f014899d5dff3c25d3bc2caed657580e3 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 12:09:45 +0900 Subject: [PATCH 27/74] =?UTF-8?q?yasgui.html=E3=81=AEno-cache=E3=82=92?= =?UTF-8?q?=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/nginx/nginx.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/node/nginx/nginx.conf b/node/nginx/nginx.conf index d0b0ab42..64fbcf6a 100644 --- a/node/nginx/nginx.conf +++ b/node/nginx/nginx.conf @@ -77,7 +77,6 @@ http { location /yasgui { try_files $uri /yasgui.html; - add_header Cache-Control no-cache; } } } From 50fc824e6863206f82d2b04934e8360215662831 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 12:52:47 +0900 Subject: [PATCH 28/74] update umakaparser --- server/poetry.lock | 555 ++++++++++++++++++++++-------------------- server/pyproject.toml | 4 +- 2 files changed, 297 insertions(+), 262 deletions(-) diff --git a/server/poetry.lock b/server/poetry.lock index 33b4fbd6..51ff73b4 100644 --- a/server/poetry.lock +++ b/server/poetry.lock @@ -1,55 +1,54 @@ [[package]] -category = "main" -description = "A database migration tool for SQLAlchemy." name = "alembic" +version = "1.4.2" +description = "A database migration tool for SQLAlchemy." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.4.2" [package.dependencies] Mako = "*" -SQLAlchemy = ">=1.1.0" python-dateutil = "*" python-editor = ">=0.3" +SQLAlchemy = ">=1.1.0" [[package]] -category = "main" -description = "A library for parsing ISO 8601 strings." name = "aniso8601" +version = "8.0.0" +description = "A library for parsing ISO 8601 strings." +category = "main" optional = false python-versions = "*" -version = "8.0.0" [[package]] -category = "dev" -description = "Atomic file writes." -marker = "sys_platform == \"win32\"" name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.4.0" [[package]] -category = "dev" -description = "Classes Without Boilerplate" name = "attrs" +version = "20.2.0" +description = "Classes Without Boilerplate" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.2.0" [package.extras] -dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"] docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] [[package]] -category = "main" -description = "httplib2 caching for requests" name = "cachecontrol" +version = "0.12.6" +description = "httplib2 caching for requests" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.12.6" [package.dependencies] msgpack = ">=0.5.2" @@ -60,90 +59,85 @@ filecache = ["lockfile (>=0.9)"] redis = ["redis (>=2.10.5)"] [[package]] -category = "main" -description = "Extensible memoizing collections and decorators" name = "cachetools" +version = "4.1.1" +description = "Extensible memoizing collections and decorators" +category = "main" optional = false python-versions = "~=3.5" -version = "4.1.1" [[package]] -category = "main" -description = "Python package for providing Mozilla's CA Bundle." name = "certifi" +version = "2020.6.20" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = "*" -version = "2020.6.20" [[package]] -category = "main" -description = "Foreign Function Interface for Python calling C code." -marker = "python_version >= \"3.5\"" name = "cffi" +version = "1.14.2" +description = "Foreign Function Interface for Python calling C code." +category = "main" optional = false python-versions = "*" -version = "1.14.2" [package.dependencies] pycparser = "*" [[package]] -category = "main" -description = "Universal encoding detector for Python 2 and 3" name = "chardet" +version = "3.0.4" +description = "Universal encoding detector for Python 2 and 3" +category = "main" optional = false python-versions = "*" -version = "3.0.4" [[package]] -category = "main" -description = "Composable command line interface toolkit" name = "click" +version = "7.1.2" +description = "Composable command line interface toolkit" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "7.1.2" [[package]] -category = "dev" -description = "Cross-platform colored terminal text." -marker = "sys_platform == \"win32\"" name = "colorama" +version = "0.4.3" +description = "Cross-platform colored terminal text." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.4.3" [[package]] -category = "main" -description = "Firebase Admin Python SDK" name = "firebase-admin" +version = "2.18.0" +description = "Firebase Admin Python SDK" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.18.0" [package.dependencies] cachecontrol = ">=0.12.4" +google-api-core = {version = ">=1.7.0,<2.0.0dev", extras = ["grpc"], markers = "platform_python_implementation != \"PyPy\""} google-api-python-client = ">=1.7.8" -google-cloud-firestore = ">=0.31.0" +google-cloud-firestore = {version = ">=0.31.0", markers = "platform_python_implementation != \"PyPy\""} google-cloud-storage = ">=1.13.0" six = ">=1.6.1" -[package.dependencies.google-api-core] -extras = ["grpc"] -version = ">=1.7.0,<2.0.0dev" - [[package]] -category = "main" -description = "A simple framework for building complex web applications." name = "flask" +version = "1.1.2" +description = "A simple framework for building complex web applications." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "1.1.2" [package.dependencies] -Jinja2 = ">=2.10.1" -Werkzeug = ">=0.15" click = ">=5.1" itsdangerous = ">=0.24" +Jinja2 = ">=2.10.1" +Werkzeug = ">=0.15" [package.extras] dev = ["pytest", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx-issues"] @@ -151,28 +145,28 @@ docs = ["sphinx", "pallets-sphinx-themes", "sphinxcontrib-log-cabinet", "sphinx- dotenv = ["python-dotenv"] [[package]] -category = "main" -description = "A Flask extension adding a decorator for CORS support" name = "flask-cors" +version = "3.0.9" +description = "A Flask extension adding a decorator for CORS support" +category = "main" optional = false python-versions = "*" -version = "3.0.9" [package.dependencies] Flask = ">=0.9" Six = "*" [[package]] -category = "main" -description = "Simple framework for creating REST APIs" name = "flask-restful" +version = "0.3.8" +description = "Simple framework for creating REST APIs" +category = "main" optional = false python-versions = "*" -version = "0.3.8" [package.dependencies] -Flask = ">=0.8" aniso8601 = ">=0.82" +Flask = ">=0.8" pytz = "*" six = ">=1.3.0" @@ -180,43 +174,43 @@ six = ">=1.3.0" docs = ["sphinx"] [[package]] -category = "main" -description = "Scripting support for Flask" name = "flask-script" +version = "2.0.6" +description = "Scripting support for Flask" +category = "main" optional = false python-versions = "*" -version = "2.0.6" [package.dependencies] Flask = "*" [[package]] -category = "main" -description = "Adds SQLAlchemy support to your Flask application." name = "flask-sqlalchemy" +version = "2.4.4" +description = "Adds SQLAlchemy support to your Flask application." +category = "main" optional = false python-versions = ">= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*" -version = "2.4.4" [package.dependencies] Flask = ">=0.10" SQLAlchemy = ">=0.8.0" [[package]] -category = "main" -description = "Google API client core library" name = "google-api-core" +version = "1.22.2" +description = "Google API client core library" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.22.2" [package.dependencies] google-auth = ">=1.21.1,<2.0dev" googleapis-common-protos = ">=1.6.0,<2.0dev" +grpcio = {version = ">=1.29.0,<2.0dev", optional = true, markers = "extra == \"grpc\""} protobuf = ">=3.12.0" pytz = "*" requests = ">=2.18.0,<3.0.0dev" -setuptools = ">=34.0.0" six = ">=1.10.0" [package.extras] @@ -225,12 +219,12 @@ grpcgcp = ["grpcio-gcp (>=0.2.2)"] grpcio-gcp = ["grpcio-gcp (>=0.2.2)"] [[package]] -category = "main" -description = "Google API Client Library for Python" name = "google-api-python-client" +version = "1.11.0" +description = "Google API Client Library for Python" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.11.0" [package.dependencies] google-api-core = ">=1.18.0,<2dev" @@ -241,30 +235,26 @@ six = ">=1.6.1,<2dev" uritemplate = ">=3.0.0,<4dev" [[package]] -category = "main" -description = "Google Authentication Library" name = "google-auth" +version = "1.21.1" +description = "Google Authentication Library" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.21.1" [package.dependencies] cachetools = ">=2.0.0,<5.0" pyasn1-modules = ">=0.2.1" -setuptools = ">=40.3.0" +rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.5\""} six = ">=1.9.0" -[package.dependencies.rsa] -python = ">=3.5" -version = ">=3.1.4,<5" - [[package]] -category = "main" -description = "Google Authentication Library: httplib2 transport" name = "google-auth-httplib2" +version = "0.0.4" +description = "Google Authentication Library: httplib2 transport" +category = "main" optional = false python-versions = "*" -version = "0.0.4" [package.dependencies] google-auth = "*" @@ -272,12 +262,12 @@ httplib2 = ">=0.9.1" six = "*" [[package]] -category = "main" -description = "Google Cloud API client core library" name = "google-cloud-core" +version = "1.4.1" +description = "Google Cloud API client core library" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.4.1" [package.dependencies] google-api-core = ">=1.19.0,<2.0.0dev" @@ -286,29 +276,25 @@ google-api-core = ">=1.19.0,<2.0.0dev" grpc = ["grpcio (>=1.8.2,<2.0dev)"] [[package]] -category = "main" -description = "Google Cloud Firestore API client library" -marker = "platform_python_implementation != \"PyPy\"" name = "google-cloud-firestore" +version = "1.9.0" +description = "Google Cloud Firestore API client library" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.9.0" [package.dependencies] +google-api-core = {version = ">=1.14.0,<2.0.0dev", extras = ["grpc"]} google-cloud-core = ">=1.4.1,<2.0dev" pytz = "*" -[package.dependencies.google-api-core] -extras = ["grpc"] -version = ">=1.14.0,<2.0.0dev" - [[package]] -category = "main" -description = "Google Cloud Storage API client library" name = "google-cloud-storage" +version = "1.31.0" +description = "Google Cloud Storage API client library" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.31.0" [package.dependencies] google-auth = ">=1.11.0,<2.0dev" @@ -316,13 +302,12 @@ google-cloud-core = ">=1.4.1,<2.0dev" google-resumable-media = ">=1.0.0,<2.0dev" [[package]] -category = "main" -description = "A python wrapper of the C library 'Google CRC32C'" -marker = "python_version >= \"3.5\"" name = "google-crc32c" +version = "1.0.0" +description = "A python wrapper of the C library 'Google CRC32C'" +category = "main" optional = false python-versions = ">=3.5" -version = "1.0.0" [package.dependencies] cffi = ">=1.0.0" @@ -331,30 +316,27 @@ cffi = ">=1.0.0" testing = ["pytest"] [[package]] -category = "main" -description = "Utilities for Google Media Downloads and Resumable Uploads" name = "google-resumable-media" +version = "1.0.0" +description = "Utilities for Google Media Downloads and Resumable Uploads" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" -version = "1.0.0" [package.dependencies] +google-crc32c = {version = ">=1.0,<2.0dev", markers = "python_version >= \"3.5\""} six = "*" -[package.dependencies.google-crc32c] -python = ">=3.5" -version = ">=1.0,<2.0dev" - [package.extras] requests = ["requests (>=2.18.0,<3.0.0dev)"] [[package]] -category = "main" -description = "Common protobufs used in Google APIs" name = "googleapis-common-protos" +version = "1.52.0" +description = "Common protobufs used in Google APIs" +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.52.0" [package.dependencies] protobuf = ">=3.6.0" @@ -363,29 +345,42 @@ protobuf = ">=3.6.0" grpc = ["grpcio (>=1.0.0)"] [[package]] +name = "grpcio" +version = "1.43.0" +description = "HTTP/2-based RPC framework" category = "main" -description = "A comprehensive HTTP client library." +optional = false +python-versions = ">=3.6" + +[package.dependencies] +six = ">=1.5.2" + +[package.extras] +protobuf = ["grpcio-tools (>=1.43.0)"] + +[[package]] name = "httplib2" +version = "0.18.1" +description = "A comprehensive HTTP client library." +category = "main" optional = false python-versions = "*" -version = "0.18.1" [[package]] -category = "main" -description = "Internationalized Domain Names in Applications (IDNA)" name = "idna" +version = "2.10" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.10" [[package]] -category = "dev" -description = "Read metadata from Python packages" -marker = "python_version < \"3.8\"" name = "importlib-metadata" +version = "1.7.0" +description = "Read metadata from Python packages" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" -version = "1.7.0" [package.dependencies] zipp = ">=0.5" @@ -395,31 +390,31 @@ docs = ["sphinx", "rst.linker"] testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] [[package]] -category = "main" -description = "An ISO 8601 date/time/duration parser and formatter" name = "isodate" +version = "0.6.1" +description = "An ISO 8601 date/time/duration parser and formatter" +category = "main" optional = false python-versions = "*" -version = "0.6.0" [package.dependencies] six = "*" [[package]] -category = "main" -description = "Various helpers to pass data to untrusted environments and back." name = "itsdangerous" +version = "1.1.0" +description = "Various helpers to pass data to untrusted environments and back." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.1.0" [[package]] -category = "main" -description = "A very fast and expressive template engine." name = "jinja2" +version = "2.11.2" +description = "A very fast and expressive template engine." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.11.2" [package.dependencies] MarkupSafe = ">=0.23" @@ -428,12 +423,12 @@ MarkupSafe = ">=0.23" i18n = ["Babel (>=0.8)"] [[package]] -category = "main" -description = "A super-fast templating language that borrows the best ideas from the existing templating languages." name = "mako" +version = "1.1.3" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.1.3" [package.dependencies] MarkupSafe = ">=0.9.2" @@ -443,158 +438,151 @@ babel = ["babel"] lingua = ["lingua"] [[package]] -category = "main" -description = "Safely add untrusted strings to HTML/XML markup." name = "markupsafe" +version = "1.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" -version = "1.1.1" [[package]] -category = "dev" -description = "More routines for operating on iterables, beyond itertools" name = "more-itertools" +version = "8.5.0" +description = "More routines for operating on iterables, beyond itertools" +category = "dev" optional = false python-versions = ">=3.5" -version = "8.5.0" [[package]] -category = "main" -description = "MessagePack (de)serializer." name = "msgpack" +version = "1.0.0" +description = "MessagePack (de)serializer." +category = "main" optional = false python-versions = "*" -version = "1.0.0" [[package]] -category = "dev" -description = "Core utilities for Python packages" name = "packaging" +version = "20.4" +description = "Core utilities for Python packages" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "20.4" [package.dependencies] pyparsing = ">=2.0.2" six = "*" [[package]] -category = "dev" -description = "plugin and hook calling mechanisms for python" name = "pluggy" +version = "0.13.1" +description = "plugin and hook calling mechanisms for python" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "0.13.1" [package.dependencies] -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] [[package]] -category = "main" -description = "Protocol Buffers" name = "protobuf" +version = "3.13.0" +description = "Protocol Buffers" +category = "main" optional = false python-versions = "*" -version = "3.13.0" [package.dependencies] -setuptools = "*" six = ">=1.9" [[package]] -category = "dev" -description = "library with cross-python path, ini-parsing, io, code, log facilities" name = "py" +version = "1.9.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.9.0" [[package]] -category = "main" -description = "ASN.1 types and codecs" name = "pyasn1" +version = "0.4.8" +description = "ASN.1 types and codecs" +category = "main" optional = false python-versions = "*" -version = "0.4.8" [[package]] -category = "main" -description = "A collection of ASN.1-based protocols modules." name = "pyasn1-modules" +version = "0.2.8" +description = "A collection of ASN.1-based protocols modules." +category = "main" optional = false python-versions = "*" -version = "0.2.8" [package.dependencies] pyasn1 = ">=0.4.6,<0.5.0" [[package]] -category = "main" -description = "C parser in Python" -marker = "python_version >= \"3.5\"" name = "pycparser" +version = "2.20" +description = "C parser in Python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "2.20" [[package]] -category = "main" -description = "Pure Python MySQL Driver" name = "pymysql" +version = "0.10.1" +description = "Pure Python MySQL Driver" +category = "main" optional = false python-versions = "*" -version = "0.10.1" [package.extras] ed25519 = ["PyNaCl (>=1.4.0)"] rsa = ["cryptography"] [[package]] -category = "main" -description = "Python parsing module" name = "pyparsing" +version = "2.4.7" +description = "Python parsing module" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -version = "2.4.7" [[package]] -category = "dev" -description = "pytest: simple powerful testing with Python" name = "pytest" +version = "5.4.3" +description = "pytest: simple powerful testing with Python" +category = "dev" optional = false python-versions = ">=3.5" -version = "5.4.3" [package.dependencies] -atomicwrites = ">=1.0" +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=17.4.0" -colorama = "*" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} more-itertools = ">=4.0.0" packaging = "*" pluggy = ">=0.12,<1.0" py = ">=1.5.0" wcwidth = "*" -[package.dependencies.importlib-metadata] -python = "<3.8" -version = ">=0.12" - [package.extras] -checkqa-mypy = ["mypy (v0.761)"] +checkqa-mypy = ["mypy (==v0.761)"] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] -category = "dev" -description = "Thin-wrapper around the mock package for easier use with py.test" name = "pytest-mock" +version = "1.13.0" +description = "Thin-wrapper around the mock package for easier use with py.test" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.13.0" [package.dependencies] pytest = ">=2.7" @@ -603,80 +591,79 @@ pytest = ">=2.7" dev = ["pre-commit", "tox"] [[package]] -category = "main" -description = "Extensions to the standard Python datetime module" name = "python-dateutil" +version = "2.8.1" +description = "Extensions to the standard Python datetime module" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -version = "2.8.1" [package.dependencies] six = ">=1.5" [[package]] -category = "main" -description = "Programmatically open an editor, capture the result." name = "python-editor" +version = "1.0.4" +description = "Programmatically open an editor, capture the result." +category = "main" optional = false python-versions = "*" -version = "1.0.4" [[package]] -category = "main" -description = "Translation library for Python" name = "python-i18n" +version = "0.3.9" +description = "Translation library for Python" +category = "main" optional = false python-versions = "*" -version = "0.3.9" [package.extras] yaml = ["pyyaml (>=3.10)"] [[package]] -category = "main" -description = "World timezone definitions, modern and historical" name = "pytz" +version = "2019.3" +description = "World timezone definitions, modern and historical" +category = "main" optional = false python-versions = "*" -version = "2019.3" [[package]] -category = "main" -description = "RDFLib is a Python library for working with RDF, a simple yet powerful language for representing information." name = "rdflib" +version = "6.1.1" +description = "RDFLib is a Python library for working with RDF, a simple yet powerful language for representing information." +category = "main" optional = false -python-versions = "*" -version = "5.0.0" +python-versions = ">=3.7" [package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8.0\""} isodate = "*" pyparsing = "*" -six = "*" [package.extras] -docs = ["sphinx (<3)", "sphinxcontrib-apidoc"] +docs = ["sphinx (<5)", "sphinxcontrib-apidoc"] html = ["html5lib"] -sparql = ["requests"] -tests = ["html5lib", "networkx", "nose", "doctest-ignore-unicode"] +tests = ["berkeleydb", "html5lib", "networkx", "pytest", "pytest-cov", "pytest-subtests"] [[package]] -category = "main" -description = "Python client for Redis key-value store" name = "redis" +version = "3.5.3" +description = "Python client for Redis key-value store" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "3.5.3" [package.extras] hiredis = ["hiredis (>=0.1.3)"] [[package]] -category = "main" -description = "Python HTTP for Humans." name = "requests" +version = "2.24.0" +description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "2.24.0" [package.dependencies] certifi = ">=2017.4.17" @@ -686,35 +673,34 @@ urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] [[package]] -category = "main" -description = "Pure-Python RSA implementation" -marker = "platform_python_implementation != \"PyPy\" and python_version >= \"3.5\" or python_version >= \"3.5\"" name = "rsa" +version = "4.6" +description = "Pure-Python RSA implementation" +category = "main" optional = false python-versions = ">=3.5, <4" -version = "4.6" [package.dependencies] pyasn1 = ">=0.1.3" [[package]] -category = "main" -description = "Python 2 and 3 compatibility utilities" name = "six" +version = "1.15.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -version = "1.15.0" [[package]] -category = "main" -description = "Database Abstraction Library" name = "sqlalchemy" +version = "1.3.19" +description = "Database Abstraction Library" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "1.3.19" [package.extras] mssql = ["pyodbc"] @@ -729,91 +715,94 @@ postgresql_psycopg2cffi = ["psycopg2cffi"] pymysql = ["pymysql"] [[package]] -category = "main" -description = "Fast, Extensible Progress Meter" name = "tqdm" +version = "4.62.3" +description = "Fast, Extensible Progress Meter" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -version = "4.56.0" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] dev = ["py-make (>=0.1.0)", "twine", "wheel"] +notebook = ["ipywidgets (>=6)"] telegram = ["requests"] [[package]] -category = "main" -description = "" name = "umakaparser" +version = "0.1.7" +description = "" +category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "0.1.1" +python-versions = ">=3.7" [package.dependencies] click = ">=7.0,<8.0" isodate = ">=0.6.0,<0.7.0" pyparsing = ">=2.4,<3.0" python-i18n = ">=0.3.9,<0.4.0" -rdflib = ">=5.0,<6.0" +rdflib = ">=6.0.0,<7.0.0" tqdm = ">=4.52.0,<5.0.0" [[package]] -category = "main" -description = "URI templates" name = "uritemplate" +version = "3.0.1" +description = "URI templates" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "3.0.1" [[package]] -category = "main" -description = "HTTP library with thread-safe connection pooling, file post, and more." name = "urllib3" +version = "1.25.10" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" -version = "1.25.10" [package.extras] brotli = ["brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] -socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] -category = "dev" -description = "Measures the displayed width of unicode strings in a terminal" name = "wcwidth" +version = "0.2.5" +description = "Measures the displayed width of unicode strings in a terminal" +category = "dev" optional = false python-versions = "*" -version = "0.2.5" [[package]] -category = "main" -description = "The comprehensive WSGI web application library." name = "werkzeug" +version = "1.0.1" +description = "The comprehensive WSGI web application library." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -version = "1.0.1" [package.extras] dev = ["pytest", "pytest-timeout", "coverage", "tox", "sphinx", "pallets-sphinx-themes", "sphinx-issues"] watchdog = ["watchdog"] [[package]] -category = "dev" -description = "Backport of pathlib-compatible object wrapper for zip files" -marker = "python_version < \"3.8\"" name = "zipp" +version = "3.1.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" optional = false python-versions = ">=3.6" -version = "3.1.0" [package.extras] docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] testing = ["jaraco.itertools", "func-timeout"] [metadata] -content-hash = "47ee2f390a3a95acf128cec725d6e84ec87d08a034727f312caafa0e4a7e90c4" -lock-version = "1.0" -python-versions = "^3.6" +lock-version = "1.1" +python-versions = "^3.7" +content-hash = "535b95b5ad21aadc54dade5d3e931cf96820b0c75237bea656207a8b43093063" [metadata.files] alembic = [ @@ -964,6 +953,52 @@ googleapis-common-protos = [ {file = "googleapis-common-protos-1.52.0.tar.gz", hash = "sha256:560716c807117394da12cecb0a54da5a451b5cf9866f1d37e9a5e2329a665351"}, {file = "googleapis_common_protos-1.52.0-py2.py3-none-any.whl", hash = "sha256:c8961760f5aad9a711d37b675be103e0cc4e9a39327e0d6d857872f698403e24"}, ] +grpcio = [ + {file = "grpcio-1.43.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:a4e786a8ee8b30b25d70ee52cda6d1dbba2a8ca2f1208d8e20ed8280774f15c8"}, + {file = "grpcio-1.43.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:af9c3742f6c13575c0d4147a8454da0ff5308c4d9469462ff18402c6416942fe"}, + {file = "grpcio-1.43.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:fdac966699707b5554b815acc272d81e619dd0999f187cd52a61aef075f870ee"}, + {file = "grpcio-1.43.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e463b4aa0a6b31cf2e57c4abc1a1b53531a18a570baeed39d8d7b65deb16b7e"}, + {file = "grpcio-1.43.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f11d05402e0ac3a284443d8a432d3dfc76a6bd3f7b5858cddd75617af2d7bd9b"}, + {file = "grpcio-1.43.0-cp310-cp310-win32.whl", hash = "sha256:c36f418c925a41fccada8f7ae9a3d3e227bfa837ddbfddd3d8b0ac252d12dda9"}, + {file = "grpcio-1.43.0-cp310-cp310-win_amd64.whl", hash = "sha256:772b943f34374744f70236bbbe0afe413ed80f9ae6303503f85e2b421d4bca92"}, + {file = "grpcio-1.43.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:cbc9b83211d905859dcf234ad39d7193ff0f05bfc3269c364fb0d114ee71de59"}, + {file = "grpcio-1.43.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:fb7229fa2a201a0c377ff3283174ec966da8f9fd7ffcc9a92f162d2e7fc9025b"}, + {file = "grpcio-1.43.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:17b75f220ee6923338155b4fcef4c38802b9a57bc57d112c9599a13a03e99f8d"}, + {file = "grpcio-1.43.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:6620a5b751b099b3b25553cfc03dfcd873cda06f9bb2ff7e9948ac7090e20f05"}, + {file = "grpcio-1.43.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:1898f999383baac5fcdbdef8ea5b1ef204f38dc211014eb6977ac6e55944d738"}, + {file = "grpcio-1.43.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47b6821238d8978014d23b1132713dac6c2d72cbb561cf257608b1673894f90a"}, + {file = "grpcio-1.43.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80398e9fb598060fa41050d1220f5a2440fe74ff082c36dda41ac3215ebb5ddd"}, + {file = "grpcio-1.43.0-cp36-cp36m-win32.whl", hash = "sha256:0110310eff07bb69782f53b7a947490268c4645de559034c43c0a635612e250f"}, + {file = "grpcio-1.43.0-cp36-cp36m-win_amd64.whl", hash = "sha256:45401d00f2ee46bde75618bf33e9df960daa7980e6e0e7328047191918c98504"}, + {file = "grpcio-1.43.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:af78ac55933811e6a25141336b1f2d5e0659c2f568d44d20539b273792563ca7"}, + {file = "grpcio-1.43.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8b2b9dc4d7897566723b77422e11c009a0ebd397966b165b21b89a62891a9fdf"}, + {file = "grpcio-1.43.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:77ef653f966934b3bfdd00e4f2064b68880eb40cf09b0b99edfa5ee22a44f559"}, + {file = "grpcio-1.43.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e95b5d62ec26d0cd0b90c202d73e7cb927c369c3358e027225239a4e354967dc"}, + {file = "grpcio-1.43.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:04239e8f71db832c26bbbedb4537b37550a39d77681d748ab4678e58dd6455d6"}, + {file = "grpcio-1.43.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b4a7152187a49767a47d1413edde2304c96f41f7bc92cc512e230dfd0fba095"}, + {file = "grpcio-1.43.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8cc936a29c65ab39714e1ba67a694c41218f98b6e2a64efb83f04d9abc4386b"}, + {file = "grpcio-1.43.0-cp37-cp37m-win32.whl", hash = "sha256:577e024c8dd5f27cd98ba850bc4e890f07d4b5942e5bc059a3d88843a2f48f66"}, + {file = "grpcio-1.43.0-cp37-cp37m-win_amd64.whl", hash = "sha256:138f57e3445d4a48d9a8a5af1538fdaafaa50a0a3c243f281d8df0edf221dc02"}, + {file = "grpcio-1.43.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:08cf25f2936629db062aeddbb594bd76b3383ab0ede75ef0461a3b0bc3a2c150"}, + {file = "grpcio-1.43.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:01f4b887ed703fe82ebe613e1d2dadea517891725e17e7a6134dcd00352bd28c"}, + {file = "grpcio-1.43.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:0aa8285f284338eb68962fe1a830291db06f366ea12f213399b520c062b01f65"}, + {file = "grpcio-1.43.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:0edbfeb6729aa9da33ce7e28fb7703b3754934115454ae45e8cc1db601756fd3"}, + {file = "grpcio-1.43.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:c354017819201053d65212befd1dcb65c2d91b704d8977e696bae79c47cd2f82"}, + {file = "grpcio-1.43.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50cfb7e1067ee5e00b8ab100a6b7ea322d37ec6672c0455106520b5891c4b5f5"}, + {file = "grpcio-1.43.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57f1aeb65ed17dfb2f6cd717cc109910fe395133af7257a9c729c0b9604eac10"}, + {file = "grpcio-1.43.0-cp38-cp38-win32.whl", hash = "sha256:fa26a8bbb3fe57845acb1329ff700d5c7eaf06414c3e15f4cb8923f3a466ef64"}, + {file = "grpcio-1.43.0-cp38-cp38-win_amd64.whl", hash = "sha256:ade8b79a6b6aea68adb9d4bfeba5d647667d842202c5d8f3ba37ac1dc8e5c09c"}, + {file = "grpcio-1.43.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:124e718faf96fe44c98b05f3f475076be8b5198bb4c52a13208acf88a8548ba9"}, + {file = "grpcio-1.43.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2f96142d0abc91290a63ba203f01649e498302b1b6007c67bad17f823ecde0cf"}, + {file = "grpcio-1.43.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:31e6e489ccd8f08884b9349a39610982df48535881ec34f05a11c6e6b6ebf9d0"}, + {file = "grpcio-1.43.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:0e731f660e1e68238f56f4ce11156f02fd06dc58bc7834778d42c0081d4ef5ad"}, + {file = "grpcio-1.43.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:1f16725a320460435a8a5339d8b06c4e00d307ab5ad56746af2e22b5f9c50932"}, + {file = "grpcio-1.43.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4b4543e13acb4806917d883d0f70f21ba93b29672ea81f4aaba14821aaf9bb0"}, + {file = "grpcio-1.43.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:594aaa0469f4fca7773e80d8c27bf1298e7bbce5f6da0f084b07489a708f16ab"}, + {file = "grpcio-1.43.0-cp39-cp39-win32.whl", hash = "sha256:5449ae564349e7a738b8c38583c0aad954b0d5d1dd3cea68953bfc32eaee11e3"}, + {file = "grpcio-1.43.0-cp39-cp39-win_amd64.whl", hash = "sha256:bdf41550815a831384d21a498b20597417fd31bd084deb17d31ceb39ad9acc79"}, + {file = "grpcio-1.43.0.tar.gz", hash = "sha256:735d9a437c262ab039d02defddcb9f8f545d7009ae61c0114e19dda3843febe5"}, +] httplib2 = [ {file = "httplib2-0.18.1-py3-none-any.whl", hash = "sha256:ca2914b015b6247791c4866782fa6042f495b94401a0f0bd3e1d6e0ba2236782"}, {file = "httplib2-0.18.1.tar.gz", hash = "sha256:8af66c1c52c7ffe1aa5dc4bcd7c769885254b0756e6e69f953c7f0ab49a70ba3"}, @@ -977,8 +1012,8 @@ importlib-metadata = [ {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"}, ] isodate = [ - {file = "isodate-0.6.0-py2.py3-none-any.whl", hash = "sha256:aa4d33c06640f5352aca96e4b81afd8ab3b47337cc12089822d6f322ac772c81"}, - {file = "isodate-0.6.0.tar.gz", hash = "sha256:2e364a3d5759479cdb2d37cce6b9376ea504db2ff90252a2e5b7cc89cc9ff2d8"}, + {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"}, + {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"}, ] itsdangerous = [ {file = "itsdangerous-1.1.0-py2.py3-none-any.whl", hash = "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"}, @@ -1153,8 +1188,8 @@ pytz = [ {file = "pytz-2019.3.tar.gz", hash = "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"}, ] rdflib = [ - {file = "rdflib-5.0.0-py3-none-any.whl", hash = "sha256:88208ea971a87886d60ae2b1a4b2cdc263527af0454c422118d43fe64b357877"}, - {file = "rdflib-5.0.0.tar.gz", hash = "sha256:78149dd49d385efec3b3adfbd61c87afaf1281c30d3fcaf1b323b34f603fb155"}, + {file = "rdflib-6.1.1-py3-none-any.whl", hash = "sha256:fc81cef513cd552d471f2926141396b633207109d0154c8e77926222c70367fe"}, + {file = "rdflib-6.1.1.tar.gz", hash = "sha256:8dbfa0af2990b98471dacbc936d6494c997ede92fd8ed693fb84ee700ef6f754"}, ] redis = [ {file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"}, @@ -1203,12 +1238,12 @@ sqlalchemy = [ {file = "SQLAlchemy-1.3.19.tar.gz", hash = "sha256:3bba2e9fbedb0511769780fe1d63007081008c5c2d7d715e91858c94dbaa260e"}, ] tqdm = [ - {file = "tqdm-4.56.0-py2.py3-none-any.whl", hash = "sha256:4621f6823bab46a9cc33d48105753ccbea671b68bab2c50a9f0be23d4065cb5a"}, - {file = "tqdm-4.56.0.tar.gz", hash = "sha256:fe3d08dd00a526850568d542ff9de9bbc2a09a791da3c334f3213d8d0bbbca65"}, + {file = "tqdm-4.62.3-py2.py3-none-any.whl", hash = "sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c"}, + {file = "tqdm-4.62.3.tar.gz", hash = "sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d"}, ] umakaparser = [ - {file = "umakaparser-0.1.1-py2.py3-none-any.whl", hash = "sha256:90e7be3ea1721beddddc11287aa4a57ce844ab8f05d1733e11293d0d6e6ff6df"}, - {file = "umakaparser-0.1.1.tar.gz", hash = "sha256:11c05a9a03d472574a3590bb6e9a01a854e47cbdcf582aeb4baa59d495197ec0"}, + {file = "umakaparser-0.1.7-py3-none-any.whl", hash = "sha256:670638655335544438bd8b5ea3d907b8818f6826cbb82744a2e8dea1daf4ee00"}, + {file = "umakaparser-0.1.7.tar.gz", hash = "sha256:35cd8f41561add6c939f91d3c11b2377789fefe7c3cc798669600243ce211042"}, ] uritemplate = [ {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, diff --git a/server/pyproject.toml b/server/pyproject.toml index 0a6772ce..10384b27 100644 --- a/server/pyproject.toml +++ b/server/pyproject.toml @@ -5,7 +5,7 @@ description = "" authors = ["Your Name "] [tool.poetry.dependencies] -python = "^3.6" +python = "^3.7" flask = "^1.1" SQLAlchemy = "^1.3" Flask-SQLAlchemy = "^2.4" @@ -17,7 +17,7 @@ flask-cors = "^3.0" pytz = "^2019.2" redis = "^3.3" PyMySQL = "^0.10.1" -umakaparser = "^0.1.1" +umakaparser = "^0.1.7" [tool.poetry.dev-dependencies] pytest = "^5.1" From a393955457b323eb273ecf20720f740d16596521 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 13:16:14 +0900 Subject: [PATCH 29/74] for develop --- dev.docker-compose.yml | 32 ++++++++++++++++++++++++++++++++ node/nginx/nginx.conf | 1 + 2 files changed, 33 insertions(+) create mode 100644 dev.docker-compose.yml diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml new file mode 100644 index 00000000..9c34c7da --- /dev/null +++ b/dev.docker-compose.yml @@ -0,0 +1,32 @@ +version: "3" +services: + mysql: + build: + context: mysql + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + MYSQL_USER: umaka_v + MYSQL_DATABASE: dbcls_production + MYSQL_PASSWORD: glnyUnLiybsaE968Z7 + volumes: + - /opt/services/umaka_v/data:/var/lib/mysql + - /home/umaka_v/restore:/restore + redis: + image: redis:5.0-alpine + api: + build: + context: server + depends_on: + - mysql + - redis + ports: + - 5000:5000 + volumes: + - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json + nginx: + build: + context: node + ports: + - 10080:80 + depends_on: + - api diff --git a/node/nginx/nginx.conf b/node/nginx/nginx.conf index 64fbcf6a..1ed60e90 100644 --- a/node/nginx/nginx.conf +++ b/node/nginx/nginx.conf @@ -45,6 +45,7 @@ http { listen 80; server_name v.umaka.dbcls.jp; server_name umaka-viewer.dbcls.jp; + server_name umaka-viewer-dev.dbcls.jp; client_max_body_size 2g; root /public; From 2b07c0d6845214a6374a3603de1ab68946693d9b Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 13:28:36 +0900 Subject: [PATCH 30/74] minorfix --- deploy/run-dev.sh | 46 ++++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 2 ++ 2 files changed, 48 insertions(+) create mode 100644 deploy/run-dev.sh diff --git a/deploy/run-dev.sh b/deploy/run-dev.sh new file mode 100644 index 00000000..9e80edd6 --- /dev/null +++ b/deploy/run-dev.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +BASE = $HOME/dev + +update() { + cd $BASE/repos/umakaviewer + git pull origin develop +} + +rebuild() { + cd $BASE/repos/umakaviewer + docker-compose -p umakaviewer build +} + +start() { + cd $BASE/repos/umakaviewer + docker-compose -p umakaviewer up -d +} + +stop() { + cd $BASE/repos/umakaviewer + docker-compose -p umakaviewer down +} + +restart() { + cd $BASE/repos/umakaviewer + docker-compose -p umakaviewer restart +} + +ps() { + cd $BASE/repos/umakaviewer + docker-compose -p umakaviewer ps +} + +main() { + case $1 in + "update" ) update ;; + "rebuild" ) rebuild ;; + "start" ) start ;; + "stop" ) stop ;; + "restart" ) restart ;; + "ps" ) ps ;; + esac +} + +main $1 diff --git a/docker-compose.yml b/docker-compose.yml index 1beaddfc..8a53f2ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,7 @@ services: redis: image: redis:5.0-alpine api: + image: dev-api build: context: server depends_on: @@ -24,6 +25,7 @@ services: volumes: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: + image: dev-nginx build: context: node ports: From fe38bbd8206fe83381e94882ef951040b6abc474 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 13:30:40 +0900 Subject: [PATCH 31/74] minorfix --- dev.docker-compose.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index 9c34c7da..f77b9103 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -8,9 +8,6 @@ services: MYSQL_USER: umaka_v MYSQL_DATABASE: dbcls_production MYSQL_PASSWORD: glnyUnLiybsaE968Z7 - volumes: - - /opt/services/umaka_v/data:/var/lib/mysql - - /home/umaka_v/restore:/restore redis: image: redis:5.0-alpine api: From df44ea05d1aaf8620bbefe1a9b1efb7708270447 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:01:30 +0900 Subject: [PATCH 32/74] minorfix --- dev.docker-compose.yml | 2 ++ docker-compose.yml | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index f77b9103..7ed34f72 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -11,6 +11,7 @@ services: redis: image: redis:5.0-alpine api: + image: api-dev build: context: server depends_on: @@ -21,6 +22,7 @@ services: volumes: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: + image: nginx-dev build: context: node ports: diff --git a/docker-compose.yml b/docker-compose.yml index 8a53f2ff..1beaddfc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,6 @@ services: redis: image: redis:5.0-alpine api: - image: dev-api build: context: server depends_on: @@ -25,7 +24,6 @@ services: volumes: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: - image: dev-nginx build: context: node ports: From 6dd88afd5cbae140e560d1efbdb4ecf23f95ac0b Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:09:08 +0900 Subject: [PATCH 33/74] minorfix --- dev.docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index 7ed34f72..d90a548a 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -1,6 +1,7 @@ version: "3" services: mysql: + image: mysql-dev build: context: mysql environment: From d9cccf9290d35bbb8053d2fc82c0256a624f169e Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:14:58 +0900 Subject: [PATCH 34/74] minorfix --- dev.docker-compose.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index d90a548a..41b80b74 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -1,7 +1,7 @@ version: "3" services: mysql: - image: mysql-dev + container_name: umaka-mysql-dev build: context: mysql environment: @@ -10,9 +10,10 @@ services: MYSQL_DATABASE: dbcls_production MYSQL_PASSWORD: glnyUnLiybsaE968Z7 redis: + container_name: umaka-redis-dev image: redis:5.0-alpine api: - image: api-dev + container_name: umaka-api-dev build: context: server depends_on: @@ -23,7 +24,7 @@ services: volumes: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: - image: nginx-dev + container_name: umaka-nginx-dev build: context: node ports: From 29693346006d786e4e296768f703ce607121a233 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:16:01 +0900 Subject: [PATCH 35/74] minorfix --- dev.docker-compose.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index 41b80b74..a8e6babc 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -1,7 +1,7 @@ version: "3" services: mysql: - container_name: umaka-mysql-dev + container_name: mysql-dev build: context: mysql environment: @@ -14,6 +14,7 @@ services: image: redis:5.0-alpine api: container_name: umaka-api-dev + image: api-dev build: context: server depends_on: @@ -25,6 +26,7 @@ services: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: container_name: umaka-nginx-dev + image: nginx-dev build: context: node ports: From 1bf5f37c7daea406ae747e5b8b2eeddbaa6c4270 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:55:26 +0900 Subject: [PATCH 36/74] minorfix --- dev.docker-compose.yml => dev/dev.docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename dev.docker-compose.yml => dev/dev.docker-compose.yml (95%) diff --git a/dev.docker-compose.yml b/dev/dev.docker-compose.yml similarity index 95% rename from dev.docker-compose.yml rename to dev/dev.docker-compose.yml index a8e6babc..c768202c 100644 --- a/dev.docker-compose.yml +++ b/dev/dev.docker-compose.yml @@ -1,7 +1,7 @@ version: "3" services: mysql: - container_name: mysql-dev + container_name: umaka-mysql-dev build: context: mysql environment: From 4ab7b4817b60ccd259cfd0b4fd25e30ac4fdaf35 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:57:39 +0900 Subject: [PATCH 37/74] minorfix --- dev/dev.docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/dev.docker-compose.yml b/dev/dev.docker-compose.yml index c768202c..086e631d 100644 --- a/dev/dev.docker-compose.yml +++ b/dev/dev.docker-compose.yml @@ -3,7 +3,7 @@ services: mysql: container_name: umaka-mysql-dev build: - context: mysql + context: ../mysql environment: MYSQL_ALLOW_EMPTY_PASSWORD: 1 MYSQL_USER: umaka_v @@ -16,7 +16,7 @@ services: container_name: umaka-api-dev image: api-dev build: - context: server + context: ../server depends_on: - mysql - redis @@ -28,7 +28,7 @@ services: container_name: umaka-nginx-dev image: nginx-dev build: - context: node + context: ../node ports: - 10080:80 depends_on: From 182cc60c2b7ded97ddd0708f7967e2bb39eb0a74 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:58:56 +0900 Subject: [PATCH 38/74] minorfix --- dev/dev.docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev/dev.docker-compose.yml b/dev/dev.docker-compose.yml index 086e631d..60dfdf42 100644 --- a/dev/dev.docker-compose.yml +++ b/dev/dev.docker-compose.yml @@ -20,8 +20,6 @@ services: depends_on: - mysql - redis - ports: - - 5000:5000 volumes: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: From f749cff1711b3fb06e4fd9afbf18ba034e7eb241 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 15:03:26 +0900 Subject: [PATCH 39/74] minorfix --- node/nginx/nginx.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/nginx/nginx.conf b/node/nginx/nginx.conf index 1ed60e90..8898ed4b 100644 --- a/node/nginx/nginx.conf +++ b/node/nginx/nginx.conf @@ -54,7 +54,7 @@ http { # return 301 https://umaka-viewer.dbcls.jp$request_uri; # } - if ($host != "umaka-viewer.dbcls.jp") { + if ($host == "v.umaka.dbcls.jp") { return 301 https://umaka-viewer.dbcls.jp$request_uri; } From 5ff49e94afc87f2581295143de12dc62e1297482 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 15:20:27 +0900 Subject: [PATCH 40/74] minorfix --- node/nginx/nginx.conf | 6 ------ 1 file changed, 6 deletions(-) diff --git a/node/nginx/nginx.conf b/node/nginx/nginx.conf index 8898ed4b..4bd8a802 100644 --- a/node/nginx/nginx.conf +++ b/node/nginx/nginx.conf @@ -43,8 +43,6 @@ http { server { listen 80; - server_name v.umaka.dbcls.jp; - server_name umaka-viewer.dbcls.jp; server_name umaka-viewer-dev.dbcls.jp; client_max_body_size 2g; root /public; @@ -54,10 +52,6 @@ http { # return 301 https://umaka-viewer.dbcls.jp$request_uri; # } - if ($host == "v.umaka.dbcls.jp") { - return 301 https://umaka-viewer.dbcls.jp$request_uri; - } - location /api { proxy_pass http://api:5000; # include /home/umaka_v/local/nginx/uwsgi_params; From 3c5c2725357c1ee962a033b7708bee10784d9d29 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 15:24:21 +0900 Subject: [PATCH 41/74] minorfix --- node/nginx/nginx.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/nginx/nginx.conf b/node/nginx/nginx.conf index 4bd8a802..50b25196 100644 --- a/node/nginx/nginx.conf +++ b/node/nginx/nginx.conf @@ -56,7 +56,7 @@ http { proxy_pass http://api:5000; # include /home/umaka_v/local/nginx/uwsgi_params; # uwsgi_pass unix:/opt/services/umaka_v/uwsgi/uwsgi.sock; - proxy_set_header host "umaka-viewer.dbcls.jp"; + proxy_set_header host "umaka-viewer-dev.dbcls.jp"; } location /static { From 66e1a8edff213f34b35857730ab0ccd133e49bfb Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 22 Dec 2021 20:05:37 +0900 Subject: [PATCH 42/74] =?UTF-8?q?fix:=20API=E3=81=AE=E3=82=A8=E3=83=B3?= =?UTF-8?q?=E3=83=89=E3=83=9D=E3=82=A4=E3=83=B3=E3=83=88=E3=82=92location.?= =?UTF-8?q?origin=E3=81=8B=E3=82=89=E5=8F=96=E5=BE=97=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/ApiClient.ts | 5 ++--- node/src/ts/utils.ts | 7 +++++++ node/webpack.config.js | 5 ----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/node/src/ts/ApiClient.ts b/node/src/ts/ApiClient.ts index 6e4ba46f..99c38e5c 100644 --- a/node/src/ts/ApiClient.ts +++ b/node/src/ts/ApiClient.ts @@ -4,8 +4,7 @@ import firebase from 'firebase' import { Url, PUBLIC_DATA_SET_SIZE_PER_PAGE } from './constants' import { PublicDataSetParams } from './actions/data-set-list' import { DataSetParams } from './actions/admin' - -declare const API_ENDPOINT: string +import { getApiEndpoint } from './utils' class ApiClient { user: firebase.User | null @@ -14,7 +13,7 @@ class ApiClient { constructor() { this.api = axios.create({ - baseURL: API_ENDPOINT, + baseURL: getApiEndpoint(), }) } diff --git a/node/src/ts/utils.ts b/node/src/ts/utils.ts index 0eb99c34..cd6a1651 100644 --- a/node/src/ts/utils.ts +++ b/node/src/ts/utils.ts @@ -23,4 +23,11 @@ export const getWrapperClassName = () => { } } +export const getApiEndpoint = () => { + const { origin } = window.location + const port = origin.endsWith('localhost') ? ':5000' : '' + const pathname = '/api/v1' + return `${origin}${port}${pathname}` +} + export default getWrapperClassName diff --git a/node/webpack.config.js b/node/webpack.config.js index 24a45b5b..374d6249 100644 --- a/node/webpack.config.js +++ b/node/webpack.config.js @@ -10,11 +10,9 @@ const INDEX_FILE = 'html/index.html' module.exports = (env, args) => { - let API_ENDPOINT let FIREBASE_CONFIG switch (args.mode) { case 'development': - API_ENDPOINT = 'http://127.0.0.1:5000/api/v1' FIREBASE_CONFIG = { apiKey: "AIzaSyB3GMmRd9JWGVvDlEtgpemtYZPo-WRkNpc", authDomain: "fabled-alchemy-246207.firebaseapp.com", @@ -26,7 +24,6 @@ module.exports = (env, args) => { } break case 'production': - API_ENDPOINT = 'https://umaka-viewer.dbcls.jp/api/v1' FIREBASE_CONFIG = { apiKey: "AIzaSyBNTb8DHaHbx32oifMkM_zKTGL4oI1QUNY", authDomain: "dbcls-ead06.firebaseapp.com", @@ -38,7 +35,6 @@ module.exports = (env, args) => { } break default: - API_ENDPOINT = 'http://127.0.0.1:5000/api/v1' FIREBASE_CONFIG = { apiKey: "", authDomain: "", @@ -132,7 +128,6 @@ module.exports = (env, args) => { ignoreOrder: false, }), new webpack.DefinePlugin({ - API_ENDPOINT: JSON.stringify(API_ENDPOINT), FIREBASE_CONFIG: JSON.stringify(FIREBASE_CONFIG), }), ], From 3820feba2e5bd9d7f82da2349632597c390e4eff Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Fri, 24 Dec 2021 12:12:16 +0900 Subject: [PATCH 43/74] =?UTF-8?q?fix:=20=E6=A4=9C=E7=B4=A2=E3=81=A8?= =?UTF-8?q?=E5=B7=A6=E3=83=9A=E3=82=A4=E3=83=B3=E3=81=AE=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E3=81=A7=E5=89=8D=E3=81=AE=E7=8A=B6=E6=85=8B=E3=82=92=E3=82=AF?= =?UTF-8?q?=E3=83=AA=E3=82=A2=E3=81=A7=E3=81=8D=E3=81=A6=E3=81=84=E3=81=AA?= =?UTF-8?q?=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/components/ClassStructure.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 8c4befa3..2a6ac46f 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -600,6 +600,7 @@ const ClassStructure: React.FC = (props) => { return } if (domain || range) { + focus(0) const subject = GraphRepository.findUriNode(domain) const object = GraphRepository.findUriNode(range) GraphRepository.targetKey = subject ? subject.data.key : null @@ -630,6 +631,7 @@ const ClassStructure: React.FC = (props) => { } } if (searchingURI) { + focus(0) const matchedNodes = search(searchingURI) if (matchedNodes.length === 1) { dispatch( From b1e87a236a3bc75f732c183277ccd748cea59f05 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Tue, 28 Dec 2021 11:19:43 +0900 Subject: [PATCH 44/74] =?UTF-8?q?fix:=20SPARQL=E3=82=A8=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=82=BF=E3=81=B8=E3=81=AE=E9=81=B7=E7=A7=BB=E3=82=92=E3=83=80?= =?UTF-8?q?=E3=83=96=E3=83=AB=E3=82=AF=E3=83=AA=E3=83=83=E3=82=AF=20->=20?= =?UTF-8?q?=E5=8F=B3=E3=82=AF=E3=83=AA=E3=83=83=E3=82=AF=E3=81=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 43 ++++++++++++------- .../ts/visualizer/hooks/useDblClickHandler.ts | 35 --------------- .../ts/visualizer/utils/GraphRepository.ts | 31 ++++++++----- 3 files changed, 47 insertions(+), 62 deletions(-) delete mode 100644 node/src/ts/visualizer/hooks/useDblClickHandler.ts diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 2a6ac46f..101f796d 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -16,7 +16,6 @@ import { getChildrenRecursive } from '../utils/node' import { ClassNames } from '../constants/ClassStructure' import { TooltipAction } from '../actions/tooltip' import { Metadata } from '../types/metadata' -import useDblClickHandler from '../hooks/useDblClickHandler' function decideNormalClass( d: NodeType, @@ -140,7 +139,7 @@ const ClassStructure: React.FC = (props) => { dispatch(DetailAction.showTree()) }, [dispatch]) - const handleSingleClickClass = React.useCallback( + const handleClickClass = React.useCallback( (event?: React.MouseEvent, d?: NodeType) => { if (!d || !event || event.defaultPrevented) { return @@ -150,8 +149,12 @@ const ClassStructure: React.FC = (props) => { }, [dispatch] ) - const handleDoubleClickClass = React.useCallback( + + const handleRightClickClass = React.useCallback( (event?: React.MouseEvent, d?: NodeType) => { + // コンテキストメニューは表示しない + event?.preventDefault() + const refUri = d ? getReferenceURL(d.data.uri) : '' if (!refUri || !event) { return @@ -176,10 +179,6 @@ const ClassStructure: React.FC = (props) => { }, [metadata?.endpoint] ) - const [handleMouseDownClass] = useDblClickHandler( - handleDoubleClickClass, - handleSingleClickClass - ) const handleShowTooltip: SVGEventHandlerType = React.useCallback( (event?: React.MouseEvent, d?: NodeType) => { @@ -347,7 +346,7 @@ const ClassStructure: React.FC = (props) => { GraphRepository.removePopup() } - const arrowDblClick = ( + const arrowRightClick = ( event?: React.MouseEvent, d?: NodeType ) => { @@ -396,7 +395,7 @@ const ClassStructure: React.FC = (props) => { GraphRepository.addArrowLineEvent( arrowMouseover, arrowMouseout, - arrowDblClick + arrowRightClick ) const selfPath: NodeType[] = isOneself ? [target] : [] @@ -404,12 +403,16 @@ const ClassStructure: React.FC = (props) => { selfPath, arrowMouseover, arrowMouseout, - arrowDblClick + arrowRightClick ) GraphRepository.avoidColidedLabel() - GraphRepository.showNodes(visibleNodes, handleMouseDownClass) + GraphRepository.showNodes( + visibleNodes, + handleClickClass, + handleRightClickClass + ) const decideClass = (d: NodeType) => { if (_.includes(both, d.data.uri)) { @@ -440,7 +443,7 @@ const ClassStructure: React.FC = (props) => { return [target].concat(rhsNodes, lhsNodes, bothNodes) }, - [classes, handleMouseDownClass, handleClickTreeImg, intl] + [classes, handleClickClass, handleClickTreeImg, intl] ) const showCircles = React.useCallback( @@ -487,7 +490,11 @@ const ClassStructure: React.FC = (props) => { const visibleNodesSet = getNodeSet(visibleNodes) GraphRepository.visibleNodesSet = visibleNodesSet - GraphRepository.showNodes(visibleNodes, handleMouseDownClass) + GraphRepository.showNodes( + visibleNodes, + handleClickClass, + handleRightClickClass + ) GraphRepository.avoidColidedLabel() let decideClass @@ -527,7 +534,7 @@ const ClassStructure: React.FC = (props) => { GraphRepository.addClass(visibleNodes, decideClass) return _.union(domainNodes, rangeNodes, focusRootNodes) }, - [handleMouseDownClass] + [handleClickClass] ) const search = React.useCallback( @@ -543,7 +550,11 @@ const ClassStructure: React.FC = (props) => { const visibleNodesSet = getNodeSet(visibleNodes) GraphRepository.visibleNodesSet = visibleNodesSet - GraphRepository.showNodes(visibleNodes, handleMouseDownClass) + GraphRepository.showNodes( + visibleNodes, + handleClickClass, + handleRightClickClass + ) GraphRepository.avoidColidedLabel() const decideClass = (d: NodeType) => { @@ -556,7 +567,7 @@ const ClassStructure: React.FC = (props) => { GraphRepository.addClass(visibleNodes, decideClass) return matchedNodes }, - [handleMouseDownClass] + [handleClickClass] ) const detail = useSelector(selector) diff --git a/node/src/ts/visualizer/hooks/useDblClickHandler.ts b/node/src/ts/visualizer/hooks/useDblClickHandler.ts deleted file mode 100644 index e46b0bb7..00000000 --- a/node/src/ts/visualizer/hooks/useDblClickHandler.ts +++ /dev/null @@ -1,35 +0,0 @@ -import React, { useCallback, useRef } from 'react' -import { NodeType, SVGEventHandlerType } from '../utils/GraphRepository' - -type TimeoutId = ReturnType - -const useDblClickHandler = ( - doubleClickHandler: SVGEventHandlerType, - singleClickHandler?: SVGEventHandlerType, - duration: number = 150 -) => { - const timeoutRef = useRef(undefined) - const handleMouseDown: SVGEventHandlerType = useCallback( - (e?: React.MouseEvent, d?: NodeType) => { - const clicked = timeoutRef.current !== undefined - if (clicked) { - e?.preventDefault() - clearTimeout(timeoutRef.current!) - - doubleClickHandler(e, d) - timeoutRef.current = undefined - } - - const clickEventId = setTimeout(() => { - singleClickHandler?.(e, d) - timeoutRef.current = undefined - }, duration) - - timeoutRef.current = clickEventId - }, - [doubleClickHandler, singleClickHandler] - ) - return [handleMouseDown] -} - -export default useDblClickHandler diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index 1370ba6c..651aeda1 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -494,7 +494,7 @@ class GraphRepository { nodes: NodeType[], handleMouseOver: SVGEventHandlerType = () => {}, handleMouseOut: SVGEventHandlerType = () => {}, - handleDblClick: SVGEventHandlerType = () => {} + handleRightClick: SVGEventHandlerType = () => {} ) { const selfLines = this.paths.self?.data(nodes, nodeKeyFn) selfLines @@ -504,7 +504,7 @@ class GraphRepository { .attr('marker-end', 'url(#arrow-head)') .on('mouseover', handleMouseOver) .on('mouseout', handleMouseOut) - .on('dblclick', handleDblClick) + .on('contextmenu', handleRightClick) selfLines?.exit().remove() } @@ -804,13 +804,13 @@ class GraphRepository { addArrowLineEvent( handleMouseOver: SVGEventHandlerType, handleMouseOut: SVGEventHandlerType, - handleDblClick: SVGEventHandlerType + handleRightClick: SVGEventHandlerType ) { this.svg ?.selectAll('.arrow-line-base') .on('mouseover', handleMouseOver) .on('mouseout', handleMouseOut) - .on('dblclick', handleDblClick) + .on('contextmenu', handleRightClick) } avoidColidedLabel() { @@ -849,23 +849,32 @@ class GraphRepository { }) } - showNodes(nodes: NodeType[], handleMouseDownClass: SVGEventHandlerType) { - this.showCircleNodes(nodes, handleMouseDownClass) + showNodes( + nodes: NodeType[], + handleClickClass: SVGEventHandlerType, + handleRightClickClass: SVGEventHandlerType + ) { + this.showCircleNodes(nodes, handleClickClass, handleRightClickClass) this.updateScale() - this.showTextNodes(nodes, handleMouseDownClass) + this.showTextNodes(nodes, handleClickClass) this.updatePosition() } showCircleNodes( nodes: NodeType[], - handleMouseDownClass: SVGEventHandlerType + handleClickClass: SVGEventHandlerType, + handleRightClickClass: SVGEventHandlerType ) { const circles = this.circles?.data(nodes, nodeKeyFn) - circles?.enter().append('svg:circle').on('mousedown', handleMouseDownClass) + circles + ?.enter() + .append('svg:circle') + .on('click', handleClickClass) + .on('contextmenu', handleRightClickClass) circles?.exit().remove() } - showTextNodes(nodes: NodeType[], handleMouseDownClass: SVGEventHandlerType) { + showTextNodes(nodes: NodeType[], handleClickClass: SVGEventHandlerType) { const { classes, locale } = this const gtexts = this.gtexts?.data( @@ -879,7 +888,7 @@ class GraphRepository { .attr('transform', (d) => `translate(${this.x(d.x)}, ${this.textY(d)})`) const texts = textAndButton ?.append('svg:text') - .on('mousedown', handleMouseDownClass) + .on('click', handleClickClass) .attr('y', (d) => (d.data.isLabelOnTop && isIE11 ? '1em' : 0)) // IE11はdominant-baselineをサポートしない From 338e63ccf1ddc5900f0eb68017e8cb9e7806ca48 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Tue, 28 Dec 2021 15:48:14 +0900 Subject: [PATCH 45/74] =?UTF-8?q?refactor:=20=E3=83=97=E3=83=AD=E3=83=91?= =?UTF-8?q?=E3=83=86=E3=82=A3=E4=B8=80=E8=A6=A7=E3=81=AE=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E7=9F=A2=E5=8D=B0=E3=81=AE?= =?UTF-8?q?=E6=8F=8F=E7=94=BB=E5=87=A6=E7=90=86=E3=82=92=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 101f796d..881bf588 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -479,6 +479,29 @@ const ClassStructure: React.FC = (props) => { const rangeNodes = range ? GraphRepository.nodes.filter((d) => d.data.uri === range) : [] + + const sbj = domainNodes.length > 0 ? domainNodes[0] : null + GraphRepository.targetKey = sbj?.data?.key ?? null + + const domainClassDetail = GraphRepository.classes[domain || ''] + const rangeClassDetail = GraphRepository.classes[range || ''] + const hasNoMultipleInheritance = (classDetail: ClassDetail) => + classDetail && + (!classDetail.subClassOf || + (!!classDetail.subClassOf && classDetail.subClassOf.length === 1)) // 親がいるなら多重継承でないものに限る + const canDrawTriple = + hasNoMultipleInheritance(domainClassDetail) && + hasNoMultipleInheritance(rangeClassDetail) + + const obj = rangeNodes.length > 0 ? rangeNodes[0] : null + if (domain && range && obj !== null && canDrawTriple) { + if (domain !== range) { + GraphRepository.updateRightLines([obj]) + } else { + GraphRepository.updateSelfLines([obj]) + } + } + const focusRootNodes = _.union( getVisibleNodes(domainNodes), getVisibleNodes(rangeNodes) @@ -612,29 +635,6 @@ const ClassStructure: React.FC = (props) => { } if (domain || range) { focus(0) - const subject = GraphRepository.findUriNode(domain) - const object = GraphRepository.findUriNode(range) - GraphRepository.targetKey = subject ? subject.data.key : null - - const domainClassDetail = classes[domain || ''] - const rangeClassDetail = classes[range || ''] - - const hasNoMultipleInheritance = (classDetail: ClassDetail) => - classDetail && - (!classDetail.subClassOf || - (!!classDetail.subClassOf && classDetail.subClassOf.length === 1)) // 親がいるなら多重継承でないものに限る - const canDrawTriple = - hasNoMultipleInheritance(domainClassDetail) && - hasNoMultipleInheritance(rangeClassDetail) - - if (domain && range && object !== undefined && canDrawTriple) { - if (domain !== range) { - GraphRepository.updateRightLines([object]) - } else { - GraphRepository.updateSelfLines([object]) - } - } - const targetNodes = showPropertyClass(domain, range) if (targetNodes.length > 0) { showCircles(targetNodes, animate, true, true) From eacae02b83bfd846eda07a481742695cbe8fc00e Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Tue, 28 Dec 2021 15:21:55 +0900 Subject: [PATCH 46/74] =?UTF-8?q?fix:=20=E3=83=97=E3=83=AD=E3=83=91?= =?UTF-8?q?=E3=83=86=E3=82=A3=E4=B8=80=E8=A6=A7=E3=81=AE=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AB=E8=87=AA=E8=BA=AB=E3=81=AE?= =?UTF-8?q?URI=E3=82=92=E5=90=AB=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/actions/detail.ts | 7 +++++-- .../src/ts/visualizer/components/ClassRelations.tsx | 7 ++++++- node/src/ts/visualizer/components/Property.tsx | 6 +++++- node/src/ts/visualizer/reducers/detail.ts | 13 ++++++------- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/node/src/ts/visualizer/actions/detail.ts b/node/src/ts/visualizer/actions/detail.ts index 95158151..aecd6dcc 100644 --- a/node/src/ts/visualizer/actions/detail.ts +++ b/node/src/ts/visualizer/actions/detail.ts @@ -9,8 +9,11 @@ export const DetailAction = { createAction(types.FOCUS_CIRCLE_KEY, { key, uri }), focusPropertyClass: (key: number | null, uri: string | null = null) => createAction(types.FOCUS_PROPERTY_CLASS, { key, uri }), - showPropertyClass: (domain: string | null, range: string | null) => - createAction(types.SHOW_PROPERTY_CLASS, { domain, range }), + showPropertyClass: ( + uri: string | null, + domain: string | null, + range: string | null + ) => createAction(types.SHOW_PROPERTY_CLASS, { uri, domain, range }), showAllAssociatedClasses: () => createAction(types.SHOW_ALL_ASSOCIATED), showRightHandSideClasses: () => createAction(types.SHOW_RIGHT_HAND_SIDE), showLeftHandSideClasses: () => createAction(types.SHOW_LEFT_HAND_SIDE), diff --git a/node/src/ts/visualizer/components/ClassRelations.tsx b/node/src/ts/visualizer/components/ClassRelations.tsx index 0bc5f57b..62b17b12 100644 --- a/node/src/ts/visualizer/components/ClassRelations.tsx +++ b/node/src/ts/visualizer/components/ClassRelations.tsx @@ -10,6 +10,7 @@ type ClassRelationProps = { relation: ClassRelationType selected: boolean indexes: [number, number] + propertyClass?: string } const ClassRelation: React.FC = (props) => { @@ -17,6 +18,7 @@ const ClassRelation: React.FC = (props) => { relation: { subject_class, object_class, object_datatype, triples }, selected, indexes: [index1, index2], + propertyClass, } = props const dispatch = useDispatch() @@ -25,6 +27,7 @@ const ClassRelation: React.FC = (props) => { e.stopPropagation() dispatch( DetailAction.showPropertyClass( + propertyClass ?? null, subject_class, object_class || object_datatype ) @@ -75,6 +78,7 @@ ClassRelation.displayName = 'ClassRelation' type ClassRelationsProps = { classRelations: ClassRelationType[] index: number + propertyClass?: string } const selector = ({ property: { showPropertyClassIndex } }: RootState) => ({ @@ -82,7 +86,7 @@ const selector = ({ property: { showPropertyClassIndex } }: RootState) => ({ }) const ClassRelations: React.FC = (props) => { - const { classRelations, index } = props + const { classRelations, index, propertyClass } = props const { showPropertyClassIndex: [selectedIndex1, selectedIndex2], } = useSelector(selector) @@ -95,6 +99,7 @@ const ClassRelations: React.FC = (props) => { relation={relation} indexes={[index, key]} selected={selectedIndex1 === index && selectedIndex2 === key} + propertyClass={propertyClass} /> ))} diff --git a/node/src/ts/visualizer/components/Property.tsx b/node/src/ts/visualizer/components/Property.tsx index 1e8bd47e..f5fd5ba6 100644 --- a/node/src/ts/visualizer/components/Property.tsx +++ b/node/src/ts/visualizer/components/Property.tsx @@ -51,7 +51,11 @@ const Property: React.FC = (props) => { {triples} {class_relations.length > 0 && } {isOpen && ( - + )} ) diff --git a/node/src/ts/visualizer/reducers/detail.ts b/node/src/ts/visualizer/reducers/detail.ts index 4aa8a3ec..918158b5 100644 --- a/node/src/ts/visualizer/reducers/detail.ts +++ b/node/src/ts/visualizer/reducers/detail.ts @@ -6,6 +6,7 @@ export interface DetailState { focusingCircleKey: number | null showParentClassesURI: string | null propertyClass: { + uri: string | null domain: string | null range: string | null } @@ -21,7 +22,7 @@ const initialState: DetailState = { focusingURI: null, focusingCircleKey: null, showParentClassesURI: null, - propertyClass: { domain: null, range: null }, + propertyClass: { uri: null, domain: null, range: null }, showRightHand: false, showLeftHand: false, showingRelation: null, @@ -40,7 +41,7 @@ export default function detail( ...state, focusingURI: action.payload.uri, showParentClassesURI: action.payload.uri, - propertyClass: { domain: null, range: null }, + propertyClass: { uri: null, domain: null, range: null }, searchingURI: null, } case types.FOCUS_CIRCLE_KEY: @@ -48,7 +49,7 @@ export default function detail( ...state, focusingCircleKey: action.payload.key, focusingURI: action.payload.uri, - propertyClass: { domain: null, range: null }, + propertyClass: { uri: null, domain: null, range: null }, showRightHand: false, showLeftHand: false, showingRelation: null, @@ -68,6 +69,7 @@ export default function detail( showRightHand: false, showLeftHand: false, propertyClass: { + uri: action.payload.uri, domain: action.payload.domain, range: action.payload.range, }, @@ -182,10 +184,7 @@ export default function detail( focusingURI: null, searchingURI: action.payload.uri, showTree: false, - propertyClass: { - domain: null, - range: null, - }, + propertyClass: { uri: null, domain: null, range: null }, } default: return state From 00918651c11f5f1db2d82c7f3fed30330bc74d80 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Tue, 28 Dec 2021 16:19:48 +0900 Subject: [PATCH 47/74] =?UTF-8?q?feat:=20=E3=83=97=E3=83=AD=E3=83=91?= =?UTF-8?q?=E3=83=86=E3=82=A3=E4=B8=80=E8=A6=A7=E3=81=8B=E3=82=89=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=97=E3=81=9F=E7=9F=A2=E5=8D=B0=E3=81=AE=E3=82=AF?= =?UTF-8?q?=E3=83=A9=E3=82=B9=E9=96=A2=E4=BF=82=E3=82=92yasgui=E3=81=A7?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 63 +++++++++++++++++-- .../ts/visualizer/utils/GraphRepository.ts | 6 +- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 881bf588..507f1b69 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -472,7 +472,7 @@ const ClassStructure: React.FC = (props) => { ) const showPropertyClass = React.useCallback( - (domain: string | null, range: string | null) => { + (uri: string | null, domain: string | null, range: string | null) => { const domainNodes = domain ? GraphRepository.nodes.filter((d) => d.data.uri === domain) : [] @@ -493,12 +493,65 @@ const ClassStructure: React.FC = (props) => { hasNoMultipleInheritance(domainClassDetail) && hasNoMultipleInheritance(rangeClassDetail) + const arrowRightClick = ( + event?: React.MouseEvent, + d?: NodeType + ) => { + if (!event || !d) return + + event.preventDefault() + + const propertyUri = getReferenceURL(uri)! + const domainUri = getReferenceURL(domain)! + const rangeUri = getReferenceURL(range)! + + const targetElement = event.currentTarget + const pathClass = targetElement?.getAttribute('class') + const isLeftLine = pathClass?.includes('left-line') + const isSelfLine = pathClass?.includes('self-line') + + const createTripe = (): [string, string[], string] => { + if (isLeftLine) { + return [domainUri, [propertyUri], rangeUri] + } + if (isSelfLine) { + return [domainUri, [propertyUri], rangeUri] + } + return [domainUri, [propertyUri], rangeUri] + } + + const [s, p, o] = createTripe() + const query = ` + SELECT ?sbj ?obj + WHERE { + ?sbj ${p.map((v) => `<${v}>`).join('|')} ?obj . + ?sbj a <${s}> . + ?obj a <${o}> . + } + LIMIT 20 + `.replace(/^\n|\s+$|^ {10}/gm, '') + + const params = new URLSearchParams() + params.append('endpoint', metadata?.endpoint ?? '') + params.append('query', query) + window.open( + `/yasgui?${params.toString()}`, + '_brank', + 'noopener,noreferrer' + ) + } + const obj = rangeNodes.length > 0 ? rangeNodes[0] : null if (domain && range && obj !== null && canDrawTriple) { if (domain !== range) { - GraphRepository.updateRightLines([obj]) + GraphRepository.updateRightLines([obj], arrowRightClick) } else { - GraphRepository.updateSelfLines([obj]) + GraphRepository.updateSelfLines( + [obj], + undefined, + undefined, + arrowRightClick + ) } } @@ -604,7 +657,7 @@ const ClassStructure: React.FC = (props) => { showLeftHand, showingRelation, searchingURI, - propertyClass: { domain, range }, + propertyClass: { domain, range, uri: propertyUri }, } = detailState GraphRepository.targetKey = focusingCircleKey @@ -635,7 +688,7 @@ const ClassStructure: React.FC = (props) => { } if (domain || range) { focus(0) - const targetNodes = showPropertyClass(domain, range) + const targetNodes = showPropertyClass(propertyUri, domain, range) if (targetNodes.length > 0) { showCircles(targetNodes, animate, true, true) return diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index 651aeda1..0980fb6a 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -480,13 +480,17 @@ class GraphRepository { bothLines?.exit().remove() } - updateRightLines(nodes: NodeType[]) { + updateRightLines( + nodes: NodeType[], + handleRightClick: SVGEventHandlerType = () => {} + ) { const rightLines = this.paths.rightHand?.data(nodes, nodeKeyFn) rightLines ?.enter() .append('path') .attr('class', 'arrow-line-base right-hand-line') .attr('marker-end', 'url(#arrow-head)') + .on('contextmenu', handleRightClick) rightLines?.exit().remove() } From 885c67df6aa191d9584216fd466f4917e01da862 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Tue, 28 Dec 2021 18:22:00 +0900 Subject: [PATCH 48/74] =?UTF-8?q?refactor:=20sparql,=20yasgui=E5=91=A8?= =?UTF-8?q?=E3=82=8A=E3=81=AE=E3=82=B3=E3=83=BC=E3=83=89=E3=82=92=E6=95=B4?= =?UTF-8?q?=E7=90=86=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../visualizer/components/ClassStructure.tsx | 109 +++++------------- node/src/ts/visualizer/utils/sparql.ts | 36 ++++++ 2 files changed, 68 insertions(+), 77 deletions(-) create mode 100644 node/src/ts/visualizer/utils/sparql.ts diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 507f1b69..48c99ab9 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -16,6 +16,11 @@ import { getChildrenRecursive } from '../utils/node' import { ClassNames } from '../constants/ClassStructure' import { TooltipAction } from '../actions/tooltip' import { Metadata } from '../types/metadata' +import { + makeQueryWhenRightClickArrow, + makeQueryWhenRightClickClass, + navigateToYasgui, +} from '../utils/sparql' function decideNormalClass( d: NodeType, @@ -152,30 +157,19 @@ const ClassStructure: React.FC = (props) => { const handleRightClickClass = React.useCallback( (event?: React.MouseEvent, d?: NodeType) => { + if (!d || !event) { + return + } + // コンテキストメニューは表示しない event?.preventDefault() - const refUri = d ? getReferenceURL(d.data.uri) : '' - if (!refUri || !event) { - return + const refUri = getReferenceURL(d?.data.uri ?? null) + if (refUri) { + const endpoint = metadata?.endpoint ?? '' + const query = makeQueryWhenRightClickClass(refUri) + navigateToYasgui(endpoint, query) } - - const query = ` - SELECT ?i - WHERE { - ?i a <${refUri}> . - } - LIMIT 20 - `.replace(/^\n|\s+$|^ {8}/gm, '') - - const params = new URLSearchParams() - params.append('endpoint', metadata?.endpoint ?? '') - params.append('query', query) - window.open( - `/yasgui?${params.toString()}`, - '_brank', - 'noopener,noreferrer' - ) }, [metadata?.endpoint] ) @@ -352,44 +346,27 @@ const ClassStructure: React.FC = (props) => { ) => { if (!event || !d) return + // コンテキストメニューは表示しない + event?.preventDefault() + const focusingUri = getReferenceURL(target.data.uri)! const targetUri = getReferenceURL(d.data.uri)! const predicateUris = getPredicates(d).map((p) => getReferenceURL(p)!) - const targetElement = event.currentTarget - const pathClass = targetElement?.getAttribute('class') - const isLeftLine = pathClass?.includes('left-line') - const isSelfLine = pathClass?.includes('self-line') - - const createTripe = (): [string, string[], string] => { - if (isLeftLine) { + const makeTriple = (): [string, string[], string] => { + const pathTypes = event.currentTarget?.classList + if (pathTypes?.contains('left-line')) { return [targetUri, predicateUris, focusingUri] } - if (isSelfLine) { + if (pathTypes?.contains('self-line')) { return [targetUri, predicateUris, targetUri] } return [focusingUri, predicateUris, targetUri] } - const [sbj, prds, obj] = createTripe() - const query = ` - SELECT ?sbj ?obj - WHERE { - ?sbj ${prds.map((p) => `<${p}>`).join('|')} ?obj . - ?sbj a <${sbj}> . - ?obj a <${obj}> . - } - LIMIT 20 - `.replace(/^\n|\s+$|^ {10}/gm, '') - - const params = new URLSearchParams() - params.append('endpoint', metadata?.endpoint ?? '') - params.append('query', query) - window.open( - `/yasgui?${params.toString()}`, - '_brank', - 'noopener,noreferrer' - ) + const endpoint = metadata?.endpoint ?? '' + const query = makeQueryWhenRightClickArrow(...makeTriple()) + navigateToYasgui(endpoint, query) } GraphRepository.addArrowLineEvent( @@ -499,46 +476,24 @@ const ClassStructure: React.FC = (props) => { ) => { if (!event || !d) return + // コンテキストメニューは表示しない event.preventDefault() const propertyUri = getReferenceURL(uri)! const domainUri = getReferenceURL(domain)! const rangeUri = getReferenceURL(range)! - const targetElement = event.currentTarget - const pathClass = targetElement?.getAttribute('class') - const isLeftLine = pathClass?.includes('left-line') - const isSelfLine = pathClass?.includes('self-line') - - const createTripe = (): [string, string[], string] => { - if (isLeftLine) { - return [domainUri, [propertyUri], rangeUri] - } - if (isSelfLine) { - return [domainUri, [propertyUri], rangeUri] + const makeTriple = (): [string, string[], string] => { + const pathTypes = event.currentTarget?.classList + if (pathTypes?.contains('self-line')) { + return [domainUri, [propertyUri], domainUri] } return [domainUri, [propertyUri], rangeUri] } - const [s, p, o] = createTripe() - const query = ` - SELECT ?sbj ?obj - WHERE { - ?sbj ${p.map((v) => `<${v}>`).join('|')} ?obj . - ?sbj a <${s}> . - ?obj a <${o}> . - } - LIMIT 20 - `.replace(/^\n|\s+$|^ {10}/gm, '') - - const params = new URLSearchParams() - params.append('endpoint', metadata?.endpoint ?? '') - params.append('query', query) - window.open( - `/yasgui?${params.toString()}`, - '_brank', - 'noopener,noreferrer' - ) + const endpoint = metadata?.endpoint ?? '' + const query = makeQueryWhenRightClickArrow(...makeTriple()) + navigateToYasgui(endpoint, query) } const obj = rangeNodes.length > 0 ? rangeNodes[0] : null diff --git a/node/src/ts/visualizer/utils/sparql.ts b/node/src/ts/visualizer/utils/sparql.ts new file mode 100644 index 00000000..4f5f0418 --- /dev/null +++ b/node/src/ts/visualizer/utils/sparql.ts @@ -0,0 +1,36 @@ +export const makeQueryWhenRightClickClass = (_class: string) => { + const query = ` + SELECT ?i + WHERE { + ?i a <${_class}> . + } + LIMIT 20 + `.replace(/^\n|\s+$|^ {4}/gm, '') + return query +} + +export const makeQueryWhenRightClickArrow = ( + domain: string, + properties: string[], + range: string +) => { + const query = ` + SELECT ?sbj ?obj + WHERE { + ?sbj ${properties.map((v) => `<${v}>`).join('|')} ?obj . + ?sbj a <${domain}> . + ?obj a <${range}> . + } + LIMIT 20 + `.replace(/^\n|\s+$|^ {4}/gm, '') + + return query +} + +export const navigateToYasgui = (endpoint: string, query: string) => { + const params = new URLSearchParams() + params.append('endpoint', endpoint) + params.append('query', query) + + window.open(`/yasgui?${params.toString()}`, '_brank', 'noopener,noreferrer') +} From 9bf3330cfdca7ba7b8f10985896af6adfb6d4e9b Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 5 Jan 2022 11:42:47 +0900 Subject: [PATCH 49/74] =?UTF-8?q?fix:=20=E3=83=95=E3=82=A9=E3=83=BC?= =?UTF-8?q?=E3=82=AB=E3=82=B9=E4=B8=AD=E3=81=AE=E3=82=AF=E3=83=A9=E3=82=B9?= =?UTF-8?q?=E3=81=AB=E7=9F=A2=E5=8D=B0=E3=81=8C=E5=90=91=E3=81=84=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=82=8B=E3=81=A8=E3=81=8D=E3=80=81=E7=94=9F=E6=88=90?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=AF=E3=82=A8=E3=83=AA=E3=81=AE=E4=B8=BB?= =?UTF-8?q?=E8=AA=9E=E3=81=A8=E7=9B=AE=E7=9A=84=E8=AA=9E=E3=81=8C=E9=80=86?= =?UTF-8?q?=E3=81=AB=E3=81=AA=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/components/ClassStructure.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 48c99ab9..19bb1c58 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -355,7 +355,7 @@ const ClassStructure: React.FC = (props) => { const makeTriple = (): [string, string[], string] => { const pathTypes = event.currentTarget?.classList - if (pathTypes?.contains('left-line')) { + if (pathTypes?.contains('left-hand-line')) { return [targetUri, predicateUris, focusingUri] } if (pathTypes?.contains('self-line')) { From 816a1990f59d939981f860ceafa4d82df4315305 Mon Sep 17 00:00:00 2001 From: Riku Yamauchi Date: Wed, 5 Jan 2022 19:28:59 +0900 Subject: [PATCH 50/74] =?UTF-8?q?fix:=20Safari=E3=81=A7Yasgui=E3=82=92?= =?UTF-8?q?=E9=96=8B=E3=81=93=E3=81=86=E3=81=A8=E3=81=99=E3=82=8B=E3=81=A8?= =?UTF-8?q?=E3=80=81=E3=83=9D=E3=83=83=E3=83=97=E3=82=A2=E3=83=83=E3=83=97?= =?UTF-8?q?=E3=81=A8=E3=81=97=E3=81=A6=E3=83=96=E3=83=AD=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=81=95=E3=82=8C=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/utils/sparql.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/ts/visualizer/utils/sparql.ts b/node/src/ts/visualizer/utils/sparql.ts index 4f5f0418..df398c8f 100644 --- a/node/src/ts/visualizer/utils/sparql.ts +++ b/node/src/ts/visualizer/utils/sparql.ts @@ -32,5 +32,5 @@ export const navigateToYasgui = (endpoint: string, query: string) => { params.append('endpoint', endpoint) params.append('query', query) - window.open(`/yasgui?${params.toString()}`, '_brank', 'noopener,noreferrer') + window.location.href = `/yasgui?${params.toString()}` } From 7ba636c7cee958cfcbf31a6185d7ff1caefe5578 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 13:16:14 +0900 Subject: [PATCH 51/74] for develop --- dev.docker-compose.yml | 32 ++++++++++++++++++++++++++++++++ node/nginx/nginx.conf | 1 + 2 files changed, 33 insertions(+) create mode 100644 dev.docker-compose.yml diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml new file mode 100644 index 00000000..9c34c7da --- /dev/null +++ b/dev.docker-compose.yml @@ -0,0 +1,32 @@ +version: "3" +services: + mysql: + build: + context: mysql + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: 1 + MYSQL_USER: umaka_v + MYSQL_DATABASE: dbcls_production + MYSQL_PASSWORD: glnyUnLiybsaE968Z7 + volumes: + - /opt/services/umaka_v/data:/var/lib/mysql + - /home/umaka_v/restore:/restore + redis: + image: redis:5.0-alpine + api: + build: + context: server + depends_on: + - mysql + - redis + ports: + - 5000:5000 + volumes: + - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json + nginx: + build: + context: node + ports: + - 10080:80 + depends_on: + - api diff --git a/node/nginx/nginx.conf b/node/nginx/nginx.conf index 64fbcf6a..1ed60e90 100644 --- a/node/nginx/nginx.conf +++ b/node/nginx/nginx.conf @@ -45,6 +45,7 @@ http { listen 80; server_name v.umaka.dbcls.jp; server_name umaka-viewer.dbcls.jp; + server_name umaka-viewer-dev.dbcls.jp; client_max_body_size 2g; root /public; From 18ddee7ce5b8553a716e886b5f6ea57e0d1d4356 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 13:28:36 +0900 Subject: [PATCH 52/74] minorfix --- deploy/run-dev.sh | 46 ++++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 2 ++ 2 files changed, 48 insertions(+) create mode 100644 deploy/run-dev.sh diff --git a/deploy/run-dev.sh b/deploy/run-dev.sh new file mode 100644 index 00000000..9e80edd6 --- /dev/null +++ b/deploy/run-dev.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +BASE = $HOME/dev + +update() { + cd $BASE/repos/umakaviewer + git pull origin develop +} + +rebuild() { + cd $BASE/repos/umakaviewer + docker-compose -p umakaviewer build +} + +start() { + cd $BASE/repos/umakaviewer + docker-compose -p umakaviewer up -d +} + +stop() { + cd $BASE/repos/umakaviewer + docker-compose -p umakaviewer down +} + +restart() { + cd $BASE/repos/umakaviewer + docker-compose -p umakaviewer restart +} + +ps() { + cd $BASE/repos/umakaviewer + docker-compose -p umakaviewer ps +} + +main() { + case $1 in + "update" ) update ;; + "rebuild" ) rebuild ;; + "start" ) start ;; + "stop" ) stop ;; + "restart" ) restart ;; + "ps" ) ps ;; + esac +} + +main $1 diff --git a/docker-compose.yml b/docker-compose.yml index 1beaddfc..8a53f2ff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,7 @@ services: redis: image: redis:5.0-alpine api: + image: dev-api build: context: server depends_on: @@ -24,6 +25,7 @@ services: volumes: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: + image: dev-nginx build: context: node ports: From 83dbc904fdd44207613f7e49f448de5befba6f68 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 13:30:40 +0900 Subject: [PATCH 53/74] minorfix --- dev.docker-compose.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index 9c34c7da..f77b9103 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -8,9 +8,6 @@ services: MYSQL_USER: umaka_v MYSQL_DATABASE: dbcls_production MYSQL_PASSWORD: glnyUnLiybsaE968Z7 - volumes: - - /opt/services/umaka_v/data:/var/lib/mysql - - /home/umaka_v/restore:/restore redis: image: redis:5.0-alpine api: From 9c4f03197674640daf3b1c6ff8fff3600b6a9f47 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:01:30 +0900 Subject: [PATCH 54/74] minorfix --- dev.docker-compose.yml | 2 ++ docker-compose.yml | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index f77b9103..7ed34f72 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -11,6 +11,7 @@ services: redis: image: redis:5.0-alpine api: + image: api-dev build: context: server depends_on: @@ -21,6 +22,7 @@ services: volumes: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: + image: nginx-dev build: context: node ports: diff --git a/docker-compose.yml b/docker-compose.yml index 8a53f2ff..1beaddfc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,6 @@ services: redis: image: redis:5.0-alpine api: - image: dev-api build: context: server depends_on: @@ -25,7 +24,6 @@ services: volumes: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: - image: dev-nginx build: context: node ports: From 847ce2d2cd16ef52f632e16f8a145f7e9ddbc895 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:09:08 +0900 Subject: [PATCH 55/74] minorfix --- dev.docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index 7ed34f72..d90a548a 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -1,6 +1,7 @@ version: "3" services: mysql: + image: mysql-dev build: context: mysql environment: From d00f855f42fa71b01938a96bcb0e1ee881b3a6fd Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:14:58 +0900 Subject: [PATCH 56/74] minorfix --- dev.docker-compose.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index d90a548a..41b80b74 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -1,7 +1,7 @@ version: "3" services: mysql: - image: mysql-dev + container_name: umaka-mysql-dev build: context: mysql environment: @@ -10,9 +10,10 @@ services: MYSQL_DATABASE: dbcls_production MYSQL_PASSWORD: glnyUnLiybsaE968Z7 redis: + container_name: umaka-redis-dev image: redis:5.0-alpine api: - image: api-dev + container_name: umaka-api-dev build: context: server depends_on: @@ -23,7 +24,7 @@ services: volumes: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: - image: nginx-dev + container_name: umaka-nginx-dev build: context: node ports: From 6c88523158e271d459c2b6a575f94837af034731 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:16:01 +0900 Subject: [PATCH 57/74] minorfix --- dev.docker-compose.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev.docker-compose.yml b/dev.docker-compose.yml index 41b80b74..a8e6babc 100644 --- a/dev.docker-compose.yml +++ b/dev.docker-compose.yml @@ -1,7 +1,7 @@ version: "3" services: mysql: - container_name: umaka-mysql-dev + container_name: mysql-dev build: context: mysql environment: @@ -14,6 +14,7 @@ services: image: redis:5.0-alpine api: container_name: umaka-api-dev + image: api-dev build: context: server depends_on: @@ -25,6 +26,7 @@ services: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: container_name: umaka-nginx-dev + image: nginx-dev build: context: node ports: From 605a9e6a1b37c0fa7823e4ac1d0958a6227bd63f Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:55:26 +0900 Subject: [PATCH 58/74] minorfix --- dev.docker-compose.yml => dev/dev.docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename dev.docker-compose.yml => dev/dev.docker-compose.yml (95%) diff --git a/dev.docker-compose.yml b/dev/dev.docker-compose.yml similarity index 95% rename from dev.docker-compose.yml rename to dev/dev.docker-compose.yml index a8e6babc..c768202c 100644 --- a/dev.docker-compose.yml +++ b/dev/dev.docker-compose.yml @@ -1,7 +1,7 @@ version: "3" services: mysql: - container_name: mysql-dev + container_name: umaka-mysql-dev build: context: mysql environment: From 9bb24303df49ddd7cad3d2219f45995259587d39 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:57:39 +0900 Subject: [PATCH 59/74] minorfix --- dev/dev.docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/dev.docker-compose.yml b/dev/dev.docker-compose.yml index c768202c..086e631d 100644 --- a/dev/dev.docker-compose.yml +++ b/dev/dev.docker-compose.yml @@ -3,7 +3,7 @@ services: mysql: container_name: umaka-mysql-dev build: - context: mysql + context: ../mysql environment: MYSQL_ALLOW_EMPTY_PASSWORD: 1 MYSQL_USER: umaka_v @@ -16,7 +16,7 @@ services: container_name: umaka-api-dev image: api-dev build: - context: server + context: ../server depends_on: - mysql - redis @@ -28,7 +28,7 @@ services: container_name: umaka-nginx-dev image: nginx-dev build: - context: node + context: ../node ports: - 10080:80 depends_on: From b97bdb00057317ec8d1ad8dd97a7af0dbaea931a Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 14:58:56 +0900 Subject: [PATCH 60/74] minorfix --- dev/dev.docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev/dev.docker-compose.yml b/dev/dev.docker-compose.yml index 086e631d..60dfdf42 100644 --- a/dev/dev.docker-compose.yml +++ b/dev/dev.docker-compose.yml @@ -20,8 +20,6 @@ services: depends_on: - mysql - redis - ports: - - 5000:5000 volumes: - /opt/services/umaka_v/firebase/adminsdk.json:/app/firebase-config.json nginx: From 6882920340f999c1562ee891375feb84d21d5b01 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 15:03:26 +0900 Subject: [PATCH 61/74] minorfix --- node/nginx/nginx.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/nginx/nginx.conf b/node/nginx/nginx.conf index 1ed60e90..8898ed4b 100644 --- a/node/nginx/nginx.conf +++ b/node/nginx/nginx.conf @@ -54,7 +54,7 @@ http { # return 301 https://umaka-viewer.dbcls.jp$request_uri; # } - if ($host != "umaka-viewer.dbcls.jp") { + if ($host == "v.umaka.dbcls.jp") { return 301 https://umaka-viewer.dbcls.jp$request_uri; } From 478a6bd4ad6e665fdec57436a51dfd6a49364826 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 15:20:27 +0900 Subject: [PATCH 62/74] minorfix --- node/nginx/nginx.conf | 6 ------ 1 file changed, 6 deletions(-) diff --git a/node/nginx/nginx.conf b/node/nginx/nginx.conf index 8898ed4b..4bd8a802 100644 --- a/node/nginx/nginx.conf +++ b/node/nginx/nginx.conf @@ -43,8 +43,6 @@ http { server { listen 80; - server_name v.umaka.dbcls.jp; - server_name umaka-viewer.dbcls.jp; server_name umaka-viewer-dev.dbcls.jp; client_max_body_size 2g; root /public; @@ -54,10 +52,6 @@ http { # return 301 https://umaka-viewer.dbcls.jp$request_uri; # } - if ($host == "v.umaka.dbcls.jp") { - return 301 https://umaka-viewer.dbcls.jp$request_uri; - } - location /api { proxy_pass http://api:5000; # include /home/umaka_v/local/nginx/uwsgi_params; From 39d76d6dfcf286642c9a08a37a9e2755720d1ffc Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Wed, 22 Dec 2021 15:24:21 +0900 Subject: [PATCH 63/74] minorfix --- node/nginx/nginx.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/nginx/nginx.conf b/node/nginx/nginx.conf index 4bd8a802..50b25196 100644 --- a/node/nginx/nginx.conf +++ b/node/nginx/nginx.conf @@ -56,7 +56,7 @@ http { proxy_pass http://api:5000; # include /home/umaka_v/local/nginx/uwsgi_params; # uwsgi_pass unix:/opt/services/umaka_v/uwsgi/uwsgi.sock; - proxy_set_header host "umaka-viewer.dbcls.jp"; + proxy_set_header host "umaka-viewer-dev.dbcls.jp"; } location /static { From 2582a15f0db19a5b9cf45068312e61595954cdb9 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Sat, 12 Feb 2022 17:49:41 +0900 Subject: [PATCH 64/74] =?UTF-8?q?=E9=96=8B=E7=99=BA=E7=92=B0=E5=A2=83?= =?UTF-8?q?=E3=81=A7=E3=83=96=E3=83=A9=E3=82=A6=E3=82=B6=E3=81=8B=E3=82=89?= =?UTF-8?q?localhost=E3=81=8B=E3=82=89localhost:5000=E3=81=AB=E7=B9=8B?= =?UTF-8?q?=E3=81=8E=E3=81=AB=E8=A1=8C=E3=81=8F=E3=81=AE=E3=82=92=E3=82=84?= =?UTF-8?q?=E3=82=81=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker/default.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker/default.conf b/docker/default.conf index f6cc8701..3a088a1b 100644 --- a/docker/default.conf +++ b/docker/default.conf @@ -16,6 +16,10 @@ server { try_files $uri /index.html; } + location /api { + proxy_pass http://docker.for.mac.localhost:5000; + } + location /yasgui { try_files $uri /yasgui.html; } From 2a848be966d458fc306df45210ca1bdd1eafaee7 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Sat, 12 Feb 2022 17:57:47 +0900 Subject: [PATCH 65/74] =?UTF-8?q?signup=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=81=AB=E8=A1=8C=E3=81=91=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F?= =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/components/App.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/node/src/ts/components/App.tsx b/node/src/ts/components/App.tsx index 6c389daa..bb4967b8 100644 --- a/node/src/ts/components/App.tsx +++ b/node/src/ts/components/App.tsx @@ -48,6 +48,7 @@ const App = (props: { runRootSaga: () => void }) => { firebaseUser === null && !location.pathname.startsWith(`${Url.VISUALIZER_PREFIX}/`) && location.pathname !== Url.PUBLIC_DATA_SETS && + location.pathname !== Url.SIGN_UP && location.pathname !== Url.LOGIN ) { return From 19e301164f7beb9a33b0cd9bada9213717cefb4f Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Sat, 12 Feb 2022 18:52:12 +0900 Subject: [PATCH 66/74] =?UTF-8?q?=E3=82=B3=E3=83=9F=E3=83=83=E3=83=88?= =?UTF-8?q?=E5=BF=98=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/utils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/node/src/ts/utils.ts b/node/src/ts/utils.ts index cd6a1651..35291a03 100644 --- a/node/src/ts/utils.ts +++ b/node/src/ts/utils.ts @@ -25,9 +25,8 @@ export const getWrapperClassName = () => { export const getApiEndpoint = () => { const { origin } = window.location - const port = origin.endsWith('localhost') ? ':5000' : '' const pathname = '/api/v1' - return `${origin}${port}${pathname}` + return `${origin}${pathname}` } export default getWrapperClassName From 556716323870d8d09f3b7faa145cf736b4508014 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Sat, 12 Feb 2022 19:32:13 +0900 Subject: [PATCH 67/74] =?UTF-8?q?=E3=83=97=E3=83=AD=E3=83=91=E3=83=86?= =?UTF-8?q?=E3=82=A3=E3=83=AA=E3=82=B9=E3=83=88=E3=81=AE=E3=83=87=E3=82=B6?= =?UTF-8?q?=E3=82=A4=E3=83=B3=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/style/style.scss | 22 +++++++++---------- .../ts/visualizer/components/PropertyList.tsx | 5 ----- node/src/ts/visualizer/locales/en.ts | 1 - node/src/ts/visualizer/locales/ja.ts | 1 - 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/node/src/style/style.scss b/node/src/style/style.scss index a7fd2993..cf2a92de 100644 --- a/node/src/style/style.scss +++ b/node/src/style/style.scss @@ -82,12 +82,12 @@ $breadcrumbs-height: 47px; } } -div:after, -ul:after { - content: ""; - display: block; - clear: both; -} +// div:after, +// ul:after { +// content: ""; +// display: block; +// clear: both; +// } html { height: 100%; @@ -246,7 +246,10 @@ body { line-height: 16px; list-style-type: none; padding: 0 0 16px 16px; - + justify-content: space-between; + align-items: center; + padding-right: 12px; + font-weight: bold; .text { display: inline-block; font-size: 14px; @@ -258,10 +261,6 @@ body { width: auto; } - li:last-child { - margin-left: auto; - margin-right: 40px; - } } } @@ -320,6 +319,7 @@ body { display: inline-block; width: 18px; height: 18px; + line-height: 18px; text-align: center; border-radius: 3px; color: white; diff --git a/node/src/ts/visualizer/components/PropertyList.tsx b/node/src/ts/visualizer/components/PropertyList.tsx index 9ad30164..99f3ed55 100644 --- a/node/src/ts/visualizer/components/PropertyList.tsx +++ b/node/src/ts/visualizer/components/PropertyList.tsx @@ -38,11 +38,6 @@ const PropertyList: React.FC = (props) => {
    -
  • - - - -
  • diff --git a/node/src/ts/visualizer/locales/en.ts b/node/src/ts/visualizer/locales/en.ts index b088c45b..dc343656 100644 --- a/node/src/ts/visualizer/locales/en.ts +++ b/node/src/ts/visualizer/locales/en.ts @@ -36,7 +36,6 @@ const messages = { 'legend.class.parent': 'Parent class', 'propertyList.hideableWrapper.target': 'Property', 'propertyList.title': 'Properties in the dataset', - 'propertyList.legend.label': 'Legend', 'propertyList.legend.text': 'Property', 'propertyList.legend.tripleCount': 'Triple count', 'searchBox.input.placeholder.search': 'Search', diff --git a/node/src/ts/visualizer/locales/ja.ts b/node/src/ts/visualizer/locales/ja.ts index c3ac9a29..2ecfecff 100644 --- a/node/src/ts/visualizer/locales/ja.ts +++ b/node/src/ts/visualizer/locales/ja.ts @@ -35,7 +35,6 @@ const messages = { 'legend.class.parent': '親クラス', 'propertyList.hideableWrapper.target': 'プロパティ', 'propertyList.title': 'データセット内プロパティ', - 'propertyList.legend.label': '凡例', 'propertyList.legend.text': 'プロパティ名', 'propertyList.legend.tripleCount': 'トリプルの数', 'searchBox.input.placeholder.search': '検索する', From 1c70db504d1e142107538615a15b7057820de854 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Thu, 17 Feb 2022 11:37:17 +0900 Subject: [PATCH 68/74] =?UTF-8?q?=E3=83=97=E3=83=AD=E3=83=91=E3=83=86?= =?UTF-8?q?=E3=82=A3=E3=81=AEURI=E3=82=92=E7=9F=AD=E7=B8=AE=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/package.json | 1 + node/src/ts/index.tsx | 1 - node/src/ts/useDBCLSFooter.ts | 6 ++- .../visualizer/components/ClassRelations.tsx | 45 ++++++++++++++++--- .../src/ts/visualizer/components/Property.tsx | 11 ++++- .../ts/visualizer/components/PropertyList.tsx | 15 +++++-- node/src/ts/visualizer/index.tsx | 2 + node/src/ts/visualizer/reducers/property.ts | 10 +++-- node/src/ts/visualizer/utils/index.ts | 2 +- node/yarn.lock | 13 ++++++ 10 files changed, 87 insertions(+), 19 deletions(-) diff --git a/node/package.json b/node/package.json index 0e54f40b..95d0f96e 100644 --- a/node/package.json +++ b/node/package.json @@ -48,6 +48,7 @@ "react-redux": "7.2.2", "react-router-dom": "5.2.0", "react-toastify": "5.5.0", + "react-tooltip": "^4.2.21", "redux": "4.0.5", "redux-form": "8.3.7", "redux-saga": "1.1.3", diff --git a/node/src/ts/index.tsx b/node/src/ts/index.tsx index 2c289152..cc734003 100644 --- a/node/src/ts/index.tsx +++ b/node/src/ts/index.tsx @@ -4,7 +4,6 @@ import { Provider } from 'react-redux' import { ConnectedRouter } from 'connected-react-router' import * as firebase from 'firebase/app' import 'firebase/auth' - import configureStore, { history } from './store' import App from './components/App' import rootSaga from './sagas' diff --git a/node/src/ts/useDBCLSFooter.ts b/node/src/ts/useDBCLSFooter.ts index d1dc0e57..3d30c54a 100644 --- a/node/src/ts/useDBCLSFooter.ts +++ b/node/src/ts/useDBCLSFooter.ts @@ -15,7 +15,7 @@ export const useDBCLSFooter = () => { }, 200) } return elm -}, [resetToken]) + }, [resetToken]) useEffect(() => { if (!footerElement) { @@ -32,5 +32,7 @@ export const useDBCLSFooter = () => { footerElement.style.display = 'block' } }, [location.pathname, footerElement]) - return {footerElement, copyElement} + return { footerElement, copyElement } } + +export default useDBCLSFooter diff --git a/node/src/ts/visualizer/components/ClassRelations.tsx b/node/src/ts/visualizer/components/ClassRelations.tsx index 62b17b12..0c9ad21e 100644 --- a/node/src/ts/visualizer/components/ClassRelations.tsx +++ b/node/src/ts/visualizer/components/ClassRelations.tsx @@ -1,10 +1,11 @@ /* eslint-disable camelcase */ -import React, { useCallback } from 'react' +import React, { useCallback, useMemo } from 'react' import { useDispatch, useSelector } from 'react-redux' import { DetailAction } from '../actions/detail' import { PropertyAction } from '../actions/property' import { RootState } from '../reducers' import { ClassRelation as ClassRelationType } from '../types/property' +import { omitUri } from '../utils' type ClassRelationProps = { relation: ClassRelationType @@ -43,27 +44,59 @@ const ClassRelation: React.FC = (props) => { const className = selected ? 'selected' : '' const literalClassName = object_datatype ? 'range-literal' : '' + const subjectTip = useMemo(() => { + if (!subject_class) { + return undefined + } + return subject_class !== omitUri(subject_class) ? subject_class : undefined + }, [subject_class]) + + const objectTip = useMemo(() => { + if (object_class) { + return object_class !== omitUri(object_class) ? object_class : undefined + } + if (object_datatype) { + return object_datatype !== omitUri(object_datatype) + ? object_datatype + : undefined + } + return undefined + }, [object_class]) + return ( // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
  • false}>

    {subject_class && ( - + S - - {subject_class || 'resource'} + + {subject_class ? omitUri(subject_class) : 'resource'} )} - {subject_class && object_class && } {(object_class || object_datatype) && ( O - {object_class || object_datatype || 'resource'} + {(() => { + if (object_class) { + return omitUri(object_class) + } + if (object_datatype) { + return omitUri(object_datatype) + } + return 'resource' + })()} )} diff --git a/node/src/ts/visualizer/components/Property.tsx b/node/src/ts/visualizer/components/Property.tsx index f5fd5ba6..88ba10b4 100644 --- a/node/src/ts/visualizer/components/Property.tsx +++ b/node/src/ts/visualizer/components/Property.tsx @@ -1,10 +1,11 @@ /* eslint-disable camelcase */ -import React, { useCallback } from 'react' +import React, { useCallback, useMemo } from 'react' import { useDispatch, useSelector } from 'react-redux' import { PropertyAction } from '../actions/property' import { RootState } from '../reducers' import { Property as PropertyType } from '../types/property' import ClassRelations from './ClassRelations' +import { omitUri } from '../utils' type PropertyProps = { property: PropertyType @@ -40,6 +41,10 @@ const Property: React.FC = (props) => { const open = isOpen ? 'open' : '' const className = [refered, open].join(' ') + const isOmittingUri = useMemo(() => { + return omitUri(uri) !== uri + }, [uri]) + return ( // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions

  • = (props) => { onClick={class_relations.length ? handleClick : undefined} onKeyDown={() => false} > - {uri} + + {omitUri(uri)} + {triples} {class_relations.length > 0 && } {isOpen && ( diff --git a/node/src/ts/visualizer/components/PropertyList.tsx b/node/src/ts/visualizer/components/PropertyList.tsx index 99f3ed55..67a4d1e7 100644 --- a/node/src/ts/visualizer/components/PropertyList.tsx +++ b/node/src/ts/visualizer/components/PropertyList.tsx @@ -1,6 +1,7 @@ -import React, { useCallback } from 'react' +import React, { useCallback, useEffect } from 'react' import { FormattedMessage, useIntl } from 'react-intl' import { useDispatch, useSelector } from 'react-redux' +import ReactTooltip from 'react-tooltip' import { UiAction } from '../actions/ui' import { RootState } from '../reducers' import { Property as PropertyType } from '../types/property' @@ -11,16 +12,24 @@ type PropertyListProps = { properties: PropertyType[] } -const selector = ({ ui: { propertyPaneVisibility } }: RootState) => ({ +const selector = ({ + ui: { propertyPaneVisibility }, + property: { openPropertyIndexes }, +}: RootState) => ({ propertyPaneVisibility, + openPropertyIndexes, }) const PropertyList: React.FC = (props) => { const { properties } = props - const { propertyPaneVisibility } = useSelector(selector) + const { propertyPaneVisibility, openPropertyIndexes } = useSelector(selector) const dispatch = useDispatch() const intl = useIntl() + useEffect(() => { + ReactTooltip.rebuild() + }, [properties, openPropertyIndexes]) + const handleToggle = useCallback(() => { dispatch(UiAction.hidePropertyPane()) }, [dispatch]) diff --git a/node/src/ts/visualizer/index.tsx b/node/src/ts/visualizer/index.tsx index 593a9400..9767a210 100644 --- a/node/src/ts/visualizer/index.tsx +++ b/node/src/ts/visualizer/index.tsx @@ -17,6 +17,7 @@ import { } from 'react-redux' import _ from 'lodash' import { useHistory } from 'react-router-dom' +import ReactTooltip from 'react-tooltip' import { getLocaleMessages, getLocaleShortString, useQuery } from './utils' import { Structure } from './types/structure' import { Metadata } from './types/metadata' @@ -216,6 +217,7 @@ const App: React.FC = (props) => {
+
) } diff --git a/node/src/ts/visualizer/reducers/property.ts b/node/src/ts/visualizer/reducers/property.ts index 9307c077..01b95b9c 100644 --- a/node/src/ts/visualizer/reducers/property.ts +++ b/node/src/ts/visualizer/reducers/property.ts @@ -27,13 +27,15 @@ export default function property( ): PropertyState { switch (action.type) { case types.SHOW_PROPERTY: { - const newState = { ...state } - newState.openPropertyIndexes[action.payload.index] = true + const openPropertyIndexes = [...state.openPropertyIndexes] + openPropertyIndexes[action.payload.index] = true + const newState = { ...state, ...{ openPropertyIndexes } } return newState } case types.CLOSE_PROPERTY: { - const newState = { ...state } - newState.openPropertyIndexes[action.payload.index] = false + const openPropertyIndexes = [...state.openPropertyIndexes] + openPropertyIndexes[action.payload.index] = false + const newState = { ...state, ...{ openPropertyIndexes } } return newState } case types.SELECT_PROERTY_CLASS: diff --git a/node/src/ts/visualizer/utils/index.ts b/node/src/ts/visualizer/utils/index.ts index e8706e95..8fbae421 100644 --- a/node/src/ts/visualizer/utils/index.ts +++ b/node/src/ts/visualizer/utils/index.ts @@ -2,7 +2,7 @@ import { useLocation } from 'react-router-dom' import locales from '../locales' import { Classes } from '../types/class' -const omitUri = (uri: string) => { +export const omitUri = (uri: string) => { // Do not allow endwith '#' or '/' because fragment or path will be empty. const uriWithoutEndDelim = uri.replace(/[#,/]$/, '') diff --git a/node/yarn.lock b/node/yarn.lock index 67c1687d..4c740f82 100644 --- a/node/yarn.lock +++ b/node/yarn.lock @@ -9435,6 +9435,14 @@ react-toastify@5.5.0: prop-types "^15.7.2" react-transition-group "^4" +react-tooltip@^4.2.21: + version "4.2.21" + resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.2.21.tgz#840123ed86cf33d50ddde8ec8813b2960bfded7f" + integrity sha512-zSLprMymBDowknr0KVDiJ05IjZn9mQhhg4PRsqln0OZtURAJ1snt1xi5daZfagsh6vfsziZrc9pErPTDY1ACig== + dependencies: + prop-types "^15.7.2" + uuid "^7.0.3" + react-transition-group@^4: version "4.4.1" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9" @@ -11159,6 +11167,11 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" From a3c122a5255d193527d637623b71d69740c0f952 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Thu, 17 Feb 2022 14:16:19 +0900 Subject: [PATCH 69/74] =?UTF-8?q?yasgui=E3=81=B8=E3=81=AE=E9=81=B7?= =?UTF-8?q?=E7=A7=BB=E3=82=92=E5=88=A5=E3=82=BF=E3=83=96=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/utils/sparql.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/node/src/ts/visualizer/utils/sparql.ts b/node/src/ts/visualizer/utils/sparql.ts index df398c8f..e39dc12b 100644 --- a/node/src/ts/visualizer/utils/sparql.ts +++ b/node/src/ts/visualizer/utils/sparql.ts @@ -31,6 +31,10 @@ export const navigateToYasgui = (endpoint: string, query: string) => { const params = new URLSearchParams() params.append('endpoint', endpoint) params.append('query', query) - - window.location.href = `/yasgui?${params.toString()}` + const win = window.open( + `/yasgui?${params.toString()}`, + '_blank', + 'noreferrer' + ) + win?.focus() } From d3d35805d8b3047b11bd610ce49ca86b28eb515b Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Thu, 17 Feb 2022 23:56:27 +0900 Subject: [PATCH 70/74] =?UTF-8?q?yasgui=E3=81=A7=E9=96=8B=E3=81=8F?= =?UTF-8?q?=E3=82=92=E5=8F=B3=E3=82=AF=E3=83=AA=E3=83=83=E3=82=AF=E3=83=A1?= =?UTF-8?q?=E3=83=8B=E3=83=A5=E3=83=BC=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/package.json | 1 + node/src/style/server/style.css | 1 + .../visualizer/components/ClassStructure.tsx | 23 +++++++++-------- node/src/ts/visualizer/components/Graph.tsx | 25 ++++++++++++++++++- node/webpack.config.js | 1 + node/yarn.lock | 9 ++++++- 6 files changed, 47 insertions(+), 13 deletions(-) diff --git a/node/package.json b/node/package.json index 95d0f96e..6903eb61 100644 --- a/node/package.json +++ b/node/package.json @@ -42,6 +42,7 @@ "moment": "2.29.1", "react": "16.14.0", "react-contenteditable": "3.3.5", + "react-contexify": "^5.0.0", "react-dom": "16.14.0", "react-dropzone": "10.2.2", "react-intl": "3.12.1", diff --git a/node/src/style/server/style.css b/node/src/style/server/style.css index ad378a96..03c9ed4b 100644 --- a/node/src/style/server/style.css +++ b/node/src/style/server/style.css @@ -2,6 +2,7 @@ @import "_mixin.css"; @import "_common.css"; @import "~sanitize.css/lib/sanitize.scss"; +@import "~react-contexify/scss/main.scss"; /*login*/ @import "_login.css"; diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 19bb1c58..1b9bece5 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -1,6 +1,7 @@ import _ from 'lodash' import React, { useRef } from 'react' import { useIntl } from 'react-intl' +import { useContextMenu } from 'react-contexify' import { useDispatch, useSelector } from 'react-redux' import { DetailAction } from '../actions/detail' import { RootState } from '../reducers' @@ -12,16 +13,16 @@ import GraphRepository, { SVGEventHandlerType, } from '../utils/GraphRepository' import { getChildrenRecursive } from '../utils/node' - import { ClassNames } from '../constants/ClassStructure' import { TooltipAction } from '../actions/tooltip' import { Metadata } from '../types/metadata' import { makeQueryWhenRightClickArrow, makeQueryWhenRightClickClass, - navigateToYasgui, } from '../utils/sparql' +export const CIRCLE_CONTEXT_MENU_ID = 'circle-context-menu-id' + function decideNormalClass( d: NodeType, visibleNodesSet: { [key: string]: boolean } @@ -140,6 +141,9 @@ const ClassStructure: React.FC = (props) => { const dispatch = useDispatch() const intl = useIntl() + const { show } = useContextMenu({ + id: CIRCLE_CONTEXT_MENU_ID, + }) const handleClickTreeImg: SVGEventHandlerType = React.useCallback(() => { dispatch(DetailAction.showTree()) }, [dispatch]) @@ -168,7 +172,7 @@ const ClassStructure: React.FC = (props) => { if (refUri) { const endpoint = metadata?.endpoint ?? '' const query = makeQueryWhenRightClickClass(refUri) - navigateToYasgui(endpoint, query) + show(event, { props: { endpoint, query } }) } }, [metadata?.endpoint] @@ -366,7 +370,7 @@ const ClassStructure: React.FC = (props) => { const endpoint = metadata?.endpoint ?? '' const query = makeQueryWhenRightClickArrow(...makeTriple()) - navigateToYasgui(endpoint, query) + show(event, { props: { endpoint, query } }) } GraphRepository.addArrowLineEvent( @@ -493,7 +497,7 @@ const ClassStructure: React.FC = (props) => { const endpoint = metadata?.endpoint ?? '' const query = makeQueryWhenRightClickArrow(...makeTriple()) - navigateToYasgui(endpoint, query) + show(event, { props: { endpoint, query } }) } const obj = rangeNodes.length > 0 ? rangeNodes[0] : null @@ -764,8 +768,8 @@ const ClassStructure: React.FC = (props) => { width, ]) - const baseElement = React.useMemo( - () => ( + return ( + <> @@ -789,11 +793,8 @@ const ClassStructure: React.FC = (props) => { - ), - [] + ) - - return baseElement } ClassStructure.displayName = 'ClassStructure' diff --git a/node/src/ts/visualizer/components/Graph.tsx b/node/src/ts/visualizer/components/Graph.tsx index 5b6eb045..eb8da09e 100644 --- a/node/src/ts/visualizer/components/Graph.tsx +++ b/node/src/ts/visualizer/components/Graph.tsx @@ -1,6 +1,8 @@ import * as d3 from 'd3' import React, { useRef } from 'react' import { useDispatch, useSelector } from 'react-redux' +import { Menu, Item, ItemParams } from 'react-contexify' + import _ from 'lodash' import { UiAction } from '../actions/ui' import { Classes } from '../types/class' @@ -11,11 +13,12 @@ import { createNodeStructure } from '../utils/node' import { NodeType } from '../utils/GraphRepository' import Legend from './Legend' import Breadcrumbs from './Breadcrumbs' -import ClassStructure from './ClassStructure' +import ClassStructure, { CIRCLE_CONTEXT_MENU_ID } from './ClassStructure' import { Tree } from './Tree' import LoadingSpinner from './LoadingSpinner' import Filter from './Filter' import { Metadata } from '../types/metadata' +import { navigateToYasgui } from '../utils/sparql' type GraphProps = { classes: Classes @@ -34,6 +37,25 @@ const selector = ({ showTree, }) +const ContextMenu: React.VFC = () => { + return ( + + + ) => { + if (e.props) { + const { endpoint, query } = e.props + navigateToYasgui(endpoint, query) + } + }} + > + YASGUIで見る + + + ) +} + const calcPosition = (node: NodeType, circleDiameter: number) => { const newNode: NodeType = node @@ -161,6 +183,7 @@ const Graph: React.FC = (props) => { containerEl={containerRef.current} loadElSelector="circle.root" /> +
) } diff --git a/node/webpack.config.js b/node/webpack.config.js index 374d6249..8670032c 100644 --- a/node/webpack.config.js +++ b/node/webpack.config.js @@ -129,6 +129,7 @@ module.exports = (env, args) => { }), new webpack.DefinePlugin({ FIREBASE_CONFIG: JSON.stringify(FIREBASE_CONFIG), + process: JSON.stringify({env: 'process/browser'}) , }), ], } diff --git a/node/yarn.lock b/node/yarn.lock index 4c740f82..0bc1a835 100644 --- a/node/yarn.lock +++ b/node/yarn.lock @@ -3275,7 +3275,7 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -clsx@1.1.1: +clsx@1.1.1, clsx@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== @@ -9343,6 +9343,13 @@ react-contenteditable@3.3.5: fast-deep-equal "^2.0.1" prop-types "^15.7.1" +react-contexify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/react-contexify/-/react-contexify-5.0.0.tgz#11b477550a0ee5a9a144399bc17c7c56bbc60057" + integrity sha512-2FIp7lxJ6dtfGr8EZ4uVV5p5TQjd0n2h/JU7PrejNIMiCeZWvSVPFh4lj1ZvjXosglBvP7q5JQQ8yUCdSaMSaw== + dependencies: + clsx "^1.1.1" + react-dom@16.14.0: version "16.14.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" From dd5aca0a1bda4c201579cc44932860168e8c480d Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Fri, 18 Feb 2022 03:08:25 +0900 Subject: [PATCH 71/74] =?UTF-8?q?=E7=9F=A2=E5=8D=B0=E4=B8=AD=E7=82=B9?= =?UTF-8?q?=E3=81=AE=E3=81=A1=E3=82=89=E3=81=A4=E3=81=8D=E3=82=92=E6=8A=91?= =?UTF-8?q?=E3=81=88=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/style/style.scss | 20 +- .../visualizer/components/ClassStructure.tsx | 15 +- node/src/ts/visualizer/components/Graph.tsx | 2 +- .../ts/visualizer/utils/GraphRepository.ts | 204 +++++++++++++++--- .../visualizer/utils/SVGElementsAccessor.ts | 15 ++ 5 files changed, 210 insertions(+), 46 deletions(-) diff --git a/node/src/style/style.scss b/node/src/style/style.scss index cf2a92de..adc068ad 100644 --- a/node/src/style/style.scss +++ b/node/src/style/style.scss @@ -142,11 +142,6 @@ html { padding: 0 4px; } -.arrow-line-base { - stroke: #666; - stroke-width: 4; - fill: none; -} .arrow-line { stroke: #666; stroke-width: 3; @@ -632,6 +627,21 @@ body { } } } + g#lines { + path { + stroke: #666; + stroke-width: 4; + fill: none; + } + } + g#lines-nodes { + circle { + r: 8; + stroke: #666; + fill: #666; + } + } + } figure { diff --git a/node/src/ts/visualizer/components/ClassStructure.tsx b/node/src/ts/visualizer/components/ClassStructure.tsx index 1b9bece5..efa37c1c 100644 --- a/node/src/ts/visualizer/components/ClassStructure.tsx +++ b/node/src/ts/visualizer/components/ClassStructure.tsx @@ -319,19 +319,9 @@ const ClassStructure: React.FC = (props) => { if (!event || !d) return const targetElement = event.currentTarget - const isSelfLine = !!targetElement - ?.getAttribute('class') - ?.includes('self-line') - const [x, y] = isSelfLine - ? [GraphRepository.x(d.x), GraphRepository.y(d.y)] - : targetElement - ?.getAttribute('d') - ?.split(' ')[1] - .split(',') - .map((v) => Number(v)) ?? [0, 0] - + const x = Number(targetElement.getAttribute('cx')) + 10 + const y = Number(targetElement.getAttribute('cy')) + 10 const predicates = getPredicates(d) - const predicateMessage = intl.formatMessage({ id: 'classStructure.text.predicate', }) @@ -791,6 +781,7 @@ const ClassStructure: React.FC = (props) => { + diff --git a/node/src/ts/visualizer/components/Graph.tsx b/node/src/ts/visualizer/components/Graph.tsx index eb8da09e..1a2a8831 100644 --- a/node/src/ts/visualizer/components/Graph.tsx +++ b/node/src/ts/visualizer/components/Graph.tsx @@ -50,7 +50,7 @@ const ContextMenu: React.VFC = () => { } }} > - YASGUIで見る + SPARQL検索 ) diff --git a/node/src/ts/visualizer/utils/GraphRepository.ts b/node/src/ts/visualizer/utils/GraphRepository.ts index 0980fb6a..5337f81f 100644 --- a/node/src/ts/visualizer/utils/GraphRepository.ts +++ b/node/src/ts/visualizer/utils/GraphRepository.ts @@ -299,6 +299,21 @@ class GraphRepository { } } + get linesNodes() { + const nodes = this.svg?.select('g#lines-nodes') + return { + same: nodes?.selectAll('circle.same-line'), + rightHand: nodes?.selectAll( + 'circle.right-hand-line' + ), + leftHand: nodes?.selectAll( + 'circle.left-hand-line' + ), + both: nodes?.selectAll('circle.both-line'), + self: nodes?.selectAll('circle.self-line'), + } + } + get texts() { const texts = this.svg?.select('g#texts') return texts?.selectAll('text') @@ -448,7 +463,7 @@ class GraphRepository { lhsNodes: NodeType[], bothNodes: NodeType[] ) { - const { paths } = this + const { paths, linesNodes } = this const sameLines = paths.same?.data(sameNodes, nodeKeyFn) sameLines?.enter().append('path').attr('class', 'same-line') @@ -458,7 +473,7 @@ class GraphRepository { rightLines ?.enter() .append('path') - .attr('class', 'arrow-line-base right-hand-line') + .attr('class', 'right-hand-line') .attr('marker-end', 'url(#arrow-head)') rightLines?.exit().remove() @@ -466,7 +481,7 @@ class GraphRepository { leftLines ?.enter() .append('path') - .attr('class', 'arrow-line-base left-hand-line') + .attr('class', 'left-hand-line') .attr('marker-end', 'url(#arrow-head)') leftLines?.exit().remove() @@ -474,10 +489,35 @@ class GraphRepository { bothLines ?.enter() .append('path') - .attr('class', 'arrow-line-base both-line') + .attr('class', 'both-line') .attr('marker-start', 'url(#arrow-head)') .attr('marker-end', 'url(#arrow-head)') bothLines?.exit().remove() + + const samePoints = linesNodes.same?.data(sameNodes, nodeKeyFn) + samePoints?.enter().append('circle').attr('class', 'same-line') + samePoints?.exit().remove() + + const rightPoints = linesNodes.rightHand?.data(rhsNodes, nodeKeyFn) + rightPoints + ?.enter() + .append('circle') + .attr('class', 'arrow-line-base right-hand-line') + rightPoints?.exit().remove() + + const leftPoints = linesNodes.leftHand?.data(lhsNodes, nodeKeyFn) + leftPoints + ?.enter() + .append('circle') + .attr('class', 'arrow-line-base left-hand-line') + leftPoints?.exit().remove() + + const bothPoints = linesNodes.both?.data(bothNodes, nodeKeyFn) + bothPoints + ?.enter() + .append('circle') + .attr('class', 'arrow-line-base both-line') + bothPoints?.exit().remove() } updateRightLines( @@ -488,7 +528,7 @@ class GraphRepository { rightLines ?.enter() .append('path') - .attr('class', 'arrow-line-base right-hand-line') + .attr('class', 'right-hand-line') .attr('marker-end', 'url(#arrow-head)') .on('contextmenu', handleRightClick) rightLines?.exit().remove() @@ -504,12 +544,19 @@ class GraphRepository { selfLines ?.enter() .append('path') - .attr('class', 'arrow-line-base self-line') + .attr('class', 'self-line') .attr('marker-end', 'url(#arrow-head)') + selfLines?.exit().remove() + + const selfPoints = this.linesNodes.self?.data(nodes, nodeKeyFn) + selfPoints + ?.enter() + .append('circle') + .attr('class', 'arrow-line-base self-line') .on('mouseover', handleMouseOver) .on('mouseout', handleMouseOut) .on('contextmenu', handleRightClick) - selfLines?.exit().remove() + selfPoints?.exit().remove() } removeTreeImg() { @@ -704,27 +751,13 @@ class GraphRepository { const f = this.targetNode if (!f) return - // lines - // const { paths } = this - const makeArrowNode = (x: number, y: number) => { - const r = 3.5 - const moveToStart = `M${x - r},${y}` - const drawUppperHalf = `A${r},${r} 0,1,0 ${x + r},${y}` - const drawBottomHalf = `A${r},${r} 0,1,0 ${x - r},${y}` - const moveToOrigin = `M${x},${y}` - return `${moveToStart} ${drawUppperHalf} ${drawBottomHalf} ${moveToOrigin}` - } - const makeArrowLineToCenter = (from: NodeType, to: NodeType) => { // 中心から中心を指す const fromX = this.x(from.x) const fromY = this.y(from.y) const toX = this.x(to.x) const toY = this.y(to.y) - const midX = (fromX + toX) / 2 - const midY = (fromY + toY) / 2 - const drawNode = makeArrowNode(midX, midY) - return `M${fromX},${fromY} ${midX},${midY} ${drawNode} ${toX},${toY}` + return `M${fromX},${fromY} ${toX},${toY}` } const makeArrowLineToSide = ( @@ -741,10 +774,7 @@ class GraphRepository { const cutTo = (dist - to.r) / dist const toX = this.x((to.x - from.x) * cutTo + from.x) const toY = this.y((to.y - from.y) * cutTo + from.y) - const midX = (fromX + toX) / 2 - const midY = (fromY + toY) / 2 - const drawNode = makeArrowNode(midX, midY) - return `M${fromX},${fromY} ${midX},${midY} ${drawNode} ${toX},${toY}` + return `M${fromX},${fromY} ${toX},${toY}` } const minSpaceBetweenCircles = (10 / scale) * 2 @@ -794,15 +824,133 @@ class GraphRepository { const moveToStart = `M${fromX},${fromY}` const drawUppperHalf = `A${lineR},${lineR} 0,0,0 ${lineMid[0]},${lineMid[1]}` - const drawNode = makeArrowNode(lineMid[0], lineMid[1]) const drawBottomHalf = `A${lineR},${lineR} 0,0,0 ${toX},${toY}` - return `${moveToStart} ${drawUppperHalf} ${drawNode} ${drawBottomHalf}` + return `${moveToStart} ${drawUppperHalf} ${drawBottomHalf}` } ctx.paths.self.attr('d', (d) => { return makeArrowLineToSelf(d) }) + + // 矢印の中点 + const getMidPointCenterToCenter = ( + from: NodeType, + to: NodeType, + xy: 'x' | 'y' + ) => { + // 中心から中心を指す + const fromX = this.x(from.x) + const fromY = this.y(from.y) + const toX = this.x(to.x) + const toY = this.y(to.y) + if (xy === 'x') { + const midX = (fromX + toX) / 2 + return midX + } + const midY = (fromY + toY) / 2 + return midY + } + + const getMidPontSideToSide = ( + from: NodeType, + to: NodeType, + xy: 'x' | 'y', + calculatedDistance?: number + ) => { + const dist = calculatedDistance ?? distance(from.x, from.y, to.x, to.y) + + // 辺から辺を指す + const cutFrom = (dist - from.r) / dist + const fromX = this.x((from.x - to.x) * cutFrom + to.x) + const fromY = this.y((from.y - to.y) * cutFrom + to.y) + const cutTo = (dist - to.r) / dist + const toX = this.x((to.x - from.x) * cutTo + from.x) + const toY = this.y((to.y - from.y) * cutTo + from.y) + if (xy === 'x') { + const midX = (fromX + toX) / 2 + return midX + } + const midY = (fromY + toY) / 2 + return midY + } + + const getMidPoint = (from: NodeType, to: NodeType, xy: 'x' | 'y') => { + const dist = distance(from.x, from.y, to.x, to.y) + + // 小数点精度の問題か何かでまれに誤って判定されることがあったので余白を設ける + const shouldPointToCenter = dist < from.r + to.r + minSpaceBetweenCircles + if (shouldPointToCenter) { + return getMidPointCenterToCenter(from, to, xy) + } + + return getMidPontSideToSide(from, to, xy, dist) + } + + ctx.linesNodes.rightHand + .attr('cx', (d) => { + return getMidPoint(f, d, 'x') + }) + .attr('cy', (d) => { + return getMidPoint(f, d, 'y') + }) + ctx.linesNodes.leftHand + .attr('cx', (d) => { + d.data.pointToCenter = true + return getMidPoint(f, d, 'x') + }) + .attr('cy', (d) => { + d.data.pointToCenter = true + return getMidPoint(f, d, 'y') + }) + + ctx.linesNodes.both + .attr('cx', (d) => { + d.data.pointToCenter = true + return getMidPoint(f, d, 'x') + }) + .attr('cy', (d) => { + d.data.pointToCenter = true + return getMidPoint(f, d, 'y') + }) + + ctx.linesNodes.same + .attr('cx', (d) => { + return getMidPontSideToSide(f, d, 'x') + }) + .attr('cy', (d) => { + return getMidPontSideToSide(f, d, 'y') + }) + + const getMidPointToSelf = (node: NodeType, xy: 'x' | 'y') => { + // 4時から11時の方向を指す + const nodeR = node.r * 1.02 + const fourOclock = Math.PI / 6 + const fromX = this.x(node.x + nodeR * Math.cos(fourOclock)) + const fromY = this.y(node.y + nodeR * Math.sin(fourOclock)) + const elevenOclock = (-Math.PI * 4) / 6 + const toX = this.x(node.x + nodeR * Math.cos(elevenOclock)) + const toY = this.y(node.y + nodeR * Math.sin(elevenOclock)) + + // 節を描画するために矢印の中点を求める + const lineR = this.r(node.r * 1.1) + const lineC = calcCenterFromPoints(fromX, fromY, toX, toY, lineR)[0] + const angle = calcCenterAngleFromPoints(lineR, fromX, fromY, toX, toY) + const theta = angle / 2 + Math.PI + const lineMid = rotateCoordinate(fromX, fromY, lineC[0], lineC[1], theta) + if (xy === 'x') { + return lineMid[0] + } + return lineMid[1] + } + + ctx.linesNodes.self + .attr('cx', (d) => { + return getMidPointToSelf(d, 'x') + }) + .attr('cy', (d) => { + return getMidPointToSelf(d, 'y') + }) } addArrowLineEvent( diff --git a/node/src/ts/visualizer/utils/SVGElementsAccessor.ts b/node/src/ts/visualizer/utils/SVGElementsAccessor.ts index c4f18c5c..61cb36fb 100644 --- a/node/src/ts/visualizer/utils/SVGElementsAccessor.ts +++ b/node/src/ts/visualizer/utils/SVGElementsAccessor.ts @@ -28,6 +28,21 @@ export default class SVGElementsAccessor { } } + get linesNodes() { + const nodes = this.ctx?.select('g#lines-nodes') + return { + same: nodes?.selectAll('circle.same-line'), + rightHand: nodes?.selectAll( + 'circle.right-hand-line' + ), + leftHand: nodes?.selectAll( + 'circle.left-hand-line' + ), + both: nodes?.selectAll('circle.both-line'), + self: nodes?.selectAll('circle.self-line'), + } + } + get texts() { const texts = this.ctx.selectAll('g#texts > g') return texts?.selectAll('text') From b810c3a22fbcfd9d6154c881f9d771600673cfeb Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Fri, 18 Feb 2022 05:41:25 +0900 Subject: [PATCH 72/74] =?UTF-8?q?cors=20proxy=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/html/yasgui.html | 4 ++- server/dbcls/api/app.py | 4 ++- server/dbcls/api/resources/proxy.py | 15 ++++++++ server/poetry.lock | 56 ++++++++++++++++++++--------- server/pyproject.toml | 1 + 5 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 server/dbcls/api/resources/proxy.py diff --git a/node/src/html/yasgui.html b/node/src/html/yasgui.html index d677fb2c..a7c2a110 100644 --- a/node/src/html/yasgui.html +++ b/node/src/html/yasgui.html @@ -10,7 +10,9 @@
diff --git a/server/dbcls/api/app.py b/server/dbcls/api/app.py index 3717221b..cab6641e 100644 --- a/server/dbcls/api/app.py +++ b/server/dbcls/api/app.py @@ -7,6 +7,7 @@ from google.auth.exceptions import TransportError from dbcls import app +from dbcls.api.resources.proxy import Proxy from dbcls.models import User, UserRole, UserRoleTypes from dbcls.api.resources.signup import SignUp from dbcls.api.resources.authenticate import Authenticate @@ -55,6 +56,7 @@ def verify_authentication(): api_v1.add_resource(SignUp, '/signup', endpoint='signup') api_v1.add_resource(Authenticate, '/auth', endpoint='auth') api_v1.add_resource(DataSetList, '/data_sets') +api_v1.add_resource(Proxy, '/proxy', endpoint='proxy') api_v1.add_resource(DataSetDetail, '/data_sets/') api_v1.add_resource(DataSetGenerator, '/data_sets/generate') api_v1.add_resource(DataSetGenerateProcessStatus, '/data_sets/generate/') @@ -66,7 +68,7 @@ def verify_authentication(): NOT_NEED_AUTHORIZATION_ENDPOINTS = [ f'{api_v1_bp.name}.{endpoint}' - for endpoint in ('healthy', 'signup', 'auth', 'public_data_sets', 'visualize') + for endpoint in ('healthy', 'signup', 'auth', 'public_data_sets', 'visualize', 'proxy') ] diff --git a/server/dbcls/api/resources/proxy.py b/server/dbcls/api/resources/proxy.py new file mode 100644 index 00000000..9d536212 --- /dev/null +++ b/server/dbcls/api/resources/proxy.py @@ -0,0 +1,15 @@ +from flask_restful import Resource, reqparse, request +from SPARQLWrapper import SPARQLWrapper, JSON + + + +class Proxy(Resource): + def post(self): + form = request.form + endpoint = form['endpoint'] + query = form['query'] + s = SPARQLWrapper(endpoint) + s.setQuery(query) + s.setReturnFormat(JSON) + res = s.query().convert() + return res diff --git a/server/poetry.lock b/server/poetry.lock index 51ff73b4..2ec1909e 100644 --- a/server/poetry.lock +++ b/server/poetry.lock @@ -86,12 +86,15 @@ python-versions = "*" pycparser = "*" [[package]] -name = "chardet" -version = "3.0.4" -description = "Universal encoding detector for Python 2 and 3" +name = "charset-normalizer" +version = "2.0.12" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false -python-versions = "*" +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] [[package]] name = "click" @@ -659,21 +662,21 @@ hiredis = ["hiredis (>=0.1.3)"] [[package]] name = "requests" -version = "2.24.0" +version = "2.27.1" description = "Python HTTP for Humans." category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" -idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" [package.extras] -security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] [[package]] name = "rsa" @@ -694,6 +697,20 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "sparqlwrapper" +version = "1.8.5" +description = "SPARQL Endpoint interface to Python" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +rdflib = ">=4.0" + +[package.extras] +keepalive = ["keepalive (>=0.5)"] + [[package]] name = "sqlalchemy" version = "1.3.19" @@ -802,7 +819,7 @@ testing = ["jaraco.itertools", "func-timeout"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "535b95b5ad21aadc54dade5d3e931cf96820b0c75237bea656207a8b43093063" +content-hash = "f2fd6016a040de9f7098522f318a4159bc65a641080c3c3f691587b8a9fcb03b" [metadata.files] alembic = [ @@ -862,9 +879,9 @@ cffi = [ {file = "cffi-1.14.2-cp38-cp38-win_amd64.whl", hash = "sha256:12a453e03124069b6896107ee133ae3ab04c624bb10683e1ed1c1663df17c13c"}, {file = "cffi-1.14.2.tar.gz", hash = "sha256:ae8f34d50af2c2154035984b8b5fc5d9ed63f32fe615646ab435b05b132ca91b"}, ] -chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, +charset-normalizer = [ + {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, + {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, ] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, @@ -1196,8 +1213,8 @@ redis = [ {file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"}, ] requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, ] rsa = [ {file = "rsa-4.6-py3-none-any.whl", hash = "sha256:6166864e23d6b5195a5cfed6cd9fed0fe774e226d8f854fcb23b7bbef0350233"}, @@ -1207,6 +1224,13 @@ six = [ {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, ] +sparqlwrapper = [ + {file = "SPARQLWrapper-1.8.5-py2-none-any.whl", hash = "sha256:357ee8a27bc910ea13d77836dbddd0b914991495b8cc1bf70676578155e962a8"}, + {file = "SPARQLWrapper-1.8.5-py2.7.egg", hash = "sha256:17ec44b08b8ae2888c801066249f74fe328eec25d90203ce7eadaf82e64484c7"}, + {file = "SPARQLWrapper-1.8.5-py3-none-any.whl", hash = "sha256:c7f9c9d8ebb13428771bc3b6dee54197422507dcc3dea34e30d5dcfc53478dec"}, + {file = "SPARQLWrapper-1.8.5-py3.4.egg", hash = "sha256:8cf6c21126ed76edc85c5c232fd6f77b9f61f8ad1db90a7147cdde2104aff145"}, + {file = "SPARQLWrapper-1.8.5.tar.gz", hash = "sha256:d6a66b5b8cda141660e07aeb00472db077a98d22cb588c973209c7336850fb3c"}, +] sqlalchemy = [ {file = "SQLAlchemy-1.3.19-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:f2e8a9c0c8813a468aa659a01af6592f71cd30237ec27c4cc0683f089f90dcfc"}, {file = "SQLAlchemy-1.3.19-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:33d29ae8f1dc7c75b191bb6833f55a19c932514b9b5ce8c3ab9bc3047da5db36"}, diff --git a/server/pyproject.toml b/server/pyproject.toml index 10384b27..412307f6 100644 --- a/server/pyproject.toml +++ b/server/pyproject.toml @@ -18,6 +18,7 @@ pytz = "^2019.2" redis = "^3.3" PyMySQL = "^0.10.1" umakaparser = "^0.1.7" +SPARQLWrapper = "^1.8.5" [tool.poetry.dev-dependencies] pytest = "^5.1" From feac6e8bd9a362e1d987d10fed04cc4a88163271 Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Fri, 18 Feb 2022 18:23:44 +0900 Subject: [PATCH 73/74] =?UTF-8?q?=E3=82=A8=E3=83=B3=E3=83=86=E3=82=A3?= =?UTF-8?q?=E3=83=86=E3=82=A3=E6=95=B0=E3=81=A7=E8=A1=A8=E7=A4=BA=E3=82=92?= =?UTF-8?q?=E5=88=B6=E9=99=90=E3=81=99=E3=82=8B=E6=95=B0=E5=80=A4=E3=81=AE?= =?UTF-8?q?=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB=E3=83=88=E3=82=921?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- node/src/ts/visualizer/components/Filter.tsx | 4 ++-- node/src/ts/visualizer/index.tsx | 2 +- node/src/ts/visualizer/reducers/filter.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/node/src/ts/visualizer/components/Filter.tsx b/node/src/ts/visualizer/components/Filter.tsx index 4e916b8a..0c17da6f 100644 --- a/node/src/ts/visualizer/components/Filter.tsx +++ b/node/src/ts/visualizer/components/Filter.tsx @@ -16,11 +16,11 @@ const Filter: React.FC = () => { const query = useQuery() const defaultEntitiesLimit = useMemo(() => { - const limit = Number(query.get('lower_limit')) + const limit = Number(query.get('lower_limit')) || 1 if (Number.isInteger(limit)) { return limit } - return 0 + return 1 }, []) const handleClick = useCallback(() => { diff --git a/node/src/ts/visualizer/index.tsx b/node/src/ts/visualizer/index.tsx index 9767a210..872dbcff 100644 --- a/node/src/ts/visualizer/index.tsx +++ b/node/src/ts/visualizer/index.tsx @@ -171,7 +171,7 @@ const App: React.FC = (props) => { return } - if (lowerLimitOfClassEntities === 0) { + if (lowerLimitOfClassEntities <= 1) { history.push({ pathname: history.location.pathname, }) diff --git a/node/src/ts/visualizer/reducers/filter.ts b/node/src/ts/visualizer/reducers/filter.ts index 36449aa9..76f00228 100644 --- a/node/src/ts/visualizer/reducers/filter.ts +++ b/node/src/ts/visualizer/reducers/filter.ts @@ -7,7 +7,7 @@ export interface FilterState { } const initialState: FilterState = { - lowerLimitOfClassEntities: 0, + lowerLimitOfClassEntities: 1, showingConditions: false, } From 68bab8f8a71af45acc9e091950e13b2c10ad4cac Mon Sep 17 00:00:00 2001 From: Fumiya Kubota Date: Fri, 18 Feb 2022 18:45:53 +0900 Subject: [PATCH 74/74] =?UTF-8?q?=E5=8F=B3=E3=83=9A=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=81=A7=E7=89=B9=E5=AE=9A=E3=81=AE=E7=9F=A2=E5=8D=B0=E3=82=92?= =?UTF-8?q?=E9=81=B8=E6=8A=9E=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E6=99=82?= =?UTF-8?q?=E3=81=9D=E3=82=8C=E3=82=92=E3=83=8F=E3=82=A4=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=83=88=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ClassRelationsDetail.tsx | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/node/src/ts/visualizer/components/ClassRelationsDetail.tsx b/node/src/ts/visualizer/components/ClassRelationsDetail.tsx index 721318ba..c950809b 100644 --- a/node/src/ts/visualizer/components/ClassRelationsDetail.tsx +++ b/node/src/ts/visualizer/components/ClassRelationsDetail.tsx @@ -1,7 +1,8 @@ import React, { useCallback, useMemo } from 'react' import { useIntl } from 'react-intl' -import { useDispatch } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { DetailAction } from '../actions/detail' +import { RootState } from '../reducers' import { Classes } from '../types/class' import { getPreferredLabel } from '../utils' import GraphRepository from '../utils/GraphRepository' @@ -15,6 +16,10 @@ type ClassRelationsDetailProps = { showRightHand: boolean } +const selector = ({ detail: { showingRelation } }: RootState) => ({ + showingRelation, +}) + const ClassRelationsDetail: React.FC = (props) => { const { title, @@ -30,6 +35,7 @@ const ClassRelationsDetail: React.FC = (props) => { classes, focusingURI, ]) + const { showingRelation } = useSelector(selector) const getPreferredTriple = useCallback( (triple: string[]) => { @@ -125,6 +131,12 @@ const ClassRelationsDetail: React.FC = (props) => { type="button" title={getPreferredTriple(triple)} onClick={relationClass} + style={{ + backgroundColor: + showingRelation === rhs + ? 'rgba(170, 170, 170, 0.5)' + : undefined, + }} > {`<${triple[0]}>`} @@ -152,6 +164,7 @@ const ClassRelationsDetail: React.FC = (props) => { intl, showRightHand, getPreferredTriple, + showingRelation, ] ) @@ -182,6 +195,12 @@ const ClassRelationsDetail: React.FC = (props) => { type="button" title={getPreferredTriple(triple)} onClick={relationClass} + style={{ + backgroundColor: + showingRelation === lhs + ? 'rgba(170, 170, 170, 0.5)' + : undefined, + }} > {`<${triple[0]}>`} @@ -209,6 +228,7 @@ const ClassRelationsDetail: React.FC = (props) => { intl, showLeftHand, getPreferredTriple, + showingRelation, ] ) if (!classDetail) {