aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoralyssaf16 <alyssa_feinberg@brown.edu>2024-07-23 14:06:11 -0400
committeralyssaf16 <alyssa_feinberg@brown.edu>2024-07-23 14:06:11 -0400
commite82c95293bb2d9db32fc9901f57f1997b74597c1 (patch)
tree0c67a0a66fd7d80f1853720400bbf9e5684cb843 /src
parent0b2a8c0d718940ddc06a966d4e027a79bb06ed6e (diff)
labeling work
Diffstat (limited to 'src')
-rw-r--r--src/client/apis/gpt/GPT.ts1
-rw-r--r--src/client/views/nodes/ImageBox.scss8
-rw-r--r--src/client/views/nodes/ImageBox.tsx98
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.scss30
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx13
-rw-r--r--src/extensions/ArrayExtensions.ts37
-rw-r--r--src/extensions/StringExtensions.ts17
-rw-r--r--src/fields/.PresField.ts.icloudbin159 -> 0 bytes
-rw-r--r--src/fields/PresField.ts6
9 files changed, 188 insertions, 22 deletions
diff --git a/src/client/apis/gpt/GPT.ts b/src/client/apis/gpt/GPT.ts
index 1bec2fb11..3550b6216 100644
--- a/src/client/apis/gpt/GPT.ts
+++ b/src/client/apis/gpt/GPT.ts
@@ -97,6 +97,7 @@ const gptAPICall = async (inputTextIn: string, callType: GPTCallType, prompt?: a
max_tokens: opts.maxTokens,
});
lastResp = response.choices[0].message.content ?? '';
+ console.log('RESP:' + lastResp);
return lastResp;
} catch (err) {
console.log(err);
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 4690e255f..4d199b360 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -157,11 +157,12 @@
.check-icon {
position: absolute;
- right: 150;
+ right: 40;
bottom: 10;
color: green;
display: inline-block;
- font-size: 100px;
+ font-size: 20px;
+ overflow: hidden;
}
.redo-icon {
@@ -170,7 +171,8 @@
bottom: 10;
color: black;
display: inline-block;
- font-size: 100px;
+ font-size: 20px;
+ overflow: hidden;
}
@keyframes spin {
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index 37827a43a..32b9e20a4 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -36,7 +36,7 @@ import { DocCast, NumCast, RTFCast, StrCast, ImageCast, Cast, toList } from '../
import './ImageBox.scss';
import { OpenWhere } from './OpenWhere';
import { URLField } from '../../../fields/URLField';
-import { gptImageLabel } from '../../apis/gpt/GPT';
+import { gptAPICall, GPTCallType, gptImageLabel } from '../../apis/gpt/GPT';
import ReactLoading from 'react-loading';
import { FollowLinkScript } from '../../documents/DocUtils';
import { basename } from 'path';
@@ -48,6 +48,12 @@ import axios from 'axios';
import { TupleType } from 'typescript';
// import stringSimilarity from 'string-similarity';
+enum quizMode {
+ SMART = 'smart',
+ NORMAL = 'normal',
+ NONE = 'none',
+}
+
export class ImageEditorData {
// eslint-disable-next-line no-use-before-define
private static _instance: ImageEditorData;
@@ -92,7 +98,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@observable private _width: number = 0;
@observable private _height: number = 0;
@observable private searchInput = '';
- @observable private _quizMode = false;
+ @observable private _quizMode = quizMode.NONE;
@observable _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
@observable _curSuffix = '';
@observable _error = '';
@@ -359,11 +365,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
};
- pushInfo = async () => {
- this._quizMode = true;
+ pushInfo = async (quiz: quizMode) => {
+ this._quizMode = quiz;
+ this._loading = true;
+ console.log('JHSDKFJHKSDJFHKSJDHFKJSDHFKJHSDKF');
const img = {
file: this.paths[0],
+ smart: quiz,
};
const response = await axios.post('http://localhost:105/labels/', img, {
headers: {
@@ -399,8 +408,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
newCol.zIndex = 1000;
newCol.forceActive = true;
newCol.quiz = text;
+ newCol.showQuiz = false;
this._quizBoxes.push(newCol);
this.addDocument(newCol);
+ this._loading = false;
}
};
// static imageUrlToBase64 = async (imageUrl: string): Promise<string> => {
@@ -496,25 +507,61 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return distance <= threshold;
};
+ extractHexAndSentences = (inputString: string) => {
+ // Regular expression to match a hexadecimal number at the beginning followed by a period and sentences
+ const regex = /^#([0-9A-Fa-f]+)\.\s*(.+)$/s;
+ const match = inputString.match(regex);
+
+ if (match) {
+ const hexNumber = match[1];
+ const sentences = match[2].trim();
+ return { hexNumber, sentences };
+ } else {
+ return { error: 'The input string does not match the expected format.' };
+ }
+ };
+
check = () => {
- this._quizBoxes.forEach(doc => {
+ this._loading = true;
+ this._quizBoxes.forEach(async doc => {
const input = StrCast(RTFCast(DocCast(doc).text)?.Text);
console.log('INP: ' + StrCast(input) + '; DOC: ' + StrCast(doc.quiz));
- const match = this.compareWords(input, StrCast(doc.quiz));
- doc.backgroundColor = match ? '#11c249' : '#eb2d2d';
+ if (this._quizMode == quizMode.SMART && input) {
+ const questionText = 'Question: What was labeled in this image?';
+ const rubricText = ' Rubric: ' + StrCast(doc.quiz);
+ // const queryText = 'RealAnswer: ' + StrCast(doc.quiz) + '. UserAnswer: ' + input + '.';
+ const queryText =
+ questionText +
+ ' UserAnswer: ' +
+ input +
+ '. ' +
+ rubricText +
+ '. One sentence and evaluate based on meaning, not wording. Provide a hex color at the beginning with a period after it on a scale of green (minor details missed) to red (big error) for how correct the answer is. Example: "#FFFFFF. Pasta is delicious."';
+ const response = await gptAPICall(queryText, GPTCallType.QUIZ);
+ const hexSent = this.extractHexAndSentences(response);
+ console.log(hexSent.hexNumber);
+ doc.quiz = hexSent.sentences;
+ doc.backgroundColor = '#' + hexSent.hexNumber;
+ } else {
+ const match = this.compareWords(input, StrCast(doc.quiz));
+ doc.backgroundColor = match ? '#11c249' : '#eb2d2d';
+ }
+ doc.showQuiz = true;
// console.log(this.compareWords(input, StrCast(doc.quiz)) ? 'Match' : 'No Match');
});
+ this._loading = false;
};
redo = () => {
this._quizBoxes.forEach(doc => {
DocCast(doc)[DocData].text = '';
doc.backgroundColor = '#e4e4e4';
+ doc.showQuiz = false;
});
};
exitQuizMode = () => {
- this._quizMode = false;
+ this._quizMode = quizMode.NONE;
this._quizBoxes.forEach(doc => {
// this._props.removeDocument?.(DocCast(doc));
// this._props.DocumentView?.()._props.removeDocument?.(doc);
@@ -531,16 +578,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const field = Cast(this.dataDoc[this.fieldKey], ImageField);
if (field) {
const funcs: ContextMenuProps[] = [];
+ const quizes: ContextMenuProps[] = [];
// funcs.push({ description: 'Create ai flashcards', event: () => this.getImageDesc(), icon: 'id-card' });
- funcs.push({
- description: 'Quiz Mode',
- event: !this._quizMode
- ? this.pushInfo
- : () => {
- this._quizMode = false;
- },
- icon: 'redo-alt',
+ quizes.push({
+ description: 'Smart Check',
+ event: this._quizMode == quizMode.NONE ? () => this.pushInfo(quizMode.SMART) : this.exitQuizMode,
+ icon: 'pen-to-square',
+ });
+ quizes.push({
+ description: 'Normal',
+ event: this._quizMode == quizMode.NONE ? () => this.pushInfo(quizMode.NORMAL) : this.exitQuizMode,
+ icon: 'pencil',
});
+ // funcs.push({ description: 'Quiz Mode', subitems: optionItems, icon: 'eye' });
+ // funcs.push({
+ // description: 'Quiz Mode',
+ // event: !this._quizMode
+ // ? () => this.pushInfo(false)
+ // : () => {
+ // this._quizMode = false;
+ // },
+ // icon: 'redo-alt',
+ // });
// funcs.push({ description: 'Get Text', event: this.check, icon: 'redo-alt' });
// funcs.push({ description: 'Get Labels2', event: this.getImageLabels2, icon: 'redo-alt' });
// funcs.push({ description: 'Get Labels', event: this.getImageLabels, icon: 'redo-alt' });
@@ -558,6 +617,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}),
icon: 'pencil-alt',
});
+ ContextMenu.Instance?.addItem({ description: 'Quiz Mode', subitems: quizes, icon: 'file-pen' });
ContextMenu.Instance?.addItem({ description: 'Options...', subitems: funcs, icon: 'asterisk' });
}
};
@@ -689,8 +749,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
)}
</div>
{this.overlayImageIcon}
- {this._quizMode ? this.checkIcon : null}
- {this._quizMode ? this.redoIcon : null}
</div>
);
}
@@ -762,7 +820,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
height: this._props.PanelWidth() ? undefined : `100%`,
pointerEvents: this.layoutDoc._lockedPosition ? 'none' : undefined,
borderRadius,
- overflow: this.layoutDoc.layout_fitWidth || this._props.fitWidth?.(this.Document) ? 'auto' : undefined,
+ overflow: this.layoutDoc.layout_fitWidth || this._props.fitWidth?.(this.Document) ? 'auto' : 'hidden',
}}>
<CollectionFreeFormView
ref={this._ffref}
@@ -816,6 +874,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// anchorMenuFlashcard={() => this.getImageDesc()}
/>
)}
+ {this._quizMode != quizMode.NONE ? this.checkIcon : null}
+ {this._quizMode != quizMode.NONE ? this.redoIcon : null}
</div>
);
}
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss
index 54643b4a5..227cd4312 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.scss
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss
@@ -79,7 +79,37 @@ audiotag:hover {
right: 8px;
position: absolute;
}
+ .answer-icon {
+ position: absolute;
+ right: 10;
+ bottom: 10;
+ color: black;
+ display: inline-block;
+ font-size: 20px;
+ cursor: pointer;
+ border-radius: 50%;
+ }
+
+ .q-icon {
+ position: absolute;
+ right: 6;
+ bottom: 10;
+ color: white;
+ display: inline-block;
+ font-size: 20px;
+ cursor: pointer;
+ border-radius: 50%;
+ }
+}
+
+.answer-tooltip {
+ font-size: 15px;
+ padding: 2px;
+ max-width: 150;
+ line-height: 150%;
+ position: relative;
}
+
.formattedTextBox-alternateButton {
align-items: center;
flex-direction: column;
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index 274330d31..b00437cf2 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -2092,6 +2092,18 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
</Tooltip>
);
}
+
+ @computed get answerIcon() {
+ return (
+ <Tooltip title={<div className="answer-tooltip">{StrCast(this.Document.quiz)}</div>}>
+ <div className="answer-tool-tip">
+ <FontAwesomeIcon className="q-icon" icon="circle" color="white" />
+ <FontAwesomeIcon className="answer-icon" icon="question" />
+ </div>
+ </Tooltip>
+ );
+ }
+
get fieldKey() {
return this._fieldKey;
}
@@ -2210,6 +2222,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
{this.noSidebar || this.Document._layout_noSidebar || this.Document._createDocOnCR || this.layoutDoc._chromeHidden || this.Document.quiz ? null : this.sidebarHandle}
{this.audioHandle}
{this.layoutDoc._layout_enableAltContentUI && !this.layoutDoc._chromeHidden ? this.overlayAlternateIcon : null}
+ {this.Document.showQuiz ? this.answerIcon : null}
</div>
</div>
);
diff --git a/src/extensions/ArrayExtensions.ts b/src/extensions/ArrayExtensions.ts
new file mode 100644
index 000000000..8e125766d
--- /dev/null
+++ b/src/extensions/ArrayExtensions.ts
@@ -0,0 +1,37 @@
+export default class ArrayExtension {
+ private readonly property: string;
+ private readonly body: <T>(this: Array<T>) => any;
+
+ constructor(property: string, body: <T>(this: Array<T>) => any) {
+ this.property = property;
+ this.body = body;
+ }
+
+ assign() {
+ Object.defineProperty(Array.prototype, this.property, {
+ value: this.body,
+ enumerable: false
+ });
+ }
+
+}
+
+/**
+ * IMPORTANT: Any extension you add here *must* have a corresponding type definition
+ * in the Array<T> interface in ./General/ExtensionsTypings.ts. Otherwise,
+ * Typescript will not recognize your new function.
+ */
+const extensions = [
+ new ArrayExtension("lastElement", function () {
+ if (!this.length) {
+ return undefined;
+ }
+ return this[this.length - 1];
+ })
+];
+
+function Assign() {
+ extensions.forEach(extension => extension.assign());
+}
+
+export { Assign }; \ No newline at end of file
diff --git a/src/extensions/StringExtensions.ts b/src/extensions/StringExtensions.ts
new file mode 100644
index 000000000..2c76e56c8
--- /dev/null
+++ b/src/extensions/StringExtensions.ts
@@ -0,0 +1,17 @@
+function Assign() {
+
+ String.prototype.removeTrailingNewlines = function () {
+ let sliced = this;
+ while (sliced.endsWith("\n")) {
+ sliced = sliced.substring(0, this.length - 1);
+ }
+ return sliced as string;
+ };
+
+ String.prototype.hasNewline = function () {
+ return this.endsWith("\n");
+ };
+
+}
+
+export { Assign }; \ No newline at end of file
diff --git a/src/fields/.PresField.ts.icloud b/src/fields/.PresField.ts.icloud
deleted file mode 100644
index e63a55cc1..000000000
--- a/src/fields/.PresField.ts.icloud
+++ /dev/null
Binary files differ
diff --git a/src/fields/PresField.ts b/src/fields/PresField.ts
new file mode 100644
index 000000000..f236a04fd
--- /dev/null
+++ b/src/fields/PresField.ts
@@ -0,0 +1,6 @@
+//insert code here
+import { ObjectField } from "./ObjectField";
+
+export abstract class PresField extends ObjectField {
+
+} \ No newline at end of file