#
# SchoolTool - common information systems platform for school administration
# Copyright (c) 2005 Shuttleworth Foundation
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
"""
Unit tests for the intervention catalog.
"""
import unittest
import doctest
from datetime import datetime

from zope.app.testing import setup
from zope.component import provideAdapter, provideUtility
from zope.component.hooks import setSite
from zope.interface import implements
from zope.intid.interfaces import IIntIds
from zope.site.folder import Folder
from zope.site.site import LocalSiteManager

from schooltool.app.interfaces import ISchoolToolApplication
from schooltool.app.catalog import getAppCatalogs
from schooltool.person.interfaces import IPerson
from schooltool.contact.interfaces import IContact

from schooltool.intervention import interfaces, intervention
from schooltool.intervention.catalog import (getMessageType, getGoalType,
    getMessagePersonsResponsible, getGoalPersonsResponsible,
    updatePersonsResponsibleIndex)


class AppStub(Folder):
    implements(ISchoolToolApplication)


class StudentStub:
    implements(IPerson)
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name


class ContactStub:
    implements(IContact)
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name


class MessageStub:
    implements(interfaces.IInterventionMessage, interfaces.IInterventionMarker)
    created = datetime(2009, 9, 9)
    def __init__(self, recipients=[], id=0):
        self.recipients = recipients
        self.id = id


class GoalStub:
    implements(interfaces.IInterventionGoal, interfaces.IInterventionMarker)
    created = datetime(2010, 1, 1)
    def __init__(self, persons_responsible=[], id=0):
        self.persons_responsible = persons_responsible
        self.id = id


class SchoolYearStub:
    __name__ = '2009-2010'


class EventStub:
    def __init__(self, participant1, participant2):
        self.rel_type = intervention.URIResponsible
        self.participant1 = participant1
        self.participant2 = participant2


def doctest_catalogSetUp():
    """
    We'll need an IntIds stub where getId return the object itself for ease of
    testing.

        >>> class IntIdsStub(object):
        ...     def getId(self, obj):
        ...         return obj
        >>> provideUtility(IntIdsStub(), IIntIds)

    Ensure that we have catalogs

        >>> app = ISchoolToolApplication(None)
        >>> catalogs = getAppCatalogs(app)

    We'll create the catalog with InterventionCatalog.

        >>> from schooltool.intervention.catalog import InterventionCatalog
        >>> catalog_obj = InterventionCatalog(app)
        >>> catalog_obj()
        >>> catalog = InterventionCatalog.get()

    Tests for the 'first_name' index:

        >>> catalog['first_name'].index_doc(11, MessageStub())
        >>> catalog['first_name'].index_doc(22, GoalStub())
        >>> sorted(catalog['first_name'].documents_to_values.items())
        [(11, 'John'), (22, 'Jane')]

    Tests for the 'last_name' index:

        >>> catalog['last_name'].index_doc(11, MessageStub())
        >>> catalog['last_name'].index_doc(22, GoalStub())
        >>> sorted(catalog['last_name'].documents_to_values.items())
        [(11, 'Deere'), (22, 'Doe')]

    Tests for the 'persons_responsible' index:

        >>> message = MessageStub(['teacher2', 'advisor1'])
        >>> goal = GoalStub(['teacher1', 'advisor2'])
        >>> catalog['persons_responsible'].index_doc(11, message)
        >>> catalog['persons_responsible'].index_doc(22, goal)
        >>> sorted(catalog['persons_responsible'].documents_to_values.items())
        [(11, ['teacher2', 'advisor1']), (22, ['teacher1', 'advisor2'])]

    Tests for the 'created' index:

        >>> catalog['created'].index_doc(11, MessageStub())
        >>> catalog['created'].index_doc(22, GoalStub())
        >>> sorted(catalog['created'].documents_to_values.items())
        [(11, datetime.datetime(2009, 9, 9, 0, 0)),
         (22, datetime.datetime(2010, 1, 1, 0, 0))]

    Tests for the 'schoolYearId' index:

        >>> catalog['schoolYearId'].index_doc(11, MessageStub())
        >>> catalog['schoolYearId'].index_doc(22, GoalStub())
        >>> sorted(catalog['schoolYearId'].documents_to_values.items())
        [(11, '2009-2010'), (22, '2009-2010')]

    Tests for the 'intervention_type' index:

        >>> catalog['intervention_type'].index_doc(11, MessageStub())
        >>> catalog['intervention_type'].index_doc(22, GoalStub())
        >>> sorted(catalog['intervention_type'].documents_to_values.items())
        [(11, 'message'), (22, 'goal')]
    """


def doctest_updatePersonsResponsibleIndex():
    """
    We'll need an IntIds stub that returns the object itself if it's an int and
    the object's id attribute for messages and goals.  Also it always
    returns True for the 'in' operator.

        >>> class IntIdsStub(dict):
        ...     def getId(self, obj):
        ...         if type(obj) == type(0):
        ...             return obj
        ...         else:
        ...             return obj.id
        ...     def queryId(self, obj, default=None):
        ...         return self.getId(obj)
        >>> provideUtility(IntIdsStub(), IIntIds)

    Ensure that we have catalogs

        >>> app = ISchoolToolApplication(None)
        >>> catalogs = getAppCatalogs(app)

    We'll create the catalog with InterventionCatalog.

        >>> from schooltool.intervention.catalog import InterventionCatalog
        >>> catalog_obj = InterventionCatalog(app)
        >>> catalog_obj()
        >>> catalog = InterventionCatalog.get()

    First we'll create a message and goal and index them.  We set the
    persons_responsible to be a list of integers for the predictablilty
    of the test.

        >>> message = MessageStub([1, 3], 11)
        >>> goal = GoalStub([4, 2], 22)
        >>> catalog.index_doc(message.id, message)
        >>> catalog.index_doc(goal.id, goal)
        >>> sorted(catalog['persons_responsible'].documents_to_values.items())
        [(11, [1, 3]), (22, [4, 2])]

    We'll change the message to have a new value for recipients and
    create an event with the message as participant1.

        >>> message.recipients = [9, 7]
        >>> event = EventStub(message, None)

    When we call updatePersonsResponsibleIndex with the event, we see that
    that persons_responsible index entry has been updated for that message.

        >>> updatePersonsResponsibleIndex(event)
        >>> sorted(catalog['persons_responsible'].documents_to_values.items())
        [(11, [9, 7]), (22, [4, 2])]

    We'll change the message to have yet a newer value for recipients and
    create an event with the message as participant2.

        >>> message.recipients = [5, 6]
        >>> event = EventStub(None, message)

    When we call updatePersonsResponsibleIndex with this event, we see that
    that persons_responsible index entry has been updated for that message.

        >>> updatePersonsResponsibleIndex(event)
        >>> sorted(catalog['persons_responsible'].documents_to_values.items())
        [(11, [5, 6]), (22, [4, 2])]

    We'll do the same two tests for the goal.

        >>> goal.persons_responsible = [0, 1]
        >>> event = EventStub(goal, None)
        >>> updatePersonsResponsibleIndex(event)
        >>> sorted(catalog['persons_responsible'].documents_to_values.items())
        [(11, [5, 6]), (22, [0, 1])]

        >>> goal.persons_responsible = [99, 55]
        >>> event = EventStub(None, goal)
        >>> updatePersonsResponsibleIndex(event)
        >>> sorted(catalog['persons_responsible'].documents_to_values.items())
        [(11, [5, 6]), (22, [99, 55])]

    """


def setUp(test):
    setup.placefulSetUp()
    setup.setUpTraversal()
    app = AppStub()
    app.setSiteManager(LocalSiteManager(app))
    setSite(app)
    provideAdapter(lambda x: app, (None,), ISchoolToolApplication)
    provideAdapter(getAppCatalogs)
    provideAdapter(lambda student: ContactStub(student.first_name,
                                               student.last_name),
                   adapts=[IPerson],
                   provides=IContact)
    provideAdapter(lambda message: StudentStub('John', 'Deere'),
                   adapts=[interfaces.IInterventionMessage],
                   provides=IPerson)
    provideAdapter(lambda goal: StudentStub('Jane', 'Doe'),
                   adapts=[interfaces.IInterventionGoal],
                   provides=IPerson)
    provideAdapter(getMessagePersonsResponsible,
                   adapts=[interfaces.IInterventionMessage],
                   provides=interfaces.IInterventionPersonsResponsible)
    provideAdapter(getGoalPersonsResponsible,
                   adapts=[interfaces.IInterventionGoal],
                   provides=interfaces.IInterventionPersonsResponsible)
    provideAdapter(lambda x: SchoolYearStub(),
                   adapts=[interfaces.IInterventionMessage],
                   provides=interfaces.IInterventionSchoolYear)
    provideAdapter(lambda x: SchoolYearStub(),
                   adapts=[interfaces.IInterventionGoal],
                   provides=interfaces.IInterventionSchoolYear)
    provideAdapter(getMessageType,
                   adapts=[interfaces.IInterventionMessage],
                   provides=interfaces.IInterventionType)
    provideAdapter(getGoalType,
                   adapts=[interfaces.IInterventionGoal],
                   provides=interfaces.IInterventionType)


def tearDown(test):
    setup.placefulTearDown()


def test_suite():
    optionflags = (doctest.ELLIPSIS |
                   doctest.NORMALIZE_WHITESPACE |
                   doctest.REPORT_ONLY_FIRST_FAILURE)
    return doctest.DocTestSuite(setUp=setUp, tearDown=tearDown,
                                optionflags=optionflags)


if __name__ == '__main__':
    unittest.main(defaultTest='test_suite')

