def __init__(self):
        super().__init__()
        self.moodle = MoodleDBSession()
        self.moodlemod = ModUserEnrollments()

        # Set up some things
        self.courses = []
        for course in self.moodle.get_teaching_learning_courses():
            self.courses.append(course.idnumber)
        self.groups = self.moodle.get_list_of_attributes('group', 'name')
from psmdlsyncer.models.datastores.autosend import AutoSendTree
from psmdlsyncer.models.datastores.moodle import MoodleTree
from psmdlsyncer.sql.MDB import MoodleDBSession
from psmdlsyncer.php import ModUserEnrollments

import re
from collections import defaultdict

if __name__ == "__main__":

    autosend = AutoSendTree()
    moodle = MoodleTree()
    moodle.process()
    autosend.process()
    dragonnet = MoodleDBSession()
    mod = ModUserEnrollments()
    results = dragonnet.get_timetable_data(active_only=False)
    TimetableInfo = dragonnet.table_string_to_class('ssis_timetable_info')

    # First thing we need to do is adjust the timetable info so that the comment
    # Has the section info
    reverse = {}
    for key, value in autosend.groups.section_maps.items():
        reverse[value] = key

    for item in results:
        # Find the section info by looking through step-by-step
        if item.name in reverse:
            section = reverse[item.name]
            with dragonnet.DBSession() as session:
class MoodleTemplate(DefaultTemplate):
    """
    Unpacks the info that comes in and sends it on to the PHP routines that acutally
    does the work
    Note: If you want to have some sort of queuing feature that, say, makes sure that
    "new_group" gets called before "add_to_group" (to ensure it's already there when adding it)
    This is the place to do it.
    """
    def __init__(self):
        super().__init__()
        self.moodle = MoodleDBSession()
        self.moodlemod = ModUserEnrollments()

        # Set up some things
        self.courses = []
        for course in self.moodle.get_teaching_learning_courses():
            self.courses.append(course.idnumber)
        self.groups = self.moodle.get_list_of_attributes('group', 'name')

    def course_exists(self, course_idnumber):
        return course_idnumber in self.courses

    def new_cohort(self, item):
        super().new_cohort(item)
        cohort_idnumber = item.param
        cohort_name = cohort_idnumber
        self.moodle.add_cohort(cohort_idnumber, cohort_name)

    def old_cohort(self, item):
        super().old_cohort(item)
        # Remove it?

    def new_student(self, item):
        """
        """
        user = self.moodle.wrap_no_result(self.moodle.get_user_from_idnumber, item.right.idnumber)
        if user:
            self.logger.warning("Putting existing student {} into the studentsALL group?".format(item.right))
            if user.deleted == 1:
                self.moodle.undelete_user(user)
            self.moodlemod.add_user_to_cohort(item.right.idnumber, 'studentsALL')
        else:
            super().new_student(item)
            student = item.right
            self.moodlemod.new_student(student)

    def login_method_changed(self, item):
        """

        """
        user = item.right
        try:
            self.moodle.update_table('user', where={
                'idnumber':user.idnumber
                },
                login_method=user.login_method)
            self.default_logger("Changed {} login method to {}".format(item.right, item.param))

        except (NoResultFound, MultipleResultsFound):
            self.logger.warn("Could not update login_method field for user {}".format(user))


    def check_for_allow_deletions(self):
        """
        If there settings.ini file contains specific instructions to delete the accounts, then do so
        Otherwise assume False
        """
        return config_get_section_attribute('MOODLE', 'deletion_mode') == 'hard_delete'

    def check_for_keep_username_startswith(self, user):
        with_what = config_get_section_attribute('MOODLE', 'keep_username_startswith')
        if not with_what:
            return False
        with_what = with_what.split(',')
        for this in with_what:
            if user.username.startswith(this):
                return True
        return False

    def remove_user_from_all_courses(self, user):
        for course in user.courses:
            self.moodlemod.deenrol_student_from_course(user.idnumber, course.idnumber)

    def remove_user_from_all_groups(self, user):
        """
        Used in old_* accounts functions
        """
        debug = config_get_section_attribute('DEBUGGING', 'inspect_soft_deletion_groups')
        for group in user.groups:
            self.logger.warning("Removing old_student {} from group {} ".format(user, group))
            self.moodlemod.remove_user_from_group(user.idnumber, group.idnumber)

    def old_student(self, item):
        super().old_student(item)
        student = item.left

        if self.check_for_keep_username_startswith(student):
            self.default_logger('I {} am a sacred account, leave me alone!'.format(student))
            return

        if self.check_for_allow_deletions():
            # This is the hard delete mode here
            # This deletes the account, probably only need this once in a while
            # Main difference from soft delete is that it unenrols them from all courses, which can delete information
            self.moodlemod.delete_account(student.idnumber)

        else:
            # The following lot item is useful if you want to provide an admin with CSV file of students that
            # are going to be deleted, in transition from soft to hard delete
            #self.logger.warn(student.to_csv)

            # Now set the homeroom field to 'left' and remove them from any groups they are in
            # Doesn't delete the account, and doesn't unenrol them from courses, either
            # Which makes recovering them a simple matter.
            # try:
            #     self.moodle.update_table('user', where={
            #         'idnumber':student.idnumber
            #         },
            #         deleted=1)

            # except (NoResultFound, MultipleResultsFound):
            #     self.logger.warn("Did not update homeroom field for student {}".format(student))

            # TODO: Make this a config item from the command line

            # try:
            #     self.moodle.update_table('user', where={
            #         'idnumber':student.idnumber
            #         },
            #         deleted=1)
            # except (NoResultFound, MultipleResultsFound):
            #     self.logger.warn("Coudefld not set deleted of student {} to 1".format(student))
            
            # Remove from all groups as well?
            self.remove_user_from_all_groups(student)
            self.remove_user_from_all_courses(student)

    def old_teacher(self, item):
        return
        super().old_teacher(item)
        teacher = item.left

        if self.check_for_keep_username_startswith(teacher):
            self.default_logger('I {} am a sacred account, leave me alone!'.format(teacher))
            return

        if self.check_for_allow_deletions():
            self.moodlemod.delete_account(teacher.idnumber)
        else:
            # self.logger.warn('Deleting teacher: {}'.format(teacher))
            # try:
            #     self.moodle.update_table('user', where={
            #         'idnumber':teacher.idnumber
            #         },
            #         deleted=1)
            # except (NoResultFound, MultipleResultsFound):
            #     self.logger.warn("Could not set deleted of teacher {} to 1".format(teacher))

            self.remove_user_from_all_groups(teacher)
            self.remove_user_from_all_courses(teacher)

    def old_parent(self, item):
        super().old_parent(item)
        parent = item.left

        if self.check_for_keep_username_startswith(parent):
            self.default_logger('I {} am a sacred account, leave me alone!'.format(parent))
            return

        if self.check_for_allow_deletions():
            self.moodlemod.delete_account(parent.idnumber)
        else:
            # self.logger.warn('Deleting parent: {}'.format(parent))
            # try:
            #     self.moodle.update_table('user', where={
            #         'idnumber':parent.idnumber
            #         },
            #         deleted=1)
            # except NoResultFound:
            #     self.logger.warn("Could not delete parent {}".format(parent))
            # except MultipleResultsFound:
            #     self.logger.warn('Multiple parent accounts for {}'.format(parent.idnumber))
    
            self.remove_user_from_all_groups(parent)
            self.remove_user_from_all_courses(parent)

    def new_teacher(self, item):
        """
        """
        user = self.moodle.wrap_no_result(self.moodle.get_user_from_username, item.right.username)
        if user:
            self.logger.warning("Staff member with username {} already exists, setting PS ID to {}.".format(item.right.username, item.right.idnumber))
            self.moodle.set_user_idnumber_from_username(item.right.username, item.right.idnumber)
            if user.deleted == 1:
                self.moodle.undelete_user(user)
        else:
            pass
            # super().new_teacher(item)
            # teacher = item.right
            # self.moodlemod.new_teacher(teacher)

    def new_parent(self, item):
        """
        """
        user = self.moodle.wrap_no_result(self.moodle.get_user_from_idnumber, item.right.idnumber)
        if user:
            if user.deleted == 1:
                self.moodle.undelete_user(user)
            self.logger.warning("Putting parent student {} into the parentsALL group".format(item.right))
            self.moodlemod.add_user_to_cohort(item.right.idnumber, 'parentsALL')
        if self.moodle.wrap_no_result(self.moodle.get_user_from_username, item.right.username):
            self.logger.warning("This parent with guardian email {0} is not linked. Search PS for 'GuardianEmail contains {0}' and email results to Admissions".format(item.right.email))
        else:
            super().new_parent(item)
            parent = item.right
            self.moodlemod.new_parent(parent)
            # Add to the appropriate cohort now to ensure it's working
            self.moodlemod.add_user_to_cohort(parent.idnumber, 'parentsALL')

    def new_group(self, item):
        """
        Although we detect new groups, the right thing to do is actually to create the group,
        as they are needed,
        when enrolling users.

        That's because groups and courses are inexplicably linked together
        and we have to do the checks for groups when enrolling anyway
        so best is to do the creation there
        """
        super().new_group(item)  # output

    # def new_course(self, item):
    #     super().new_course(item)
    #     course = item.right
    #     self.moodlemod.create_new_course(course.idnumber, course.name)

    def enrol_in_course(self, item):
        course_idnumber = item.param.course
        yes_no = course_idnumber in self.courses
        if yes_no:
            super().enrol_in_course(item)  # for output
        return yes_no

    def enrol_student_into_course(self, item):
        student = item.right.idnumber
        course = item.param.course
        group = item.param.group
        self.moodlemod.enrol_student_into_course(student, course, group) # just pass the whole schedule object itself
        # if self.enrol_in_course(item):   # for output and checking
        #     self.moodlemod.enrol_student_into_course(student, course, group) # just pass the whole schedule object itself
        # else:
        #     self.logger.debug("Did NOT enrol {} into course {}, because it does not exist in Moodle".format(item.right, course))

    def enrol_teacher_into_course(self, item):
        teacher = item.right.idnumber
        course = item.param.course
        group = item.param.group
        self.moodlemod.enrol_teacher_into_course(teacher, course, group) # just pass the whole schedule object itself
        # if self.enrol_in_course(item):   # for output and checking
        #     self.moodlemod.enrol_teacher_into_course(teacher, course, group) # just pass the whole schedule object itself
        # else:
        #     self.logger.debug("Did NOT enrol {} into course {}, because it does not exist in Moodle".format(item.right, course))

    def enrol_parent_into_course(self, item):
        parent = item.right.idnumber
        course = item.param.course
        group = item.param.group
        self.moodlemod.enrol_parent_into_course(parent, course, group) # just pass the whole schedule object itself
        # if self.enrol_in_course(item):   # for output and checking
        #     self.moodlemod.enrol_parent_into_course(parent, course, group) # just pass the whole schedule object itself
        # else:
        #     self.logger.debug("Did NOT enrol {} into course {}, because it does not exist in Moodle".format(item.right, course))

    def deenrol_teacher_from_course(self, item):
        return
        super().deenrol_from_course(item)   # for output
        user = item.right.idnumber
        course = item.param.course
        group = item.param.group.idnumber
        #self.moodlemod.deenrol_teacher_from_course(user, course)

    def deenrol_student_from_course(self, item):
        return
        user = item.right.idnumber
        course = item.param.course
        group = item.param.group.idnumber
        if hasattr(user, 'grade') and user.grade == 12:
            return
        super().deenrol_from_course(item)   # for output
        self.moodlemod.deenrol_student_from_course(user, course)

    def deenrol_parent_from_course(self, item):
        return
        super().deenrol_from_course(item)   # for output
        user = item.right.idnumber
        course = item.param.course
        group = item.param.group.idnumber
        self.moodlemod.deenrol_parent_from_course(user, course)

    def add_to_cohort(self, item):
        super().add_to_cohort(item)
        user = item.right.idnumber
        cohort = item.param
        self.moodlemod.add_user_to_cohort(user, cohort)

    def remove_from_cohort(self, item):
        super().remove_from_cohort(item)
        user = item.left.idnumber
        cohort = item.param
        self.moodlemod.remove_user_from_cohort(user, cohort)

    def new_group(self, item):
        if not item.right.course:
            self.default_logger("Did NOT add group {} because no course available".format(item.param))
            return
        course = item.right.course
        group = item.right
        if group in self.groups:
            self.logger.debug("Did NOT add group {} because it's already there....".format(group, course_idnumber))
        elif course.idnumber in self.courses:
            super().new_group(item)
            self.moodlemod.add_group(group.idnumber, group.name, course.idnumber)
        else:
            self.logger.debug("Did NOT add group {} because course {} does not exist.".format(group, course.idnumber))

    def new_custom_profile_field(self, item):
        """
        """
        right = item.right
        name = right.idnumber
        self.default_logger("Found a new custom profile field {}".format(name))
        self.moodle.make_new_custom_profile_field(name)

    def old_custom_profile_field(self, item):
        """
        This actually means that a particular user has lost a particular profile field
        (It does NOT mean that this profile field should be deleted...)
        or does it?
        """
        # not sure what to do yet

    def old_group(self, item):
        group = item.left
        course_idnumber = group.course_idnumber
        if course_idnumber not in self.courses:
            self.default_logger("Did NOT delete group {} because the correspoding course {} does not exist.".format(group, course_idnumber))
        elif re.search('-[a-z]{1}$', group.idnumber):
            self.moodlemod.delete_group(group.idnumber, course_idnumber)
        else:
            self.default_logger("Did NOT delete group {} in {} because it looks like a one manually maintained.".format(group, course_idnumber))

    def add_to_group(self, item):
        user = item.right
        group = item.param.group
        course = item.param.course
        if course in self.courses:
            super().add_to_group(item)
            # We don't actually need the course...

            self.moodlemod.add_user_to_group(user.idnumber, group.idnumber)

            #self.default_logger("Successfully put user {} into group {}".format(user, group))
        else:
            self.logger.debug("Did NOT put {} in group {} because course {} does not exist.".format(user, group, course))

    def remove_from_group(self, item):
        user = item.right.idnumber
        group = item.param.group
        course = item.param.course
        # We don't actually need the course...
        if not '-' in group.idnumber:
            # Must be a manual one, leave it alone
            return
        if group.idnumber.split('-') == 3:
            super().remove_from_group(item)
            self.moodlemod.remove_user_from_group(user, group.idnumber)
        else:
            # Take them out for now, this may need to change
            return
            super().remove_from_group(item)
            self.moodlemod.remove_user_from_group(user, group.idnumber)

    def username_changed(self, item):
        user = item.left
        idnumber = user.idnumber
        from_what = item.left.username
        to_what = item.right.username.strip()

        if user.idnumber.endswith('P'):

            # If two students do not have the same family ID, but contain the same guardian information
            # Then an error would result if we tried to change the username here
            # Because we cannot have two users with the same username

            if self.moodle.wrap_no_result(self.moodle.get_user_from_username, to_what):
                self.logger.critical("- Cannot change parent username for {} because username {} is already taken".format(user, to_what))
                return

            # It's a parent, so we need to change the username and email, and inform them
            newpassword = str(random.randint(111111, 999999))  # generates pseudo-random 6 digit number
            self.moodlemod.change_parent_username(user.idnumber, to_what, newpassword)

            # Send username change with item.right in order to provide the right info
            inform.inform_parent_username_changed(item.right, newpassword)

            self.logger.warning('Parent {} username changed from {} to {} and password is {}'.format(user, from_what, to_what, newpassword))

        elif hasattr(item.right, 'login_method') and item.right.login_method == 'nologin':
            # Just go ahead and change it automatically, no need to inform anyone or anything
            # because the account isn't active anyway
            # test for 'login_method' because teachers don't have that TODO: Add that to the model!
            super().username_changed(item)
            return

            # Just in case
            # TODO: Also update email field!
            #try:
            #    self.moodle.update_table('user', where={
            #        'idnumber':idnumber
            #        },
            #        username=to_what)
            #except IntegrityError:
            #    self.logger.warning('Got integrity error trying to change user {}\'s username to {}'.format(idnumber, to_what))
            #except NoResultFound:
            #    self.default_logger('Cannot change username for {} as not found in moodle: {}'.format(idnumber, item))    
            #super().username_changed(item)

        else:
            justgrade = functools.partial(re.sub, '[a-z_]', '')
            if justgrade(item.left.username) != justgrade(item.right.username):
                self.logger.critical("Grade change: Username {} has changed grade, needs username changed to {}".format(from_what, to_what))
            else:
                msg = "Username {} needs his/her username changed manually to {} this happens when passport info gets changed".format(from_what, to_what)
                if '_' in from_what:
                    if from_what.replace('_', '') == to_what:
                        self.default_logger("Student {} with an underscore and whose username has NOT changed to {}.".format(from_what, to_what))
                    else:
                        self.logger.warning(msg)
                else:
                    self.logger.warning(msg)

    def custom_profile_value_changed(self, item):
        person = item.left.useridnumber
        field = item.param.field
        value = item.param.value
        self.moodle.set_user_custom_profile(person, field, value)

    def add_custom_profile_field_to_user(self, item):
        super().add_custom_profile_field_to_user(item)
        person = item.right.idnumber
        field = item.param.field
        value = item.param.value
        self.moodle.add_user_custom_profile(person, field, value)

    def homeroom_changed(self, item):
        user = item.left
        from_what = item.left.homeroom
        if user.kind == 'student':
            homeroom = item.param
        elif user.kind == 'parent':
            homeroom = ','.join(item.param)
        # if user.idnumber == '4340P':
        #     from IPython import embed;
        #     print('4340P')
        #     embed()
        #     exit()
        try:
            self.moodle.update_table('user', where={
                'idnumber':user.idnumber
                },
                department=homeroom)
            super().homeroom_changed(item)
        except NoResultFound:
            self.default_logger('Could not change homeroom for {} as not found in moodle'.format(item))

    def course_grade_changed(self, item):
        for i in range(len(item.param)):
            grade = item.param[i]
            field = 'grade{}'.format(i+1)

            exists = self.moodle.get_rows_in_table('course_ssis_metadata',
                courseid = item.left.database_id,
                field=field)

            if not exists:
                self.moodle.insert_table('course_ssis_metadata',
                    courseid = item.left.database_id,
                    field=field,
                    value=grade)
            else:
                # we just need to update the existing one
                self.moodle.update_table('course_ssis_metadata',
                    where = dict(
                        courseid=item.left.database_id,
                        field=field,
                        ),
                    value=grade
                )

    def new_parent_link(self, item):
        """
        Go through all the children and make the association
        """
        super().new_parent_link(item)
        for child_idnumber in item.right.children:
            self.moodlemod.associate_child_to_parent(item.right.parent_idnumber, child_idnumber)

    def associate_child_to_parent(self, item):
        """
        Go through all the children and make the association
        """
        super().new_parent_link(item)
        for child_idnumber in item.right.children:
            self.moodlemod.associate_child_to_parent(item.right.parent_idnumber, child_idnumber)

    def deassociate_child_from_parent(self, item):
        return
        super().deassociate_child_from_parent(item)

    def new_mrbs_editor(self, item):
        super().new_mrbs_editor(item)
        self.moodle.add_mrbs_editor(item.param)

    def new_timetable(self, item):
        pass  # meaningless

    def old_timetable(self, item):
        pass # meaningless

    def new_timetable_data(self, item):
        super().new_timetable_data(item)
        #self.moodle.add_timetable_data(item.right)

    def old_timetable_data(self, item):
        super().old_timetable_data(item)
        #self.moodle.set_timetable_data_inactive(item.left)

    def new_course_metadata(self, item):
        super().new_course_metadata(item)
        self.moodle.add_course_metadata(item.right)

    def new_online_portfolio(self, item):
        super().new_online_portfolio(item)