def __init__(self, left, right, template_klass=None, teachersonly=False, studentsonly=False, **kwargs):

        self.left = left
        self.right = right
        self.teachersonly = teachersonly
        self.studentsonly = studentsonly

        if not self.left._processed:
            self.left.process()
        if not self.right._processed:
            self.right.process()

        if not template_klass:
            self.template = DefaultTemplate()
        else:
            self.template = template_klass()
        self.logger = logging.getLogger(self.__class__.__name__)
        self.default_logger = self.logger.debug   # change this to debug for verbosity
        self.default_logger("Inside FindDifferences")
        self.default_logger("Left: {}".format(self.left))
        self.default_logger("Right: {}".format(self.right))
class DetermineChanges:
    """
    BOILERPLATE STUFF FOR MAKING THE WHEEL TURN
    LEFT IS "HAVE"
    RIGHT IS "NEED"
    """
    def __init__(self, left, right, template_klass=None, teachersonly=False, studentsonly=False, **kwargs):

        self.left = left
        self.right = right
        self.teachersonly = teachersonly
        self.studentsonly = studentsonly

        if not self.left._processed:
            self.left.process()
        if not self.right._processed:
            self.right.process()

        if not template_klass:
            self.template = DefaultTemplate()
        else:
            self.template = template_klass()
        self.logger = logging.getLogger(self.__class__.__name__)
        self.default_logger = self.logger.debug   # change this to debug for verbosity
        self.default_logger("Inside FindDifferences")
        self.default_logger("Left: {}".format(self.left))
        self.default_logger("Right: {}".format(self.right))

    def go(self, **kwargs):
        debug = config_get_section_attribute('DEBUGGING', 'print_dispatches')
        for item in self.subtract():
            if self.template and hasattr(item, 'status'):
                if item.status in ['new_teacher', 'new_student']:
                    self._repeat = True
                dispatch = self.template.get(item.status)
            else:
                dispatch = None
            if dispatch:
                debug and print(item)
                if self.teachersonly:
                    if item.right and hasattr(item.right, 'idnumber') and self.right.teachers.get_key(item.right.idnumber):
                        dispatch(item)
                elif self.studentsonly:
                    if item.right and hasattr(item.right, 'idnumber') and self.right.students.get_key(item.right.idnumber):
                        dispatch(item)
                else:
                    dispatch(item)
            else:
                #TODO: Handle unrecognized statuses here
                self.logger.warning("This item wasn't handled, because a handler ({}) wasn't defined!".format(item.status))

            #input('::')


    def get_subbranch(self, tree, subbranch):
        """
        Returns the subbranch of tree
        TODO: Check and throw KeyError exception if key is missing
        """
        return tree.__class__._store[tree.__class__.qual_name_format.format(
                branch=tree.__class__.__name__,
                delim=tree.__class__.qual_name_delimiter,
                subbranch=subbranch)]

    def subtract(self):
        """
        Yields ModificationStatements, which get fed back into the go method, which in turn
        dispatches the message to lower-level routines
        The ModfiicationStatements represent the changes that have occured.
        This method inspects meta data and passes on further inspections to the model itself
        """

        # First, process the meta data about the model, which
        # is basically just checking the keys for additions
        # then looking for differences between each item
        # then we check the keys for subtractions

        assert(len(self.left.get_subbranches()) == len(self.right.get_subbranches()))

        # NOTE, removed custom_profile_fields and timetable_datas

        subbranches = [
        'cohorts', 'courses', 'teachers', 'students',
        'groups', 'parents', 'parent_links', 'online_portfolios'
        ] # self.left.get_subbranches()

        for subbranch in subbranches:
            self.default_logger("Subbranch: {}".format(subbranch))

            left_branch = self.get_subbranch(self.left, subbranch)
            right_branch = self.get_subbranch(self.right, subbranch)
            self.default_logger("There are {} items in {}'s left branch".format(len(right_branch), subbranch))
            self.default_logger("There are {} items in {}'s right branch".format(len(right_branch), subbranch))

            self.default_logger("Looking for new {}:".format(subbranch))

            # Loop through missing stuff and do with it what we must
            for key in right_branch.keys() - left_branch.keys():
                yield ModificationStatement(
                    left = left_branch.get(key),
                    right = right_branch.get(key),
                    status = NS2.string("new_{term}", term=subbranch[:-1]),
                    param = key
                    )

        # Now go through the model and inspect the individual items
        # We have to go through each key on the left side, and on the right

        for subbranch in subbranches:
            self.default_logger("Individual items: {}".format(subbranch))
            left_branch = self.get_subbranch(self.left, subbranch)
            right_branch = self.get_subbranch(self.right, subbranch)

            # Left side:
            for item_key in left_branch:
                item_left = left_branch.get(item_key)
                item_right = right_branch.get(item_key)
                if item_left and item_right:

                    # The model itself defines how to look at each item with __sub__
                    # And we just yield that result
                    for left_minus_right in item_left - item_right:
                        yield ModificationStatement(
                            left = left_minus_right.left,
                            right = left_minus_right.right,
                            status = left_minus_right.status,
                            param = left_minus_right.param
                            )

            # short circuit out of this

            if subbranch in ["parent_links", "online_portfolios"]:
                # not compatible with the below, especially with make, because it wants the child
                continue

            # Right side:
            for item_key in right_branch:
                item_left = left_branch.get(item_key)
                item_right = right_branch.get(item_key)

                # Same principal as with the left side, but
                # there is a special case where, there is something on the right
                # but nothing on the left
                # The modification statement for "new_" will be spit out by the above
                # so we can just make a proxy object in order to manufacture the yield statements
                if item_right and not item_left and item_right.idnumber:

                    # Makes an empty, default one
                    fake = getattr(self.left, subbranch).\
                            make(item_right.idnumber)

                    # Notice fake - item_right
                    for right_minus_left in fake - item_right:
                        yield ModificationStatement(
                            left = right_minus_left.left,
                            right = right_minus_left.right,
                            status = right_minus_left.status,
                            param = right_minus_left.param
                            )


        for subbranch in subbranches:
            self.default_logger("Looking for old {}".format(subbranch))
            left_branch = self.get_subbranch(self.left, subbranch)
            right_branch = self.get_subbranch(self.right, subbranch)

            for key in left_branch.keys() - right_branch.keys():
                yield ModificationStatement(
                    left = left_branch.get(key),
                    right = right_branch.get(key),
                    status = NS2.string("old_{term}", term=subbranch[:-1]),
                    param = key
                    )