# Copyright (C) 2015 Google Inc., authors, and contributors <see AUTHORS file>
# Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
# Created By: anze@reciprocitylabs.com
# Maintained By: anze@reciprocitylabs.com

"""Module for Assessment object"""

from sqlalchemy.orm import validates

from ggrc import db
from ggrc.models import reflection
from ggrc.models.mixins_assignable import Assignable
from ggrc.models.mixin_autostatuschangable import AutoStatusChangable
from ggrc.models.mixins import BusinessObject
from ggrc.models.mixins import CustomAttributable
from ggrc.models.mixins import FinishedDate
from ggrc.models.mixins import VerifiedDate
from ggrc.models.mixins import TestPlanned
from ggrc.models.mixins import Timeboxed
from ggrc.models.mixins import deferred
from ggrc.models import mixins_reminderable
from ggrc.models.object_document import Documentable
from ggrc.models.object_owner import Ownable
from ggrc.models.object_person import Personable
from ggrc.models.reflection import PublishOnly
from ggrc.models.relationship import Relatable
from ggrc.models.track_object_state import HasObjectState
from ggrc.models.track_object_state import track_state_for_class
from ggrc.models.comment import Commentable


class Assessment(AutoStatusChangable, Assignable, HasObjectState, TestPlanned,
                 CustomAttributable, Documentable, Commentable, Personable,
                 mixins_reminderable.Reminderable, Timeboxed,
                 Ownable, Relatable, FinishedDate, VerifiedDate,
                 BusinessObject, db.Model):
  """Class representing Assessment.

  Assessment is an object representing an individual assessment performed on
  a specific object during an audit to ascertain whether or not
  certain conditions were met for that object.
  """

  __tablename__ = 'assessments'

  START_STATE = {u'Open'}
  PROGRESS_STATE = {u'In Progress'}
  DONE_STATE = {u'Finished'}
  END_STATES = {u'Verified', u'Final'}

  NOT_DONE_STATES = START_STATE | PROGRESS_STATE
  DONE_STATES = DONE_STATE | END_STATES
  VALID_STATES = tuple(NOT_DONE_STATES | DONE_STATES)
  ASSIGNEE_TYPES = (u"Creator", u"Assessor", u"Verifier")

  FIRST_CLASS_EDIT = START_STATE | END_STATES
  ASSIGNABLE_EDIT = END_STATES

  REMINDERABLE_HANDLERS = {
      "statusToPerson": {
          "handler":
              mixins_reminderable.Reminderable.handle_state_to_person_reminder,
          "data": {
              "Open": "Assessor",
              "In Progress": "Assessor"
          },
          "reminders": {"assessment_assessor_reminder", }
      }
  }

  status = deferred(db.Column(db.Enum(*VALID_STATES), nullable=False,
                    default=tuple(START_STATE)[0]), "Assessment")

  design = deferred(db.Column(db.String), "Assessment")
  operationally = deferred(db.Column(db.String), "Assessment")

  object = {}  # we add this for the sake of client side error checking
  audit = {}

  VALID_CONCLUSIONS = frozenset([
      "Effective",
      "Ineffective",
      "Needs improvement",
      "Not Applicable"
  ])

  # REST properties
  _publish_attrs = [
      'design',
      'operationally',
      PublishOnly('audit'),
      PublishOnly('object')
  ]

  _tracked_attrs = {
      'contact_id',
      'description',
      'design',
      'notes',
      'operationally',
      'reference_url',
      'secondary_contact_id',
      'test_plan',
      'title',
      'url',
      'start_date',
      'end_date'
  }

  _aliases = {
      "audit": {
          "display_name": "Audit",
          "mandatory": True,
      },
      "url": "Assessment URL",
      "design": "Conclusion: Design",
      "operationally": "Conclusion: Operation",
      "related_creators": {
          "display_name": "Creator",
          "mandatory": True,
          "filter_by": "_filter_by_related_creators",
          "type": reflection.AttributeInfo.Type.MAPPING,
      },
      "related_assessors": {
          "display_name": "Assessor",
          "mandatory": True,
          "filter_by": "_filter_by_related_assessors",
          "type": reflection.AttributeInfo.Type.MAPPING,
      },
      "related_verifiers": {
          "display_name": "Verifier",
          "filter_by": "_filter_by_related_verifiers",
          "type": reflection.AttributeInfo.Type.MAPPING,
      },
  }

  def validate_conclusion(self, value):
    return value if value in self.VALID_CONCLUSIONS else ""

  @validates("operationally")
  def validate_opperationally(self, key, value):
    # pylint: disable=unused-argument
    return self.validate_conclusion(value)

  @validates("design")
  def validate_design(self, key, value):
    # pylint: disable=unused-argument
    return self.validate_conclusion(value)

  @classmethod
  def _filter_by_related_creators(cls, predicate):
    return cls._get_relate_filter(predicate, "Creator")

  @classmethod
  def _filter_by_related_assessors(cls, predicate):
    return cls._get_relate_filter(predicate, "Assessor")

  @classmethod
  def _filter_by_related_verifiers(cls, predicate):
    return cls._get_relate_filter(predicate, "Verifier")

track_state_for_class(Assessment)

@@ -833,24 +833,66 @@
 eminderable%0A
+from ggrc.models import mixins_statusable%0A
 from ggrc.mo
@@ -1306,16 +1306,63 @@
 essment(
+mixins_statusable.Statusable,%0A                 
 AutoStat
@@ -1933,415 +1933,63 @@
 %0A%0A  
-START_STATE = %7Bu'Open'%7D%0A  PROGRESS_STATE = %7Bu'In Progress'%7D%0A  DONE_STATE = %7Bu'Finished'%7D%0A  END_STATES = %7Bu'Verified', u'Final'%7D%0A%0A  NOT_DONE_STATES = START_STATE %7C PROGRESS_STATE%0A  DONE_STATES = DONE_STATE %7C END_STATES%0A  VALID_STATES = tuple(NOT_DONE_STATES %7C DONE_STATES)%0A  ASSIGNEE_TYPES = (u%22Creator%22, u%22Assessor%22, u%22Verifier%22)%0A%0A  FIRST_CLASS_EDIT = START_STATE %7C END_STATES%0A  ASSIGNABLE_EDIT = END_STATES
+ASSIGNEE_TYPES = (u%22Creator%22, u%22Assessor%22, u%22Verifier%22)
 %0A%0A  
@@ -2179,14 +2179,48 @@
     
-%22Open%22
+mixins_statusable.Statusable.START_STATE
 : %22A
@@ -2357,145 +2357,8 @@
  %7D%0A%0A
-  status = deferred(db.Column(db.Enum(*VALID_STATES), nullable=False,%0A                    default=tuple(START_STATE)%5B0%5D), %22Assessment%22)%0A%0A
   de

Update Assessment object to use Statusable mixin