diff --git a/app/.prettierrc b/app/.prettierrc
new file mode 100644
index 0000000..abdf5c6
--- /dev/null
+++ b/app/.prettierrc
@@ -0,0 +1,17 @@
+{
+ "arrowParens": "avoid",
+ "bracketSpacing": true,
+ "htmlWhitespaceSensitivity": "css",
+ "insertPragma": false,
+ "jsxBracketSameLine": false,
+ "jsxSingleQuote": true,
+ "printWidth": 80,
+ "proseWrap": "always",
+ "quoteProps": "as-needed",
+ "requirePragma": false,
+ "semi": true,
+ "singleQuote": true,
+ "tabWidth": 2,
+ "trailingComma": "all",
+ "useTabs": false
+}
\ No newline at end of file
diff --git a/app/src/lib/api/mapping_service/index.ts b/app/src/lib/api/mapping_service/index.ts
new file mode 100644
index 0000000..35c234a
--- /dev/null
+++ b/app/src/lib/api/mapping_service/index.ts
@@ -0,0 +1,110 @@
+import encodeFileToBase64 from '../../../utils/base64encoder';
+import ApiService from '../../services/api_service';
+import { MappingGraph } from './types';
+
+class MappingService {
+ private static getApiClient(): ApiService {
+ return ApiService.getInstance('default');
+ }
+
+ public static async getMappingsInWorkspace(
+ workspaceUuid: string,
+ ): Promise {
+ const result = await this.getApiClient().callApi(
+ `/workspaces/${workspaceUuid}/mapping`,
+ {
+ method: 'GET',
+ parser: data => data as MappingGraph[],
+ },
+ );
+
+ if (result.type === 'success') {
+ return result.data;
+ }
+
+ throw new Error(
+ `Failed to get mappings: ${result.message} (status: ${result.status})`,
+ );
+ }
+
+ public static async createMappingInWorkspace(
+ workspaceUuid: string,
+ name: string,
+ description: string,
+ content: File,
+ sourceType: 'csv' | 'json',
+ extra: Record,
+ ): Promise {
+ const base64EncodedFile = await encodeFileToBase64(content);
+ const data = {
+ name,
+ description,
+ content: base64EncodedFile,
+ source_type: sourceType,
+ extra,
+ };
+
+ const result = await this.getApiClient().callApi(
+ `/workspaces/${workspaceUuid}/mapping`,
+ {
+ method: 'POST',
+ body: data,
+ parser: () => true,
+ },
+ );
+
+ if (result.type === 'success') {
+ return result.data;
+ }
+
+ throw new Error(
+ `Failed to create mapping: ${result.message} (status: ${result.status})`,
+ );
+ }
+
+ public static async deleteMappingInWorkspace(
+ workspaceUuid: string,
+ mappingUuid: string,
+ ): Promise {
+ const result = await this.getApiClient().callApi(
+ `/workspaces/${workspaceUuid}/mapping/${mappingUuid}`,
+ {
+ method: 'DELETE',
+ parser: () => true,
+ },
+ );
+
+ if (result.type === 'success') {
+ return result.data;
+ }
+
+ throw new Error(
+ `Failed to delete mapping: ${result.message} (status: ${result.status})`,
+ );
+ }
+
+ public static async updateMapping(
+ workspaceUuid: string,
+ mappingUuid: string,
+ data: MappingGraph,
+ ): Promise {
+ const result = await this.getApiClient().callApi(
+ `/workspaces/${workspaceUuid}/mapping/${mappingUuid}`,
+ {
+ method: 'PUT',
+ body: data,
+ parser: () => true,
+ },
+ );
+
+ if (result.type === 'success') {
+ return result.data;
+ }
+
+ throw new Error(
+ `Failed to update mapping: ${result.message} (status: ${result.status})`,
+ );
+ }
+}
+
+export default MappingService;
diff --git a/app/src/lib/api/mapping_service/types.ts b/app/src/lib/api/mapping_service/types.ts
new file mode 100644
index 0000000..3493f8e
--- /dev/null
+++ b/app/src/lib/api/mapping_service/types.ts
@@ -0,0 +1,43 @@
+export enum MappingNodeType {
+ ENTITY = 'entity',
+ LITERAL = 'literal',
+ URIRef = 'uri_ref',
+}
+
+export interface MappingNode {
+ id: string;
+ type: MappingNodeType.ENTITY;
+ label: string;
+ uri_pattern: string;
+ rdf_type: string[];
+}
+
+export interface MappingLiteral {
+ id: string;
+ type: MappingNodeType.LITERAL;
+ label: string;
+ value: string;
+ literal_type: string;
+}
+
+export interface MappingURIRef {
+ id: string;
+ type: MappingNodeType.URIRef;
+ uri_pattern: string;
+}
+
+export interface MappingEdge {
+ id: string;
+ source: string;
+ target: string;
+ predicate_uri: string;
+}
+
+export interface MappingGraph {
+ uuid: string;
+ name: string;
+ description: string;
+ source_id: string;
+ nodes: (MappingNode | MappingLiteral | MappingURIRef)[];
+ edges: MappingEdge[];
+}
diff --git a/app/src/pages/prefixes_page/components/PrefixCardItem/index.tsx b/app/src/pages/prefixes_page/components/PrefixCardItem/index.tsx
index e7a2095..a89f923 100644
--- a/app/src/pages/prefixes_page/components/PrefixCardItem/index.tsx
+++ b/app/src/pages/prefixes_page/components/PrefixCardItem/index.tsx
@@ -12,18 +12,14 @@ const PrefixCardItem = ({ prefix, onDelete }: PrefixCardItemProps) => {
-
- URI: {prefix.uri}
-
- >
+
+ URI: {prefix.uri}
+
}
actions={
- <>
-
- >
+
}
/>
);
diff --git a/app/src/pages/workspace_page/components/MappingCard/index.tsx b/app/src/pages/workspace_page/components/MappingCard/index.tsx
new file mode 100644
index 0000000..de620bc
--- /dev/null
+++ b/app/src/pages/workspace_page/components/MappingCard/index.tsx
@@ -0,0 +1,34 @@
+import { Button } from '@blueprintjs/core';
+import CardItem from '../../../../components/CardItem';
+import { MappingGraph } from '../../../../lib/api/mapping_service/types';
+
+interface MappingCardItemProps {
+ mapping: MappingGraph;
+ onSelected: (mapping: MappingGraph) => void;
+ onDelete: (mapping: MappingGraph) => void;
+}
+
+const MappingCardItem = ({
+ mapping,
+ onSelected,
+ onDelete,
+}: MappingCardItemProps) => {
+ return (
+
+ Description: {mapping.description}
+
+ }
+ actions={
+ <>
+
+
+ >
+ }
+ />
+ );
+};
+
+export default MappingCardItem;
diff --git a/app/src/pages/workspace_page/index.tsx b/app/src/pages/workspace_page/index.tsx
index a57be51..16aaa21 100644
--- a/app/src/pages/workspace_page/index.tsx
+++ b/app/src/pages/workspace_page/index.tsx
@@ -1,9 +1,10 @@
-import { Button, ButtonGroup, Navbar } from '@blueprintjs/core';
+import { Button, ButtonGroup, Navbar, NonIdealState } from '@blueprintjs/core';
import { useEffect } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import useWorkspacePageState from './state';
import useErrorToast from '../../hooks/useErrorToast';
+import MappingCardItem from './components/MappingCard';
import './styles.scss';
type WorkspacePageURLProps = {
@@ -14,6 +15,7 @@ const WorkspacePage = () => {
const props = useParams();
const navigation = useNavigate();
const workspace = useWorkspacePageState(state => state.workspace);
+ const mappingGraphs = useWorkspacePageState(state => state.mappingGraphs);
const isLoading = useWorkspacePageState(state => state.isLoading);
const error = useWorkspacePageState(state => state.error);
const loadWorkspace = useWorkspacePageState(state => state.loadWorkspace);
@@ -64,6 +66,37 @@ const WorkspacePage = () => {
+
+ {!workspace && <>>}
+ {workspace && mappingGraphs.length === 0 && (
+
navigation('create')}
+ >
+ Create New Mapping
+
+ }
+ />
+ )}
+ {workspace && mappingGraphs.length > 0 && (
+
+ {mappingGraphs.map(mappingGraph => (
+ {}}
+ onSelected={() => {}}
+ />
+ ))}
+
+ )}
+
);
};
diff --git a/app/src/pages/workspace_page/state.ts b/app/src/pages/workspace_page/state.ts
index 11e49e3..801828f 100644
--- a/app/src/pages/workspace_page/state.ts
+++ b/app/src/pages/workspace_page/state.ts
@@ -2,21 +2,34 @@ import { Workspace } from '../../lib/api/workspaces_api/types';
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';
+import MappingService from '../../lib/api/mapping_service';
+import { MappingGraph } from '../../lib/api/mapping_service/types';
import WorkspacesApi from '../../lib/api/workspaces_api';
import { ZustandActions } from '../../utils/zustand';
interface WorkspacePageState {
workspace: Workspace | null;
+ mappingGraphs: MappingGraph[];
isLoading: string | null; // If the value is null, it means that the data is not being loaded, otherwise it will contain the loading message
error: string | null;
}
interface WorkspacePageStateActions {
- loadWorkspace: (uuid: string) => void;
+ loadWorkspace: (uuid: string) => Promise;
+ createMapping: (
+ workspaceUuid: string,
+ name: string,
+ description: string,
+ content: File,
+ sourceType: 'csv' | 'json',
+ extra: Record,
+ ) => Promise;
+ deleteMapping: (workspaceUuid: string, mappingUuid: string) => Promise;
}
const defaultState: WorkspacePageState = {
workspace: null,
+ mappingGraphs: [],
isLoading: null,
error: null,
};
@@ -25,20 +38,66 @@ const functions: ZustandActions<
WorkspacePageStateActions,
WorkspacePageState
> = set => ({
- loadWorkspace(uuid) {
+ async loadWorkspace(uuid) {
set({ isLoading: 'Loading workspace...' });
- WorkspacesApi.getWorkspace(uuid)
- .then(workspace => {
- set({ workspace, error: null });
- })
- .catch(error => {
- if (error instanceof Error) {
- set({ error: error.message });
- }
- })
- .finally(() => {
- set({ isLoading: null });
- });
+ try {
+ const workspacePromise = WorkspacesApi.getWorkspace(uuid);
+ const mappingGraphsPromise = await MappingService.getMappingsInWorkspace(
+ uuid,
+ );
+ // Wait for all the promises to resolve
+ const [workspace, mappingGraphs] = await Promise.all([
+ workspacePromise,
+ mappingGraphsPromise,
+ ]);
+ set({ workspace, mappingGraphs, error: null });
+ } catch (error) {
+ if (error instanceof Error) {
+ set({ error: error.message });
+ }
+ } finally {
+ set({ isLoading: null });
+ }
+ },
+ async createMapping(
+ workspaceUuid,
+ name,
+ description,
+ content,
+ sourceType,
+ extra,
+ ) {
+ set({ isLoading: 'Creating mapping...' });
+ try {
+ await MappingService.createMappingInWorkspace(
+ workspaceUuid,
+ name,
+ description,
+ content,
+ sourceType,
+ extra,
+ );
+ await this.loadWorkspace(workspaceUuid);
+ } catch (error) {
+ if (error instanceof Error) {
+ set({ error: error.message });
+ }
+ } finally {
+ set({ isLoading: null, error: null });
+ }
+ },
+ async deleteMapping(workspaceUuid, mappingUuid) {
+ set({ isLoading: 'Deleting mapping...' });
+ try {
+ await MappingService.deleteMappingInWorkspace(workspaceUuid, mappingUuid);
+ await this.loadWorkspace(workspaceUuid);
+ } catch (error) {
+ if (error instanceof Error) {
+ set({ error: error.message });
+ }
+ } finally {
+ set({ isLoading: null, error: null });
+ }
},
});
diff --git a/app/src/pages/workspace_page/styles.scss b/app/src/pages/workspace_page/styles.scss
index 8d16404..c247f7e 100644
--- a/app/src/pages/workspace_page/styles.scss
+++ b/app/src/pages/workspace_page/styles.scss
@@ -6,3 +6,9 @@
width: 100vw;
height: 100vh;
}
+
+.workspace-page-content {
+ margin-top: bp-vars.$pt-navbar-height;
+ width: 100vw;
+ height: calc(100vh - #{bp-vars.$pt-navbar-height});
+}
diff --git a/bootstrap.py b/bootstrap.py
index 8887c25..25d4139 100644
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -43,6 +43,8 @@ async def bootstrap():
f"Application directory set to {di['APP_DIR']}"
)
+ di["TEMP_DIR"] = di["APP_DIR"] / "temp"
+
# Detecting system and architecture for later use
di["SYSTEM"] = platform.system()
di["ARCH"] = platform.machine()
diff --git a/server/routers/workspaces/workspaces.py b/server/routers/workspaces/workspaces.py
index 33d16ca..30f37f5 100644
--- a/server/routers/workspaces/workspaces.py
+++ b/server/routers/workspaces/workspaces.py
@@ -46,6 +46,7 @@
GetPrefixInWorkspaceFacade,
)
from server.models.mapping import MappingGraph
+from server.models.ontology import Ontology
from server.models.workspace import WorkspaceModel
from server.routers.models import BasicResponse
from server.routers.workspaces.models import (
@@ -293,7 +294,7 @@ async def delete_prefix(
async def get_ontologies(
workspace_id: str,
get_ontology_in_workspace_facade: GetOntologyInWorkspaceFacadeDep,
-) -> list[dict]:
+) -> list[Ontology]:
facade_response = (
get_ontology_in_workspace_facade.execute(
workspace_id=workspace_id,
diff --git a/server/service_protocols/mapping_service_protocol/__init__.py b/server/service_protocols/mapping_service_protocol/__init__.py
index 95dceea..73189d4 100644
--- a/server/service_protocols/mapping_service_protocol/__init__.py
+++ b/server/service_protocols/mapping_service_protocol/__init__.py
@@ -58,13 +58,3 @@ def delete_mapping(self, mapping_id: str) -> None:
None
"""
pass
-
- @abstractmethod
- def list_mappings(self) -> list[str]:
- """
- List all mappings
-
- Returns:
- list[str]: List of mapping IDs
- """
- pass
diff --git a/server/utils/schema_extractor/__init__.py b/server/utils/schema_extractor/__init__.py
index 5bf9005..f65062c 100644
--- a/server/utils/schema_extractor/__init__.py
+++ b/server/utils/schema_extractor/__init__.py
@@ -1,12 +1,12 @@
from kink import inject
-from utils.schema_extractor.i_schema_extractor import (
+from server.utils.schema_extractor.i_schema_extractor import (
ISchemaExtractor,
)
-from utils.schema_extractor.json_schema_extractor import (
+from server.utils.schema_extractor.json_schema_extractor import (
JSONSchemaExtractor, # noqa: F401
)
-from utils.schema_extractor.tabular_schema_extractor import (
+from server.utils.schema_extractor.tabular_schema_extractor import (
TabularSchemaExtractor, # noqa: F401
)
diff --git a/server/utils/schema_extractor/json_schema_extractor.py b/server/utils/schema_extractor/json_schema_extractor.py
index ad8ed69..c6578b3 100644
--- a/server/utils/schema_extractor/json_schema_extractor.py
+++ b/server/utils/schema_extractor/json_schema_extractor.py
@@ -2,7 +2,8 @@
from io import BytesIO
from kink import inject
-from utils.schema_extractor.i_schema_extractor import (
+
+from server.utils.schema_extractor.i_schema_extractor import (
ISchemaExtractor,
)
@@ -11,7 +12,7 @@
class JSONSchemaExtractor(ISchemaExtractor):
def __init__(
self,
- temp_storage: str,
+ TEMP_DIR: str,
):
super().__init__("JSON Schema Extractor", ["json"])
diff --git a/server/utils/schema_extractor/tabular_schema_extractor.py b/server/utils/schema_extractor/tabular_schema_extractor.py
index 9038d10..0ad13d2 100644
--- a/server/utils/schema_extractor/tabular_schema_extractor.py
+++ b/server/utils/schema_extractor/tabular_schema_extractor.py
@@ -1,9 +1,9 @@
from io import BytesIO
-from kink import inject
import pandas as pd
+from kink import inject
-from utils.schema_extractor.i_schema_extractor import (
+from server.utils.schema_extractor.i_schema_extractor import (
ISchemaExtractor,
)
@@ -12,7 +12,7 @@
class TabularSchemaExtractor(ISchemaExtractor):
def __init__(
self,
- temp_storage: str,
+ TEMP_DIR: str,
):
super().__init__(
"Tabular Schema Extractor",