aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralyssaf16 <alyssa_feinberg@brown.edu>2024-06-06 09:53:12 -0400
committeralyssaf16 <alyssa_feinberg@brown.edu>2024-06-06 09:53:12 -0400
commitc76505f56a83625e3993427838aaee0c54c5fb81 (patch)
tree99f498b643b9862e5d1ec34b75ffdaeeb0a52877
parent585f03bf45df4ac7ed61d22c9dbe10d8e453199d (diff)
Fixes to chatcard and quizcard
-rw-r--r--src/client/apis/gpt/GPT.ts2
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx7
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx43
-rw-r--r--src/client/views/nodes/DocumentView.tsx19
4 files changed, 40 insertions, 31 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 05007960d..b036349dc 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -51,7 +51,7 @@ const callTypeMap: { [type: string]: GPTCallOpts } = {
model: 'gpt-4-turbo',
maxTokens: 1024,
temp: 0,
- prompt: 'List unique differences between the content of the UserAnswer and Rubric. Before each difference, label it and provide any additional information the UserAnswer missed and explain it in second person without separating it into UserAnswer and Rubric content and additional information. If there are no differences, say correct',
+ prompt: 'List unique differences between the content of the UserAnswer and Rubric. Before each difference, label it and provide any additional information the UserAnswer missed and explain it in second person without separating it into UserAnswer and Rubric content and additional information. If the Rubric is incorrect, explain why. If there are no differences, say correct. If it is empty, say there is nothing for me to evaluate.',
},
};
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 2893de762..53d14e6e0 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -35,6 +35,7 @@ export class CollectionCarouselView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
@observable private _message = 'Drag a document';
get practiceField() { return this.fieldKey + "_practice"; } // prettier-ignore
+ get sideField() { return "_" + this.fieldKey + "_usePath"; } // prettier-ignore
get starField() { return this.fieldKey + "_star"; } // prettier-ignore
constructor(props: any) {
@@ -131,9 +132,6 @@ export class CollectionCarouselView extends CollectionSubView() {
curDoc.layout[this.practiceField] = val;
this.advance(e);
};
- clearContent = () => {
- this.carouselItems?.map(doc => (doc.layout[this.practiceField] = undefined));
- };
captionStyleProvider = (doc: Doc | undefined, captionProps: Opt<FieldViewProps>, property: string): any => {
// first look for properties on the document in the carousel, then fallback to properties on the container
const childValue = doc?.['caption_' + property] ? this._props.styleProvider?.(doc, captionProps, property) : undefined;
@@ -146,7 +144,8 @@ export class CollectionCarouselView extends CollectionSubView() {
setFilterMode = (mode: cardMode) => {
this.layoutDoc.filterOp = mode;
if (mode == cardMode.STAR) this.move(1);
- this.clearContent();
+ if (mode == cardMode.QUIZ) this.carouselItems?.map(doc => (doc.layout[this.sideField] = undefined));
+ this.carouselItems?.map(doc => (doc.layout[this.practiceField] = undefined));
};
specificMenu = (): void => {
const cm = ContextMenu.Instance;
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index c0c173d9a..6d4af2f3e 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -25,6 +25,8 @@ import { DocumentView } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { FormattedTextBox } from './formattedText/FormattedTextBox';
import ReactLoading from 'react-loading';
+import { ContextMenu } from '../ContextMenu';
+import { ContextMenuProps } from '../ContextMenuItem';
@observer
export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@@ -282,7 +284,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
// Call the GPT model and get the output
this.layoutDoc[`_${this._props.fieldKey}_usePath`] = 'alternate';
this._outputValue = '';
- if (this._inputValue) this.askGPT();
+ if (this._inputValue) this.askGPT(GPTCallType.QUIZ);
};
@action handleRenderClick = () => {
@@ -290,12 +292,22 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
this.layoutDoc[`_${this._props.fieldKey}_usePath`] = undefined;
};
- animateRes = (resIndex: number, newText: string) => {
+ animateRes = (resIndex: number, newText: string, callType: GPTCallType) => {
if (resIndex < newText.length) {
// const marks = this._editorView?.state.storedMarks ?? [];
- this._outputValue += newText[resIndex];
+ switch (callType) {
+ case GPTCallType.CHATCARD:
+ DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text += newText[resIndex];
+ break;
+ case GPTCallType.QUIZ:
+ this._outputValue += newText[resIndex];
+ break;
+ default:
+ return;
+ }
+
// this._editorView?.dispatch(this._editorView?.state.tr.insertText(newText[resIndex]).setStoredMarks(this._outputValue));
- setTimeout(() => this.animateRes(resIndex + 1, newText), 20);
+ setTimeout(() => this.animateRes(resIndex + 1, newText, callType), 20);
}
};
@@ -303,18 +315,22 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
* Calls the GPT model to create QuizCards. Evaluates how similar the user's response is to the alternate
* side of the flashcard.
*/
- askGPT = async (): Promise<string | undefined> => {
+ askGPT = async (callType: GPTCallType): Promise<string | undefined> => {
const questionText = 'Question: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_1']).text)?.Text);
const rubricText = ' Rubric: ' + StrCast(RTFCast(DocCast(this.dataDoc[this.fieldKey + '_0']).text)?.Text);
const queryText = questionText + ' UserAnswer: ' + this._inputValue + '. ' + rubricText;
this._loading = true;
+ if (callType == GPTCallType.CHATCARD) {
+ DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = '';
+ this.flipFlashcard();
+ }
try {
- const res = await gptAPICall(queryText, GPTCallType.QUIZ);
+ const res = await gptAPICall(callType == GPTCallType.QUIZ ? queryText : questionText, callType);
if (!res) {
console.error('GPT call failed');
return;
}
- this.animateRes(0, '\n\n' + res);
+ this.animateRes(0, res, callType);
// this._outputValue = res;
console.log(res);
} catch (err) {
@@ -325,6 +341,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
layoutWidth = () => NumCast(this.layoutDoc.width, 200);
layoutHeight = () => NumCast(this.layoutDoc.height, 200);
+ specificMenu = (): void => {
+ const cm = ContextMenu.Instance;
+ cm.addItem({ description: 'Create an Answer on the Back', event: () => this.askGPT(GPTCallType.CHATCARD), icon: 'pencil' });
+ };
+
render() {
const clearButton = (which: string) => (
<Tooltip title={<div className="dash-tooltip">remove</div>}>
@@ -425,7 +446,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
</div>
<div className="submit-button" style={{ overflow: 'hidden', marginBottom: '2px', display: this.layoutDoc[`_${this._props.fieldKey}_usePath`] === 'alternate' ? 'flex' : 'none' }}>
<button type="button" onClick={this.handleRenderClick} style={{ borderRadius: '2px' }}>
- Try Again
+ Redo the Question
</button>
</div>
</div>
@@ -436,6 +457,7 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
return (
<div
className={`comparisonBox${this._props.isContentActive() ? '-interactive' : ''}`} /* change className to easily disable/enable pointer events in CSS */
+ onContextMenu={this.specificMenu}
style={{ display: 'flex', flexDirection: 'column' }}
onMouseEnter={() => {
this.hoverFlip('alternate');
@@ -446,6 +468,11 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
// onPointerUp={() => (this._isAnyChildContentActive = true)}
>
{displayBox(`${this.fieldKey}_${side === 0 ? 1 : 0}`, side, this._props.PanelWidth() - 3)}
+ {this._loading ? (
+ <div className="loading-spinner" style={{ position: 'absolute' }}>
+ <ReactLoading type="spin" height={30} width={30} color={'blue'} />
+ </div>
+ ) : null}
{this.overlayAlternateIcon}
</div>
);
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 8bf7b094f..a25249eac 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -111,6 +111,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
private _downTime: number = 0;
private _lastTap: number = 0;
private _doubleTap = false;
+ private _loading = false;
private _mainCont = React.createRef<HTMLDivElement>();
private _titleRef = React.createRef<EditableView>();
private _dropDisposer?: DragManager.DragDropDisposer;
@@ -501,21 +502,6 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
input.click();
};
- askGPT = async (): Promise<string | undefined> => {
- const queryText = RTFCast(DocCast(this.dataDoc[this.props.fieldKey + '_1']).text)?.Text;
- try {
- const res = await gptAPICall('Question: ' + StrCast(queryText), GPTCallType.CHATCARD);
- if (!res) {
- console.error('GPT call failed');
- return;
- }
- DocCast(this.dataDoc[this.props.fieldKey + '_0'])[DocData].text = res;
- console.log(res);
- } catch (err) {
- console.error('GPT call failed');
- }
- };
-
onContextMenu = (e?: React.MouseEvent, pageX?: number, pageY?: number) => {
if (e && this.layoutDoc.layout_hideContextMenu && Doc.noviceMode) {
e.preventDefault();
@@ -574,9 +560,6 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
appearanceItems.splice(0, 0, { description: 'Open in Lightbox', event: () => DocumentView.SetLightboxDoc(this.Document), icon: 'external-link-alt' });
}
appearanceItems.push({ description: 'Pin', event: () => this._props.pinToPres(this.Document, {}), icon: 'eye' });
- if (this.Document._layout_isFlashcard) {
- appearanceItems.push({ description: 'Create an answer on the back', event: () => this.askGPT(), icon: 'id-card' });
- }
!Doc.noviceMode && templateDoc && appearanceItems.push({ description: 'Open Template ', event: () => this._props.addDocTab(templateDoc, OpenWhere.addRight), icon: 'eye' });
!appearance && appearanceItems.length && cm.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'compass' });