From 88caa55967b1dbf670b156dd08efc4f559067af7 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Mon, 19 Feb 2024 16:19:05 -0500 Subject: ai (updates from sophie, then added dataviz summary) --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 32 +++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 66a08f13e..33b7dddfc 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -18,7 +18,7 @@ import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponen import { MarqueeAnnotator } from '../../MarqueeAnnotator'; import { SidebarAnnos } from '../../SidebarAnnos'; import { AnchorMenu } from '../../pdf/AnchorMenu'; -import { GPTPopup } from '../../pdf/GPTPopup/GPTPopup'; +import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup'; import { DocumentView } from '../DocumentView'; import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView'; import { PinProps } from '../trails'; @@ -28,6 +28,8 @@ import { LineChart } from './components/LineChart'; import { PieChart } from './components/PieChart'; import { TableBox } from './components/TableBox'; import { Checkbox } from '@mui/material'; +import { ContextMenu } from '../../ContextMenu'; +import { GPTCallType, gptAPICall } from '../../../apis/gpt/GPT'; export enum DataVizView { TABLE = 'table', @@ -43,6 +45,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im private _annotationLayer: React.RefObject = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; anchorMenuClick?: () => undefined | ((anchor: Doc) => void); + sidebarAddDoc: ((doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean) | undefined; crop: ((region: Doc | undefined, addCrop?: boolean) => Doc | undefined) | undefined; @observable _marqueeing: number[] | undefined = undefined; @observable _savedAnnotations = new ObservableMap(); @@ -402,6 +405,32 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im this.layoutDoc.dataViz_schemaLive = !this.layoutDoc.dataViz_schemaLive } + specificContextMenu = (e: React.MouseEvent): void => { + const cm = ContextMenu.Instance; + const options = cm.findByDescription('Options...'); + const optionItems = options && 'subitems' in options ? options.subitems : []; + optionItems.push({ description: `Analyze with AI`, event: () => this.askGPT(), icon: 'lightbulb' }); + !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' }); + } + + + askGPT = action(async () => { + GPTPopup.Instance.setVisible(true); + GPTPopup.Instance.setSidebarId('data_sidebar'); + GPTPopup.Instance.addDoc = this.sidebarAddDocument; + GPTPopup.Instance.setMode(GPTPopupMode.DATA); + GPTPopup.Instance.setLoading(true); + try { + let data = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href); + let input = JSON.stringify(data); + let res = await gptAPICall(input, GPTCallType.DATA); + GPTPopup.Instance.setText(res || 'Something went wrong.'); + } catch (err) { + console.error(err); + } + GPTPopup.Instance.setLoading(false); + }); + render() { const scale = this._props.NativeDimScaling?.() || 1; return !this.records.length ? ( @@ -418,6 +447,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im transform: `scale(${scale})`, position: 'absolute', }} + onContextMenu={this.specificContextMenu} onWheel={e => e.stopPropagation()} ref={this._mainCont}>
-- cgit v1.2.3-70-g09d2 From 54c597bb865aba15f16e0692c2f2119109d119aa Mon Sep 17 00:00:00 2001 From: srichman333 Date: Wed, 28 Feb 2024 19:24:22 -0500 Subject: chat with ai --- src/client/apis/gpt/GPT.ts | 5 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 16 ++---- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 62 ++++++++++++++++++++---- 3 files changed, 59 insertions(+), 24 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts index 82436c42b..97a332294 100644 --- a/src/client/apis/gpt/GPT.ts +++ b/src/client/apis/gpt/GPT.ts @@ -33,7 +33,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = { * @param inputText Text to process * @returns AI Output */ -const gptAPICall = async (inputText: string, callType: GPTCallType) => { +const gptAPICall = async (inputText: string, callType: GPTCallType, prompt?: any) => { if (callType === GPTCallType.SUMMARY) inputText += '.'; const opts: GPTCallOpts = callTypeMap[callType]; try { @@ -43,8 +43,9 @@ const gptAPICall = async (inputText: string, callType: GPTCallType) => { }; const openai = new OpenAI(configuration); + let usePrompt = prompt? prompt: opts.prompt; let messages: ChatCompletionMessageParam[] = [ - { role: 'system', content: opts.prompt }, + { role: 'system', content: usePrompt }, { role: 'user', content: inputText }, ]; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 33b7dddfc..0a7fe59a5 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -29,7 +29,6 @@ import { PieChart } from './components/PieChart'; import { TableBox } from './components/TableBox'; import { Checkbox } from '@mui/material'; import { ContextMenu } from '../../ContextMenu'; -import { GPTCallType, gptAPICall } from '../../../apis/gpt/GPT'; export enum DataVizView { TABLE = 'table', @@ -415,20 +414,13 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im askGPT = action(async () => { - GPTPopup.Instance.setVisible(true); GPTPopup.Instance.setSidebarId('data_sidebar'); GPTPopup.Instance.addDoc = this.sidebarAddDocument; GPTPopup.Instance.setMode(GPTPopupMode.DATA); - GPTPopup.Instance.setLoading(true); - try { - let data = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href); - let input = JSON.stringify(data); - let res = await gptAPICall(input, GPTCallType.DATA); - GPTPopup.Instance.setText(res || 'Something went wrong.'); - } catch (err) { - console.error(err); - } - GPTPopup.Instance.setLoading(false); + let data = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href); + let input = JSON.stringify(data); + GPTPopup.Instance.setDataJson(input); + GPTPopup.Instance.generateDataAnalysis(); }); render() { diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 8c575748f..6301721e8 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, IconButton, Type } from 'browndash-components'; +import { Button, EditableText, IconButton, Size, Type } from 'browndash-components'; import { action, makeObservable, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -28,6 +28,7 @@ interface GPTPopupProps {} @observer export class GPTPopup extends ObservableReactComponent { static Instance: GPTPopup; + @observable private chatMode: boolean = false; @observable public visible: boolean = false; @@ -53,6 +54,13 @@ export class GPTPopup extends ObservableReactComponent { public setSelectedText = (text: string) => { this.selectedText = text; }; + @observable + public dataJson: string = ''; + public dataChatPrompt: string | null = null; + @action + public setDataJson = (text: string) => { + this.dataJson = text; + }; @observable public imgDesc: string = ''; @@ -86,6 +94,7 @@ export class GPTPopup extends ObservableReactComponent { @action public setDone = (done: boolean) => { this.done = done; + this.chatMode = false; }; // change what can be a ref into a ref @@ -153,6 +162,18 @@ export class GPTPopup extends ObservableReactComponent { GPTPopup.Instance.setLoading(false); } + generateDataAnalysis = async () => { + GPTPopup.Instance.setVisible(true); + GPTPopup.Instance.setLoading(true); + try { + let res = await gptAPICall(this.dataJson, GPTCallType.DATA, this.dataChatPrompt); + GPTPopup.Instance.setText(res || 'Something went wrong.'); + } catch (err) { + console.error(err); + } + GPTPopup.Instance.setLoading(false); + } + /** * Transfers the summarization text to a sidebar annotation text document. */ @@ -197,6 +218,16 @@ export class GPTPopup extends ObservableReactComponent { DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' }); }; + /** + * Creates a chatbox for analyzing data so that users can ask specific questions. + */ + private chatWithAI = () => { + this.chatMode = true; + } + dataPromptChanged = action((e: React.ChangeEvent) => { + this.dataChatPrompt = e.target.value; + }); + private getPreviewUrl = (source: string) => source.split('.').join('_m.'); constructor(props: GPTPopupProps) { @@ -310,22 +341,33 @@ export class GPTPopup extends ObservableReactComponent {
{!this.loading && (
- {this.done ? ( + {this.done? + this.chatMode?( + { + e.key === 'Enter' ? this.generateDataAnalysis() : null; + e.stopPropagation(); + }} + type="text" + placeholder="Ask GPT a question about the data..." + id="search-input" + className="searchBox-input" + style={{width: "100%"}} + /> + ) + :( <>
)} -- cgit v1.2.3-70-g09d2 From dfef33c3f1cde9a24d734e0bd47e7f377a3ae4c9 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Tue, 5 Mar 2024 16:17:14 -0500 Subject: chat resets for new boxes to analyze --- src/client/views/nodes/DataVizBox/DataVizBox.scss | 4 ++++ src/client/views/nodes/DataVizBox/DataVizBox.tsx | 9 ++++++--- src/client/views/pdf/GPTPopup/GPTPopup.tsx | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.scss b/src/client/views/nodes/DataVizBox/DataVizBox.scss index 6b5738790..e9a346fbe 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.scss +++ b/src/client/views/nodes/DataVizBox/DataVizBox.scss @@ -32,6 +32,10 @@ .liveSchema-checkBox { margin-bottom: -35px; } + + .displaySchemaLive { + margin-bottom: 20px; + } .dataviz-sidebar { position: absolute; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 0a7fe59a5..b4da41fa0 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -416,6 +416,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im askGPT = action(async () => { GPTPopup.Instance.setSidebarId('data_sidebar'); GPTPopup.Instance.addDoc = this.sidebarAddDocument; + GPTPopup.Instance.setDataJson(""); GPTPopup.Instance.setMode(GPTPopupMode.DATA); let data = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href); let input = JSON.stringify(data); @@ -450,9 +451,11 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im {(this.layoutDoc && this.layoutDoc.dataViz_asSchema)?( -
- - Display Live Updates to Canvas +
+
+ + Display Live Updates to Canvas +
) : null} diff --git a/src/client/views/pdf/GPTPopup/GPTPopup.tsx b/src/client/views/pdf/GPTPopup/GPTPopup.tsx index 5f399e86a..29b1ca365 100644 --- a/src/client/views/pdf/GPTPopup/GPTPopup.tsx +++ b/src/client/views/pdf/GPTPopup/GPTPopup.tsx @@ -59,6 +59,7 @@ export class GPTPopup extends ObservableReactComponent { public dataChatPrompt: string | null = null; @action public setDataJson = (text: string) => { + if (text=="") this.dataChatPrompt = ""; this.dataJson = text; }; -- cgit v1.2.3-70-g09d2 From 75387187e260a7f569b73b1e6e8057e5623b9650 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 29 Mar 2024 18:16:13 -0400 Subject: small fix to retoreTargetView. reformatted file. --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 79 ++++++++++++------------ 1 file changed, 40 insertions(+), 39 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DataVizBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 66a08f13e..22f1f7b79 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -118,8 +118,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im @action // pinned / linked anchor doc includes selected rows, graph titles, and graph colors restoreView = (data: Doc) => { - const changedView = this.dataVizView !== data.config_dataViz && (this.layoutDoc._dataViz = data.config_dataViz); - const changedAxes = this.axes.join('') !== StrListCast(data.config_dataVizAxes).join('') && (this.layoutDoc._dataViz_axes = new List(StrListCast(data.config_dataVizAxes))); + const changedView = data.config_dataViz && this.dataVizView !== data.config_dataViz && (this.layoutDoc._dataViz = data.config_dataViz); + const changedAxes = data.config_dataVizAxes && this.axes.join('') !== StrListCast(data.config_dataVizAxes).join('') && (this.layoutDoc._dataViz_axes = new List(StrListCast(data.config_dataVizAxes))); this.layoutDoc.dataViz_selectedRows = Field.Copy(data.dataViz_selectedRows); this.layoutDoc.dataViz_histogram_barColors = Field.Copy(data.dataViz_histogram_barColors); this.layoutDoc.dataViz_histogram_defaultColor = data.dataViz_histogram_defaultColor; @@ -266,7 +266,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im if (!DataVizBox.dataset.has(CsvCast(this.dataDoc[this.fieldKey]).url.href)) this.fetchData(); this._disposers.datavis = reaction( () => { - if (this.layoutDoc.dataViz_schemaLive==undefined) this.layoutDoc.dataViz_schemaLive = true; + if (this.layoutDoc.dataViz_schemaLive == undefined) this.layoutDoc.dataViz_schemaLive = true; const getFrom = DocCast(this.layoutDoc.dataViz_asSchema); const keys = Cast(getFrom?.schema_columnKeys, listSpec('string'))?.filter(key => key != 'text'); if (!keys) return; @@ -283,42 +283,43 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im }); current.push(row); }); - if (!this.layoutDoc._dataViz_schemaOG){ // makes a copy of the original table for the "live" toggle - let csvRows = []; - csvRows.push(keys.join(',')); - for (let i = 0; i < children.length-1; i++) { - let eachRow = []; - for (let j = 0; j < keys.length; j++) { - var cell = children[i][keys[j]]; - if (cell && (cell as string)) cell = cell.toString().replace(/\,/g, ''); - eachRow.push(cell); - } - csvRows.push(eachRow); + if (!this.layoutDoc._dataViz_schemaOG) { + // makes a copy of the original table for the "live" toggle + let csvRows = []; + csvRows.push(keys.join(',')); + for (let i = 0; i < children.length - 1; i++) { + let eachRow = []; + for (let j = 0; j < keys.length; j++) { + var cell = children[i][keys[j]]; + if (cell && (cell as string)) cell = cell.toString().replace(/\,/g, ''); + eachRow.push(cell); } - const blob = new Blob([csvRows.join('\n')], { type: 'text/csv' }); - const options = { x: 0, y: 0, title: 'schemaTable for static dataviz', _width: 300, _height: 100, type: 'text/csv' }; - const file = new File([blob], 'schemaTable for static dataviz', options); - const loading = Docs.Create.LoadingDocument(file, options); - DocUtils.uploadFileToDoc(file, {}, loading); - this.layoutDoc._dataViz_schemaOG = loading; - } - const ogDoc = this.layoutDoc._dataViz_schemaOG as Doc - const ogHref = CsvCast(ogDoc[this.fieldKey])? CsvCast(ogDoc[this.fieldKey]).url.href : undefined; - const href = CsvCast(this.Document[this.fieldKey]).url.href - if (ogHref && !DataVizBox.datasetSchemaOG.has(href)){ // sets original dataset to the var - const lastRow = current.pop(); - DataVizBox.datasetSchemaOG.set(href, current); - current.push(lastRow!); - fetch('/csvData?uri=' + ogHref) - .then(res => res.json().then(action(res => !res.errno && DataVizBox.datasetSchemaOG.set(href, res)))); + csvRows.push(eachRow); } + const blob = new Blob([csvRows.join('\n')], { type: 'text/csv' }); + const options = { x: 0, y: 0, title: 'schemaTable for static dataviz', _width: 300, _height: 100, type: 'text/csv' }; + const file = new File([blob], 'schemaTable for static dataviz', options); + const loading = Docs.Create.LoadingDocument(file, options); + DocUtils.uploadFileToDoc(file, {}, loading); + this.layoutDoc._dataViz_schemaOG = loading; + } + const ogDoc = this.layoutDoc._dataViz_schemaOG as Doc; + const ogHref = CsvCast(ogDoc[this.fieldKey]) ? CsvCast(ogDoc[this.fieldKey]).url.href : undefined; + const href = CsvCast(this.Document[this.fieldKey]).url.href; + if (ogHref && !DataVizBox.datasetSchemaOG.has(href)) { + // sets original dataset to the var + const lastRow = current.pop(); + DataVizBox.datasetSchemaOG.set(href, current); + current.push(lastRow!); + fetch('/csvData?uri=' + ogHref).then(res => res.json().then(action(res => !res.errno && DataVizBox.datasetSchemaOG.set(href, res)))); + } return current; }, current => { if (current) { - const href = CsvCast(this.Document[this.fieldKey]).url.href; - if (this.layoutDoc.dataViz_schemaLive) DataVizBox.dataset.set(href, current); - else DataVizBox.dataset.set(href, DataVizBox.datasetSchemaOG.get(href)!); + const href = CsvCast(this.Document[this.fieldKey]).url.href; + if (this.layoutDoc.dataViz_schemaLive) DataVizBox.dataset.set(href, current); + else DataVizBox.dataset.set(href, DataVizBox.datasetSchemaOG.get(href)!); } }, { fireImmediately: true } @@ -399,8 +400,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im @action changeLiveSchemaCheckbox = () => { - this.layoutDoc.dataViz_schemaLive = !this.layoutDoc.dataViz_schemaLive - } + this.layoutDoc.dataViz_schemaLive = !this.layoutDoc.dataViz_schemaLive; + }; render() { const scale = this._props.NativeDimScaling?.() || 1; @@ -427,11 +428,11 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() im (this.layoutDoc._dataViz = DataVizView.PIECHART)} toggleStatus={this.layoutDoc._dataViz == -DataVizView.PIECHART} />
- {(this.layoutDoc && this.layoutDoc.dataViz_asSchema)?( + {this.layoutDoc && this.layoutDoc.dataViz_asSchema ? (
- - Display Live Updates to Canvas -
+ + Display Live Updates to Canvas + ) : null} {this.renderVizView} -- cgit v1.2.3-70-g09d2