def delete_word():
    """Delete a word from the dictioary

    Admins are able to delete a whole word object and animation from the database

    query parameter: /dictionary/delete?input=someword
    no request body

    :rtype: None
    """
    input_ = request.args.get('input', None)
    if not input_:
        return Response('Word not found', 204)

    input_ = ''.join(filter(str.isalpha, input_)).lower()
    if Dictionary.objects(word=input_):
        try:
            url = Dictionary.objects.get(word=input_).url
            url = url.split('/')
            s3_helper.delete_file_from_s3(url[-1])
            Dictionary.objects(word=input_).delete()
        except Exception as e:
            print(str(datetime.now()) + ' ', e)
            return Response('Failed: error deleting word', 501)
        return Response('Success: word deleted from the dictionary', 200)
    return Response('Word not found', 204)
def deny_word():
    """Deny words, remove the file from the S3 bucket and not the DB

    Deletes the specified word from the S3 bucket and removes the URL field for that word

    request body

    :rtype: None
    """
    if request.content_type != 'application/json':
        return Response('Failed: Content-type must be application/json', 415)

    r = request.get_json()
    if 'word' not in r:
        return Response('Failed: invalid request', 400)

    if len(r['word']) == 0:
        return Response('Failed: no words provided', 400)

    for e in r['word']:
        if Dictionary.objects(word=e):
            try:
                word = Dictionary.objects.get(word=e)
                if word.url == None:
                    return Response('Failed: word has no url field', 400)
                url = word.url
                word.update(unset__url=1)
                url = url.split('/')
                s3_helper.delete_file_from_s3(url[-1])
            except Exception as e:
                print(str(datetime.now()) + ' ', e)
                return Response('Failed: error deleting word', 501)
    return Response('Success: words deleted', 200)
def add_word():
    """Add a word to an existing module

    An admin will be able to add a word to an existing module

    request body

    :rtype: None
    """
    if request.content_type != 'application/json':
        return Response('Failed: Content-type must be application/json', 415)

    r = request.get_json()

    if 'word_id' or 'module_id' not in r:
        return Response('Failed: invalid request', 400)
    derr = Dictionary.error_checker(r['word_id'])
    merr = Module.error_checker(r['module_id'])
    if derr:
        return derr
    elif merr:
        return merr

    word = Dictionary.objects.get(id=r['word_id'])
    Module.objects(id=r['module_id']).update_one(push__words=word)
    return Response('Success: word added to module', 200)
def add_word():
    """Add a word to the dictionary

    Admins are able to add an ASL animation to our dictionary

    path parameter: /admin/dictionary/create
    request body

    :rtype: None
    """
    if 'file' not in request.files:
        return Response('Failed: missing file', 400)
    file = request.files['file']
    r = request.form.to_dict()
    """
        These attributes are also available

        file.filename
        file.content_type
        file.content_length
        file.mimetype

    """
    if file:
        file.filename = secure_filename(file.filename)
        w = enchant.Dict("en_US")
        word = ''.join(filter(str.isalpha, r['word'])).lower()
        # if it's an actual word try to upload the word
        if w.check(word):
            try:
                output = s3_helper.upload_file_to_s3(file)
                if Dictionary.objects(word=word):
                    Dictionary.objects(word=word).update_one(
                        url=output, in_dictionary=True)
                else:
                    Dictionary(word=word, url=output,
                               in_dictionary=True).save()
            except Exception as e:
                print(str(datetime.now()) + ' ', e)
                return Response('Failed: error uploading word', 501)
        else:
            return Response(
                'Failed: word provided is not a vaild english word', 400)
    else:
        return redirect('/admin/create')
    return Response('Success', 200)
def get_words():
    """Get all words in the dictionary

    Returns a JSON response contianing the word and id of all words in the db

    path parameter: /dictionary
    no request body

    :rtype: JSON
    """
    return Response(Dictionary.objects(in_dictionary=True).only('word').to_json(), 200, mimetype='application/json')
def get_word(word):
    """
    GET:
        Get a word in the dictionary

        Returns a JSON response contianing the word document of the word requested

        path parameter: /dictionary/{word}
        no request body

        :rtype: JSON
    POST:
        Request that we add a word to our dictionary

        Increments the times requested counter for a word

        path parameter: /dictionary/{word}
        no request body

        :rtype: None
    """
    word = ''.join(filter(str.isalpha, word)).lower()

    if request.method == 'GET':
        o = Dictionary.objects.get_or_404(word=word, in_dictionary=True)
        return Response(o.to_json(), 200, mimetype='application/json')

    if request.method == 'POST':
        w = enchant.Dict("en_US")
        if w.check(word):
            if Dictionary.objects(word=word, in_dictionary=False):
                Dictionary.objects(word=word).update_one(
                    upsert=True, inc__times_requested=1)
            elif Dictionary.objects(word=word, in_dictionary=True):
                return Response('Failed: word already exists', 409)
            else:
                Dictionary(word=word, times_requested=1).save()
        else:
            return Response('Failed: word provided is not a vaild english word', 400)
        return Response('Success: request received', 200)
def approve_word():
    """Approve words and make them publicly available

    Sets the in_dictionary value to true for the words sent

    request body

    :rtype: None
    """
    if request.content_type != 'application/json':
        return Response('Failed: Content-type must be application/json', 415)

    r = request.get_json()
    if 'word' not in r:
        return Response('Failed: invalid request', 400)

    if len(r['word']) == 0:
        return Response('Failed: no words provided', 400)

    for e in r['word']:
        Dictionary.objects(word=e).update(in_dictionary=True)

    return Response('Success, updated the dictionary', 200)
def list_words():
    """Get a list of all words that have have not been approved yet.

    Returns a list of all word objects that have been uploaded to our crowdsorcing page
    but have not been approved

    :query param start: Where the user wants the list to start
    :type submission_id: int
    :query param limit: How many words the user wants per page
    :type quiz: int

    :rtype: JSON
    """

    start = request.args.get('start', None)
    if start:
        start = int(start)
        if start >= Dictionary.objects(in_dictionary=False,
                                       url__exists=1).count():
            start = 0
    else:
        start = 0

    limit = request.args.get('limit', None)
    if limit:
        limit = int(limit)
        if limit < 5 or limit > 100:
            limit = 20
    else:
        limit = 20

    return Response(Dictionary.objects(
        in_dictionary=False,
        url__exists=1).order_by('-word')[start:limit].to_json(),
                    200,
                    mimetype='application/json')
def get_stats():
    """Get top most requested words

    Returns a list of JSON objects with the top 'N' where 5 <= N <= 100 most downloaded words.

    query parameter: /stats?limit=somenumber
    where 5 <= somenumber <= 100

    no request body

    :rtype: List[Dictionary]
    """
    limit = int(request.args.get('limit'))
    if limit < 5 or limit > 100:
        limit = 20
    o = Dictionary.objects(in_dictionary=False).order_by(
        '-times_requested')[:limit]
    return Response(o.to_json(), mimetype='application/json')
def add_questions(id_, r):
    if 'questions' not in r:
        return Response('Failed: no questions provided', 400)

    for e in r['questions']:
        if 'word' not in e:
            return Response('Failed: word not provided', 400)

        if not ObjectId.is_valid(e['word']):
            return Response('Failed: invalid Id', 400)

        if not Dictionary.objects(id=e['word'], in_dictionary=True):
            return Response('', 204)

    for e in r['questions']:
        try:
            question = Question(
                question_text=e['question_text'], word=e['word'])
            question.save()
            Quiz.objects(id=id_).update_one(push__questions=question)
        except Exception as e:
            print(str(datetime.now()) + ' ', e)
            return Response('Failed: invalid request', 400)
def create_module():
    """Create a module

    An admin will be able to create a new learning module

    request body

    :rtype: None
    """
    if request.content_type != 'application/json':
        return Response('Failed: Content-type must be application/json', 415)

    r = request.get_json()

    # build and save the module
    try:
        mod = Module(module_name=r['module_name'], details=r['details'])
    except Exception as e:
        print(str(datetime.now()) + ' ', e)
        return Response('Failed: Bad request', 400)

    # save it so we have an Id to reference
    mod.save()

    # if a parent is not provided then the new module is the new head of the module list
    if 'parent' not in r:
        # checks to make sure it is not the first module
        if Module.objects(parent__exists=0, id__ne=mod.id):
            # find the module with no parent node and upsert parent to new modules id
            Module.objects(parent__exists=0).update_one(upsert=True,
                                                        parent=mod.id)

    # get the parent if it exists
    if 'parent' in r:
        err = Module.error_checker(id=r['parent'])
        # if parent id is wrong throw error
        if err:
            return err
        # else get parent object and add it to the new module
        parent = Module.objects.get(id=r['parent'])
        mod.parent = parent.id

        # if there is a child get the id of the child just in case we need to revert it
        if Module.objects(parent=r['parent']):
            old_id = Module.objects.get(parent=r['parent']).id

        # change the childs parent id to be the new module id
        Module.objects(parent=r['parent']).update_one(parent=mod.id)

    # if the user provided a list of words the verify them and add them to the module
    if 'words' in r:
        for i in r['words']:
            # a word id is wrong return error
            err = Dictionary.error_checker(i)
            if err:
                mod.delete()
                return err
            # get the word object and add it to the module word array
            word = Dictionary.objects.get(id=i)
            Module.objects(id=mod.id).update_one(push__words=word)

    # if the user provided a list of quizzes the verify them and add them to the module
    if 'quiz' in r:
        for i in r['quiz']:
            # a quiz id is wrong return error
            err = Quiz.error_checker(i)
            if err:
                mod.delete()
                return err
            quiz = Quiz.objects.get(id=i)
            Module.objects(id=mod.id).update_one(push__quiz=quiz)

    try:
        mod.save()
    except Exception as e:
        print(str(datetime.now()) + ' ', e)
        if old_id:
            Module.objects(parent=r['parent']).update_one(parent=old_id)

    return Response('Success', 200)
def add_all():
    # adds words
    try:
        word_test = Dictionary(word='test',
                               url='http://test.com',
                               in_dictionary=False,
                               times_requested=7)
        word_hello = Dictionary(word='hello',
                                url='http://hello.com',
                                in_dictionary=True,
                                times_requested=2)
        word_sick = Dictionary(word='sick',
                               url='http://sick.com',
                               in_dictionary=False,
                               times_requested=1)
        word_foo = Dictionary(word='foo',
                              url='http://foo.com',
                              in_dictionary=True,
                              times_requested=10)
        word_bar = Dictionary(word='bar',
                              url='http://bar.com',
                              in_dictionary=True,
                              times_requested=30)
        word_food = Dictionary(word='food',
                               url='http://food.com',
                               in_dictionary=False,
                               times_requested=5)
        word_goodbye = Dictionary(word='goodbye',
                                  url='http://goodbye.com',
                                  in_dictionary=True,
                                  times_requested=1)
        word_pizza = Dictionary(word='pizza',
                                url='http://pizza.com',
                                in_dictionary=True,
                                times_requested=7)
        word_hack = Dictionary(word='hack',
                               url='http://hack.com',
                               in_dictionary=False,
                               times_requested=30)
        word_computer = Dictionary(word='computer',
                                   url='http://computer.com',
                                   in_dictionary=False,
                                   times_requested=10)

        # import json
        # print(word_test.to_json())
        word_test.save()
        word_hello.save()
        word_sick.save()
        word_foo.save()
        word_bar.save()
        word_food.save()
        word_goodbye.save()
        word_pizza.save()
        word_hack.save()
        word_computer.save()

        q0 = Question(question_text='Sign for test', word=word_test)
        q1 = Question(question_text='Sign for hello', word=word_hello)
        q2 = Question(question_text='Sign for sick', word=word_sick)
        q3 = Question(question_text='Sign for foo', word=word_foo)
        q4 = Question(question_text='Sign for bar', word=word_bar)
        q5 = Question(question_text='Sign for food', word=word_food)
        q6 = Question(question_text='Sign for goodbye', word=word_goodbye)
        q7 = Question(question_text='Sign for pizza', word=word_pizza)
        q8 = Question(question_text='Sign for hack', word=word_hack)
        q9 = Question(question_text='Sign for computer', word=word_computer)

        q0.save()
        q1.save()
        q2.save()
        q3.save()
        q4.save()
        q5.save()
        q6.save()
        q7.save()
        q8.save()
        q9.save()

        quiz_1 = Quiz(quiz_name='Test quiz 1',
                      details='This is the first test quiz',
                      questions=[q0, q1, q2, q3, q4])
        quiz_1.save()

        quiz_2 = Quiz(quiz_name='Test quiz 2',
                      details='This is the second test quiz',
                      questions=[q5, q8, q9])
        quiz_2.save()

        quiz_3 = Quiz(quiz_name='Test quiz 3',
                      details='This is the third test quiz',
                      questions=[q3, q5, q6, q7])
        quiz_3.save()

        quiz_4 = Quiz(quiz_name='Test quiz 4',
                      details='This is the forth test quiz',
                      questions=[q6, q8])
        quiz_4.save()

        mod_1 = Module(module_name='Test module 1',
                       details='This is the first module',
                       words=[
                           word_computer, word_test, word_hello, word_sick,
                           word_foo, word_bar, word_hack, word_food
                       ],
                       quiz=[quiz_1, quiz_2])

        mod_2 = Module(
            module_name='Test module 2',
            details='This is the two module',
            words=[word_foo, word_food, word_goodbye, word_pizza, word_hack],
            quiz=[quiz_3])

        mod_3 = Module(module_name='Test module 3',
                       details='This is the three module',
                       words=[word_goodbye, word_hack],
                       quiz=[quiz_4])

        mod_1.save()
        mod_2.save()
        mod_3.save()

        mod_3.parent = mod_2.id
        mod_3.save()

        mod_2.parent = mod_1.id
        mod_2.save()

        mod_1.save()

        user_1 = User(username='******',
                      firstname='jon',
                      lastname='wick',
                      dob='1964-09-02')
        user_1.creation_date = '2019-09-13 15:47:18.171396'
        comp1 = Completed_Modules(module_id=mod_1.id,
                                  module_name=mod_1.module_name)

        user_1.completed_modules = [comp1]
        user_1.save()

        ans0 = UserAnswers(question_id=q0.id,
                           user_answer=q0.word.word,
                           correct_answer=q0.word.word)
        ans1 = UserAnswers(question_id=q1.id,
                           user_answer=q1.word.word,
                           correct_answer=q1.word.word)
        ans2 = UserAnswers(question_id=q2.id,
                           user_answer=word_bar.word,
                           correct_answer=q2.word.word)
        ans3 = UserAnswers(question_id=q3.id,
                           user_answer=q3.word.word,
                           correct_answer=q3.word.word)
        ans4 = UserAnswers(question_id=q4.id,
                           user_answer=q4.word.word,
                           correct_answer=q4.word.word)
        sub_1 = Submission(user_id=user_1,
                           quiz_id=quiz_1,
                           module_id=mod_1,
                           user_answers=[ans0, ans1, ans2, ans3, ans4],
                           grade=4)
        sub_1.save()

        ans5 = UserAnswers(question_id=q3.id,
                           user_answer=q3.word.word,
                           correct_answer=q3.word.word)
        ans6 = UserAnswers(question_id=q5.id,
                           user_answer=q5.word.word,
                           correct_answer=q5.word.word)
        ans7 = UserAnswers(question_id=q6.id,
                           user_answer=q6.word.word,
                           correct_answer=q6.word.word)
        ans8 = UserAnswers(question_id=q7.id,
                           user_answer=q7.word.word,
                           correct_answer=q7.word.word)

        sub_2 = Submission(user_id=user_1,
                           quiz_id=quiz_3,
                           module_id=mod_2,
                           user_answers=[ans5, ans6, ans7, ans8],
                           grade=4)
        sub_2.save()

        ans9 = UserAnswers(question_id=q6.id,
                           user_answer=word_foo.word,
                           correct_answer=q6.word.word)
        ans10 = UserAnswers(question_id=q8.id,
                            user_answer=word_bar.word,
                            correct_answer=q8.word.word)

        sub_3 = Submission(user_id=user_1,
                           quiz_id=quiz_4,
                           module_id=mod_2,
                           user_answers=[ans9, ans10],
                           grade=0)
        sub_3.save()

    except Exception:
        print("An error occured filling the database.")
        print("================== ERROR =====================")
        traceback.print_exc()
        print("==============================================")
        print("Cleaning up ...")
        myclient.drop_database("asl_tutor")
        print("Done")