Skip to content

Commit

Permalink
datacube: upload data via file into DataCube grid (#3886)
Browse files Browse the repository at this point in the history
* datacube: add csv file as one of the data cube source for hosted datacube

* datacube: adding a minor comment

* Rename CSVFIleQueryDataCubeSource.ts to CSVFileQueryDataCubeSource.ts

* datacube: renaming file names, adding preview and warning
  • Loading branch information
gs-gunjan authored Feb 14, 2025
1 parent 82ea08b commit 582fda1
Show file tree
Hide file tree
Showing 13 changed files with 604 additions and 25 deletions.
7 changes: 7 additions & 0 deletions .changeset/early-socks-enjoy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@finos/legend-application-data-cube': minor
'@finos/legend-data-cube': patch
'@finos/legend-shared': patch
---

DataCube: Add CSV File as one of the data cube source
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
import { useEffect } from 'react';
import { LegendDataCubeSettingStorageKey } from '../../__lib__/LegendDataCubeSetting.js';
import type { LegendDataCubeBuilderStore } from '../../stores/builder/LegendDataCubeBuilderStore.js';
import { CSVFileDataCubeSource } from '../../stores/model/CSVFileDataCubeSource.js';

const LegendDataCubeBuilderHeader = observer(() => {
const store = useLegendDataCubeBuilderStore();
Expand All @@ -57,7 +58,10 @@ const LegendDataCubeBuilderHeader = observer(() => {
<FormButton
compact={true}
className="ml-1.5"
disabled={!store.builder?.dataCube}
disabled={
!store.builder?.dataCube ||
store.builder.source instanceof CSVFileDataCubeSource
}
onClick={() => store.saverDisplay.open()}
>
Save DataCube
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { LegendQueryDataCubeSourceBuilder } from './source/LegendQueryDataCubeSo
import { AdhocQueryDataCubeSourceBuilder } from './source/AdhocQueryDataCubeSourceBuilder.js';
import { AdhocQueryDataCubeSourceBuilderState } from '../../stores/builder/source/AdhocQueryDataCubeSourceBuilderState.js';
import { useLegendDataCubeBuilderStore } from './LegendDataCubeBuilderStoreProvider.js';
import { CSVFileDataCubeSourceBuilderState } from '../../stores/builder/source/CSVFileDataCubeSourceBuilderState.js';
import { CSVFileDataCubeSourceBuilder } from './source/CSVFileDataCubeSourceBuilder.js';

export const LegendDataCubeCreator = observer(() => {
const store = useLegendDataCubeBuilderStore();
Expand Down Expand Up @@ -61,6 +63,7 @@ export const LegendDataCubeCreator = observer(() => {
{[
LegendDataCubeSourceBuilderType.LEGEND_QUERY,
LegendDataCubeSourceBuilderType.ADHOC_QUERY,
LegendDataCubeSourceBuilderType.CSV_FILE_QUERY,
].map((type) => (
<FormDropdownMenuItem
key={type}
Expand Down Expand Up @@ -89,6 +92,9 @@ export const LegendDataCubeCreator = observer(() => {
sourceBuilder={sourceBuilder}
/>
)}
{sourceBuilder instanceof CSVFileDataCubeSourceBuilderState && (
<CSVFileDataCubeSourceBuilder sourceBuilder={sourceBuilder} />
)}
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/**
* Copyright (c) 2020-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { observer } from 'mobx-react-lite';
import type { CSVFileDataCubeSourceBuilderState } from '../../../stores/builder/source/CSVFileDataCubeSourceBuilderState.js';
import { csvStringify, parseCSVFile } from '@finos/legend-shared';
import { DataCubeIcon } from '@finos/legend-art';
import { FormBadge_WIP } from '@finos/legend-data-cube';
import { useState } from 'react';

export const CSVFileDataCubeSourceBuilder = observer(
(props: { sourceBuilder: CSVFileDataCubeSourceBuilderState }) => {
const { sourceBuilder } = props;
const [isDataUploaded, setIsDataUploaded] = useState<boolean>(false);
const [csvData, setCsvData] = useState<unknown[][]>([]);

const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setIsDataUploaded(false);

const file = e.target.files ? e.target.files[0] : null;

if (file) {
parseCSVFile(file, {
complete: (result) => {
// Set the parsed data to state
sourceBuilder.setFileData(
csvStringify(result.data, { escapeChar: `'`, quoteChar: `'` }),
);
sourceBuilder.setFileName(file.name);
sourceBuilder.setRowCount(result.data.length);

setCsvData(result.data.slice(0, 5));
setIsDataUploaded(true);
},
header: true,
dynamicTyping: false,
skipEmptyLines: true,
});
}
};

// TODO: use a react library which gives preview and let users set advance options
return (
<div className="flex h-full w-full flex-col space-y-4 p-4">
<div className="mb-4 w-full border-l-2 border-amber-500 bg-gray-200 p-4 text-black">
<div className="flex flex-col space-y-2">
<div className="flex items-center space-x-4">
<DataCubeIcon.Warning className="h-12 w-12 text-amber-500" />
<div className="flex flex-col space-y-3">
<p className="text-lg font-medium">
This is an experimental feature with following limitations:
</p>
<ul className="mt-2 list-disc space-y-1 pl-5 text-base">
<li>Not all CSV files may be compatible.</li>
<li>
Data cube views created from CSV data cannot be saved.
</li>
<li>
Data is processed locally and cannot be stored or shared.
</li>
</ul>
</div>
</div>
</div>
</div>
<div className="flex h-6 w-full items-center text-neutral-500">
<FormBadge_WIP />
<input
type="file"
onChange={handleFileChange}
className="ml-1 w-full"
/>
</div>
{isDataUploaded && csvData.length > 0 && (
<div className="mt-4 overflow-x-auto">
<table className="min-w-max table-auto border-collapse border border-gray-300">
<thead>
<tr>
{csvData[0] &&
Object.keys(csvData[0]).map((key) => (
<th
key={key}
className="border bg-gray-200 px-4 py-2 text-left"
>
{key}
</th>
))}
</tr>
</thead>
<tbody>
{csvData.map((row) => {
const rowKey = Object.values(row).join('-');

return (
<tr key={rowKey} className="bg-white hover:bg-gray-50">
{Object.entries(row).map(([columnName, value]) => {
const cellKey = `${rowKey}-${columnName}`;

return (
<td key={cellKey} className="border px-4 py-2">
{value as string}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
)}
</div>
);
},
);
Loading

0 comments on commit 582fda1

Please sign in to comment.