aboutsummaryrefslogtreecommitdiff
path: root/src/models
diff options
context:
space:
mode:
Diffstat (limited to 'src/models')
-rw-r--r--src/models/CashoutMethod.ts33
-rw-r--r--src/models/answer.ts9
-rw-r--r--src/models/answerSlice.ts241
-rw-r--r--src/models/bucket.ts32
-rw-r--r--src/models/bucketSlice.ts8
-rw-r--r--src/models/question.ts272
-rw-r--r--src/models/questionSlice.ts3
7 files changed, 253 insertions, 345 deletions
diff --git a/src/models/CashoutMethod.ts b/src/models/CashoutMethod.ts
deleted file mode 100644
index 8da88ac..0000000
--- a/src/models/CashoutMethod.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import {CashoutMethodOut} from "@/api/models/cashout-method-out.ts"
-
-export class CashoutMethod implements CashoutMethodOut {
- id: string; // uuid
- currency: string; // 3-letter all caps
- originalCurrency: string; // 3-letter all caps
- data: any;
- description: string; // html description
- imageUrl: string; // cloudfront cdn
- maxValue: number; // pixels int
- minValue: number; // pixels int
- name: string;
- type: string; // TANGO
- extId: string; // U179271
- productId?: any;
- productUserId?: any;
-
- constructor(data) {
- this.originalCurrency = data.original_currency;
- this.currency = data.currency;
- this.data = data.data;
- this.description = data.description;
- this.extId = data.ext_id;
- this.id = data.id;
- this.imageUrl = data.image_url;
- this.maxValue = data.max_value;
- this.minValue = data.min_value;
- this.name = data.name;
- this.type = data.type;
-
- }
-
-} \ No newline at end of file
diff --git a/src/models/answer.ts b/src/models/answer.ts
new file mode 100644
index 0000000..ffdb200
--- /dev/null
+++ b/src/models/answer.ts
@@ -0,0 +1,9 @@
+
+export interface Answer {
+ questionId: string;
+ values: string[];
+ error_msg: string;
+
+ _complete: boolean;
+ _processing: boolean;
+} \ No newline at end of file
diff --git a/src/models/answerSlice.ts b/src/models/answerSlice.ts
new file mode 100644
index 0000000..1255854
--- /dev/null
+++ b/src/models/answerSlice.ts
@@ -0,0 +1,241 @@
+import {createSlice, PayloadAction} from '@reduxjs/toolkit'
+import type {RootState} from '@/store'
+import {selectQuestionById} from "@/models/questionSlice.ts"
+// import {Answer} from "@/models/answer.ts";
+// import {stringify} from "querystring";
+// import {PatternType} from "@/types.ts";
+
+// import { enableMapSet } from 'immer'
+// enableMapSet()
+
+// const initialState = new Map<string, Answer>();
+const initialState = {};
+
+const answerSlice = createSlice({
+ name: 'answers',
+ initialState,
+ reducers: {
+ addAnswer(state, action: PayloadAction<{questionId: string, val: string}>) {
+ let question = selectQuestionById(state, action.payload.questionId)
+ let val = action.payload.val.trim();
+
+ console.log("addAnswer:", question, val)
+
+ // switch (question.questionType) {
+ // case "TE":
+ // let answer: Answer = {
+ // questionId: question.questionId,
+ // values: [val]
+ // } as Answer
+ //
+ // break
+ // // return {question.questionId: answer}
+ //
+ // case "MC":
+ // let answer = selectAnswerByQuestionId(questionId)
+ //
+ // if (answer) {
+ // let current_values: string[] = answer.values
+ // } else {
+ // let current_values: string[] = []
+ // }
+ //
+ // current_values.push(val)
+ // let new_answer = new Anwer(question.questionId, val);
+ //
+ // return {question.questionId: new_answer}
+ //
+ // default:
+ // throw new Error("Incorrect Question Type provided");
+ // }
+
+ // this.validate()
+ },
+
+ setAnswer(state, action: PayloadAction<{ questionId: string, val: string }>) {
+ const {questionId, val} = action.payload
+ console.log(questionId, val)
+ const existingQuestion = state.find(q => q.questionId === action.payload.questionId)
+ if (existingQuestion) {
+ // existingQuestion.addAnswer(action.payload.val)
+ // existingQuestion.error_msg = "yess"
+ }
+ }
+
+ // removeAnswer(val: string): null {
+ // switch (this.getType()) {
+ //
+ // // You can only remove a value from a MultiChoice
+ // case "MC":
+ // // TODO: implement this
+ // // let current_values: string[] = this._answer?.values
+ // // current_values.push(val)
+ // // this._answer = new ProfilingAnswer(this.questionId, current_values);
+ // break
+ //
+ // default:
+ // throw new Error("Incorrect Question Type provided");
+ // }
+ //
+ // this.validate()
+ // }
+
+
+ // validate(): boolean {
+ // /*
+ // If the question is MC, validate:
+ // - validate selector SA vs MA (1 selected vs >1 selected)
+ // - the answers match actual codes in the choices
+ // - validate configuration.max_select
+ // - validate choices.exclusive
+ //
+ // If the question is TE, validate that:
+ // - configuration.max_length
+ // - validation.patterns
+ // */
+ //
+ // if (this._answer == null) {
+ // this.error_msg = "An answer is required"
+ // return false
+ // }
+ //
+ // let qa: ProfilingAnswer = this._answer;
+ //
+ // switch (this.getType()) {
+ // case "TE":
+ // if (qa.values.length == 0) {
+ // this.error_msg = "An answer is required"
+ // return false
+ // }
+ //
+ // if (qa.values.length > 1) {
+ // this.error_msg = "Only one answer allowed"
+ // return false
+ // }
+ //
+ // let answer: string = qa.values[0]
+ //
+ // if (answer.length <= 0) {
+ // this.error_msg = "Must provide answer"
+ // return false
+ // }
+ //
+ // const max_length: number = (this.configuration ?? {})["max_length"] ?? 100000
+ //
+ // if (answer.length > max_length) {
+ // this.error_msg = "Answer longer than allowed"
+ // return false
+ // }
+ //
+ // const patterns: PatternType[] = (this.validation ?? {})["patterns"] ?? []
+ //
+ // patterns.forEach((pattern) => {
+ // let re = new RegExp(pattern["pattern"])
+ // if (answer.search(re) == -1) {
+ // this.error_msg = pattern["message"]
+ // return false
+ // }
+ // })
+ //
+ // this.error_msg = ""
+ // return true
+ //
+ // case "MC":
+ // // if (qa.values.length == 0) {
+ // // this.error_msg = "MC question with no selected answers"
+ // // return false
+ // // }
+ // //
+ // // const choice_codes = map(this.getChoices().toJSON(), "choice_id")
+ // //
+ // // switch (this.getSelector()) {
+ // // case "SA":
+ // // if (qa.values.length > 1) {
+ // // this.error_msg = "Single Answer MC question with >1 selected answers"
+ // // return false
+ // // }
+ // // break
+ // // case "MA":
+ // // if (qa.values.length > choice_codes.length) {
+ // // this.error_msg = "More options selected than allowed"
+ // // return false
+ // // }
+ // // break
+ // // }
+ // //
+ // // if (!every(qa.values, (v) => {
+ // // return includes(choice_codes, v["value"])
+ // // })) {
+ // // this.error_msg = "Invalid Options Selected"
+ // // return false
+ // // }
+ // //
+ // // const max_select: number = (this.configuration ?? {})["max_select"] ?? choice_codes.length
+ // // if (qa.values.length > max_select) {
+ // // this.error_msg = "More options selected than allowed"
+ // // return false
+ // // }
+ //
+ // /*
+ // exclusive_choice = next((x for x in question["choices"] if x.get("exclusive")), None)
+ // if exclusive_choice:
+ // exclusive_choice_id = exclusive_choice["choice_id"]
+ // assert answer == [exclusive_choice_id] or \
+ // exclusive_choice_id not in answer, "Invalid exclusive selection"
+ // */
+ //
+ // this.error_msg = ""
+ // return true
+ //
+ // default:
+ // throw new Error("Incorrect Question Type provided");
+ // }
+ //
+ // }
+
+
+
+ // save() {
+ // let question: ProfilingQuestion = this;
+ // // @ts-ignore
+ // let answer: ProfilingAnswer = question._answer;
+ //
+ // if (this._complete || this._processing) {
+ // return
+ // }
+ // this._processing = true
+ //
+ // let res = JSON.stringify({
+ // "answers": [{
+ // "question_id": answer.get('question_id'),
+ // "answer": map(answer.get("values"), "value")
+ // }]
+ // });
+ //
+ // $.ajax({
+ // url: ["https://fsb.generalresearch.com", questions.BPID, "profiling-questions", ""].join("/") + "?" + stringify({"bpuid": questions.BPUID}),
+ // xhrFields: {withCredentials: false},
+ // processData: false,
+ // type: "POST",
+ // contentType: "application/json; charset=utf-8",
+ // data: res,
+ // success: function (data) {
+ // channel.trigger("ProfilingQuestions:start");
+ // },
+ // error: function (data) {
+ // channel.trigger("ProfilingQuestions:start");
+ // Sentry.captureMessage("Profiling Question submission failed.");
+ // }
+ // });
+ // }
+
+
+ }
+})
+
+// Export the generated reducer function
+export const {setAnswer, setQuestions, questionAdded, questionUpdated} = answerSlice.actions;
+export default answerSlice.reducer
+
+export const selectAnswerCount = (state: RootState) => state.answers.size
+// export const selectAnswerByQuestionId = (state: RootState, questionId: string) => state.answers.find(a => q.questionId === questionId) \ No newline at end of file
diff --git a/src/models/bucket.ts b/src/models/bucket.ts
deleted file mode 100644
index a9419a6..0000000
--- a/src/models/bucket.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import {SoftPairBucket} from "@/api/models/soft-pair-bucket.ts"
-
-export class SoftPairBucketClass implements SoftPairBucket {
- readonly id: string;
- readonly uri: string;
- readonly category: any | null;
- readonly contents: any;
- readonly eligibility: any;
- readonly missingQuestions: any | null;
- readonly loi: any;
- readonly payout: any;
-
- constructor(data: {
- id: string,
- uri: string,
- category: any | null,
- contents: any,
- eligibility: any,
- missingQuestions: any | null,
- loi: any,
- payout: any
- }) {
- this.id = data.id;
- this.uri = data.uri;
- this.category = data.category;
- this.contents = data.contents;
- this.eligibility = data.eligibility;
- this.missingQuestions = data.missingQuestions;
- this.loi = data.loi;
- this.payout = data.payout;
- }
-} \ No newline at end of file
diff --git a/src/models/bucketSlice.ts b/src/models/bucketSlice.ts
index 9c6aee8..3a2c0d1 100644
--- a/src/models/bucketSlice.ts
+++ b/src/models/bucketSlice.ts
@@ -1,13 +1,10 @@
import {createSlice, PayloadAction} from '@reduxjs/toolkit'
-import {SoftPairBucketClass} from "@/models/bucket.ts"
-// import {ProfilingQuestion} from "@/models/question.ts";
import type {RootState} from '@/store'
-import {SoftPairBucket} from "@/api/models/soft-pair-bucket.ts"
+import {SoftPairBucket} from "@/api";
+
-// Create an initial state value for the reducer, with that type
const initialState: SoftPairBucket[] = []
-// Create the slice and pass in the initial state
const bucketSlice = createSlice({
name: 'buckets',
initialState,
@@ -21,7 +18,6 @@ const bucketSlice = createSlice({
}
})
-// Export the generated reducer function
export const {setBuckets, bucketAdded} = bucketSlice.actions;
export default bucketSlice.reducer
diff --git a/src/models/question.ts b/src/models/question.ts
deleted file mode 100644
index 996589b..0000000
--- a/src/models/question.ts
+++ /dev/null
@@ -1,272 +0,0 @@
-import {stringify} from "querystring";
-import {ChoiceType, ConfigurationType, PatternType, QuestionType, SelectorType, ValidationType} from "@/types.ts"
-import {UpkQuestion} from "@/api/models/upk-question.ts"
-import {UpkQuestionType} from "@/api";
-
-
-export class ProfilingAnswer {
- // let values: Array<{ value: string }> = question_answer.values || []
-
- questionId: string;
- values: string[] = [];
-
- constructor(qid: string, values: string[]) {
- this.questionId = qid;
- this.values = values
- }
-}
-
-export class ProfilingQuestion implements UpkQuestion {
- questionId: string; // It's a UUID
- countryIso: string; // 2-letter lower
- languageIso: string; // 3-letter lower
-
- questionType: UpkQuestionType;
- selector: SelectorType;
- questionText: string; // "title" of the question
-
- choices?: ChoiceType[];
-
- extQuestionId?: any;
- configuration: ConfigurationType | null = null;
- validation: ValidationType | null = null;
-
- importance?: any;
- task_count: number;
- task_score: number;
-
- private _complete: boolean = false;
- private _processing: boolean = false;
- private _answer: ProfilingAnswer | null = null;
-
- error_msg: string = "";
-
- constructor(data) {
- this.questionId = data.question_id;
- this.countryIso = data.country_iso;
- this.languageIso = data.language_iso;
-
- this.questionType = data.question_type;
- this.selector = data.selector;
- this.questionText = data.question_text;
-
- this.choices = data.choices
-
- this.extQuestionId = data.ext_question_id;
- this.configuration = data.configuration;
- this.validation = data.validation;
- }
-
- // --- Properties ---
-
- getType(): QuestionType {
- return this.questionType as QuestionType
- }
-
- getSelector(): SelectorType {
- return this.selector as SelectorType
- }
-
- getChoices(): ChoiceType[] | null {
- const choices: ChoiceType[] = this.choices;
- if (choices.length > 0) {
- return choices;
- } else {
- return null;
- }
- }
-
- // --- Methods ---
-
- addAnswer(val: string): null {
- val = val.trim();
-
- switch (this.getType()) {
-
- case "TE":
- this._answer = new ProfilingAnswer(this.questionId, [val]);
- break
-
- case "MC":
- let current_values: string[] = this._answer?.values
- current_values.push(val)
- this._answer = new ProfilingAnswer(this.questionId, current_values);
- break
-
- default:
- throw new Error("Incorrect Question Type provided");
- }
-
- this.validate()
- }
-
- removeAnswer(val: string): null {
- switch (this.getType()) {
-
- // You can only remove a value from a MultiChoice
- case "MC":
- // TODO: implement this
- // let current_values: string[] = this._answer?.values
- // current_values.push(val)
- // this._answer = new ProfilingAnswer(this.questionId, current_values);
- break
-
- default:
- throw new Error("Incorrect Question Type provided");
- }
-
- this.validate()
- }
-
- validate(): boolean {
- /*
- If the question is MC, validate:
- - validate selector SA vs MA (1 selected vs >1 selected)
- - the answers match actual codes in the choices
- - validate configuration.max_select
- - validate choices.exclusive
-
- If the question is TE, validate that:
- - configuration.max_length
- - validation.patterns
- */
-
- if (this._answer == null) {
- this.error_msg = "An answer is required"
- return false
- }
-
- let qa: ProfilingAnswer = this._answer;
-
- switch (this.getType()) {
- case "TE":
- if (qa.values.length == 0) {
- this.error_msg = "An answer is required"
- return false
- }
-
- if (qa.values.length > 1) {
- this.error_msg = "Only one answer allowed"
- return false
- }
-
- let answer: string = qa.values[0]
-
- if (answer.length <= 0) {
- this.error_msg = "Must provide answer"
- return false
- }
-
- const max_length: number = (this.configuration ?? {})["max_length"] ?? 100000
-
- if (answer.length > max_length) {
- this.error_msg = "Answer longer than allowed"
- return false
- }
-
- const patterns: PatternType[] = (this.validation ?? {})["patterns"] ?? []
-
- patterns.forEach((pattern) => {
- let re = new RegExp(pattern["pattern"])
- if (answer.search(re) == -1) {
- this.error_msg = pattern["message"]
- return false
- }
- })
-
- this.error_msg = ""
- return true
-
- case "MC":
- // if (qa.values.length == 0) {
- // this.error_msg = "MC question with no selected answers"
- // return false
- // }
- //
- // const choice_codes = map(this.getChoices().toJSON(), "choice_id")
- //
- // switch (this.getSelector()) {
- // case "SA":
- // if (qa.values.length > 1) {
- // this.error_msg = "Single Answer MC question with >1 selected answers"
- // return false
- // }
- // break
- // case "MA":
- // if (qa.values.length > choice_codes.length) {
- // this.error_msg = "More options selected than allowed"
- // return false
- // }
- // break
- // }
- //
- // if (!every(qa.values, (v) => {
- // return includes(choice_codes, v["value"])
- // })) {
- // this.error_msg = "Invalid Options Selected"
- // return false
- // }
- //
- // const max_select: number = (this.configuration ?? {})["max_select"] ?? choice_codes.length
- // if (qa.values.length > max_select) {
- // this.error_msg = "More options selected than allowed"
- // return false
- // }
-
- /*
- exclusive_choice = next((x for x in question["choices"] if x.get("exclusive")), None)
- if exclusive_choice:
- exclusive_choice_id = exclusive_choice["choice_id"]
- assert answer == [exclusive_choice_id] or \
- exclusive_choice_id not in answer, "Invalid exclusive selection"
- */
-
- this.error_msg = ""
- return true
-
- default:
- throw new Error("Incorrect Question Type provided");
- }
-
- }
-
- // -- Database / Format --
- static fromJson(json: any): ProfilingQuestion {
- return new ProfilingQuestion(json);
- }
-
- save() {
- let question: ProfilingQuestion = this;
- // @ts-ignore
- let answer: ProfilingAnswer = question._answer;
-
- if (this._complete || this._processing) {
- return
- }
- this._processing = true
-
- let res = JSON.stringify({
- "answers": [{
- "question_id": answer.get('question_id'),
- "answer": map(answer.get("values"), "value")
- }]
- });
-
- $.ajax({
- url: ["https://fsb.generalresearch.com", questions.BPID, "profiling-questions", ""].join("/") + "?" + stringify({"bpuid": questions.BPUID}),
- xhrFields: {withCredentials: false},
- processData: false,
- type: "POST",
- contentType: "application/json; charset=utf-8",
- data: res,
- success: function (data) {
- channel.trigger("ProfilingQuestions:start");
- },
- error: function (data) {
- channel.trigger("ProfilingQuestions:start");
- Sentry.captureMessage("Profiling Question submission failed.");
- }
- });
- }
-
-} \ No newline at end of file
diff --git a/src/models/questionSlice.ts b/src/models/questionSlice.ts
index 6a4da6f..e3674be 100644
--- a/src/models/questionSlice.ts
+++ b/src/models/questionSlice.ts
@@ -1,10 +1,9 @@
import {createSlice, PayloadAction} from '@reduxjs/toolkit'
import type {RootState} from '@/store'
-import {UpkQuestion} from "@/api/models/upk-question.ts"
+import {UpkQuestion} from "@/api";
const initialState: UpkQuestion[] = []
-// Create the slice and pass in the initial state
const questionSlice = createSlice({
name: 'questions',
initialState,