'''The tests for all CSC 3340 project problems, as well as the solution to the problem.
   Author: Kyle Burke <kburke@flsouthern.edu>'''

import math
import sys
import pymongo
#from unittest import assert_true

project_number = 5


def test_part(func, context, message_list):
    '''Runs f, a function with the elements of context (followed by message_list) as it's parameters that tests the part.  If f completes, then the test succeeds, and this returns True.  If f throws an exception, then it prints the string at message_list[0] and returns False.  f should be written so that it stores the relevant error message in message_list[0] before any statement that could cause an exception.'''
    #print("Called test_part...")
    
    try:
        func(*context, message_list)
        #print("Finished calling func!")
    except Exception as e:
        print(e)
        return False
    return True

def assert_true(test):
    if not test:
        raise Exception()
    
def assert_false(test):
    if test:
        raise Exception()

   
def sub_record(key, result):
    for entry in key:
        if not entry in result:
            return False
    return True



############### Tests!!!!!!! ###########################

### Table Setup tests ###

print("Starting the grading!")

print("IMPORTANT: This does not include all of my tests!  Your score might be lower than what you get here if you don't test all other things carefully.  For most parts, my tests are more comprehensive.  The best thing you can do is to add more tests.  (If you do that, do not share with other groups if it includes solutions.)  You can still upload your code and ask me to review it.")

students = []

students.append(input("Please enter your teamname: "))

feedbacks = []

for student in students:
   
    print("*******************Grading team " + student + "!****************")
    
    feedback_text = ""
    score = 0
    
    connection = pymongo.MongoClient()
    

    #Import the student file and call their main function if they have one
    stu_module = __import__(student + "_project" + str(project_number))
    if hasattr(stu_module, "main"):
        stu_module.main()
    
    stu_db = connection[student]
    
    
    
    
    parts = []
    
    
    
    
    #Part 0: british-royals
    def part_test_f0(stu_db, error_list):
        #check whether the collection exists
        error_list[0] = "No collection named british-royals."
        assert_true('british-royals' in stu_db.list_collection_names())
        
    parts.append((part_test_f0, [stu_db], 10))
    
    
    #Part 1: QE2
    def part_test_f1(stu_db, error_list):
        
        error_list[0] = "No collection named 'british-royals'."
        stu_collection = stu_db['british-royals']
        qeii = stu_collection.find_one({"full name" : "Elizabeth Alexandra Mary"})
        
        #check that a document was returned
        error_list[0] = "No document in the collection with the correct value in the 'full name' property."
        assert_true(qeii != None)
        
        #check that the royal name is correct
        error_list[0] = "The document doesn't have the correct value for the 'royal name' field."
        assert_true(qeii['royal name'] == "Queen Elizabeth II")
        
    parts.append((part_test_f1, [stu_db], 10))
    
    
    #Part 2: Four More Royals
    def part_test_f2(stu_db, error_list):
        
        error_list[0] = "No collection named 'british-royals'."
        stu_collection = stu_db['british-royals']
        qeii = stu_collection.find_one({"full name" : "Elizabeth Alexandra Mary"})
        
        #get the parents of QE2
        error_list[0] = "No parents field for Queen Elizabeth II"
        parent_ids = qeii['parents']
        
        #get the two objects from the ids 
        error_list[0] = "Not enough parents of Queen Elizabeth II"
        assert_true(len(parent_ids) == 2)
        
        
        parents = []
        for parent_id in parent_ids:
            parent = stu_collection.find_one({"_id" : parent_id})
            error_list[0] = "One of the elements of a parents field isn't a legitimate _id field for a parent document."
            assert_true(parent != None)
            parents.append(parent)
        
        #check that the parents were found
        parent_full_names = []
        for parent in parents:
            full_name = parent['full name']
            parent_full_names.append(full_name)
            if full_name == 'Albert Frederick Arthur George':
                kg6 = parent #we'll need him later
        
        error_list[0] = "Incorrect full name for one of Queen Elizabeth II's parents."
        #assert_true("Albert Frederick Arthur George" in parent_full_names and "Elizabeth Angela Marguerite Bowes-Lyon" in parent_full_names)
        parent_full_names.sort()
        print("parent_full_names:", parent_full_names)
        assert_true("Albert Frederick Arthur George" in parent_full_names)
        
        
        grandparent_ids = kg6['parents']
        error_list[0] = "Not enough grandparents of Queen Elizabeth II."
        assert_true(len(grandparent_ids) == 2)
        
        grandparents = []
        for single_id in grandparent_ids:
            grandparent = stu_collection.find_one({"_id": single_id})
            error_list[0]  = "One of the parents of King George VI isn't a legitimate _id for that document."
            assert_true(grandparent != None)
            grandparents.append(grandparent)
            
        grandparent_full_names = []
        for grandparent in grandparents:
            full_name = grandparent['full name']
            grandparent_full_names.append(full_name)
            
        error_list[0] = "Incorrect full name for one of Queen Elizabeth II's grandparents."
        assert_true("Victoria Mary Augusta Louise Olga Pauline Claudine Agnes" in grandparent_full_names)
    
    parts.append((part_test_f2, [stu_db], 20))
    
    
    #Part 3: get_id_by_full_name
    def part_test_f3(stu_db, stu_module, error_list):
        
        error_list[0] = "No collection named 'british-royals'."
        stu_collection = stu_db['british-royals']
        
        error_list[0] = "Threw an exception when looking for Queen Elizabeth II."
        qeii_id = stu_module.get_id_by_full_name(stu_collection, "Elizabeth Alexandra Mary")
        
        #check that a document was returned
        error_list[0] = "Doesn't return a document for Queen Elizabeth II."
        assert_true(qeii_id != None)
        
        error_list[0] = "Dictionary for a returned document doesn't have a 'parents' field when it should."
        parent_ids = stu_collection.find_one({"_id" : qeii_id})['parents']
        
        error_list[0] = "Dictionary for a returned document doesn't have the parents when it should."
        parent = stu_collection.find_one({"_id" : parent_ids[0]})
        
        error_list[0] = "Dictionary for a returned document doesn't have the proper parent_ids."
        assert_true(parent != None)
    
    parts.append((part_test_f3, [stu_db, stu_module], 20))
    
    
    #Part 4: is_parent
    def part_test_f4(stu_db, stu_module, error_list):
        
        error_list[0] = "No collection named 'british-royals'."
        stu_collection = stu_db['british-royals']
        
        error_list[0] = "Threw an exception while calling is_parent."
        result_a = stu_module.is_parent(stu_collection, "Elizabeth Angela Marguerite Bowes-Lyon", "Elizabeth Alexandra Mary")
        result_b = stu_module.is_parent(stu_collection, "Elizabeth Alexandra Mary", "Elizabeth Angela Marguerite Bowes-Lyon")
        
        error_list[0] = "Doesn't work when one royal is the parent of another."
        assert_true(result_a)
        
        error_list[0] = "Doesn't work when one royal isn't the parent of another."
        assert_true(not result_b)
    
    parts.append((part_test_f4, [stu_db, stu_module], 10))
    
    
    
    #Part 5: get_parent_names
    def part_test_f5(stu_db, stu_module, error_list):
        
        error_list[0] = "No collection named 'british-royals'."
        stu_collection = stu_db['british-royals']
        
        
        error_list[0] = "Threw an exception while calling the function."
        qeii_parent_names = stu_module.get_parent_names(stu_collection, "Elizabeth Alexandra Mary")
        
        error_list[0] = "Doesn't return a list."
        qeii_parent_names.sort()
        
        
        error_list[0] = "Doesn't return the correct names."
        assert_true("Albert Frederick Arthur George" in qeii_parent_names)
    
    parts.append((part_test_f5, [stu_db, stu_module], 10))
    
    
    
    
    #Part 6: get_child_and_grandchild_names
    def part_test_f6(stu_db, stu_module, error_list):
        
        error_list[0] = "No collection named 'british-royals'."
        stu_collection = stu_db['british-royals']
        
        
        error_list[0] = "Threw an exception while calling the function."
        teck_descendant_names = stu_module.get_child_and_grandchild_names(stu_collection, "Victoria Mary Augusta Louise Olga Pauline Claudine Agnes")
        
        
        
        error_list[0] = "Returns a list that is missing at least one name."
        assert_true("Albert Frederick Arthur George" in teck_descendant_names)
        
        error_list[0] = "Returns a list that has too many names."
        assert_true("Victoria Mary Augusta Louise Olga Pauline Claudine Agnes" not in teck_descendant_names)
    
    parts.append((part_test_f6, [stu_db, stu_module], 20))
    
    
    
    
    #TODO: this should probably be encapsulated into a function.
    for i in range(len(parts)):
        part_tuple = parts[i]
        part_test = part_tuple[0]
        context = part_tuple[1]
        max_points = part_tuple[2]
        
        error_list = ["Blank"]
        #print("Calling test_part...")
        correct = test_part(part_test, context, error_list)
    
        if correct:
            part_points = max_points
            error_message = ""
        else:
            part_points = 0
            error_message = "\n  " + error_list[0]
    
        feedback_text += "Part " + str(i) + ": " + str(part_points) + "/" + str(max_points) + error_message + "\n"
        
        feedback_text += "\n"
        score += part_points
    
    
    

    print("Team " + student + ": " + str(score) + "/100\n\n" + feedback_text)
    print()
    print()