Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0a5b1d6
set up and edited textToSpeech page
YifanWang0 Apr 23, 2021
b27dca5
Fixed a few errors
taokinbo Apr 30, 2021
4f4fe66
testing something
taokinbo Apr 30, 2021
122b141
properlly connected backend to front end
taokinbo Apr 30, 2021
18c708b
got page to show
YifanWang0 Apr 30, 2021
8d85bae
Audio now plays
taokinbo Apr 30, 2021
613d146
Got the arrow keys working
taokinbo Apr 30, 2021
5ce3617
Merge branch 'master' into TTS
taokinbo May 3, 2021
91db718
fixed small error
taokinbo May 3, 2021
f0019a3
started creatinf grader
taokinbo May 4, 2021
75b9b5d
updated grader to match which words are correct and incorrect
taokinbo May 5, 2021
b8f392c
text box
adanna-a May 9, 2021
772aafc
Merge branch 'TTS' of https://github.com/dhmit/lang_learn into TTS
adanna-a May 9, 2021
dc3cc4a
added api call
taokinbo May 9, 2021
985c05e
fixing JSON error
adanna-a May 9, 2021
c2c4b75
more debugging done for api call
taokinbo May 10, 2021
2271a65
connected grader to frontend
taokinbo May 12, 2021
07da894
upgraded graded to grade word comparisions
taokinbo May 12, 2021
74b07d4
updating usertext once submitted
YifanWang0 May 14, 2021
f6d7af9
made some edits
taokinbo May 14, 2021
29b43e1
displayed grade and added buttons
taokinbo May 18, 2021
b63c9b2
Added missing words and improved display
taokinbo May 21, 2021
0fef2c7
fixed linter issues
taokinbo May 21, 2021
28d0e2e
comment text_parser
adanna-a May 22, 2021
cd1fcc2
code clean up/commenting
adanna-a May 22, 2021
63cfc73
Merge branch 'master' into TTS
taokinbo May 22, 2021
3f62955
fixed linter issues
taokinbo May 22, 2021
065c8c1
fixed more linter issues
taokinbo May 22, 2021
a8701d9
updated code to account for review
taokinbo May 24, 2021
84248da
fixed linter error
taokinbo May 24, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 165 additions & 0 deletions backend/app/analysis/text_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
"""
Custom command to populate text model with definition, examples, and images
"""
import string
import nltk
import Levenshtein as Lev


# Modified Code from Bing library
def get_sentences(text):
"""
This function, when given a string of words, breaks it apart into individual
sentences through markers of sentence breaks and line breaks(\n)

:param text: str, any text
:return: list containing the fragments of the broken down text
"""
# breaks up text by line breaks first
lines = [p for p in text.split('\n') if p]
sentences = []

# breaks up each line by sentence markers
for line in lines:
sentences.extend(nltk.tokenize.sent_tokenize(line))

return sentences


def correct_sentence(given_sent, correct_sent):
"""
Function takes the user's sentence and the correct sentence and compares them to find missing
words, the incorrect words, and a grade

:param given_sent: str, sentence the user types into the text box
:param correct_sent: str, the correct sentence that the instructor inputs
:return: dictionary with parameters holding the missing words, correct words,
word/correctness at each index
"""

grade = {}

# tokenize given_sent and correct_sent and turn them both into lists
given_tok = nltk.tokenize.word_tokenize(given_sent.lower().translate(
str.maketrans('', '', string.punctuation)))
correct_tok = nltk.tokenize.word_tokenize(correct_sent.lower().translate(
str.maketrans('', '', string.punctuation)))
Copy link
Contributor

@MBJean MBJean May 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a takeaway for the future, it's always worth considering how to make your code as readable as possible for the next developer. These long chained method calls with nested chained method certainly take me a second to parse fully, which is probably the impulse behind leaving a comment here. In this case, we could pull out the maketrans dictionary to improve readability:

translate_dictionary = str.maketrans('', '', string.punctuation)
given_tok = nltk.tokenize.word_tokenize(given_sent.lower().translate(translate_dictionary))
correct_tok = nltk.tokenize.word_tokenize(correct_sent.lower().translate(translate_dictionary))

Or even increase the verbosity even further to improve readability:

translation_dictionary = str.maketrans('', '', string.punctuation)
given_sanitized_sentence = given_sent.lower().translate(translation_dictionary)
correct_sanitized_sentence = correct_sent.lower().translate(translation_dictionary)
given_tokenized_sentence = nltk.tokenize.word_tokenize(given_sanitized_sentence)
correct_tokenized_sentence = nltk.tokenize.word_tokenize(correct_sanitized_sentence)

The above pattern does two things:

  1. Allows the next developer to skim more quickly and, in effect, ignore the right side of the expression. The expressively named variables tell me more about the method used to get to the end result of these transformations without even needing to read the implementation and they make the exact pieces of the implementation easier to find in the event that I do need to investigate the implementation.
  2. Removes the need for an explicit comment. This is a personal opinion, but I often find comments are ways of getting around writing expressive code.


# hold all the correct words and removed every word that is found to hold only
# the missing words
grade["missing"] = correct_tok.copy()

for word in given_tok:
if grade["missing"].count(word) != 0:
# remove the words that the user typed in to get the missing words
grade["missing"].remove(word)


grade["incorrect_word_index"] = []

# True if the user inputs the same sentence as the correct answer
grade["isCorrect"] = given_tok == correct_tok

grade["words"] = []

index = 0
for ind_1, word in enumerate(given_tok):
match_found = False

# loop until the match is found or all words are looked at and no match is found
for ind_2, match in enumerate(correct_tok[index:]):

if word == match:
match_found = True
match_index = ind_2 + index
break

# if the match is found then the grade is correct, incorrect otherwise
# increase match_index by 1 if the match is found
if match_found:
grade["words"].append({"word": word,
"grade": "correct"})
index = match_index + 1
else:
grade["incorrect_word_index"].append(ind_1)
grade["words"].append({"word": word,
"grade": "incorrect"})

# incorrect indices
for word_index in grade["incorrect_word_index"]:

# find the most similar word
sim_word = most_similar_word(given_tok[word_index], grade["missing"])

if sim_word is not None:
word_grade = correct_words(given_tok[word_index], sim_word)
grade["words"][word_index]["word_grade"] = word_grade

return grade


def most_similar_word(word, comparisons):
"""
Take a word and find the most similar word in a given list

:param word: string with one word
:param comparisons: list of words
:return: string, most similar word in comparisons to word
"""
min_lev_val = None
min_lev_word = None

# check the missing words and see how similar they are
for current_word in comparisons:
current_val = Lev.distance(word, current_word)

# see if the word is less or more similar
if min_lev_val is None or min_lev_val > current_val:
min_lev_val = current_val
min_lev_word = current_word

# return the most similar word
return min_lev_word


def correct_words(given_word, correct_word):
"""
Determine which letters in a input word are correct and incorrect

:param given_word: string with one word, the incorrect word from the user's input
:param correct_word: string with one word, the similar word that is correct
:return:
"""
grade = {}

char_missing = []

char_missing[:0] = correct_word

# remove the character if the character is there, find out how many are missing
for char in given_word:
if char_missing.count(char) != 0:
char_missing.remove(char)

grade["missing"] = list(char_missing)
grade["letters"] = []

# for each character, hold whether it is correct or incorrect
index = 0
for char in given_word:
match_found = False
for ind_2, match in enumerate(correct_word[index:]):

if char == match:
match_found = True
match_index = ind_2 + index
break
if match_found:
grade["letters"].append({"char": char,
"grade": "correct"})
index = match_index + 1
else:
grade["letters"].append({"char": char,
"grade": "incorrect"})

return grade
30 changes: 29 additions & 1 deletion backend/app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@
from .analysis.crosswords import (
get_crosswords,
)
from .quiz_creation.conjugation_quiz import get_quiz_sentences
from .quiz_creation.conjugation_quiz import (
get_quiz_sentences,
)
from .analysis.text_parser import (
get_sentences,
correct_sentence,
)



@api_view(['GET'])
Expand Down Expand Up @@ -243,3 +250,24 @@ def get_response_quiz_data(request, text_id):
raise Http404 from text_not_exist
res = get_quiz_questions(text_obj.content)
return Response(res)


@api_view(['GET'])
def get_indiv_sentences(request, text_id):
"""
API endpoint for getting the individual sentences from the given text.
"""
text_obj = Text.objects.get(id=text_id)
sentences = get_sentences(text_obj.content)
res = [{'sentence': sentence} for sentence in sentences]
return Response(res)


@api_view(['GET'])
def get_sentence_grade(request, user_sent, actual_sent):
"""
API endpoint for getting the individual sentences from the given text.
"""
graded_sentence = correct_sentence(user_sent, actual_sent)

return Response(graded_sentence)
9 changes: 8 additions & 1 deletion backend/config/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
get_picturebook_data,
get_crossword,
get_quiz_data,
text, get_response_quiz_data,
text,
get_indiv_sentences,
get_sentence_grade,
get_response_quiz_data,
)


Expand Down Expand Up @@ -61,6 +64,9 @@ def react_view_path(route, component_name):
path('api/text/<int:text_id>', text),
path('api/get_picturebook_prompt/<int:text_id>/<str:part_of_speech>', get_picturebook_prompt),
path('api/get_picturebook_data', get_picturebook_data),
path('api/get_indiv_sentences/<int:text_id>', get_indiv_sentences),
path('api/get_sentence_grade/<str:user_sent>/<str:actual_sent>', get_sentence_grade),


# View paths
react_view_path('', 'IndexView'),
Expand All @@ -74,4 +80,5 @@ def react_view_path(route, component_name):
react_view_path('picturebook/<int:textID>/<str:partOfSpeech>', 'PictureBookView'),
react_view_path('response_quiz/', 'ResponseAllQuizView'),
react_view_path('response_quiz/<int:textID>/', 'ResponseQuizView'),
react_view_path('textToSpeech/<int:textID>', 'TextToSpeech'),
]
1 change: 1 addition & 0 deletions frontend/src/UILibrary/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@import '../instructorView/instructorView';
@import '../flashcard/flashcardView';
@import '../pictureBookView/pictureBookView';
@import '../textToSpeech/textToSpeech';

html {
position: relative;
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { PictureBookView } from './pictureBookView/pictureBookView';
import { CrosswordView } from './crosswordView/crosswordView';
import { ResponseAllQuizView } from './responseQuizView/responseAllQuizView';
import { ResponseQuizView } from './responseQuizView/responseQuizView';
import { TextToSpeech } from './textToSpeech/textToSpeech';

// Import all styles
import './UILibrary/styles.scss';
Expand All @@ -36,4 +37,5 @@ window.app_modules = {
QuizView,
ResponseAllQuizView,
ResponseQuizView,
TextToSpeech,
};
1 change: 1 addition & 0 deletions frontend/src/index/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const QUIZ_TYPES = {
'Quiz': ['quiz', idLink],
'Crossword': ['crossword', posLink],
'Story Generator': ['picturebook', posLink],
'TextToSpeech': ['textToSpeech', idLink],
};

class TextInfo extends React.Component {
Expand Down
Loading