"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/dulcinea/lib/survey.py $
$Id: survey.py 23466 2004-02-13 19:12:31Z dbinger $
"""

from mx import DateTime
from dulcinea.keyed import Keyed, KeyedMap
from dulcinea.base import DulcineaBase, DulcineaPersistent
from dulcinea.persistent_list import PersistentList
from dulcinea.spec import any, sequence
from dulcinea.typeutils import typecheck, typecheck_seq
from dulcinea.user import DulcineaUser


class Question(Keyed, DulcineaPersistent):
    """
    Instance attributes:
      title : string
        text of the question
      sub_title : string
        sub title
    """

    title_is = any(str, None)
    sub_title_is = any(str, None)

    def __init__(self, title, sub_title=None):
        assert self.__class__ is not Question, "abstract class"
        Keyed.__init__(self)
        self.title = title
        self.sub_title = sub_title

    def get_title(self):
        return self.title

    def get_sub_title(self):
        return self.sub_title

    def get_new_answer(self):
        raise NotImplementedError, "abstract method"


class TextQuestion(Question):

    """
    Instance attributes: none
    """
    def get_new_answer(self):
        return TextAnswer(self)


class SelectQuestion(Question):
    """
    Instance attributes:
      column_headings : [ string ]
        text to place above each column
      row_headings : [ string ]
        text that preceeds each row
      range_labels : [ string ]
        labels to be placed in a row above column headings at the right and left
      last_row_has_text : boolean
        flag to tell the widget to display a TextWidget after the last row label

    A SelectQuestion is an abstract class for representing selections
    from either checkboxes or radio buttons
    """

    column_headings_is = [ any(str, None) ]
    row_headings_is = [ any (str, None) ]
    range_labels_is = [ str ]
    last_row_has_text_is = bool

    def __init__(self, title, sub_title=None,
                 column_headings=None, row_headings=None,
                 range_labels=None, last_row_has_text=False):
        Question.__init__(self, title, sub_title=sub_title)
        self.column_headings = column_headings or []
        self.row_headings = row_headings or []
        self.range_labels = range_labels or []
        assert len(self.range_labels) in (0,2), "must have two range labels"
        self.last_row_has_text = last_row_has_text

    def get_row_headings(self):
        return self.row_headings

    def get_column_headings(self):
        return self.column_headings

    def get_range_labels(self):
        return self.range_labels

    def get_new_answer(self):
        return SelectAnswer(self)



class QuestionDatabase(KeyedMap):
    """
    Instance attributes: none
    """
    def __init__(self):
        KeyedMap.__init__(self, value_spec=Question)

    def get_answers(self, question_keys):
        """(question_keys: [int]) -> [Answer]
        """
        return [self.get(question_key).get_new_answer()
                for question_key in question_keys]

class Answer(DulcineaBase):
    """
    Instance attributes:
      question : Question
        the question associated with this answer
    """

    question_is = Question

    def __init__(self, question):
        assert self.__class__ is not Answer, "abstract class"
        self.question = question

    def get_question(self):
        return self.question


class TextAnswer(Answer):
    """
    Instance attributes:
      answer : string
        text answer to a question
    """

    answer_is = str

    def __init__(self, question):
        Answer.__init__(self, question)
        self.answer = None

    def get_answer(self):
        return self.answer

    def set_answer(self, answer):
        self.answer = answer

    def has_answer(self):
        return self.answer is not None

class SelectAnswer(Answer):
    """
    Instance attributes:
      selected : [ string | boolean ]
        list of selected row answers
      other_text : string
        contents of the filled in 'last_row_has_text' textfield
    """
    selected_is = [any(str, bool)]

    def __init__(self, question):
        Answer.__init__(self, question)
        self.selected = None
        self.other_text = None

    def set_other_text(self, other_text):
        self.other_text = other_text

    def get_other_text(self):
        return self.other_text

    def set_answer(self, selected):
        self.selected = selected

    def get_answer(self):
        return self.selected

    def has_answer(self):
        return self.selected is not None


class Survey(DulcineaPersistent):
    """A set of Questions and Answers attached to a single object.

    Instance attributes:
      user : DulcineaUser
        user taking the survey
      timestamp : DateTime
        set when the Answers are added
      answers : [ Answer ]
        answers to the questions that comprise the object's survey
    """

    user_is = any(DulcineaUser, None)
    timestamp_is = DateTime.DateTimeType
    answers_is = [Answer]

    def __init__(self, answers):
        """(answers:[Answer])
        """
        typecheck_seq(answers, Answer)
        self.answers = answers
        self.timestamp = None
        self.user = None

    def __str__(self):
        return "By %s on %s: %d answers" % (
            self.user, self.timestamp, len(self.answers))

    def __len__(self):
        return len(self.answers)

    def get_questions(self):
        return [answer.get_question() for answer in self.answers]

    def get_answers(self):
        return self.answers

    def get_timestamp(self):
        return self.timestamp

    def get_user(self):
        return self.user

    def set_timestamp(self):
        self.timestamp = DateTime.now()

    def set_user(self, user):
        """(user : DuclineaUser)
        """
        typecheck(user, DulcineaUser, allow_none=1)
        self.user = user

    def set_answers(self, answers, user):
        """(answers : [Answer], user : DulcineaUser)
        """
        typecheck_seq(answers, Answer)
        self.answers = answers
        self.set_user(user)
        self.set_timestamp()

    def has_answers(self):
        """() -> boolean

        Return true if any answers have been answered for this survey
        """
        for answer in self.answers:
            if answer.has_answer():
                return True
        return False


class Surveyable:
    """A mixin for objects that have Surveys

    Instance attributes:
      surveys : PersistentList [ Survey ]
    """

    surveys_is = any([Survey], None)

    def __init__(self):
        self.surveys = None

    def get_surveys(self):
        """() -> [Survey]
        """
        return self.surveys or []

    def get_survey(self, index):
        return self.surveys and self.surveys[index]

    def get_new_survey(self):
        """() -> Survey
        """
        raise NotImplementedError, "abstract method"

    def add_survey(self, survey):
        if self.surveys is None:
            self.surveys = PersistentList()
        self.surveys.append(survey)
