only_ever_generator 0.4.9 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -38,32 +38,32 @@ Object.defineProperty(exports, "OnlyEverGenerator", { enumerable: true, get: fun
38
38
  // let headings = returnHeadings();
39
39
  // cardResp.metadata = {
40
40
  // "req_time": cardResp.generated_at ?? new Date(),
41
- // "req_type": cardResp.type,
41
+ // "req_type": "card",
42
42
  // "req_tokens": cardResp.usage_data?.prompt_tokens,
43
43
  // "res_tokens": cardResp.usage_data?.completion_tokens,
44
44
  // "model": '40-mini'
45
45
  // };
46
- // let parsedData = new GenerateCards(new OpenAiService("","")).parse(cardResp,false,headings);
46
+ // let parsedData = new ParseCardResponse().parse(cardResp,false);
47
47
  // res.send(parsedData)
48
48
  // });
49
- // // app.get("/typology", async (req, res) => {
50
- // // {
51
- // // let typologyPrompt = returnTypologyPrompt();
52
- // // let cardPrompt = returnCardGenPrompt();
53
- // // let args = new GenerateArgs(
54
- // // true,
55
- // // true,
56
- // // false,
57
- // // {
58
- // // typology_prompt: typologyPrompt,
59
- // // card_gen_prompt: cardPrompt,
60
- // // summary_prompt: "",
61
- // // }
62
- // // )
63
- // // let typologyRequest = await oeGen.generate(false, true);
64
- // // res.send(typologyRequest);
65
- // // }
66
- // // });
49
+ // app.get("/typology", async (req, res) => {
50
+ // {
51
+ // let typologyPrompt = returnTypologyPrompt();
52
+ // let cardPrompt = returnCardGenPrompt();
53
+ // let args = new GenerateArgs(
54
+ // true,
55
+ // true,
56
+ // false,
57
+ // {
58
+ // typology_prompt: typologyPrompt,
59
+ // card_gen_prompt: cardPrompt,
60
+ // summary_prompt: "",
61
+ // }
62
+ // )
63
+ // let typologyRequest = await oeGen.generate(false, true);
64
+ // res.send(typologyRequest);
65
+ // }
66
+ // });
67
67
  // app.listen(port, () => {
68
68
  // console.log(`Example app listening at http://localhost:${port}`);
69
69
  // });
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParseClozeCard = void 0;
4
+ class ParseClozeCard {
5
+ parse(data) {
6
+ try {
7
+ let displayTitle = this._generateClozeCardDisplayTitle(data.card_content.prompt, data.card_content.options);
8
+ let clozeCardData = {
9
+ type: {
10
+ category: "learning",
11
+ sub_type: data.type,
12
+ },
13
+ heading: data.card_reference,
14
+ displayTitle: displayTitle,
15
+ content: {
16
+ question: data.card_content.prompt,
17
+ options: data.card_content.options,
18
+ },
19
+ concepts: data.concepts,
20
+ facts: data.facts,
21
+ bloomLevel: data.bloom_level,
22
+ };
23
+ return this._validateCloze(clozeCardData);
24
+ }
25
+ catch (e) {
26
+ return null;
27
+ }
28
+ }
29
+ _generateClozeCardDisplayTitle(question, answers) {
30
+ let optionsString = "";
31
+ if (answers.length !== 0) {
32
+ optionsString = answers
33
+ .map((item) => {
34
+ if (item.option !== undefined) {
35
+ return item.option;
36
+ }
37
+ else {
38
+ return "";
39
+ }
40
+ })
41
+ .join(", ");
42
+ }
43
+ return `${question} ---- ${optionsString}`;
44
+ }
45
+ /// validate the cloze card
46
+ // 1. Has Empty cloze
47
+ // 2. has Duplicate Clozes
48
+ // 3. doesnt have any valid option,
49
+ // 4. Question length 320
50
+ // 5. More than 6 options
51
+ // 6. Less than 2 options
52
+ // 7. Max character for individual cloze: 90
53
+ _validateCloze(clozeCard) {
54
+ var _a;
55
+ let clozeRegex = /\{\{c(\d+):([^}]+)\}\}/g;
56
+ try {
57
+ /// validate emptu cloze
58
+ let options = (_a = clozeCard.content.options) !== null && _a !== void 0 ? _a : [];
59
+ let question = clozeCard.content.question;
60
+ if (options.length < 2 || options.length > 6) {
61
+ throw Error("Number of cloze options are invalid");
62
+ }
63
+ /// There are no correct clozes// or null cloze or empty cloze
64
+ let correctOptions = options.find((e) => e.cloze != "null" && e.cloze != null && e.cloze.trim() != "");
65
+ if (correctOptions) {
66
+ }
67
+ else {
68
+ throw Error(" No valid clozes exists");
69
+ }
70
+ let rightClozes = options.filter((e) => e.cloze.startsWith("c"));
71
+ /// matches our cloze syntax
72
+ let clozeMatches = [...question.matchAll(clozeRegex)];
73
+ if (clozeMatches.length == 0) {
74
+ throw Error("Question Invalid");
75
+ }
76
+ else if (clozeMatches.length != rightClozes.length) {
77
+ throw Error(" Clozes in question doesnt match to clozes in options");
78
+ }
79
+ return clozeCard;
80
+ }
81
+ catch (e) {
82
+ return false;
83
+ }
84
+ }
85
+ }
86
+ exports.ParseClozeCard = ParseClozeCard;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParseMatchCard = void 0;
4
+ class ParseMatchCard {
5
+ parse(cardData) {
6
+ try {
7
+ let content = cardData.card_content;
8
+ let displayTitle = this._generateMatchCardDisplayTitle(content);
9
+ let matchCard = {
10
+ type: {
11
+ category: "learning",
12
+ sub_type: cardData.type,
13
+ },
14
+ heading: cardData.card_reference,
15
+ content: content,
16
+ // content: cardData.card_content,
17
+ displayTitle: displayTitle,
18
+ concepts: cardData.concepts,
19
+ facts: cardData.facts,
20
+ bloomLevel: cardData.bloom_level,
21
+ };
22
+ return this._validateMatch(matchCard);
23
+ }
24
+ catch (e) {
25
+ return null;
26
+ }
27
+ }
28
+ _generateMatchCardDisplayTitle(answers) {
29
+ let titles = [];
30
+ let counter = 65;
31
+ for (let data of answers) {
32
+ let value = data.right_item.join(",");
33
+ let leftData = data.left_item;
34
+ let letter = String.fromCharCode(counter);
35
+ titles.push(`${letter}. ${leftData} -- ${value}`);
36
+ counter++;
37
+ }
38
+ let displayTitle = titles.join(",");
39
+ return displayTitle;
40
+ }
41
+ _validateMatch(matchCard) {
42
+ let matches = matchCard.content;
43
+ try {
44
+ for (let elem of matches) {
45
+ if (elem.left_item.length > 24 || elem.left_item.length == 0) {
46
+ throw Error("Invalid Length of left item ");
47
+ }
48
+ else if (elem.right_item[0].length > 24 || elem.right_item[0].length == 0) {
49
+ throw Error(" Invalid Length of right item");
50
+ }
51
+ }
52
+ return matchCard;
53
+ }
54
+ catch (e) {
55
+ return null;
56
+ }
57
+ }
58
+ }
59
+ exports.ParseMatchCard = ParseMatchCard;
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ParseMcqCard = void 0;
4
+ class ParseMcqCard {
5
+ parse(data) {
6
+ try {
7
+ let mcqAnswers = [];
8
+ if (data.card_content.choices !== undefined &&
9
+ data.card_content.choices.length != 0) {
10
+ for (let choice of data.card_content.choices) {
11
+ let answer = {
12
+ answer: choice.choice,
13
+ is_correct: choice.is_correct,
14
+ };
15
+ mcqAnswers.push(answer);
16
+ }
17
+ }
18
+ let displayTitle = this._generateMcqCardDisplayTitle(data.card_content.prompt, mcqAnswers);
19
+ let mcqCard = {
20
+ type: {
21
+ category: "learning",
22
+ sub_type: data.type,
23
+ },
24
+ heading: data.card_reference,
25
+ displayTitle: displayTitle,
26
+ content: {
27
+ question: data.card_content.prompt,
28
+ answers: mcqAnswers,
29
+ },
30
+ concepts: data.concepts,
31
+ facts: data.facts,
32
+ bloomLevel: data.bloom_level,
33
+ };
34
+ // return mcqCard;
35
+ const isValid = this._validate(mcqCard);
36
+ if (isValid == true) {
37
+ return mcqCard;
38
+ }
39
+ else {
40
+ return null;
41
+ }
42
+ }
43
+ catch (e) {
44
+ return null;
45
+ }
46
+ }
47
+ _generateMcqCardDisplayTitle(question, answers) {
48
+ let answersString = [];
49
+ if (answers.length != 0) {
50
+ for (let option of answers) {
51
+ let currentIndex = answers.indexOf(option) + 1;
52
+ let temp = `${currentIndex} . ${option.answer} `;
53
+ answersString.push(temp);
54
+ }
55
+ let resultString = answersString.join("");
56
+ let finalDisplayTitle = `${question} ---- ${resultString}`;
57
+ return finalDisplayTitle;
58
+ }
59
+ else {
60
+ return question;
61
+ }
62
+ }
63
+ /// validate mcq card
64
+ // 1. Check if atleast 1 correct answer exists
65
+ // 2. Length of answer shouldnt exceed 24 chars
66
+ // 3. Length of question shouldnt exceed 90 chars
67
+ // 4. If Any option is Empty
68
+ _validate(mcqCard) {
69
+ var _a;
70
+ try {
71
+ let isQuestionValid = mcqCard.content.question.length <= 90;
72
+ if (!isQuestionValid) {
73
+ throw new Error("Question length exceeded");
74
+ }
75
+ /// check if all are wrong answers
76
+ let exists = this._checkIfAllAnswersAreWrong(mcqCard.content.answers);
77
+ if (exists) {
78
+ }
79
+ else {
80
+ throw new Error("Every answers are wrong");
81
+ }
82
+ /// check if answers are of length <40 or is 0
83
+ let answerWhoseLengthisGreaterThan40or0 = ((_a = mcqCard.content.answers) !== null && _a !== void 0 ? _a : []).find((e) => e.answer.length == 0 || e.answer.length > 40);
84
+ if (answerWhoseLengthisGreaterThan40or0) {
85
+ throw new Error("Option has length more than 40 or is Empty");
86
+ }
87
+ return true;
88
+ }
89
+ catch (e) {
90
+ return false;
91
+ }
92
+ }
93
+ _checkIfAllAnswersAreWrong(answers) {
94
+ let rightAnswer = answers.find((e) => e.is_correct == true);
95
+ if (rightAnswer) {
96
+ return true;
97
+ }
98
+ else {
99
+ return false;
100
+ }
101
+ }
102
+ }
103
+ exports.ParseMcqCard = ParseMcqCard;
@@ -1,288 +1,98 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ParseCardResponse = void 0;
4
- const cardResponse = {
5
- "status_code": 200,
6
- "usage_data": {
7
- "prompt_tokens": 9781,
8
- "completion_tokens": 1915,
9
- "total_tokens": 11696
10
- },
11
- "generated_content": {
12
- "missing_concepts": [
13
- "Faraday's laws of electrolysis",
14
- "Humphry Davy",
15
- "Electrolytic cell",
16
- "Decomposition potential",
17
- "Oxidation",
18
- "Reduction",
19
- "Electrolysis of seawater",
20
- "Electrometallurgy",
21
- "Electroplating",
22
- "Electrochemical machining",
23
- "Electrochemistry",
24
- "Electrocatalysis",
25
- "Electrorefining",
26
- "Electrolysis of carbon dioxide",
27
- "Energy changes during electrolysis",
28
- "Electrocrystallization",
29
- "Electrolysis of Iron Ore"
30
- ],
31
- "missing_facts": [
32
- "Michael Faraday introduced the term 'electrolysis' in 1834",
33
- "The first demonstration of key electrolysis concepts was by William Nicholson and Anthony Carlisle in the early nineteenth century",
34
- "Humphry Davy discovered several alkali and alkaline earth metals by electrolysis",
35
- "Electrolysis played a key role in isolating and identifying new elements like lithium, chlorine, and fluorine",
36
- "The Hall–Héroult process led to a significant drop in the price of aluminum",
37
- "Hydrogen and oxygen are produced in a 2:1 ratio by the electrolysis of water",
38
- "The electrolysis of seawater can result in the production of alkali hydroxides",
39
- "Electrometallurgy is used in the production of various metals",
40
- "Electroplating involves the deposition of a thin metal film onto a substrate material",
41
- "Electrochemical machining is used for deburring or etching metal surfaces",
42
- "Electrocatalysis involves the acceleration of electrochemical reactions",
43
- "Electrorefining is used to obtain pure metals from impure ones",
44
- "The electrolysis of carbon dioxide can produce methane, ethylene, or ethanol",
45
- "Energy changes during electrolysis involve the addition of electrical energy, equal to the change in Gibbs free energy plus system losses",
46
- "Electrocrystallization is a method for obtaining conductive crystals using electrolysis",
47
- "Electrolysis of Iron Ore can eventually result in the reduction of emissions from steel production"
48
- ],
49
- "test_cards": [
50
- {
51
- "type": "flash",
52
- "card_content": {
53
- "front": "Who introduced the term 'electrolysis' and in which year?",
54
- "back": "Michael Faraday introduced the term 'electrolysis' in 1834"
55
- },
56
- "card_reference": "Electrolysis#Etymology",
57
- "concepts": [],
58
- "facts": [
59
- "Michael Faraday introduced the term 'electrolysis' in 1834"
60
- ]
61
- },
62
- {
63
- "type": "flash",
64
- "card_content": {
65
- "front": "What was the significance of the Hall–Héroult process?",
66
- "back": "The Hall–Héroult process led to a significant drop in the price of aluminum"
67
- },
68
- "card_reference": "Electrolysis#Industrial uses",
69
- "concepts": [
70
- "Hall–Héroult process"
71
- ],
72
- "facts": [
73
- "The Hall–Héroult process led to a significant drop in the price of aluminum"
74
- ]
75
- },
76
- {
77
- "type": "mcq",
78
- "card_content": {
79
- "prompt": "What does the electrolysis of water produce?",
80
- "choices": [
81
- {
82
- "choice": "Oxygen and carbon dioxide",
83
- "is_correct": false
84
- },
85
- {
86
- "choice": "Hydrogen and oxygen",
87
- "is_correct": true
88
- },
89
- {
90
- "choice": "Nitrogen and helium",
91
- "is_correct": false
4
+ const logger_1 = require("../logger");
5
+ const parse_cloze_card_1 = require("./parse_card/parse_cloze_card");
6
+ const parse_match_card_1 = require("./parse_card/parse_match_card");
7
+ const parse_mcq_card_1 = require("./parse_card/parse_mcq_card");
8
+ class ParseCardResponse {
9
+ parse(generatedData, isGapFill) {
10
+ let usage_data = generatedData.metadata;
11
+ try {
12
+ const cardData = [];
13
+ const unparsedTestCards = generatedData.generated_content.test_cards;
14
+ const type = generatedData.type;
15
+ if (unparsedTestCards !== undefined && unparsedTestCards.length != 0) {
16
+ for (let elem of unparsedTestCards) {
17
+ if (elem.type == "flash") {
18
+ const flashCard = this.parseFlashCard(elem);
19
+ if (flashCard != null && flashCard) {
20
+ this.parseFlashCard(flashCard);
92
21
  }
93
- ]
94
- },
95
- "card_reference": "Electrolysis#Process of electrolysis",
96
- "concepts": [
97
- "Electrometallurgy"
98
- ],
99
- "facts": [
100
- "Hydrogen and oxygen are produced in a 2:1 ratio by the electrolysis of water"
101
- ]
102
- },
103
- {
104
- "type": "cloze",
105
- "card_content": {
106
- "prompt": "Electrolysis is the passing of a direct electric current through an {{c0:electrolyte}} producing chemical reactions at the electrodes and decomposition of the materials.",
107
- "options": [
108
- {
109
- "option": "electrolyte",
110
- "cloze": "c0"
111
- },
112
- {
113
- "option": "anode",
114
- "cloze": "null"
115
- },
116
- {
117
- "option": "cathode",
118
- "cloze": "null"
22
+ }
23
+ else if (elem.type == "mcq") {
24
+ const mcqCard = new parse_mcq_card_1.ParseMcqCard().parse(elem);
25
+ if (mcqCard != null && mcqCard) {
26
+ cardData.push(mcqCard);
119
27
  }
120
- ]
121
- },
122
- "card_reference": "Electrolysis#Overview",
123
- "concepts": [],
124
- "facts": []
125
- },
126
- {
127
- "type": "match",
128
- "card_content": {
129
- "right_choice 1": "Electrometallurgy",
130
- "right_choice 2": "Electroplating",
131
- "right_choice 3": "Electrochemical machining",
132
- "right_choice 4": "Electrochemistry",
133
- "right_choice 5": "Electrocatalysis",
134
- "right_choice 6": "Electrorefining",
135
- "right_choice 7": "Electrolysis of carbon dioxide",
136
- "right_choice 8": "Energy changes during electrolysis",
137
- "Electrometallurgy": "The process of producing metals using electricity",
138
- "Electroplating": "Deposition of thin metal film onto a substrate material",
139
- "Electrochemical machining": "Deburring or etching metal surfaces",
140
- "Electrochemistry": "Study of the interchange of chemical and electrical energy",
141
- "Electrocatalysis": "Acceleration of electrochemical reactions",
142
- "Electrorefining": "Obtaining pure metals from impure ones"
143
- },
144
- "card_reference": "Electrolysis#Industrial uses",
145
- "concepts": [
146
- "Electrometallurgy",
147
- "Electroplating",
148
- "Electrochemical machining",
149
- "Electrochemistry",
150
- "Electrocatalysis",
151
- "Electrorefining",
152
- "Electrolysis of carbon dioxide",
153
- "Energy changes during electrolysis"
154
- ],
155
- "facts": [
156
- "Electrometallurgy is used in the production of various metals",
157
- "Electroplating involves the deposition of a thin metal film onto a substrate material",
158
- "Electrochemical machining is used for deburring or etching metal surfaces",
159
- "Electrochemistry is the study of the interchange of chemical and electrical energy",
160
- "Electrocatalysis involves the acceleration of electrochemical reactions",
161
- "Electrorefining is used to obtain pure metals from impure ones",
162
- "The electrolysis of carbon dioxide can produce methane, ethylene, or ethanol",
163
- "Energy changes during electrolysis involve the addition of electrical energy, equal to the change in Gibbs free energy plus system losses"
164
- ]
165
- },
166
- {
167
- "type": "mcq",
168
- "card_content": {
169
- "prompt": "What is a key application of electrolysis?",
170
- "choices": [
171
- {
172
- "choice": "Production of metal from ore",
173
- "is_correct": false
174
- },
175
- {
176
- "choice": "Pulsing current results",
177
- "is_correct": false
178
- },
179
- {
180
- "choice": "Generating electrical potential",
181
- "is_correct": false
182
- },
183
- {
184
- "choice": "Production of chlorine and sodium hydroxide",
185
- "is_correct": true
28
+ }
29
+ else if (elem.type == "cloze") {
30
+ const clozeCard = new parse_cloze_card_1.ParseClozeCard().parse(elem);
31
+ if (clozeCard && clozeCard != null) {
32
+ cardData.push(clozeCard);
186
33
  }
187
- ]
188
- },
189
- "card_reference": "Electrolysis#Industrial uses",
190
- "concepts": [
191
- "Electrochemistry",
192
- "Electrocatalysis",
193
- "Electrorefining"
194
- ],
195
- "facts": [
196
- "Production of chlorine and sodium hydroxide, called the Chloralkali process"
197
- ]
198
- },
199
- {
200
- "type": "cloze",
201
- "card_content": {
202
- "prompt": "Oxidation of ions or neutral molecules occurs at the {{c0:anode}}. Reduction of ions or neutral molecules occurs at the {{c1:cathode}}.",
203
- "options": [
204
- {
205
- "option": "anode",
206
- "cloze": "c0"
207
- },
208
- {
209
- "option": "cathode",
210
- "cloze": "c1"
211
- },
212
- {
213
- "option": "electrolyte",
214
- "cloze": "null"
34
+ }
35
+ else if (elem.type == "match") {
36
+ const matchCard = new parse_match_card_1.ParseMatchCard().parse(elem);
37
+ if (matchCard && matchCard != null) {
38
+ cardData.push(matchCard);
215
39
  }
216
- ]
217
- },
218
- "card_reference": "Electrolysis#Oxidation and reduction at the electrodes",
219
- "concepts": [
220
- "Oxidation",
221
- "Reduction"
222
- ],
223
- "facts": []
224
- },
225
- {
226
- "type": "match",
227
- "card_content": {
228
- "right_choice 1": "Electrolysis",
229
- "right_choice 2": "Electrocrystallization",
230
- "right_choice 3": "Electrolysis of Iron Ore",
231
- "right_choice 4": "Electrolysis of seawater",
232
- "right_choice 5": "Electrometallurgy"
233
- },
234
- "card_reference": "Electrolysis#Research trends",
235
- "concepts": [
236
- "Electrolysis",
237
- "Electrocrystallization",
238
- "Electrolysis of Iron Ore",
239
- "Electrolysis of seawater",
240
- "Electrometallurgy"
241
- ],
242
- "facts": []
40
+ }
41
+ }
243
42
  }
244
- ]
245
- },
246
- "generated_at": 1718625081,
247
- "type": "card_gen"
248
- };
249
- class ParseCardResponse {
250
- constructor(generatedData) {
251
- this.generatedData = generatedData;
252
- }
253
- parse() {
254
- const cardData = [];
255
- const usage_data = this.generatedData.usage_data;
256
- const created_at = this.generatedData.created_at;
257
- const unparsedTestCards = this.generatedData.test_cards;
258
- for (let elem of unparsedTestCards) {
259
- if (elem.type == 'flash') {
260
- cardData.push(this.parseFlashCard(elem));
261
- }
262
- else if (elem.type == 'mcq') {
263
- cardData.push(this.parseFlashCard(elem));
264
- }
265
- else if (elem.type == 'cloze') {
266
- cardData.push(this.parseClozeCard(elem));
267
- }
268
- else if (elem.type == 'match') {
269
- cardData.push(this.parseMatchCard(elem));
43
+ else {
44
+ if (!isGapFill) {
45
+ usage_data.status = "failed";
46
+ }
270
47
  }
48
+ return {
49
+ status_code: 200,
50
+ metadata: usage_data,
51
+ type: type,
52
+ missing_concepts: [],
53
+ missing_facts: [],
54
+ cards_data: cardData,
55
+ };
56
+ }
57
+ catch (e) {
58
+ new logger_1.ErrorLogger({
59
+ type: "card_parsing",
60
+ data: e.message,
61
+ response: generatedData,
62
+ }).log();
63
+ return {
64
+ status_code: 500,
65
+ metadata: usage_data,
66
+ type: generatedData.type,
67
+ };
271
68
  }
272
- usage_data['created_at'] = created_at;
273
- usage_data['type'] = 'card_gen';
274
- return {
275
- 'usage_data': usage_data,
276
- 'cards_data': cardData
277
- };
278
69
  }
279
70
  parseFlashCard(data) {
71
+ try {
72
+ let displayTitle = this.generateFlashCardDisplayTitle(data.card_content.front, data.card_content.back);
73
+ let flashCardData = {
74
+ type: {
75
+ category: "learning",
76
+ sub_type: data.type,
77
+ },
78
+ heading: data.card_reference,
79
+ displayTitle: displayTitle,
80
+ content: {
81
+ front_content: data.card_content.front,
82
+ back_content: data.card_content.back,
83
+ },
84
+ concepts: data.concepts,
85
+ facts: data.facts,
86
+ bloomLevel: data.bloom_level,
87
+ };
88
+ return flashCardData;
89
+ }
90
+ catch (e) {
91
+ return null;
92
+ }
280
93
  }
281
- parseMcqCard(data) {
282
- }
283
- parseClozeCard(data) {
284
- }
285
- parseMatchCard(data) {
94
+ generateFlashCardDisplayTitle(front, back) {
95
+ return `${front} ---- ${back}`;
286
96
  }
287
97
  }
288
98
  exports.ParseCardResponse = ParseCardResponse;