diff --git a/components.d.ts b/components.d.ts index c424b4e..6e5f75b 100644 --- a/components.d.ts +++ b/components.d.ts @@ -8,13 +8,20 @@ export {} declare module '@vue/runtime-core' { export interface GlobalComponents { LoginMenu: typeof import('./src/components/LoginMenu.vue')['default'] + NetworkSubsetter: typeof import('./src/components/NetworkSubsetter.vue')['default'] VAvatar: typeof import('vuetify/lib')['VAvatar'] VBtn: typeof import('vuetify/lib')['VBtn'] VCard: typeof import('vuetify/lib')['VCard'] + VCardText: typeof import('vuetify/lib')['VCardText'] + VCardTitle: typeof import('vuetify/lib')['VCardTitle'] + VDivider: typeof import('vuetify/lib')['VDivider'] VIcon: typeof import('vuetify/lib')['VIcon'] VList: typeof import('vuetify/lib')['VList'] VListItem: typeof import('vuetify/lib')['VListItem'] VListItemAction: typeof import('vuetify/lib')['VListItemAction'] VMenu: typeof import('vuetify/lib')['VMenu'] + VOverlay: typeof import('vuetify/lib')['VOverlay'] + VRow: typeof import('vuetify/lib')['VRow'] + VSlider: typeof import('vuetify/lib')['VSlider'] } } diff --git a/src/components/NetworkSubsetter.vue b/src/components/NetworkSubsetter.vue new file mode 100644 index 0000000..d0ec356 --- /dev/null +++ b/src/components/NetworkSubsetter.vue @@ -0,0 +1,82 @@ + + + diff --git a/src/main.ts b/src/main.ts index c36b180..3e06984 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,7 @@ import LoginMenu from './components/LoginMenu.vue'; +import NetworkSubsetter from './components/NetworkSubsetter.vue'; export { LoginMenu, + NetworkSubsetter, }; \ No newline at end of file diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..a754d1f --- /dev/null +++ b/src/types.ts @@ -0,0 +1,26 @@ +import { TableRow } from 'multinet'; + +export interface Node extends TableRow { + type: string; + neighbors: string[]; + degreeCount: number; + children?: Node[]; + parentPosition?: number; + [propName: string]: unknown; +} + +export interface Edge extends TableRow { + _from: string; + _to: string; + [propName: string]: unknown; +} + +export interface Network { + nodes: Node[]; + edges: Edge[]; +} + +export interface LoadError { + message: string; + href: string; +} diff --git a/src/utils/queryUtils.ts b/src/utils/queryUtils.ts new file mode 100644 index 0000000..c078322 --- /dev/null +++ b/src/utils/queryUtils.ts @@ -0,0 +1,46 @@ +import { LoadError, Network } from '../types'; +import { multinetApi } from 'multinet'; + +export async function subsetNetwork( + workspaceName: string, + subsetAmount: number, + nodeTableNames: string[], + edgeTableName: string, + loadError: LoadError, + setLoadError: (a: LoadError) => void, + networkName: string, + api: ReturnType +) { + const aqlQuery = `let nodes = (FOR n in [${nodeTableNames}][**] LIMIT ${subsetAmount} RETURN n) let edges = (FOR e in ${edgeTableName} filter e._from in nodes[**]._id && e._to in nodes[**]._id RETURN e) + RETURN {"nodes": nodes[**], edges}`; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let newAQLNetwork: Network = { nodes: [], edges: []}; + + try { + newAQLNetwork = (await api.aql(workspaceName, { query: aqlQuery, bind_vars: {} }) as Network[])[0]; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (error: any) { + if (error.status === 400) { + setLoadError({ + message: error.statusText, + href: 'https://multinet.app', + }); + } else { + setLoadError({ + message: 'An unexpected error ocurred', + href: 'https://multinet.app', + }); + } + } finally { + if (loadError.message === 'The network you are loading is too large' && typeof newAQLNetwork === 'undefined') { + // Catches CORS errors, issues when DB/API are down, etc. + setLoadError({ + message: 'There was a network issue when getting data', + href: `./?workspace=${workspaceName}&network=${networkName}`, + }); + } + } + + return newAQLNetwork; +} \ No newline at end of file