From ddc7c944747f80b44054cac53965f85ec9c43fd3 Mon Sep 17 00:00:00 2001 From: Peihua Huang Date: Tue, 2 Feb 2021 14:43:54 -0500 Subject: [PATCH 01/14] updated shuffle button --- frontend/src/anagramView/anagramView.js | 2 +- frontend/src/anagramView/anagramView.scss | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/anagramView/anagramView.js b/frontend/src/anagramView/anagramView.js index b4cc39f..cd63f13 100644 --- a/frontend/src/anagramView/anagramView.js +++ b/frontend/src/anagramView/anagramView.js @@ -448,7 +448,7 @@ export class AnagramView extends React.Component {
-
-

Instructions will go here.

+

With the list of given letters, rearrange them (or some of them) + to form words from the text you selected. You may look at the "Definitions" + column as a hint. If you find a word that was not part of the text, + it will appear on the "Extra Words" column.

With the list of given letters, rearrange them (or some of them) - to form words from the text you selected. You may look at the - "Definitions" column as a hint. If you find a word that was not - part of the text, it will appear on the "Extra Words" column.

+ to form words from the text you selected. To enter the words, type in the slot + that says "Type Here" then press enter, and your score is shown on the top right + corner in the green box labeled "Score". To shuffle the letters, click on the button + to the left of the list of letters. You may also look at the "Definitions" + column as a hint. If you find a word that was not part of the text, + it will appear on the "Extra Words" column. When you hover over the words + in "Target Words", you can view example sentences that may help with better + understanding the vocabulary.

+

To give up and view all the target words, click on the "Give Up" button. To + restart the game again, click on the "Restart" button. The game will end if either + the timer runs out or if you have found all the target words.

With the list of given letters, rearrange them (or some of them) - to form words from the text you selected. To enter the words, type in the slot - that says "Type Here" then press enter, and your score is shown on the top right - corner in the green box labeled "Score". To shuffle the letters, click on the button - to the left of the list of letters. You may also look at the "Definitions" - column as a hint. If you find a word that was not part of the text, - it will appear on the "Extra Words" column. When you hover over the words - in "Target Words", you can view example sentences that may help with better + to form words from the text you selected. To enter the words, + type in the slot that says "Type Here" then press enter, and + your score is shown on the top right corner in the green box + labeled "Score". To shuffle the letters, click on the button + to the left of the list of letters. You may also look at the + "Definitions" column as a hint. If you find a word that was + not part of the text, it will appear on the "Extra + Words" column. When you hover over the words in "Target Words", + you can view example sentences that may help with better understanding the vocabulary.

-

To give up and view all the target words, click on the "Give Up" button. To - restart the game again, click on the "Restart" button. The game will end if either - the timer runs out or if you have found all the target words.

+

To give up and view all the target words, click on the "Give Up" + button. To restart the game again, click on the "Restart" button. + The game will end if either the timer runs out or if you have + found all the target words.

+

+ Category: {this.props.partOfSpeech} +

@@ -391,8 +390,7 @@ export class AnagramView extends React.Component { : null }
@@ -424,11 +422,13 @@ export class AnagramView extends React.Component {
-
+

Time Left: {this.state.timeLeft}

-

Category: {this.props.partOfSpeech}

{ this.state.gameOver ?
@@ -439,8 +439,8 @@ export class AnagramView extends React.Component {
: null } -
-
+
+
@@ -448,38 +448,46 @@ export class AnagramView extends React.Component { onClick={this.giveUp} disabled={this.state.gameOver}>Give Up
-
+
Score: {this.state.score}
-
-

Extra Words

-
    - { - this.state.extraWordsFound.map((word, i) => ( -
  • - {word.toUpperCase()} -
  • - )) - } -
+
+
+

Extra Words

+
    + { + this.state.extraWordsFound.map((word, i) => ( +
  • + {word.toUpperCase()} +
  • + )) + } +
+
-
-

Words Found

-
    {wordsFound}
+
+
+

Words Found

+
    {wordsFound}
+
-
-

Definitions

- -
    {definitions}
+
+
+

Definitions

+ +
    {definitions}
+
-
-
-
+
{ this.state.letters.map((letter, k) => { let letterClass = 'light-letter'; @@ -508,9 +516,9 @@ export class AnagramView extends React.Component {

-
+
-
+
{ this.setState({ shake: false }); }} /> -
diff --git a/frontend/src/anagramView/anagramView.scss b/frontend/src/anagramView/anagramView.scss index a8a2a24..3c0e3c5 100644 --- a/frontend/src/anagramView/anagramView.scss +++ b/frontend/src/anagramView/anagramView.scss @@ -1,26 +1,20 @@ -.shuffle-icon { - height: 25px; - width: 25px; -} - -.letters { - font-size: 30px; -} - +/* Styles for Anagram */ .score { background-color: #278E73; - padding: 8px; + display: inline-block; + padding: 7px 12px; border-radius: 5px; color: white; - font-size: 20px; + font-size: 16px; font-weight: bold; } .shaded-box { background: #5941A9; - margin: 10px 0 20px 0; - padding-top: 10px; border-radius: 5px; + height: 100%; + padding: 15px; + margin: 0; } ol { @@ -40,23 +34,7 @@ li span { line-height: 1.33; } -.loading-spinner { - margin: 0 auto auto; -} - -.loading-text { - margin: auto auto 30px; - font-size: 30px; - font-weight: bold; -} - -.Modal { - color: #2D2155; - position: fixed; - z-index: 500; - transition: all 0.4s ease-out; -} - +/* Modal Styles */ .backdrop { width: 100%; @@ -68,13 +46,24 @@ li span { background-color: rgba(0,0,0,0.5); } -@media (min-width: 600px) { +.Modal { + color: #2D2155; + position: fixed; + z-index: 500; + transition: all 0.4s ease-out; + width: 90%; + left: 5%; +} + +@media (min-width: 992px) { .Modal { - width: 500px; - left: calc(50% - 250px); + width: 60%; + left: 20%; } } +/* Styles for User Input */ + .light-letter { color: white; } @@ -110,3 +99,40 @@ li span { .shuffle-btn:hover { background-color: black; } + +.submit-button { + width: 100%; + margin-top: 20px; +} + +.form-control { + width: 100% !important; +} + +@media (min-width: 768px) { + .submit-button { + width: 80px; + margin: 0 20px 0; + } + + .form-control { + width: 350px !important; + } +} + +.shuffle-icon { + height: 25px; + width: 25px; +} + +.letters { + font-size: 22px; + word-wrap: break-word; +} + +@media (min-width: 768px) { + .letters { + font-size: 30px; + } +} + From 644e8c0e580398b81a801347d54b0c87546488e7 Mon Sep 17 00:00:00 2001 From: Peihua Huang Date: Fri, 5 Feb 2021 14:01:09 -0500 Subject: [PATCH 10/14] changed text to content in models --- backend/app/management/commands/textdata.py | 4 ++-- .../app/migrations/0002_auto_20210128_2157.py | 18 ------------------ ...0129_1853.py => 0002_auto_20210205_1856.py} | 14 ++++++++++++-- .../app/migrations/0003_auto_20210128_2158.py | 18 ------------------ .../app/migrations/0004_auto_20210129_1706.py | 18 ------------------ backend/app/models.py | 2 +- backend/app/serializers.py | 2 +- backend/app/views.py | 4 ++-- 8 files changed, 18 insertions(+), 62 deletions(-) delete mode 100644 backend/app/migrations/0002_auto_20210128_2157.py rename backend/app/migrations/{0005_auto_20210129_1853.py => 0002_auto_20210205_1856.py} (55%) delete mode 100644 backend/app/migrations/0003_auto_20210128_2158.py delete mode 100644 backend/app/migrations/0004_auto_20210129_1706.py diff --git a/backend/app/management/commands/textdata.py b/backend/app/management/commands/textdata.py index 3929c3e..ebb715d 100644 --- a/backend/app/management/commands/textdata.py +++ b/backend/app/management/commands/textdata.py @@ -63,9 +63,9 @@ def handle(self, *args, **options): for pos in part_of_speech: print("Getting definitions and examples for " + pos + " in the text " + text_obj.title + "... (This might take a while)") - words = get_valid_words(text_obj.text.lower(), pos) + words = get_valid_words(text_obj.content.lower(), pos) definitions = get_word_definition(words, pos) - examples = get_word_examples(words, pos, text_obj.text.lower()) + examples = get_word_examples(words, pos, text_obj.content.lower()) print("Updating database for " + pos + " in the text " + text_obj.title) for word in tqdm.tqdm(words): diff --git a/backend/app/migrations/0002_auto_20210128_2157.py b/backend/app/migrations/0002_auto_20210128_2157.py deleted file mode 100644 index dc2c673..0000000 --- a/backend/app/migrations/0002_auto_20210128_2157.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2021-01-28 21:57 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='text', - name='images', - field=models.JSONField(blank=True, default={'test': 'test'}, null=True), - ), - ] diff --git a/backend/app/migrations/0005_auto_20210129_1853.py b/backend/app/migrations/0002_auto_20210205_1856.py similarity index 55% rename from backend/app/migrations/0005_auto_20210129_1853.py rename to backend/app/migrations/0002_auto_20210205_1856.py index 308f48f..a417635 100644 --- a/backend/app/migrations/0005_auto_20210129_1853.py +++ b/backend/app/migrations/0002_auto_20210205_1856.py @@ -1,4 +1,4 @@ -# Generated by Django 3.1.5 on 2021-01-29 18:53 +# Generated by Django 3.1.5 on 2021-02-05 18:56 from django.db import migrations, models @@ -6,10 +6,15 @@ class Migration(migrations.Migration): dependencies = [ - ('app', '0004_auto_20210129_1706'), + ('app', '0001_initial'), ] operations = [ + migrations.RenameField( + model_name='text', + old_name='text', + new_name='content', + ), migrations.AddField( model_name='text', name='definitions', @@ -20,4 +25,9 @@ class Migration(migrations.Migration): name='examples', field=models.JSONField(blank=True, default=dict, null=True), ), + migrations.AlterField( + model_name='text', + name='images', + field=models.JSONField(blank=True, default=dict, null=True), + ), ] diff --git a/backend/app/migrations/0003_auto_20210128_2158.py b/backend/app/migrations/0003_auto_20210128_2158.py deleted file mode 100644 index f41b89f..0000000 --- a/backend/app/migrations/0003_auto_20210128_2158.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2021-01-28 21:58 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0002_auto_20210128_2157'), - ] - - operations = [ - migrations.AlterField( - model_name='text', - name='images', - field=models.JSONField(blank=True, null=True), - ), - ] diff --git a/backend/app/migrations/0004_auto_20210129_1706.py b/backend/app/migrations/0004_auto_20210129_1706.py deleted file mode 100644 index b5caf0b..0000000 --- a/backend/app/migrations/0004_auto_20210129_1706.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.1.5 on 2021-01-29 17:06 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('app', '0003_auto_20210128_2158'), - ] - - operations = [ - migrations.AlterField( - model_name='text', - name='images', - field=models.JSONField(blank=True, default=dict, null=True), - ), - ] diff --git a/backend/app/models.py b/backend/app/models.py index a484cf3..d314c10 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -9,7 +9,7 @@ class Text(models.Model): This model will hold the piece of text that will be used to generate exercises such as the anagram and quiz. """ - text = models.TextField(max_length=5000, null=True) + content = models.TextField(max_length=5000, null=True) title = models.CharField(max_length=252, null=True) images = models.JSONField(null=True, blank=True, default=dict) examples = models.JSONField(null=True, blank=True, default=dict) diff --git a/backend/app/serializers.py b/backend/app/serializers.py index 63e457c..3a4c67f 100644 --- a/backend/app/serializers.py +++ b/backend/app/serializers.py @@ -15,4 +15,4 @@ class TextSerializer(serializers.ModelSerializer): """ class Meta: model = Text - fields = ['id', 'title', 'text'] + fields = ['id', 'title', 'content'] diff --git a/backend/app/views.py b/backend/app/views.py index 23aad22..98929f2 100644 --- a/backend/app/views.py +++ b/backend/app/views.py @@ -54,7 +54,7 @@ def get_flashcards(request, text_id, part_of_speech): definitions = text_obj.definitions examples = text_obj.examples - words = get_valid_words(text_obj.text, part_of_speech) + words = get_valid_words(text_obj.content, part_of_speech) res = [{'word': word, 'definition': definitions[word].get(part_of_speech, []), @@ -73,7 +73,7 @@ def get_anagram(request, text_id, part_of_speech): text_obj = Text.objects.get(id=text_id) definitions = text_obj.definitions examples = text_obj.examples - words = get_valid_words(text_obj.text, part_of_speech) + words = get_valid_words(text_obj.content, part_of_speech) random.shuffle(words) words = words[:5] From 37e135219fcc1e42c1f30fb1be5beee9a7528d6c Mon Sep 17 00:00:00 2001 From: Peihua Huang Date: Fri, 5 Feb 2021 15:18:40 -0500 Subject: [PATCH 11/14] changed text prop to content --- frontend/src/index/index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/frontend/src/index/index.js b/frontend/src/index/index.js index 59b9c74..d346fa2 100644 --- a/frontend/src/index/index.js +++ b/frontend/src/index/index.js @@ -24,13 +24,14 @@ class TextInfo extends React.Component { render() { // const posLink = pos.toLowerCase(); - const { title, text, textId } = this.props; + const { title, content, textId } = this.props; + console.log(this.props); const { quizType, partOfSpeech } = this.state; return (

{title}

-

{text}

+

{content}

{ this.setState({ shake: false }); }} + /> -
-
- { - this.state.letters.map((letter, k) => { - let letterClass = 'light-letter'; - const letterKey = letter.toLowerCase(); - if (letterKey in enteredFreq - && curFreq[letterKey] < enteredFreq[letterKey]) { - letterClass = 'dark-letter'; - curFreq[letterKey]++; - } - return ( - - {letter} - - ); - }) - } -
-
-
-
-
-
-
-
- { this.setState({ shake: false }); }} - /> - -
-
+
+ ; + } + + render() { + if (!this.state.targetWordDefs) { + return (); + } + + return ( + +
+ { this.renderHeader() } + { this.renderAnagramInfo() } + { this.renderInput() } +
); } diff --git a/frontend/src/anagramView/anagramView.scss b/frontend/src/anagramView/anagramView.scss index 3c0e3c5..ab0e1bd 100644 --- a/frontend/src/anagramView/anagramView.scss +++ b/frontend/src/anagramView/anagramView.scss @@ -1,24 +1,30 @@ /* Styles for Anagram */ +.anagram-category { + font-size: 28px; + padding-bottom: 10px; +} + .score { background-color: #278E73; - display: inline-block; - padding: 7px 12px; border-radius: 5px; color: white; + display: inline-block; font-size: 16px; font-weight: bold; + padding: 7px 12px; } -.shaded-box { +.anagram-shaded-box { background: #5941A9; border-radius: 5px; height: 100%; - padding: 15px; margin: 0; + padding: 15px; } -ol { +.anagram-ol { font-weight: bold; + margin: 0; } li span { @@ -26,39 +32,39 @@ li span { } .btn-circle { - width: 50px; - height: 50px; - padding: 10px 16px; border-radius: 35px; font-size: 24px; + height: 50px; line-height: 1.33; + padding: 10px 16px; + width: 50px; } /* Modal Styles */ .backdrop { - width: 100%; + background-color: rgba(0,0,0,0.5); height: 100%; - position: fixed; - z-index: 100; left: 0; + position: fixed; top: 0; - background-color: rgba(0,0,0,0.5); + width: 100%; + z-index: 100; } .Modal { color: #2D2155; + left: 5%; position: fixed; - z-index: 500; transition: all 0.4s ease-out; width: 90%; - left: 5%; + z-index: 500; } @media (min-width: 992px) { .Modal { - width: 60%; left: 20%; + width: 60%; } } @@ -100,9 +106,8 @@ li span { background-color: black; } -.submit-button { - width: 100%; - margin-top: 20px; +.anagram-submit-button { + margin-left: 20px; } .form-control { @@ -111,8 +116,8 @@ li span { @media (min-width: 768px) { .submit-button { - width: 80px; margin: 0 20px 0; + width: 80px; } .form-control { From 98530f43e8ab0112416d508999638033a1d7d396 Mon Sep 17 00:00:00 2001 From: Jason Lin Date: Sun, 4 Apr 2021 22:39:09 -0400 Subject: [PATCH 13/14] Added single word anagram mode --- backend/app/analysis/anagrams.py | 11 ++ backend/app/views.py | 4 +- frontend/src/anagramView/anagramView.js | 140 ++++++++++++++++------ frontend/src/anagramView/anagramView.scss | 14 +++ 4 files changed, 133 insertions(+), 36 deletions(-) diff --git a/backend/app/analysis/anagrams.py b/backend/app/analysis/anagrams.py index b1c55c8..94bb98b 100644 --- a/backend/app/analysis/anagrams.py +++ b/backend/app/analysis/anagrams.py @@ -2,6 +2,7 @@ Helper functions for generating and checking anagrams """ import os +import random from django.conf import settings @@ -15,6 +16,16 @@ def get_word_set(): return word_set +def scramble_word(word): + """ + :param word: the word to scramble for single word anagrams + :return: a string of the word with the letters scrambled + """ + word_as_list = list(word) + random.shuffle(word_as_list) + return ''.join(word_as_list) + + def get_letter_freq(letters): """ Given a word, find the frequency of letters in this word diff --git a/backend/app/views.py b/backend/app/views.py index bc508b8..9a5a39b 100644 --- a/backend/app/views.py +++ b/backend/app/views.py @@ -20,6 +20,7 @@ from .analysis.anagrams import ( get_letter_freq, get_word_set, + scramble_word, ) from .analysis.textdata import ( get_text_data, @@ -101,7 +102,8 @@ def get_anagram(request, text_id, part_of_speech): 'letters': scrambled_letters, 'word_data': [{'word': word, 'definition': definitions[word].get(part_of_speech, []), - 'example': examples[word].get(part_of_speech, [])} + 'example': examples[word].get(part_of_speech, []), + 'scrambled': scramble_word(word)} for word in words] } return Response(res) diff --git a/frontend/src/anagramView/anagramView.js b/frontend/src/anagramView/anagramView.js index cd88928..498654b 100644 --- a/frontend/src/anagramView/anagramView.js +++ b/frontend/src/anagramView/anagramView.js @@ -44,23 +44,30 @@ const generateFreq = (word) => { return freq; }; +// Modes +const UNSELECTED = 0; +const SINGLE = 1; +const COMBINED = 2; + export class AnagramView extends React.Component { constructor(props) { super(props); this.state = { - targetWordDefs: null, - targetWords: [], - targetExamples: [], - userInput: '', - targetWordsFound: [], extraWordsFound: [], - score: 0, - letters: [], gameOver: false, - timeLeft: 90, - showModal: true, + letters: [], + mode: UNSELECTED, + score: 0, shake: false, showConfetti: false, + showModal: false, + scrambled: [], + targetExamples: [], + targetWords: [], + targetWordDefs: null, + targetWordsFound: [], + timeLeft: 90, + userInput: '', }; this.timer = 0; } @@ -152,18 +159,21 @@ export class AnagramView extends React.Component { reset = () => { this.setState({ - targetWordDefs: null, - targetWords: [], - targetExamples: [], - userInput: '', - targetWordsFound: [], extraWordsFound: [], - score: 0, - letters: [], gameOver: false, - timeLeft: 90, + letters: [], + mode: UNSELECTED, + score: 0, + scrambled: [], shake: false, showConfetti: false, + showModal: false, + targetExamples: [], + targetWords: [], + targetWordDefs: null, + targetWordsFound: [], + timeLeft: 90, + userInput: '', }); this.timer = 0; } @@ -178,6 +188,7 @@ export class AnagramView extends React.Component { let letters = []; const targetWordDefs = []; const targetExamples = []; + const scrambled = []; const wordData = data['word_data']; for (let i = 0; i < wordData.length; i++) { const curData = wordData[i]; @@ -185,6 +196,7 @@ export class AnagramView extends React.Component { const examples = curData['example']; targetWords.push(word); targetWordDefs.push(curData['definition']); + scrambled.push(curData['scrambled']); if (examples.length === 0) { targetExamples.push(['N/A']); } else { @@ -196,10 +208,11 @@ export class AnagramView extends React.Component { } letters = shuffleArray(letters); this.setState({ - targetWordDefs: targetWordDefs, - targetWords: targetWords, - letters: letters, - targetExamples: targetExamples, + targetWordDefs, + targetWords, + letters, + targetExamples, + scrambled, }); } catch (e) { console.log(e); @@ -395,7 +408,7 @@ export class AnagramView extends React.Component {
; - renderHeader = () => <> + renderAnagramHeader = () => <>

@@ -418,6 +431,9 @@ export class AnagramView extends React.Component {

Time Left: {this.state.timeLeft}

+ + + renderHeader = () => <> { this.state.gameOver ?
@@ -443,19 +459,44 @@ export class AnagramView extends React.Component {
; + renderExtraWords = () => <> +

Extra Words

+
    + { + this.state.extraWordsFound.map((word, i) => ( +
  • + {word.toUpperCase()} +
  • + )) + } +
+ ; + + renderScrambled = () => <> +

Scrambed Words (Click One)

+
    + { + this.state.scrambled.map((scrambledWord, k) => ( +
  1. { this.setState({ letters: [...scrambledWord] }); }} + key={k} + > + { scrambledWord } +
  2. + )) + } +
+ ; + renderAnagramInfo = () =>
-

Extra Words

-
    - { - this.state.extraWordsFound.map((word, i) => ( -
  • - {word.toUpperCase()} -
  • - )) - } -
+ { + this.state.mode === SINGLE + ? this.renderScrambled() + : this.renderExtraWords() + }
@@ -547,6 +588,28 @@ export class AnagramView extends React.Component { ; } + renderModeSelection = () => <> +

Please select a mode:

+ + + ; + render() { if (!this.state.targetWordDefs) { return (); @@ -555,9 +618,16 @@ export class AnagramView extends React.Component { return (
- { this.renderHeader() } - { this.renderAnagramInfo() } - { this.renderInput() } + { this.renderAnagramHeader() } + { + this.state.mode === UNSELECTED + ? this.renderModeSelection() + : <> + { this.renderHeader() } + { this.renderAnagramInfo() } + { this.renderInput() } + + }