# coding: utf-8
#
# Copyright 2014 The Oppia Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS-IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Domain object for statistics models."""

__author__ = '<NAME>'

import copy
import operator

from core.platform import models
(stats_models,) = models.Registry.import_models([models.NAMES.statistics])


class StateCounter(object):
    """Domain object that keeps counts associated with states.

    All methods and properties in this file should be independent of the
    specific storage model used.
    """
    def __init__(self, first_entry_count, subsequent_entries_count,
                 resolved_answer_count, active_answer_count):
        self.first_entry_count = first_entry_count
        self.subsequent_entries_count = subsequent_entries_count
        self.resolved_answer_count = resolved_answer_count
        self.active_answer_count = active_answer_count

    @property
    def total_entry_count(self):
        """Total number of entries to the state."""
        return self.first_entry_count + self.subsequent_entries_count

    @property
    def no_answer_count(self):
        """Number of times a reader left without entering an answer."""
        return (self.first_entry_count + self.subsequent_entries_count
                - self.resolved_answer_count - self.active_answer_count)

    @classmethod
    def get(cls, exploration_id, state_name):
        state_counter_model = stats_models.StateCounterModel.get_or_create(
            exploration_id, state_name)
        return cls(
            state_counter_model.first_entry_count,
            state_counter_model.subsequent_entries_count,
            state_counter_model.resolved_answer_count,
            state_counter_model.active_answer_count
        )


class StateRuleAnswerLog(object):
    """Domain object that stores answers which match different state rules.

    All methods and properties in this file should be independent of the
    specific storage model used.
    """
    def __init__(self, answers):
        # This dict represents a log of answers that hit this rule and that
        # have not been resolved. The keys of this dict are the answers encoded
        # as HTML strings, and the values are integer counts representing how
        # many times the answer has been entered.
        self.answers = copy.deepcopy(answers)

    @property
    def total_answer_count(self):
        """Total count of answers for this rule that have not been resolved."""
        # TODO(sll): Cache this computed property.
        total_count = 0
        for answer, count in self.answers.iteritems():
            total_count += count
        return total_count

    @classmethod
    def get_multi(cls, exploration_id, rule_data):
        """Gets domain objects corresponding to the given rule data.

        Args:
            exploration_id: the exploration id
            rule_data: a list of dicts, each with the following keys:
                (state_name, handler_name, rule_str).
        """
        # TODO(sll): Should each rule_str be unicode instead?
        answer_log_models = (
            stats_models.StateRuleAnswerLogModel.get_or_create_multi(
                exploration_id, rule_data))
        return [cls(answer_log_model.answers)
                for answer_log_model in answer_log_models]

    @classmethod
    def get(cls, exploration_id, state_name, handler_name, rule_str):
        # TODO(sll): Deprecate this method.
        return cls.get_multi(exploration_id, [{
            'state_name': state_name,
            'handler_name': handler_name,
            'rule_str': rule_str
        }])[0]

    def get_top_answers(self, N):
        """Returns the top N answers.

        Args:
            N: the maximum number of answers to return.

        Returns:
            A list of (answer, count) tuples for the N answers with the highest
            counts.
        """
        return sorted(
            self.answers.iteritems(), key=operator.itemgetter(1),
            reverse=True)[:N]


class FeedbackItem(object):
    """Domain object for a generic feedback item.

    All methods and properties in this file should be independent of the
    specific storage model used.
    """
    def __init__(self, feedback_item_model):
        self.id = feedback_item_model.id
        self.target_id = feedback_item_model.target_id
        self.content = feedback_item_model.content
        self.additional_data = feedback_item_model.additional_data
        self.submitter_id = feedback_item_model.submitter_id
        self.status = feedback_item_model.status

    @classmethod
    def _get_feedback_items_for_target(cls, target_id):
        return [
            FeedbackItem(f) for f in
            stats_models.FeedbackItemModel.get_new_feedback_items_for_target(
                target_id)]

    @classmethod
    def _get_target_id_for_state(cls, exploration_id, state_name):
        return 'state:%s.%s' % (exploration_id, state_name)

    @classmethod
    def _get_target_id_for_exploration(cls, exploration_id):
        return 'exploration:%s' % (exploration_id)

    @classmethod
    def get_feedback_items_for_state(cls, exploration_id, state_name):
        target_id = cls._get_target_id_for_state(exploration_id, state_name)
        return cls._get_feedback_items_for_target(target_id)

    @classmethod
    def get_feedback_items_for_exploration(cls, exploration_id):
        target_id = cls._get_target_id_for_exploration(exploration_id)
        return cls._get_feedback_items_for_target(target_id)

    @classmethod
    def _create_feedback_for_target(
            cls, target_id, content, additional_data, submitter_id):
        feedback_item_model = stats_models.FeedbackItemModel.get_or_create(
            target_id, content, additional_data, submitter_id)
        return cls(feedback_item_model)

    @classmethod
    def create_feedback_for_state(
            cls, exploration_id, state_name, content, additional_data=None,
            submitter_id=None):
        target_id = cls._get_target_id_for_state(exploration_id, state_name)
        return cls._create_feedback_for_target(
            target_id, content, additional_data, submitter_id)

    @classmethod
    def create_feedback_for_exploration(
            cls, exploration_id, content, additional_data=None,
            submitter_id=None):
        target_id = cls._get_target_id_for_exploration(exploration_id)
        return cls._create_feedback_for_target(
            target_id, content, additional_data, submitter_id)

    def change_status(self, new_status):
        self.status = new_status

# coding: utf-8
#
# Copyright 2014 The Oppia Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS-IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Domain object for statistics models."""

__author__ = '<NAME>'

import copy
import operator

from core.platform import models
(stats_models,) = models.Registry.import_models([models.NAMES.statistics])


class StateCounter(object):
    """Domain object that keeps counts associated with states.

    All methods and properties in this file should be independent of the
    specific storage model used.
    """
    def __init__(self, first_entry_count, subsequent_entries_count,
                 resolved_answer_count, active_answer_count):
        self.first_entry_count = first_entry_count
        self.subsequent_entries_count = subsequent_entries_count
        self.resolved_answer_count = resolved_answer_count
        self.active_answer_count = active_answer_count

    @property
    def total_entry_count(self):
        """Total number of entries to the state."""
        return self.first_entry_count + self.subsequent_entries_count

    @property
    def no_answer_count(self):
        """Number of times a reader left without entering an answer."""
        return (self.first_entry_count + self.subsequent_entries_count
                - self.resolved_answer_count - self.active_answer_count)

    @classmethod
    def get(cls, exploration_id, state_name):
        state_counter_model = stats_models.StateCounterModel.get_or_create(
            exploration_id, state_name)
        return cls(
            state_counter_model.first_entry_count,
            state_counter_model.subsequent_entries_count,
            state_counter_model.resolved_answer_count,
            state_counter_model.active_answer_count
        )


class StateRuleAnswerLog(object):
    """Domain object that stores answers which match different state rules.

    All methods and properties in this file should be independent of the
    specific storage model used.
    """
    def __init__(self, answers):
        # This dict represents a log of answers that hit this rule and that
        # have not been resolved. The keys of this dict are the answers encoded
        # as HTML strings, and the values are integer counts representing how
        # many times the answer has been entered.
        self.answers = copy.deepcopy(answers)

    @property
    def total_answer_count(self):
        """Total count of answers for this rule that have not been resolved."""
        # TODO(sll): Cache this computed property.
        total_count = 0
        for answer, count in self.answers.iteritems():
            total_count += count
        return total_count

    @classmethod
    def get_multi(cls, exploration_id, rule_data):
        """Gets domain objects corresponding to the given rule data.

        Args:
            exploration_id: the exploration id
            rule_data: a list of dicts, each with the following keys:
                (state_name, handler_name, rule_str).
        """
        # TODO(sll): Should each rule_str be unicode instead?
        answer_log_models = (
            stats_models.StateRuleAnswerLogModel.get_or_create_multi(
                exploration_id, rule_data))
        return [cls(answer_log_model.answers)
                for answer_log_model in answer_log_models]

    @classmethod
    def get(cls, exploration_id, state_name, handler_name, rule_str):
        # TODO(sll): Deprecate this method.
        return cls.get_multi(exploration_id, [{
            'state_name': state_name,
            'handler_name': handler_name,
            'rule_str': rule_str
        }])[0]

    def get_top_answers(self, N):
        """Returns the top N answers.

        Args:
            N: the maximum number of answers to return.

        Returns:
            A list of (answer, count) tuples for the N answers with the highest
            counts.
        """
        return sorted(
            self.answers.iteritems(), key=operator.itemgetter(1),
            reverse=True)[:N]


class FeedbackItem(object):
    """Domain object for a generic feedback item.

    All methods and properties in this file should be independent of the
    specific storage model used.
    """
    def __init__(self, feedback_item_model):
        self.id = feedback_item_model.id
        self.target_id = feedback_item_model.target_id
        self.content = feedback_item_model.content
        self.additional_data = feedback_item_model.additional_data
        self.submitter_id = feedback_item_model.submitter_id
        self.status = feedback_item_model.status

    @classmethod
    def _get_feedback_items_for_target(cls, target_id):
        return [
            FeedbackItem(f) for f in
            stats_models.FeedbackItemModel.get_new_feedback_items_for_target(
                target_id)]

    @classmethod
    def _get_target_id_for_state(cls, exploration_id, state_name):
        return 'state:%s.%s' % (exploration_id, state_name)

    @classmethod
    def _get_target_id_for_exploration(cls, exploration_id):
        return 'exploration:%s' % (exploration_id)

    @classmethod
    def get_feedback_items_for_state(cls, exploration_id, state_name):
        target_id = cls._get_target_id_for_state(exploration_id, state_name)
        return cls._get_feedback_items_for_target(target_id)

    @classmethod
    def get_feedback_items_for_exploration(cls, exploration_id):
        target_id = cls._get_target_id_for_exploration(exploration_id)
        return cls._get_feedback_items_for_target(target_id)

    @classmethod
    def _create_feedback_for_target(
            cls, target_id, content, additional_data, submitter_id):
        feedback_item_model = stats_models.FeedbackItemModel.get_or_create(
            target_id, content, additional_data, submitter_id)
        return cls(feedback_item_model)

    @classmethod
    def create_feedback_for_state(
            cls, exploration_id, state_name, content, additional_data=None,
            submitter_id=None):
        target_id = cls._get_target_id_for_state(exploration_id, state_name)
        return cls._create_feedback_for_target(
            target_id, content, additional_data, submitter_id)

    @classmethod
    def create_feedback_for_exploration(
            cls, exploration_id, content, additional_data=None,
            submitter_id=None):
        target_id = cls._get_target_id_for_exploration(exploration_id)
        return cls._create_feedback_for_target(
            target_id, content, additional_data, submitter_id)

    def change_status(self, new_status):
        self.status = new_status

# coding: utf-8 # # Copyright 2014 The Oppia Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # #      http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS-IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. Domain object for statistics models. Domain object that keeps counts associated with states.      All methods and properties in this file should be independent of the     specific storage model used. Total number of entries to the state. Number of times a reader left without entering an answer. Domain object that stores answers which match different state rules.      All methods and properties in this file should be independent of the     specific storage model used. # This dict represents a log of answers that hit this rule and that # have not been resolved. The keys of this dict are the answers encoded # as HTML strings, and the values are integer counts representing how # many times the answer has been entered. Total count of answers for this rule that have not been resolved. # TODO(sll): Cache this computed property. Gets domain objects corresponding to the given rule data.          Args:             exploration_id: the exploration id             rule_data: a list of dicts, each with the following keys:                 (state_name, handler_name, rule_str). # TODO(sll): Should each rule_str be unicode instead? # TODO(sll): Deprecate this method. Returns the top N answers.          Args:             N: the maximum number of answers to return.          Returns:             A list of (answer, count) tuples for the N answers with the highest             counts. Domain object for a generic feedback item.      All methods and properties in this file should be independent of the     specific storage model used.