# Bungeni Parliamentary Information System - http://www.bungeni.org/
# Copyright (C) 2010 - Africa i-Parliaments - http://www.parliaments.info/
# Licensed under GNU GPL v2 - http://www.gnu.org/licenses/gpl-2.0.txt

"""Form Schemas for Domain Objects

$Id: descriptor.py 6629 2010-05-25 09:00:14Z mianonjoka $
"""

from copy import deepcopy
from datetime import date
from zope import schema, interface

from zope.security.management import getInteraction
from zope.publisher.interfaces import IRequest
import zope.app.form.browser
from zope.i18n import translate
from zc.table import column

from ore.alchemist import Session
from ore.alchemist.model import ModelDescriptor

from alchemist.ui import widgets

from bungeni.models import domain

from bungeni.core.translation import get_default_language
from bungeni.core.translation import get_all_languages
from bungeni.core.workflows.question import states as question_wf_state
from bungeni.core.workflows.motion import states as motion_wf_state
from bungeni.core.workflows.bill import states as bill_wf_state
from bungeni.core.workflows.agendaitem import states as agendaitem_wf_state
from bungeni.core.workflows.tableddocument import states as tableddocument_wf_state
from bungeni.core.workflows.groups import states as group_wf_state
from bungeni.core.workflows.attachedfile import states as af_wf_state
from bungeni.core.workflows.address import states as address_wf_state
from bungeni.core.workflows.event import states as event_wf_state
from bungeni.core.workflows.groupsitting import states as sitting_wf_state
from bungeni.core.workflows.heading import states as heading_wf_state
from bungeni.core.workflows.committee import states as committee_wf_state
from bungeni.core.workflows.parliament import states as parliament_wf_state
from bungeni.core.workflows.version import states as version_wf_state
from bungeni.core.translation import translate_obj

#from bungeni.ui.widgets import SelectDateWidget, SelectDateTimeWidget
from bungeni.ui.widgets import TextDateWidget as DateWidget
from bungeni.ui.widgets import TextDateTimeWidget as DateTimeWidget

from bungeni.ui.widgets import CustomRadioWidget
from bungeni.ui.widgets import HTMLDisplay
from bungeni.ui.widgets import RichTextEditor
from bungeni.ui.widgets import ImageDisplayWidget
from bungeni.ui.widgets import ImageInputWidget
from bungeni.ui.widgets import SupplementaryQuestionDisplay
from bungeni.ui.widgets import OneTimeEditWidget
from bungeni.ui.widgets import FileEditWidget
from bungeni.ui.widgets import FileAddWidget
from bungeni.ui.widgets import FileDisplayWidget
from bungeni.ui.widgets import NoInputWidget
from bungeni.ui import constraints
from bungeni.ui.forms import validations
from bungeni.ui.i18n import _
import bungeni.ui.utils as ui_utils
from bungeni.ui import vocabulary

###
# Listing Columns
#
def _column(name, title, renderer, default=""):
    def getter(item, formatter):
        value = getattr(item, name )
        if value:
            return renderer(value )
        return default
    return column.GetterColumn(title, getter )

def day_column(name, title, default=""):
    renderer = lambda x: x.strftime('%Y-%m-%d')
    return _column(name, title, renderer, default)

def date_from_to_column(name, title, default=""):
    def getter(item, formatter):
        start = getattr(item, 'start_date')
        end = getattr(item, 'end_date')
        if start:
            start = start.strftime('%Y-%m-%d %H:%M')
        if end:
            end = end.strftime('%H:%M')
        return u"%s - %s"%(start, end)
    return column.GetterColumn(title, getter)

def datetime_column(name, title, default=""):
    renderer = lambda x: x.strftime('%Y-%m-%d %H:%M')
    return _column(name, title, renderer, default)

def time_column(name, title, default=""):
    renderer = lambda x: x.strftime('%H:%M')
    return _column(name, title, renderer, default)

def name_column(name, title, default=""):
    def renderer(value, size=50):
        if len(value) > size:
            return "%s..."%value[:size]
        return value
    return _column(name, title, renderer, default)

def user_name_column(name, title, attr, default=u""):
    def getter(item, formatter):
        if attr:
            user = getattr(item, attr, None)
            if user:
                return u"%s %s"%(user.first_name, user.last_name)
        else:
            return u"%s %s"%(item.first_name, item.last_name)
    return column.GetterColumn(title, getter )

def member_title_column(name, title, default=u""):
    def getter(item, formatter):
        return item.title_name.user_role_name
    return column.GetterColumn(title, getter)

def current_titles_in_group_column(name, title, default=u""):
    def getter(item, formatter):
        value = getattr(item, name)
        today = date.today()
        if not value:
            return default
        session = Session()
        title_list = []
        for title in item.member_titles:
            if title.start_date <= today:
                if title.end_date:
                    if title.end_date >= today:
                        obj = translate_obj(title.title_name)
                        title_list.append(obj.user_role_name)
                else:
                    obj = translate_obj(title.title_name)
                    title_list.append(obj.user_role_name)
        return ", ".join(title_list)
    return column.GetterColumn(title, getter)

def inActiveDead_Column(name, title, default):
    aid = { 'A': _(u"active"),
        'I': _(u"inactive"),
        'D': _(u"deceased")}
    renderer = lambda x: aid[x]
    return _column(name, title, renderer, default)

def item_name_column(name, title, default=u""):
    def getter(item, formatter):
        session = Session()
        return u"%s %s"%(item.item.type, item.item.short_name)
    return column.GetterColumn(title, getter )

def group_name_column(name, title, default=u""):
    def getter(item, formatter):
        session = Session()
        obj = translate_obj(item)
        #TODO: translate group.type
        return u"%s %s"%(item.group.type, obj.group.short_name)
    return column.GetterColumn(title, getter)

def workflow_column(name, title, default=u""):
    def getter(item, formatter):
        state_title =  ui_utils.misc.get_wf_state(item)
        interaction = getInteraction()
        for participation in interaction.participations:
            if IRequest.providedBy(participation):
                request = participation
        return translate(
            str(state_title),
            domain="bungeni.core",
            context=request)
    return column.GetterColumn(title, getter)

def constituency_column(name, title, default=u""):
    def getter(item, formatter):
        obj = translate_obj(item.constituency)
        return obj.name
    return column.GetterColumn(title, getter)

def party_column(name, title, default=u""):
    def getter(item, formatter):
        obj = item.party
        if obj is not None:
            return translate_obj(obj).full_name
        return u"-"
    return column.GetterColumn(title, getter)

def committee_type_column(name, title, default=u""):
    def getter(item, formatter):
        obj = translate_obj(item.committee_type)
        return obj.committee_type
    return column.GetterColumn(title, getter)

def ministry_column(name, title, default=u""):
    def getter(item, formatter):
        obj = translate_obj(item.ministry)
        return obj.short_name
    return column.GetterColumn(title, getter)

def sitting_type_column(name, title, default=u""):
    def getter(item, formatter):
        obj = translate_obj(item.sitting_type)
        return obj.sitting_type
    return column.GetterColumn(title, getter)

def attendance_column(name, title, default=u""):
    def getter(item, formatter):
        obj = translate_obj(item.attendance_type)
        return obj.attendance_type
    return column.GetterColumn(title, getter)

def province_column(name, title, default=u""):
    def getter(item, formatter):
        obj = translate_obj(item.province)
        return obj.province
    return column.GetterColumn(title, getter)

def region_column(name, title, default=u""):
    def getter(item, formatter):
        obj = translate_obj(item.region)
        return obj.region
    return column.GetterColumn(title, getter)

####
#  Constraints / Invariants
#

def ElectionAfterStart(obj):
    """ Start Date must be after Election Date"""
    if obj.election_date >= obj.start_date:
        raise interface.Invalid(
            _("A parliament has to be elected before it can be sworn in"),
            "election_date",
            "start_date")

def EndAfterStart(obj):
    """ End Date must be after Start Date"""
    if obj.end_date is None: return
    if obj.end_date <= obj.start_date:
        raise interface.Invalid(
            _("End Date must be after Start Date"),
            "start_date",
            "end_date")

def DissolutionAfterReinstatement(obj):
    """ A committee must be disolved before it can be reinstated """
    if (obj.dissolution_date is None) or (obj.reinstatement_date is None):
        return
    if obj.dissolution_date > obj.reinstatement_date:
        raise interface.Invalid(
            _("A committee must be disolved before it can be reinstated"),
            "dissolution_date",
            "reinstatement_date")

def ActiveAndSubstituted(obj):
    """ A person cannot be active and substituted at the same time"""
    if obj.active_p and obj.replaced_id:
        raise interface.Invalid(
            _("A person cannot be active and substituted at the same time"),
            "active_p",
            "replaced_id")

def SubstitudedEndDate(obj):
    """ If a person is substituted he must have an end date"""
    if not (obj.end_date) and obj.replaced_id:
        raise interface.Invalid(
            _("If a person is substituted End Date must be set"),
            "replaced_id",
            "end_date")

def InactiveNoEndDate(obj):
    """ If you set a person inactive you must provide an end date """
    if not obj.active_p:
        if not (obj.end_date):
            raise interface.Invalid(
                _("If a person is inactive End Date must be set"),
                "end_date",
                "active_p")

def MpStartBeforeElection(obj):
    """ For members of parliament start date must be after election """
    if obj.election_nomination_date > obj.start_date:
        raise interface.Invalid(
            _("A parliament member has to be elected/nominated before she/he can be sworn in"),
            "election_nomination_date",
            "start_date")

def DeathBeforeLife(User):
    """Check if date of death is after date of birth"""
    if User.date_of_death is None: return
    if User.date_of_death < User.date_of_birth:
        raise interface.Invalid(
            _(u"One cannot die before being born"),
            "date_of_death",
            "date_of_birth")

def POBoxOrAddress(obj):
    """
    An Address must have either an entry for a physical address or a P.O. Box
    """
    if obj.po_box is None and  obj.address is None:
        raise interface.Invalid(
            _(u"You have to enter an Address"),
            "po_box",
            "address")

####
# Fields

# Each field is described as a dict, for details see:
# ore.alchemist.model.Field.fromDict(dict)
#
# NOTE: contrary to what is indicated in ore.alchemist.model, the "differ"
# field descriptor dictionary key is actually ignored.

def LanguageField(name="language"):
    return dict(name="language", label=_(u"Language"),
                listing=False, add=True, edit=True, omit=False,
                required=True,
                property=schema.Choice(
                    title=_(u"Language"),
                    default=get_default_language(),
                    vocabulary="language_vocabulary"
                ),
            )

####
# Descriptors

class UserDescriptor(ModelDescriptor):
    display_name = _(u"User")
    container_name = _(u"Users")

    fields = [
        dict(name="user_id",
                label="Name",
                listing_column=user_name_column("user_id", _(u'Name'), None),
                listing=True,
                edit=False,
                add=False,
                view=False),
        dict(name="titles",
            property = schema.TextLine(
              title=_(u"Salutation"),
                description=_(u"e.g. Mr. Mrs, Prof. etc."),
                required=True)),
        dict(name="first_name",
            property = schema.TextLine(title=_(u"First Name"),required=True)),
        dict(name="middle_name",
            property = schema.TextLine(title=_(u"Middle Name"),required=False)),
        dict(name="last_name",
            property = schema.TextLine(title=_(u"Last Name"),required=True)),
        dict(name="email",
            property = schema.TextLine(title =_(u"Email"),
                description=_(u"Email address"),
                constraint=constraints.check_email,
                required=True
            ),
        ),
        dict(name="login",
                property = schema.TextLine(title=_(u"Login"), required=True),
            add=True, edit=False, view=True),
        dict(name="password", omit = True),
        dict(name="_password",
                property = schema.TextLine(
                    title=_(u"Initial password"),
                    required=True),
            add=True, view=False, edit=False,
            required=True),
        dict(name="national_id",
            property = schema.TextLine(title=_(u"National Id"),required=False)),
        dict(name="gender",
              property = schema.Choice(
              title=_(u"Gender"),
              source=vocabulary.Gender),
              edit_widget=CustomRadioWidget,
              add_widget=CustomRadioWidget,
        ),
        dict(name="date_of_birth",
              property = schema.Date(
              title=_(u"Date of Birth")),
              edit_widget=DateWidget,
              add_widget=DateWidget),
        dict(name="birth_country",
              property = schema.Choice(
              title=_(u"Country of Birth"),
              source=vocabulary.DatabaseSource(domain.Country,
                    token_field='country_id',
                    title_field='country_name',
                    value_field='country_id'),
              required=True),
        ),
        dict(name="birth_nationality",
              property = schema.Choice(
                title=_(u"Nationality at Birth"),
                source=vocabulary.DatabaseSource(domain.Country,
                    token_field='country_id',
                    title_field='country_name',
                    value_field='country_id'),
                required=True),
        ),
        dict(name="current_nationality",
              property = schema.Choice(
                title=_(u"Current Nationality"),
                source=vocabulary.DatabaseSource(domain.Country,
                token_field='country_id',
                title_field='country_name',
                value_field='country_id'),
            required=True),
        ),
        dict(name="date_of_death",
              property=schema.Date(title=_(u"Date of Death"), required=False),
              #view_permission="bungeni.user.AdminRecord",
              edit_permission="bungeni.user.AdminRecord",
              edit_widget=DateWidget,
              add_widget=DateWidget),
        dict(name="active_p", label=_(u"Status"),
              omit=True),
        LanguageField("language"),
        dict(name="description",
              property=schema.Text(title=_(u"Biographical notes"), required=False),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor
        ),
        dict(name ="image",
                property=schema.Bytes(title=_(u"Image"),
                    description=_(u"Picture of the person"),
                   required=False),
                view_widget=ImageDisplayWidget,
                edit_widget = ImageInputWidget,
                listing=False),
        dict(name="salt", omit=True),
        dict(name="type", omit=True),
        ]

    schema_invariants = [DeathBeforeLife]
    custom_validators = []

class UserDelegationDescriptor(ModelDescriptor):
    """ delegate rights to act on behalf of that user """
    display_name=_(u"Delegate to user")
    container_name=_(u"Delegations")
    fields = [
        dict(name="user_id",
              omit=True,
            ),
        dict(name="delegation_id",
              property=schema.Choice(
                title=_(u"User"),
                source=vocabulary.DatabaseSource(domain.User,
                    token_field='user_id',
                    title_field='fullname',
                    value_field='user_id')),
                listing_column=user_name_column("delegation_id",
                    _(u'User'), 'user'),
              listing=True,
            ),
]

class GroupMembershipDescriptor(ModelDescriptor):

    SubstitutionSource = vocabulary.SubstitutionSource(
                    token_field='user_id',
                    title_field='fullname',
                    value_field='user_id')

    fields = [
        dict(name="start_date",
                property=schema.Date(title=_(u"Start Date"), required=True),
                listing_column=day_column("start_date", _(u'Start Date')),
                listing=True,
                edit_widget=DateWidget, add_widget=DateWidget
        ),
        dict(name="end_date",
                property=schema.Date(title=_(u"End Date"), required=False),
                listing=False,
                listing_column=day_column("end_date",
                _(u'End Date')),
                edit_widget=DateWidget,
                add_widget=DateWidget),
        dict(name="active_p",
            property=schema.Bool(title=_(u"Active"), default=True, required=True),
             #label=_(u"Active"),
             #required=True,
             view=False),
        dict(name="substitution_type",
                property=schema.TextLine(title=_(u"Type of Substitution"), required=False),
                add = False),
        dict(name="replaced_id",
                property=schema.Choice(
                    title=_(u"Substituted by"),
                    source=SubstitutionSource,
                    required=False),
                add = False
        ),
        dict(name="group_id", omit=True),
        dict(name="membership_id", label=_(u"Roles/Titles"),
            add=False, edit=False, view=False, listing=False,
            listing_column=current_titles_in_group_column("membership_id",
                _(u"Roles/Titles"))),
        dict(name="membership_type", omit=True),
        LanguageField("language"),
        ]

    schema_invariants = [EndAfterStart, ActiveAndSubstituted,
        SubstitudedEndDate, InactiveNoEndDate]
    custom_validators = [validations.validate_date_range_within_parent,
        validations.validate_group_membership_dates]

class MpDescriptor(ModelDescriptor):
    display_name = _(u"Member of parliament")
    container_name = _(u"Members of parliament")

    fields = [dict(name="user_id",
              property=schema.Choice(
                title=_(u"Name"),
                source=vocabulary.UserSource(
                    token_field="user_id",
                    title_field="fullname",
                    value_field="user_id")),
                listing_column=user_name_column("user_id",
                    _(u"Name"),"user"),
              listing=True,
            ),]

    fields.extend(deepcopy(GroupMembershipDescriptor.fields))
    constituencySource=vocabulary.DatabaseSource(domain.Constituency,
                    token_field="constituency_id",
                    title_field="name",
                    value_field="constituency_id")
    partySource=vocabulary.DatabaseSource(domain.PoliticalParty,
                    token_field="party_id",
                    title_field="full_name",
                    value_field="party_id")
    fields.extend([
        dict(name="elected_nominated",
            property=schema.Choice(
                title=_(u"elected/nominated"),
                source=vocabulary.ElectedNominated),
                listing=True),
        dict(name="election_nomination_date",
            property=schema.Date(title=_("Election/Nomination Date"), required=True),
            required=True,
            edit_widget=DateWidget,
            add_widget=DateWidget),
        dict(name="constituency_id",
            property=schema.Choice(
                title=_(u"Constituency"),
                source=constituencySource,),
            listing_column=constituency_column("constituency_id", "Constituency"),
            listing=True),
        dict(name="party_id",
            property=schema.Choice(
                title=_(u"Political Party"),
                source=partySource,
                required=False),
            listing_column=party_column("party_id", "Party"),
            listing=True),
        dict(name="leave_reason",
            property=schema.Text(title=_("Leave Reason"),required=False)),
        dict(name="notes",
              property=schema.Text(title=_(u"Notes"), required=False),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor),
    ])
    schema_invariants = [EndAfterStart, ActiveAndSubstituted,
            SubstitudedEndDate, InactiveNoEndDate, MpStartBeforeElection]
    custom_validators = [validations.validate_date_range_within_parent,
        validations.validate_group_membership_dates]

class PartyMemberDescriptor(ModelDescriptor):
    """membership of a user in a party"""
    display_name=_(u"member")
    container_name=_(u"members")

    fields = [dict(name="user_id",
              property=schema.Choice(
                title=_(u"Name"),
                source=vocabulary.MemberOfParliamentSource(
                    "user_id",)),
                listing_column=user_name_column("user_id",
                    _(u"Name"),"user"),
              listing=True,
            ),]

    fields.extend(deepcopy(GroupMembershipDescriptor.fields))
    fields.extend([
        dict(name="notes",
              property=schema.Text(title=_(u"Notes"), required=False),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor,
        ),
    ])

    custom_validators = [validations.validate_date_range_within_parent,
                    validations.validate_party_membership]

class MemberOfPartyDescriptor(ModelDescriptor):
    """partymemberships of a member of a user"""

    display_name = _(u"Party membership")
    container_name = _(u"Party memberships")

    fields = [
        dict(name="user_id", omit=True),
        dict(name='short_name',
            property=schema.Text(title=_(u"Political Party"), required=True),
            listing=True),
        dict(name="start_date",
            property=schema.Date(title=_(u"Start Date"), required=True),
            listing_column=day_column("start_date", _(u'Start Date')),
            listing=True,
            edit_widget=DateWidget,
            add_widget=DateWidget,
        ),
        dict(name="end_date",
            property=schema.Date(title=_(u"End Date"), required=False),
            listing=True,
            listing_column=day_column("end_date", _(u'End Date')),
            edit_widget=DateWidget,
            add_widget=DateWidget,
        ),
        dict(name="active_p",
            property=schema.Bool(title=_(u"Active"), default=True, required=True),
            #label=_(u"Active"),
            #required=True,
            view=False),
        LanguageField("language"),
        dict(name="notes",
                property=schema.Text(title=_(u"Notes"), required=False),
                view_widget=HTMLDisplay,
                edit_widget=RichTextEditor,
                add_widget=RichTextEditor,
        ),
        dict(name="substitution_type", omit=True),
        dict(name="replaced_id", omit=True),
        dict(name="membership_id", omit=True),
        dict(name="status", omit=True),
        dict(name="membership_type", omit=True),
    ]

    #schema_invariants = [EndAfterStart]
    #custom_validators =[validations.validate_date_range_within_parent,]

class GroupDescriptor(ModelDescriptor):

    fields = [
        dict(name="group_id", omit=True),
        dict(name="type", omit=True),
        dict(name="full_name",
                property=schema.TextLine(title=_(u"Name"), required=True),
                listing=True,
                listing_column=name_column("full_name",
                    _(u'Full Name'))),
        dict(name="short_name",
                property=schema.TextLine(title=_(U"Acronym"), required=True),
                listing=True,
                listing_column=name_column("short_name",
                    _(u'Name'))),
        LanguageField("language"),
        dict(name="description",
                property=schema.Text(title=_(u"Description") , required=False),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor,
        ),
        dict(name="start_date",
                property=schema.Date(title=_(u"Start Date"), required=True),
                listing=True,
                listing_column=day_column("start_date",
                    _(u'Start Date')),
                edit_widget=DateWidget,
                add_widget=DateWidget),
        dict(name="end_date",
                property=schema.Date(title=_(u"End Date"), required=False),
                listing=True,
                listing_column=day_column('end_date',
                    _(u'End Date')),
                    edit_widget=DateWidget,
                    add_widget=DateWidget),
        dict(name="status", omit=True),
        dict(name="status_date", omit=True),
        ]

    schema_invariants = [EndAfterStart]
    custom_validators = [validations.validate_date_range_within_parent,]
    public_wfstates = [group_wf_state[u'active'].id, group_wf_state[u'dissolved'].id ]

class ParliamentDescriptor(GroupDescriptor):
    display_name = _(u"Parliament")
    container_name = _(u"Parliaments")
    custom_validators = validations.validate_parliament_dates,

    fields = [
        dict(name="group_id", omit=True),
        dict(name="parliament_id", omit=True),
        dict(name="full_name",
                property=schema.TextLine(title=_(u"Name"), required=True),
                description=_(u"Parliament name"),
                listing=True,
                listing_column=name_column("full_name", "Name"),
        ),
        dict(name="short_name",
                property=schema.TextLine(title=_(u"Parliament Identifier"),
                description=_(u"Unique identifier of each Parliament (e.g. IX Parliament)"),
                required=True),
                listing=True,
        ),
        LanguageField("language"),
        dict(name="description",
              property=schema.Text(title=_(u"Description"), required=False),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor,
        ),
        dict(name="election_date",
              property=schema.Date(title=_(u"Election Date"),
              description=_(u"Date of the election"),
              required=True),
              edit_widget=DateWidget,
              add_widget=DateWidget
        ),
        dict(name="start_date",
              property=schema.Date(title=_(u"In power from"),
              description=_(u"Date of the swearing in"),
              required=True),
              listing_column=day_column("start_date", _(u"In power from")),
              listing=True,
              edit_widget=DateWidget,
              add_widget=DateWidget
        ),
        dict(name="end_date",
              property=schema.Date(title=_(u"In power till"),
              description=_(u"Date of the dissolution"),
              required=False),
              listing_column=day_column("end_date", _(u"In power till")),
              listing=True,
              edit_widget=DateWidget,
              add_widget=DateWidget
        ),
        dict(name="status", omit=True),
        dict(name="status_date", omit=True),
        dict(name="type", omit=True),
    ]
    schema_invariants = [EndAfterStart, ElectionAfterStart]
    public_wfstates = [parliament_wf_state[u'active'].id, parliament_wf_state[u'dissolved'].id ]

parliamentSource = vocabulary.DatabaseSource(domain.Parliament,
                    token_field='parliament_id',
                    title_field='short_name',
                    value_field='parliament_id')

class CommitteeDescriptor(GroupDescriptor):
    display_name = _(u"Profile")
    container_name = _(u"Committees")
    custom_validators = [validations.validate_date_range_within_parent,]

    fields = deepcopy(GroupDescriptor.fields)
    typeSource = vocabulary.DatabaseSource(domain.CommitteeType,
                    token_field='committee_type_id',
                    title_field='committee_type',
                    value_field='committee_type_id')
    fields.extend([
        dict(name='committee_id', omit=True),
        dict(name = 'committee_type_id',
                property=schema.Choice(title=_(u"Type of committee"),
                    source=typeSource),
                listing_column=committee_type_column("committee_type_id", _(u"Type")),
                listing=True,
        ),
        dict(name='no_members',
            property=schema.Int(title=_(u"Number of members"), required=False),
        ),
        dict(name='min_no_members',
            property=schema.Int(title =_(u"Minimum Number of Members"), required=False)),
        dict(name='quorum',
            property=schema.Int(title=_(u"Quorum"), required=False)),
        dict(name='no_clerks',
            property=schema.Int(title=_(u"Number of clerks"),required=False)),
        dict(name='no_researchers',
            property=schema.Int(title=_(u"Number of researchers"),required=False)),
        dict(name='proportional_representation',
            property=schema.Bool(title=_(u"Proportional representation"), default=True, required=False)),
        dict(name='default_chairperson',
            label=_(u"Default chairperson"),
            omit=True),
        dict(name='reinstatement_date',
            property=schema.Date(title=_(u"Reinstatement Date"), required=False),
            edit_widget=DateWidget,
            add_widget=DateWidget),
    ])
    schema_invariants = [EndAfterStart,] # DissolutionAfterReinstatement]
    public_wfstates = [committee_wf_state[u'active'].id, committee_wf_state[u'dissolved'].id ]

class CommitteeMemberDescriptor(ModelDescriptor):
    display_name = _(u"Member")
    container_name = _(u"Membership")
    fields = [
        dict(name="user_id",
            property=schema.Choice(title=_(u"Name"),
                source=vocabulary.UserSource(
                    token_field='user_id',
                    title_field='fullname',
                    value_field='user_id')),
            listing_column=user_name_column("user_id", _(u'Name'),'user'),
            listing=True,
        ),
    ]
    fields.extend(deepcopy(GroupMembershipDescriptor.fields))
    fields.extend([
        dict(name="notes",
              property=schema.TextLine(title=_(u"Notes"), required=False),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor,
        ),
    ])

    custom_validators = [validations.validate_date_range_within_parent,]
    schema_invariants = [EndAfterStart, ActiveAndSubstituted,
            SubstitudedEndDate, InactiveNoEndDate]

class AddressTypeDescriptor(ModelDescriptor):
    display_name =_(u"Address type")
    container_name =_(u"Address types")

    fields = [
        dict(name="address_type_id", omit=True),
        dict(name="address_type_name",
            property=schema.TextLine(title=_(u"Address Type"))),
    ]

class AddressDescriptor(ModelDescriptor):
    display_name = _(u"Address")
    container_name = _(u"Addresses")

    fields = [
        dict(name="address_id", omit=True),
        dict(name="role_title_id", omit=True),
        dict(name="user_id", omit=True),
        dict(name="address_type_id",
            property = schema.Choice(
            title =_(u"Address Type"),
            source=vocabulary.DatabaseSource(domain.AddressType,
                title_field='address_type_name',
                token_field='address_type_id',
                value_field='address_type_id'),)
             ),
        dict(name="po_box", property=schema.TextLine(title=_(u"P.O. Box"), required=False)),
        dict(name="address",
                property = schema.Text(
                    title =_(u"Address"),required=False),
              edit_widget=zope.app.form.browser.TextAreaWidget,
              add_widget=zope.app.form.browser.TextAreaWidget,
              ),
        dict(name="city",
            property=schema.TextLine(title=_(u"City"),required=False)),
        dict(name="zipcode", label=_(u"Zip Code")),
        dict(name="country",
                property = schema.Choice(title=_(u"Country"),
                source=vocabulary.DatabaseSource(domain.Country,
                     title_field='country_name',
                     token_field='country_id',
                     value_field='country_id'),
                required=False),
             ),
        dict(name="phone",
                property = schema.Text(title=_(u"Phone Number(s)"),
                description=_(u"Enter one phone number per line"),
                required=False),
              edit_widget=zope.app.form.browser.TextAreaWidget,
              add_widget=zope.app.form.browser.TextAreaWidget,
              #view_widget=zope.app.form.browser.ListDisplayWidget,
              ),
        dict(name="fax",
                property = schema.Text(
                    title=_(u"Fax Number(s)"),
                    description=_(u"Enter one fax number per line"),
                    required=False),
              edit_widget=zope.app.form.browser.TextAreaWidget,
              add_widget=zope.app.form.browser.TextAreaWidget,
              ),
        dict(name="email",
              property = schema.TextLine(
                title =_(u"Email"),
                description=_(u"Email address"),
                constraint=constraints.check_email,
                required=False
                ),
                listing = True,
             ),
        dict(name="im_id",
            property=schema.TextLine(title=_(u"Instant Messenger Id"),
            description=_(u"ICQ, AOL IM, GoogleTalk..."), required=False)),
        dict(name="status", omit=True),
        dict(name="status_date", omit=True,
                ) ,
        ]

    schema_invariants = [POBoxOrAddress]
    public_wfstates = [address_wf_state[u'public'].id]

class MemberRoleTitleDescriptor(ModelDescriptor):
    display_name= _(u"Title")
    container_name= _(u"Titles")

    address_fields = deepcopy(AddressDescriptor.fields)

    fields = [
        dict(name='role_title_id', omit=True),
        dict(name='membership_id', omit=True),
        dict(name='title_name_id', label=_(u"Title"),
                property=schema.Choice(title=_(u"Title"),
                  source=vocabulary.MemberTitleSource('title_name_id'),
                  ),
                  listing_column = member_title_column(
                        "title_name_id",
                        _(u'Title')),
                  listing=True,
              ),
        dict(name="start_date",
                property=schema.Date(title=_(u"Start Date"), required=True),
                listing=True,
                listing_column=day_column("start_date",
                     _(u'Start Date')),
                edit_widget=DateWidget,
                add_widget=DateWidget),
        dict(name="end_date",
                property=schema.Date(title=_(u"End Date"), required=False),
                listing=True,
                listing_column=day_column('end_date',
                    _(u'End Date')),
                    edit_widget=DateWidget,
                    add_widget=DateWidget),
       LanguageField("language"),
    ] + address_fields

    schema_invariants = [EndAfterStart, POBoxOrAddress]
    custom_validators = [validations.validate_date_range_within_parent,
            validations.validate_member_titles]

class CommitteeStaffDescriptor(ModelDescriptor):
    display_name = _(u"Staff")
    container_name = _(u"Staff")
    fields = [dict(name="user_id",
              property=schema.Choice(
                title=_(u"Name"),
                source=vocabulary.UserNotMPSource(
                    token_field='user_id',
                    title_field='fullname',
                    value_field='user_id')),
                listing_column=user_name_column("user_id",
                    _(u'Name'),'user'),
              listing=True,
            ),]
    fields.extend(deepcopy(GroupMembershipDescriptor.fields))
    fields.extend([
        dict(name="notes",
              property=schema.Text(title=_(u"Notes"), required=False),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor,
        ),
    ])
    custom_validators = [validations.validate_date_range_within_parent,]
    schema_invariants = [EndAfterStart, ActiveAndSubstituted,
            SubstitudedEndDate, InactiveNoEndDate]

class PoliticalPartyDescriptor(GroupDescriptor):
    display_name = _(u"political party")
    container_name = _(u"political parties")
    custom_validators = [validations.validate_date_range_within_parent,]

    fields = deepcopy(GroupDescriptor.fields )
    fields.extend([
        dict(name='logo_data',
                property=schema.Bytes(title=_(u"Logo"), required=False),
                edit=True,
                add=True,
                view=True,
                listing=False,
                view_widget=ImageDisplayWidget,
                edit_widget = ImageInputWidget),
        dict(name="party_id", omit=True),
     ])

    schema_invariants = [EndAfterStart]

class PoliticalGroupDescriptor(PoliticalPartyDescriptor):
    display_name = _(u"political group")
    container_name = _(u"political groups")

    fields = deepcopy(PoliticalPartyDescriptor.fields )

class OfficeDescriptor(GroupDescriptor):
    display_name = _(u"Office")
    container_name = _(u"Offices")
    custom_validators = [validations.validate_date_range_within_parent,]

    fields = deepcopy(GroupDescriptor.fields )
    fields.insert(0,
        dict(name="office_type", listing=False,
            property = schema.Choice(
                title = _(u"Type"),
                description = _(u"Type of Office"),
                source = vocabulary.OfficeType
            )
        )
    )

class OfficeMemberDescriptor(ModelDescriptor):
    display_name = _(u"Office Member")
    container_name = _(u"Office Members")
    fields = [dict(name="user_id",
              property=schema.Choice(
                title=_(u"Name"),
                source=vocabulary.UserNotMPSource(
                    token_field='user_id',
                    title_field='fullname',
                    value_field='user_id')),
                listing_column=user_name_column("user_id",
                    _(u'Name'),'user'),
              listing=True,
            ),]

    fields.extend(deepcopy(GroupMembershipDescriptor.fields))
    fields.extend([
        dict(name="notes",
              property=schema.Text(title=_(u"Notes"), required=False),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor,
        ),
    ])
    custom_validators = [validations.validate_date_range_within_parent,]
    schema_invariants = [ActiveAndSubstituted, SubstitudedEndDate,
            InactiveNoEndDate]

class MinistryDescriptor(GroupDescriptor):
    display_name = _(u"Ministry")
    container_name = _(u"Ministries")
    custom_validators = [validations.validate_date_range_within_parent,]

    fields = deepcopy(GroupDescriptor.fields )

    schema_invariants = [EndAfterStart]

class MinisterDescriptor(ModelDescriptor):
    display_name = _(u"Minister")
    container_name = _(u"Ministers")
    fields = [dict(name="user_id",
              property=schema.Choice(
                title=_(u"Name"),
                source=vocabulary.UserSource(
                    token_field='user_id',
                    title_field='fullname',
                    value_field='user_id')),
                listing_column=user_name_column("user_id",
                    _(u'Name'),'user'),
              listing=True,
            ),]

    fields.extend(deepcopy(GroupMembershipDescriptor.fields))
    fields.extend([
        dict(name="notes",
              property=schema.Text(title=_(u"Notes"), required=False),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor,
        ),
    ])
    custom_validators = [validations.validate_date_range_within_parent,]
    schema_invariants = [ActiveAndSubstituted, SubstitudedEndDate,
            InactiveNoEndDate]

class GovernmentDescriptor(GroupDescriptor):
    display_name = _(u"Government")
    container_name = _(u"Governments")

    fields = [
        dict(name="group_id", omit=True),
        dict(name="short_name",
                property=schema.TextLine(title=_(u"Name"),
                description=_(u"Name"), required=False),
                listing=True),
        dict(name="full_name",
                label=_(u"Number")),
        dict(name="start_date",
                property=schema.Date(title=_(u"In power from"),
                    required=True),
                listing=True,
                listing_column=day_column("start_date",
                    _(u"In power from")),
                edit_widget=DateWidget,
                add_widget=DateWidget),
        dict(name="end_date",
                property=schema.Date(title=_(u"In power till"),
                    required=False),
                listing=True,
                listing_column=day_column("end_date", _(u"In power till")),
                edit_widget=DateWidget,
                add_widget=DateWidget),
        LanguageField("language"),
            dict(name="description",
                property=schema.Text(title=_(u"Notes") , required=False),
                view_widget=HTMLDisplay,
                edit_widget=RichTextEditor,
                add_widget=RichTextEditor,
        ),
        dict(name="status", omit=True),
        dict(name="status_date", omit=True
                ) ,
        dict(name="type", omit=True),
        ]

    schema_invariants = [EndAfterStart]
    custom_validators = [validations.validate_government_dates,]

class GroupItemAssignmentDescriptor(ModelDescriptor):
    fields= [
        dict(name="assignment_id", omit=True),
        dict(name="start_date",
            property=schema.Date(title=_(u"Start Date"),
                required=True),
            listing_column=day_column("start_date",
                _(u'Start Date')),
                listing=True,
            edit_widget=DateWidget, add_widget=DateWidget),
        dict(name="end_date",
            property=schema.Date(title=_(u"End Date"),
            required=False),
            listing_column=day_column("end_date",
                _(u'End Date')),
                listing=True,
            edit_widget=DateWidget, add_widget=DateWidget),
        dict(name="due_date",
            property=schema.Date(title=_(u"Due Date"),
            required=False),
            listing_column=day_column("due_date",
                _(u'Due Date')),
                listing=True,
            edit_widget=DateWidget, add_widget=DateWidget),
        dict(name="status", omit=True),
        dict(name="status_date", add=False, edit=False,
            label=_(u"Status date"),
            view=False, listing=False,
            listing_column=day_column("status_date",
                _(u'Status date')),
                ) ,
        dict(name="notes",
            property=schema.Text(title=_(u"Notes") , required=False),
                view_widget=HTMLDisplay,
                edit_widget=RichTextEditor,
                add_widget=RichTextEditor,
        ),
        LanguageField("language"),
    ]

class ItemGroupItemAssignmentDescriptor(GroupItemAssignmentDescriptor):
    display_name =_(u"Assigned bill")
    container_name =_(u"Assigned bills")
    fields= [
        dict(name="item_id",
            property = schema.Choice(
                title=_(u"Bill"),
                source=vocabulary.BillSource(
                    title_field='short_name',
                    token_field='parliamentary_item_id',
                    value_field ='parliamentary_item_id'),
                ),
              listing_column = item_name_column(
                        "parliamentary_item_id",
                        _(u'Item')),
            listing=True,
        ),
        dict(name="group_id", omit=True),
    ]
    fields.extend(deepcopy(GroupItemAssignmentDescriptor.fields ))

class GroupGroupItemAssignmentDescriptor(GroupItemAssignmentDescriptor):
    display_name =_(u"Assigned group")
    container_name =_(u"Assigned groups")
    fields= [
        dict(name="item_id",omit=True),
        dict(name="group_id",
         property = schema.Choice(
                title=_(u"Committee"),
                source=vocabulary.CommitteeSource(
                    title_field='short_name',
                    token_field='group_id',
                    value_field = 'group_id'),
                ),
              listing_column = group_name_column(
                        "group_id",
                        _(u'Group')),
            listing=True,
        ),
    ]
    fields.extend(deepcopy(GroupItemAssignmentDescriptor.fields ))

class AttachedFileDescriptor(ModelDescriptor):
    display_name =_(u"File")
    container_name =_(u"Files")
    fields =[
        dict(name="attached_file_id", omit=True),
        dict(name="item_id", omit=True),
        dict(name="file_version_id", omit=True),
        LanguageField("language"),
        dict(name="file_title",
            property=schema.TextLine(title=_(u"Title"), required=True),
            listing=True,
            edit_widget = widgets.LongTextWidget,
            add_widget = widgets.LongTextWidget,
            add=True,
            edit=True,
            omit=False),
        dict(name="file_description",
              property = schema.Text(title=_(u"Description"), required=False),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor,
        ),
        dict(name="file_data",
            property=schema.Bytes(title=_(u"File"),required=True),
            description=_(u"Upload a file"),
            edit_widget=FileEditWidget,
            add_widget=FileAddWidget,
            view_widget=FileDisplayWidget,
            required=True,
            listing=False),
        dict(name="file_name", label=u"",
            edit_widget=NoInputWidget,
            add_widget=NoInputWidget,
            view=False),
        dict(name="file_mimetype", label=u"",
            edit_widget=NoInputWidget,
            add_widget=NoInputWidget,
            view=False),
        dict(name="status",
            label=_(u"Status"),
             property=schema.Choice(
                 title=_(u"Status"),
                 vocabulary="bungeni.vocabulary.workflow",
                 ),
            listing_column=workflow_column("status","Workflow status"),
            add=False,
            listing=True,
            omit=False),
        dict(name="status_date", add=False, edit=False,
            view=True, listing=True,
            label=_(u"Status date"),
            listing_column=day_column("status_date",
                _(u'Status date')),
                ) ,
    ]

    public_wfstates = [af_wf_state[u'public'].id]

class AttachedFileVersionDescriptor(ModelDescriptor):
    display_name = _(u"Attached file version")
    container_name = _(u"Versions")
    fields = deepcopy(AttachedFileDescriptor.fields)

class ParliamentaryItemDescriptor(ModelDescriptor):
    fields= [
        dict(name="parliamentary_item_id", omit=True),
        dict(name="parliament_id",
            property = schema.Choice(
                title=_(u"Parliament"),
                source=parliamentSource,
                required=True),
                add=False,
        ),
        dict(name="short_name",
            property=schema.TextLine(title=_(u"Title"),required=True),
            listing=True,
            edit_widget = widgets.LongTextWidget,
            add_widget = widgets.LongTextWidget,
            add=True,
            edit=True,
            omit=False,
        ),
        dict(name="full_name",
            #property=schema.TextLine(title=_(u"Summary"),required=False),
            listing=False,
            edit_widget = widgets.LongTextWidget,
            add_widget = widgets.LongTextWidget,
            add=True,
            edit=True,
            omit=True),
        dict(name="registry_number",
            property=schema.Int(title=_(u"Registry number"),required=False),
            listing=False,
            add=False,
            edit=True,),
        dict(name="owner_id",
              property = schema.Choice(
                title=_(u"Moved by"),
                description=_(u"Select the user who moved the document"),
                source=vocabulary.MemberOfParliamentDelegationSource('owner_id'),
                ),
              listing_column=user_name_column("owner_id",
                    _(u'Name'), 'owner'),
              listing = True
            ),
        #LanguageField("language"),
        dict(name="language",
             listing=False,
             add=True,
             edit=True,
             omit=False,
             property=schema.Choice(
                 title=_(u"Language"),
                 default=get_default_language(),
                 vocabulary="language_vocabulary",
                 required=True,
                 ),
             ),
        dict(name="body_text",
              property = schema.Text(title=_(u"Text"), required=False),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor,
        ),
        dict(name="submission_date",
            property=schema.Date(title=_(u"Submission Date"), required=False),
            add=False,
            edit=True,
            view_permission = "bungeni.edit.historical",
            edit_permission = "bungeni.edit.historical",
            listing=True ,
            edit_widget=DateWidget,
            add_widget=DateWidget,
            omit=False),
        dict(name="status",
            label=_(u"Status"),
             property=schema.Choice(
                 title=_(u"Status"),
                 vocabulary="bungeni.vocabulary.workflow",
                 ),
            listing_column=workflow_column("status","Workflow status"),
            add=False,
            listing=True,
            omit=False),
        dict(name="status_date", add=False, edit=False,
            view=True, listing=True,
            label=_(u"Status date"),
            listing_column=day_column("status_date",
                _(u'Status date')),
                ) ,
        dict(name="note",
            label=_(u"Notes"),
            description="Recommendation note",
              property=schema.Text(title=_(u"Notes"),
                description=_(u"Recommendation note"), required=False),
              edit = True,
              add = True,
              view = False,
              add_widget=OneTimeEditWidget,
              edit_widget=OneTimeEditWidget,),
        dict(name="receive_notification",
                property = schema.Choice(
                title=_(u"Receive notification"),
                description=_(u"Select this option to receive notifications for this item"),
                source=vocabulary.YesNoSource),
              edit_widget=CustomRadioWidget,
              add_widget=CustomRadioWidget,
              listing=False,
              omit=False),
        dict(name="type",
              omit=False,
              listing = False,
              edit = False,
              add = False,
              view = False,),
        ]

class ChangeDescriptor(ModelDescriptor):
    display_name =_(u"Change")
    container_name =_(u"Changes")
    fields= [
        dict(name="change_id", omit=True),
        dict(name="content_id", omit=True),
        dict(name="action", property=schema.TextLine(title=_(u"Action")),listing=True),
        dict(name="date",
            property=schema.Date(title=_(u"Date")),
            listing=True ,
            edit_widget=DateTimeWidget,
            add_widget=DateTimeWidget,),
        dict(name="user_id", label=_(u"user"), listing=True),
        dict(name="description", label= _(u"Description"),
            property = schema.Text(title=_(u"Description")),
            listing=True,
            ),
        dict(name="notes",
            label=_(u"Notes"),
            property = schema.Text(title=_(u"Notes"), required=False),
            view_widget=HTMLDisplay,
            edit_widget=RichTextEditor,
            add_widget=RichTextEditor,
        ),
    ]

class VersionDescriptor(ModelDescriptor):
    fields= [
        dict(name="parliamentary_item_id", omit=True),
        dict(name="parliament_id", omit=True),
        dict(name="owner_id",
            omit = True,
            ),
        dict(name="short_name",
            property=schema.TextLine(title=_(u"Title"),required=True),
            edit_widget = widgets.LongTextWidget,
            add_widget = widgets.LongTextWidget,
            listing=True,
            add=True,
            edit=True,
            omit=False,
        ),
        dict(name="full_name",
            property=schema.TextLine(title=_(u"Summary"),required=True),
            listing=False,
            edit_widget = widgets.LongTextWidget,
            add_widget = widgets.LongTextWidget,
            add=True,
            edit=True,
            omit=False,
        ),
        #LanguageField("language"),
        dict(name="language",
             listing=True,
             add=True,
             edit=False,
             omit=False,
             required=True,
             property=schema.Choice(
                 title=_(u"Language"),
                 default=get_default_language(),
                 vocabulary="language_vocabulary",
                 ),
             ),
        dict(name="body_text",
              property = schema.Text(title=_(u"Text")),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor,
        ),
        dict(name="submission_date",
            omit=True),
        dict(name="status",
            omit=True),
        dict(name="note",
            description="Recommendation note",
              property=schema.Text(title=_(u"Notes"),
                description=_(u"Recommendation note"), required=False),
              edit = True,
              add = True,
              view = False,
              edit_widget=OneTimeEditWidget,),
        dict(name="receive_notification",
              omit=True),
        dict(name="type",
              omit=True,),
        ]

    public_wfstates = [version_wf_state[u'archived'].id]

class HeadingDescriptor(ParliamentaryItemDescriptor):
    display_name =_(u"Heading")
    container_name =_(u"Headings")
    fields = deepcopy(ParliamentaryItemDescriptor.fields )
    public_wfstates = [heading_wf_state[u'public'].id]

class AgendaItemDescriptor(ParliamentaryItemDescriptor):
    display_name =_(u"Agenda item")
    container_name =_(u"Agenda items")
    fields = deepcopy(ParliamentaryItemDescriptor.fields )
    fields.extend([
        dict(name="agenda_item_id", omit=True),
        dict(name="group_id", omit=True),
    ])
    public_wfstates = [agendaitem_wf_state[u'admissible'].id,
                       agendaitem_wf_state[u'schedule_pending'].id,
                       agendaitem_wf_state[u'scheduled'].id,
                       agendaitem_wf_state[u'deferred'].id,
                       agendaitem_wf_state[u'postponed'].id,
                       agendaitem_wf_state[u'elapsed'].id,
                       agendaitem_wf_state[u'debated'].id,
                       agendaitem_wf_state[u'debate_adjourned'].id,
                       agendaitem_wf_state[u'withdrawn_public'].id,
                    ]

class AgendaItemVersionDescriptor(VersionDescriptor):
    display_name = _(u"Agenda Item version")
    container_name = _(u"Versions")
    fields = deepcopy(VersionDescriptor.fields)

class AgendaItemChangeDescriptor(ChangeDescriptor):
    display_name = _(u"Agenda Item change")
    container_name = _(u"Changes")
    fields = deepcopy(ChangeDescriptor.fields)

class MotionDescriptor(ParliamentaryItemDescriptor):
    display_name = _(u"Motion")
    container_name = _(u"Motions")
    fields = deepcopy(ParliamentaryItemDescriptor.fields )
    fields.extend([
        dict(name="approval_date",
            property=schema.Date(title=_(u"Approval Date"), required=False),
            view_permission = "bungeni.edit.historical",
            edit_permission = "bungeni.edit.historical",
            add = False,
            listing=False,
            edit_widget=DateWidget,
            add_widget=DateWidget),
        dict(name="notice_date",
            property=schema.Date(title=_(u"Notice Date"), required=False),
            add=False,
            edit= False,
            listing=False,
            edit_widget=DateWidget,
            add_widget=DateWidget),
        dict(name="motion_number",
            property=schema.Int(title=_(u"Identifier"), required=False),
            add=False,
            edit = False,
            listing=True),

        # TODO omit for now
        dict(name="entered_by", label=_(u"Entered By"), omit=True),
        dict(name="party_id",
#            property = schema.Choice(title=_(u"Political Party"),
#              source=vocabulary.MotionPartySource(
#                title_field='short_name',
#                token_field='party_id',
#                value_field = 'party_id'),
#              required=False),
              omit=True,),
        ])

    public_wfstates = [motion_wf_state[u'admissible'].id,
                       motion_wf_state[u'schedule_pending'].id,
                       motion_wf_state[u'scheduled'].id,
                       motion_wf_state[u'deferred'].id,
                       motion_wf_state[u'postponed'].id,
                       motion_wf_state[u'elapsed'].id,
                       motion_wf_state[u'debated'].id,
                       motion_wf_state[u'debate_adjourned'].id,
                       motion_wf_state[u'withdrawn_public'].id,
                    ]

class MotionVersionDescriptor(VersionDescriptor):
    display_name = _(u"Motion version")
    container_name = _(u"Versions")
    fields = deepcopy(VersionDescriptor.fields)

class MotionChangeDescriptor(ChangeDescriptor):
    display_name = _(u"Motion change")
    container_name = _(u"Changes")
    fields = deepcopy(ChangeDescriptor.fields)

class BillDescriptor(ParliamentaryItemDescriptor):
    display_name = _(u"Bill")
    container_name = _(u"Bills")
    fields = deepcopy(ParliamentaryItemDescriptor.fields )
    for field in fields:
        if field['name'] == 'body_text':
            field['label'] = _(u"Statement of Purpose")
            field['property'] = schema.Text(title=_(u"Statement of Purpose"),
                required=False)

    fields.extend([
        dict(name="bill_id", omit=True),
        dict(name="bill_type_id", property = schema.Choice(
                title =_(u"Bill Type"),
                source=vocabulary.DatabaseSource(domain.BillType,
                    title_field='bill_type_name',
                    token_field='bill_type_id',
                    value_field='bill_type_id'),
                ),
             ),
        dict(name="ministry_id",
              property = schema.Choice(title=_(u"Ministry"),
                    source=vocabulary.MinistrySource("ministry_id"),
                    required=False,
                    ),
             listing = False,),
        dict(name="identifier",
            property=schema.Text(title=_(u"Identifier"), required=False),
                add=False),
        dict(name="publication_date",
                property=schema.Date(title=_(u"Publication Date"),required=False),
                listing=True,
                add=True,
                edit_widget=DateWidget,
                add_widget=DateWidget ,
                listing_column=day_column("publication_date",
                    _(u"Publication Date")),),

        ])

    public_wfstates = [bill_wf_state[u"gazetted"].id ,
                        bill_wf_state[u"first_reading"].id ,
                        bill_wf_state[u"first_reading_postponed"].id ,
                        bill_wf_state[u"first_committee"].id ,
                        bill_wf_state[u"second_reading"].id ,
                        bill_wf_state[u"second_reading_postponed"].id ,
                        bill_wf_state[u"whole_house"].id ,
                        bill_wf_state[u"second_committee"].id ,
                        bill_wf_state[u"whole_house_postponed"].id ,
                        bill_wf_state[u"report_reading_postponed"].id ,
                        bill_wf_state[u"report_reading"].id ,
                        bill_wf_state[u"third_reading"].id,
                        bill_wf_state[u"third_reading_postponed"].id,
                        bill_wf_state[u'withdrawn_public'].id,
                        bill_wf_state[u"rejected"].id,
                        bill_wf_state[u'approved'].id,
                    ]

class BillVersionDescriptor(VersionDescriptor):
    display_name = _(u"Bill version")
    container_name = _(u"Versions")
    fields = deepcopy(VersionDescriptor.fields)

class BillChangeDescriptor(ChangeDescriptor):
    display_name = _(u"Bill change")
    container_name = _(u"Changes")
    fields = deepcopy(ChangeDescriptor.fields)

class QuestionDescriptor(ParliamentaryItemDescriptor):
    display_name = _(u"Question")
    container_name = _(u"Questions")
    custom_validators = ()

    fields = deepcopy(ParliamentaryItemDescriptor.fields )
    fields.extend([
        dict(name="question_id", omit=True),
        dict(name="question_number",
            property=schema.Int(title=_(u"Question Number"), required=False),
            listing=True, add=False, edit=True),
        dict(name="short_name", omit=True),
        dict(name="supplement_parent_id",
            label=_(u"Initial/supplementary question"),
            view_widget=SupplementaryQuestionDisplay,
            add=False, edit=False, view=False),
        dict(name="ministry_id",
                property = schema.Choice(title=_(u"Ministry"),
                 source=vocabulary.MinistrySource("ministry_id"),
                 required=True),
                listing_column=ministry_column("ministry_id" , _(u'Ministry')),
                listing=True,
            ),
        dict(name="approval_date",
            property=schema.Date(title=_(u"Date approved"), required=False),
            edit_widget=DateWidget,
            add_widget=DateWidget,
            listing=False,
            add=False),
        dict(name="ministry_submit_date",
            property=schema.Date(title=_(u"Submitted to ministry"), required=False),
            edit_widget=DateWidget,
            add_widget=DateWidget,
            listing=False,
            add=False),
        dict(name="question_type",
            listing=False,
            property=schema.Choice(title=_(u"Question Type"),
                        description=_("Ordinary or Private Notice"),
                        vocabulary=vocabulary.QuestionType) ,
              ),
        dict(name="response_type",
            listing=False,
            property=schema.Choice(title=_(u"Response Type"),
                      description=_("Oral or Written"),
                      vocabulary=vocabulary.ResponseType),

             ),
        dict(name="response_text",
                property=schema.TextLine(title=_(u"Response"),
                description=_(u"Response to the Question"),
                required=False),
                view_widget=HTMLDisplay,
                edit_widget=RichTextEditor,
                edit=True,
                view=True,
                add=False,
        ),
        dict(name="sitting_time",
            label=_(u"Sitting Time"),
            listing=False,
            omit=True),
        ])

    public_wfstates = [question_wf_state[u'admissible'].id,
                       question_wf_state[u'schedule_pending'].id,
                       question_wf_state[u'scheduled'].id,
                       question_wf_state[u'deferred'].id,
                       question_wf_state[u'postponed'].id,
                       question_wf_state[u'elapsed'].id,
                       question_wf_state[u'debated'].id,
                       question_wf_state[u'debate_adjourned'].id,
                       question_wf_state[u'withdrawn_public'].id,
                    ]

class QuestionVersionDescriptor(VersionDescriptor):
    display_name = _(u"Question version")
    container_name = _(u"Versions")
    fields = deepcopy(VersionDescriptor.fields)

class QuestionChangeDescriptor(ChangeDescriptor):
    display_name = _(u"Question change")
    container_name = _(u"Changes")
    fields = deepcopy(ChangeDescriptor.fields)

class EventItemDescriptor(ParliamentaryItemDescriptor):
    display_name =_(u"Event")
    container_name =_(u"Events")

    fields = deepcopy(ParliamentaryItemDescriptor.fields )
    fields.extend([
        dict(name="event_item_id", omit=True),
        dict(name="item_id", omit=True),
        dict(name="event_date",
            property=schema.Date(title=_(u"Date"), required=True),
            listing_column=day_column("event_date", _(u"Date")),
            listing=True,
            edit_widget=DateWidget,
            add_widget=DateWidget,
            required=True),
    ])
    public_wfstates = [event_wf_state[u'public'].id]

class TabledDocumentDescriptor(ParliamentaryItemDescriptor):
    display_name =_(u"Tabled document")
    container_name =_(u"Tabled documents")
    fields = deepcopy(ParliamentaryItemDescriptor.fields )
    fields.extend([
        dict(name="tabled_document_id", omit=True),
        dict(name="group_id", omit=True),
        dict(name="tabled_document_number",
            property=schema.Int(title=_(u"Tabled document Number"), required=True),
            add= False,
            listing=False,
            edit=True,
            view=True,),
    ])
    public_wfstates = [tableddocument_wf_state[u'admissible'].id,
                       tableddocument_wf_state[u'schedule_pending'].id,
                       tableddocument_wf_state[u'scheduled'].id,
                       tableddocument_wf_state[u'deferred'].id,
                       tableddocument_wf_state[u'postponed'].id,
                       tableddocument_wf_state[u'elapsed'].id,
                       tableddocument_wf_state[u'debated'].id,
                       tableddocument_wf_state[u'debate_adjourned'].id,
                       tableddocument_wf_state[u'withdrawn_public'].id,
                    ]

class TabledDocumentVersionDescriptor(VersionDescriptor):
    display_name = _(u"Tabled Document version")
    container_name = _(u"Versions")
    fields = deepcopy(VersionDescriptor.fields)

class TabledDocumentChangeDescriptor(ChangeDescriptor):
    display_name = _(u"Tabled Document change")
    container_name = _(u"Changes")
    fields = deepcopy(ChangeDescriptor.fields)

class SittingDescriptor(ModelDescriptor):
    display_name = _(u"Sitting")
    container_name = _(u"Sittings")

    fields = [
        dict(name="sitting_id", omit=True),
        dict(name="group_id", omit=True),
        LanguageField("language"),
        dict(name="sitting_type_id",
              listing_column = sitting_type_column("sitting_type_id",
                _(u"Sitting Type")),
              property = schema.Choice(
                    title=_(u"Sitting Type"),
                    source=vocabulary.SittingTypes(
                        title_field='sitting_type',
                        token_field='sitting_type_id',
                        value_field='sitting_type_id'),
                    required=True),
                    listing=True),
        dict(name="start_date",
            property=schema.Datetime(title=_(u"Date"),  required=True),
            listing_column=date_from_to_column("start_date",
                _(u'Start')),
            edit_widget=DateTimeWidget,
            add_widget=DateTimeWidget,
            listing=True),
        dict(name="end_date",
                property=schema.Datetime(title=_(u"End"),  required=True),
                listing_column=time_column("end_date",
                    _(u'End Date')),
                edit_widget=DateTimeWidget,
                add_widget=DateTimeWidget,
                listing=False),
        dict(name="venue_id",
              property=schema.Choice(
                title=_(u"Venue"),
                source=vocabulary.DatabaseSource(domain.Venue,
                    title_field='short_name',
                    token_field='venue_id',
                    value_field = 'venue_id'),
                    required = False),
              listing=False,)
        ]

    schema_invariants = [EndAfterStart]
    custom_validators = [validations.validate_date_range_within_parent,
                         validations.validate_start_date_equals_end_date,
                         validations.validate_venues,
                         #validations.validate_non_overlapping_sitting
                         ]

    public_wfstates = [sitting_wf_state[u'published-agenda'].id,
                       sitting_wf_state[u'draft-minutes'].id,
                       sitting_wf_state[u'published-minutes'].id,
                    ]

class SittingTypeDescriptor(ModelDescriptor):
    display_name = _(u"Type")
    container_name = _(u"Types")

class SessionDescriptor(ModelDescriptor):
    display_name = _(u"Parliamentary session")
    container_name = _(u"Parliamentary sessions")

    fields = [
        dict(name="session_id", omit=True),
        dict(name="parliament_id", omit=True),
        dict(name="short_name",
            property=schema.TextLine(title=_(u"Short Name"), required=True),
            listing=True),
        dict(name="full_name",
            property=schema.TextLine(title=_(u"Full Name"), required=True)),
        dict(name="start_date",
            property=schema.Date(title=_(u"Start Date"),required=True),
            listing=True,
            listing_column=day_column("start_date",
                _(u'Start Date')),
            edit_widget=DateWidget,
            add_widget=DateWidget),
        dict(name="end_date",
            property=schema.Date(title=_(u"End Date"), required=True),
            listing=True,
            listing_column=day_column("end_date",
                _(u'End Date')),
            edit_widget=DateWidget,
            add_widget=DateWidget),
        LanguageField("language"),
        dict(name="notes", label=_(u"Notes"), required=False)
        ]
    schema_invariants = [EndAfterStart]
    custom_validators = [validations.validate_date_range_within_parent,]

#class DebateDescriptor (ModelDescriptor):
#    display_name = _(u"Debate")
#    container_name = _(u"Debate")

#    fields = [
#        dict(name="sitting_id", omit=True),
#        dict(name="debate_id", omit=True),
#        dict(name="short_name",
#                label=_(u"Short Name"),
#                listing=True,
#                listing_column=name_column("short_name",
#                    _(u'Name'))),
#        dict(name="body_text", label=_(u"Transcript"),
#              property = schema.Text(title=u"Transcript"),
#              view_widget=HTMLDisplay,
#              edit_widget=RichTextEditor,
#              add_widget=RichTextEditor,
#             ),
#        ]

class AttendanceDescriptor(ModelDescriptor):
    display_name =_(u"Sitting attendance")
    container_name =_(u"Sitting attendances")

    attendanceVocab = vocabulary.DatabaseSource(
        domain.AttendanceType,
                    token_field='attendance_id',
                    title_field='attendance_type',
                    value_field='attendance_id' )

    fields = [
        dict(name="sitting_id", omit=True),
        dict(name="member_id", listing=True,
                property = schema.Choice(title=_(u"Attendance"),
                source=vocabulary.SittingAttendanceSource(
                    title_field='fullname',
                    token_field='user_id',
                    value_field = 'member_id')),
              listing_column=user_name_column("member_id",
                _(u'Name'), 'user' )
              ),
        dict(name="attendance_id", listing=True,
                property = schema.Choice(
                    title=_(u"Attendance"),
                    source=attendanceVocab,
                    required=True),
                listing_column = attendance_column(
                    "attendance_id",
                    _(u'Attendance') )),
        ]

class AttendanceTypeDescriptor(ModelDescriptor):
    display_name =_(u"Sitting attendance")
    container_name =_(u"Sitting attendances")

    fields = [
        dict (name="attendance_id", omit=True),
        dict (name="attendance_type",
            property=schema.TextLine(title=_(u"Attendance type"), required=True)),
        LanguageField("language"),
        ]

class ConsignatoryDescriptor(ModelDescriptor):
    display_name = _(u"Cosignatory")
    container_name = _(u"Cosignatories")

    fields = [
        dict(name="bill_id", omit = True),
        dict(name="user_id",
              property=schema.Choice(
                title=_(u"Cosignatory"),
                source=vocabulary.MemberOfParliamentDelegationSource('user_id'),
                required=True),
              listing_column=user_name_column("user_id",
                _(u'Cosignatory'), 'owner'),
            listing=True,
            ),
    ]

class ConstituencyDescriptor(ModelDescriptor):
    display_name = _(u"Constituency")
    container_name = _(u"Constituencies")

    provinceSource=vocabulary.DatabaseSource(domain.Province,
                    token_field='province_id',
                    title_field='province',
                    value_field='province_id')
    regionSource=vocabulary.DatabaseSource(domain.Region,
                    token_field='region_id',
                    title_field='region',
                    value_field='region_id')
    fields = [
        dict(name="constituency_id", omit=True),
        LanguageField("language"),
        dict(name="name",
            property=schema.TextLine(title=_(u"Name"),
            description=_("Name of the constituency"),
            required=True),
            listing=True),
        dict(name="province_id",
            property = schema.Choice(
                title=_(u"Province"),
                source=provinceSource,
                required=True),
            listing_column = province_column("province_id",
                _(u"Province")),
            listing=True,
        ),
        dict(name="region_id",
            label=_(u"Region"),
            property = schema.Choice(
                title=_(u"Region"),
                source=regionSource,
                required=True),
             listing_column = region_column("region_id",
                _(u"Region")),
            listing=True,
        ),
        dict(name="start_date",
                property=schema.Date(title=_(u"Start Date"), required=True),
                listing=True,
                listing_column=day_column('start_date', _(u"Start Date")),
                edit_widget=DateWidget,
                add_widget=DateWidget),
        dict(name="end_date",
                property=schema.Date(title=_(u"End Date"), required=False),
                listing=True ,
                listing_column=day_column('end_date', _(u"End Date")),
                edit_widget=DateWidget,
                add_widget=DateWidget),
        ]

schema_invariants = [EndAfterStart]

class ProvinceDescriptor(ModelDescriptor):
    display_name = _(u"Province")
    container_name = _(u"Provinces")
    fields = [
        LanguageField("language"),
        dict(name="province_id", omit=True),
        dict(name="province",
                property=schema.TextLine(title=_(u"Province"),
                description=_(u"Name of the Province"),
                required=True),
                listing=True),
        ]

class RegionDescriptor(ModelDescriptor):
    display_name = _(u"Region")
    container_name = _(u"Regions")
    fields = [
        LanguageField("language"),
        dict(name="region_id", omit=True),
        dict(name="region",
                property=schema.TextLine(title=_(u"Region"),
                description=_(u"Name of the Region"),
                required=True),
                listing=True),
        ]

class CountryDescriptor(ModelDescriptor):
    fields = [
        LanguageField("language"),
        dict(name="country_id",
                property=schema.TextLine(title=_(u"Country Code"),
                description =_(u"ISO Code of the  country"))),
        dict(name="country_name",
                property=schema.TextLine(title=_(u"Country"),
                description=_(u"Name of the Country")),
                listing=True),
        ]

class ConstituencyDetailDescriptor(ModelDescriptor):
    display_name = _(u"Constituency details")
    container_name = _(u"Details")
    fields = [
        dict(name="constituency_detail_id", omit=True),
        dict(name="constituency_id",
                label=_(u"Name"),
                description=_("Name of the constituency"),
                listing=False,
                omit=True),
        dict(name="date",
                property=schema.Date(title=_(u"Date"),
                description=_(u"Date the data was submitted from the Constituency"),
                required=True),
                listing=True,
                listing_column=day_column("date", "Date"),
                edit_widget=DateWidget,
                add_widget=DateWidget),
        dict(name="population",
                property=schema.Int(title=_(u"Population"),
                description=_(u"Total Number of People living in this Constituency"),
                required=True),
                listing=True),
        dict(name="voters",
                property=schema.Int(title=_(u"Voters"),
                description=_(u"Number of Voters registered in this Constituency"),
                required=True),
                listing=True),
        ]

################
# Hansard
################
class RotaDescriptor(ModelDescriptor):
    fields = [
        dict(name="rota_id", omit=True),
        dict(name="reporter_id", omit=True), #XXX
        dict(name="identifier",
            title=_("Rota Identifier"), listing=True),
        dict(name="start_date",
            label=_(u"Start Date"),
            listing_column=day_column("start_date",
                _(u"Start Date")),
            listing=True ,
            edit_widget=DateWidget,
            add_widget=DateWidget),
        dict(name="end_date",
            label=_(u"End Date"),
            listing_column=day_column("end_date",
                _(u"End Date")),
                listing=True,
                edit_widget=DateWidget,
                add_widget=DateWidget),
        ]

    schema_invariants = [EndAfterStart]

class DocumentSourceDescriptor(ModelDescriptor):
    display_name =_(u"Document source")
    container_name =_(u"Document sources")

    fields = [
        dict(name="document_source_id", omit=True),
        dict(name="document_source", label=_(u"Document Source")),
    ]

class ItemScheduleDescriptor(ModelDescriptor):
    display_name =_(u"Scheduling")
    container_name =_(u"Schedulings")

    fields = [
        dict(name="sitting_id", omit=True),
        dict(name="schedule_id", omit=True),
        dict(name="item_id",
              property = schema.Choice(
                  title=_(u"Item"),
                  source=vocabulary.DatabaseSource(
                      domain.ParliamentaryItem,
                      token_field='parliamentary_item_id',
                      value_field = 'parliamentary_item_id',
                      title_getter=lambda obj: "%s - %s" % (type(obj).__name__, obj.short_name),

                      )
                  ),
              listing = False,
              ),
        ]

class ScheduledItemDiscussionDescriptor(ModelDescriptor):
    display_name =_(u"Discussion")
    container_name =_(u"Discussions")

    fields = [
        LanguageField("language"),
        dict(name="schedule_id", omit=True),
        dict(name="body_text", label=_(u"Minutes"),
              property = schema.Text(title=_(u"Minutes")),
              view_widget=HTMLDisplay,
              edit_widget=RichTextEditor,
              add_widget=RichTextEditor,
        ),
#        dict(name="sitting_time",
#             label=_(u"Sitting time"),
#             description=_(u"The time at which the discussion took place."),
#             listing=True),
        ]

class ReportDescriptor(ModelDescriptor):
    display_name = _(u"Report")
    container_name = _(u"Reports")

    fields = [
        dict(name="report_id", omit=True),
        #LanguageField("language"),
        dict(name="language",
             label=_(u"Language"),
             listing=False,
             add=True,
             edit=True,
             omit=False,
             required=True,
             view=False,
             property=schema.Choice(
                 title=_(u"Language"),
                 default=get_default_language(),
                 vocabulary="language_vocabulary",
                 ),
             ),
        dict(name="report_type",
                label=_(u"Publications type"),
                listing=True,
                view=False),
        dict(name="created_date",
            label=_(u"Date published"),
            listing_column=day_column("created_date",
                _(u"Date Published")),
            listing=True ,
            edit_widget=DateWidget,
            add_widget=DateWidget,
            view=False),
        dict(name="start_date",
            label=_(u"Report Start Date"),
            listing_column=day_column("start_date",
                _(u"Sitting Date")),
            listing=True ,
            edit_widget=DateWidget,
            add_widget=DateWidget,
            view=False),

        dict(name="user_id",
                label=_(u"Edited by"),
                listing=True,
                view=False),
        dict(name="note",
                label=_(u"Note"),
                listing=True,
                view=False),
        dict(name="body_text",
                label=_(u"Text"),
                property = schema.Text(title=_(u"Text")),
                view_widget=HTMLDisplay,
                edit_widget=RichTextEditor,
                add_widget=RichTextEditor,
        ),

    ]

class Report4SittingDescriptor(ReportDescriptor):
    fields = deepcopy(ReportDescriptor.fields )
    fields.extend([
        dict(name="sitting_report_id", omit=True),
        dict(name="report_id", omit=True),
        dict(name="sitting_id", omit=True),
    ])


import sys
from multiprocessing.forkserver import ForkServer as _ForkServer

from gevent.os import make_nonblocking, nb_read, nb_write

__implements__ = ["ForkServer", "_forkserver", "ensure_running",
                  "get_inherited_fds", "connect_to_new_process",
                  "set_forkserver_preload"]
__target__ = "multiprocessing.forkserver"

class ForkServer(_ForkServer):
    def connect_to_new_process(self, fds):
        parent_r, parent_w = super().connect_to_new_process(fds)
        make_nonblocking(parent_r)
        make_nonblocking(parent_w)
        return parent_r, parent_w

    def ensure_running(self):
        super().ensure_running()
        make_nonblocking(self._forkserver_alive_fd)

if sys.version_info[:2] > (3, 6):
    __implements__ += ["read_signed", "write_signed"]

    from multiprocessing.forkserver import SIGNED_STRUCT

    def read_signed(fd):
        data = b''
        length = SIGNED_STRUCT.size
        while len(data) < length:
            s = nb_read(fd, length - len(data))
            if not s:
                raise EOFError('unexpected EOF')
            data += s
        return SIGNED_STRUCT.unpack(data)[0]

    def write_signed(fd, n):
        msg = SIGNED_STRUCT.pack(n)
        while msg:
            nbytes = nb_write(fd, msg)
            if nbytes == 0:
                raise RuntimeError('should not get here')
            msg = msg[nbytes:]
else:
    __implements__ += ["read_unsigned", "write_unsigned"]

    from multiprocessing.forkserver import UNSIGNED_STRUCT

    def read_unsigned(fd):
        data = b''
        length = UNSIGNED_STRUCT.size
        while len(data) < length:
            s = nb_read(fd, length - len(data))
            if not s:
                raise EOFError('unexpected EOF')
            data += s
        return UNSIGNED_STRUCT.unpack(data)[0]

    def write_unsigned(fd, n):
        msg = UNSIGNED_STRUCT.pack(n)
        while msg:
            nbytes = nb_write(fd, msg)
            if nbytes == 0:
                raise RuntimeError('should not get here')
            msg = msg[nbytes:]

_forkserver = ForkServer()
ensure_running = _forkserver.ensure_running
get_inherited_fds = _forkserver.get_inherited_fds
connect_to_new_process = _forkserver.connect_to_new_process
set_forkserver_preload = _forkserver.set_forkserver_preload


#
# Copyright (c) 2016 Intel Corporation
#
# 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.
#

from datetime import datetime
import os
import subprocess

import paramiko
import sshtunnel

import config
from modules.command import run
from modules.exceptions import CommandExecutionException
from modules.tap_logger import log_command, get_logger

logger = get_logger(__name__)

SSH_POLICY = paramiko.AutoAddPolicy()
EXIT_STATUS_OK = 0

class RemoteCommand(object):
    """Remember to use .decode() on stdout/stderr output file-like objects."""

    def __init__(self, command: str, channel: paramiko.channel.Channel, stdin: paramiko.channel.ChannelFile,
                 stdout: paramiko.channel.ChannelFile, stderr: paramiko.channel.ChannelFile):
        self.command = command
        self.channel = channel
        self.stdin = stdin
        self.stdout = stdout
        self.stderr = stderr

    def get_return_code(self):
        """Return exit status of the process, wait if necessary."""
        return self.channel.recv_exit_status()

    def assert_return_code_ok(self):
        status = self.get_return_code()
        if status != EXIT_STATUS_OK:
            msg = "Wrong status code {} of command {}".format(status, self.command)
            logger.error("%s, stdout: %s, stderr: %s", msg, self.get_stdout_as_str(), self.get_stderr_as_str())
            raise CommandExecutionException(msg)

    def get_stdout_as_str(self):
        return self.stdout.read().decode()

    def get_stderr_as_str(self):
        return self.stderr.read().decode()

class DirectSshClient(object):

    def __init__(self, hostname, username, port=22, path_to_key=None):
        self.hostname = hostname
        self.username = username
        self.port = port
        self.path_to_key = path_to_key
        self.client = paramiko.SSHClient()
        self.client.set_missing_host_key_policy(SSH_POLICY)

    def connect(self):
        self.client.connect(self.hostname, port=self.port, username=self.username, key_filename=self.path_to_key)

    def disconnect(self):
        self.client.close()

    def exec_command(self, command):
        log_command(command)
        command = " ".join(command)
        _, stdout, stderr = self.client.exec_command(command)
        return stdout.read().decode(), stderr.read().decode()

class NestedSshClient(object):

    def __init__(self, hostname, username, path_to_key=None, port=22, via_hostname=None, via_username=None,
                 via_path_to_key=None, via_port=22, local_port=1234):
        self.hostname = hostname
        self.username = username
        self.port = port
        self.path_to_key = path_to_key
        self.via_hostname = via_hostname
        self.via_username = via_username
        self.via_path_to_key = via_path_to_key
        self.via_port = via_port
        self.local_port = local_port
        if self.via_username is None:
            self.via_username = self.username
        if self.via_path_to_key is None:
            self.via_path_to_key = self.path_to_key
        self.proxy = paramiko.SSHClient()
        self.proxy.set_missing_host_key_policy(SSH_POLICY)
        self.client = paramiko.SSHClient()
        self.client.set_missing_host_key_policy(SSH_POLICY)

    def connect(self):
        self.proxy.connect(self.via_hostname, port=self.via_port, username=self.via_username,
                           key_filename=self.via_path_to_key)
        proxy_transport = self.proxy.get_transport()
        channel = proxy_transport.open_channel("direct-tcpip", dest_addr=(self.hostname, self.port),
                                               src_addr=("localhost", self.local_port))
        self.client.connect("localhost", port=self.local_port, username=self.username, sock=channel,
                            key_filename=self.path_to_key)

    def disconnect(self):
        self.proxy.close()
        self.client.close()

    def exec_command_non_interactive(self, command):
        ssh_cmd = self.exec_command_interactive(command)
        return (
            ssh_cmd.get_return_code(),
            ssh_cmd.get_stdout_as_str(),
            ssh_cmd.get_stderr_as_str()
        )

    def exec_command_interactive(self, command):
        log_command(command)
        command = " ".join(command)
        channel = self.client.get_transport().open_session()
        channel.exec_command(command)
        return RemoteCommand(
            command,
            channel,
            channel.makefile('wb', -1),
            channel.makefile('r', -1),
            channel.makefile_stderr('r', -1)
        )

class SshTunnel(object):

    def __init__(self, hostname, username, path_to_key=None, port=22, via_hostname=None, via_port=22, local_port=1234):
        self.hostname = hostname
        self.username = username
        self.port = port
        self.path_to_key = path_to_key
        self.via_hostname = via_hostname
        self.via_port = via_port
        self.local_port = local_port
        ssh_address = (self.via_hostname, self.via_port)
        ssh_private_key = os.path.expanduser(self.path_to_key)
        ssh_private_key = paramiko.RSAKey.from_private_key_file(ssh_private_key)
        local_bind_address = ("localhost", self.local_port)
        remote_bind_address = (self.hostname, self.port)
        self.client = sshtunnel.SSHTunnelForwarder(ssh_address=ssh_address, ssh_username=self.username,
                                                   ssh_private_key=ssh_private_key, local_bind_address=local_bind_address,
                                                   remote_bind_address=remote_bind_address)

    def connect(self):
        self.client.start()

    def disconnect(self):
        self.client.stop()

class SshTunnelException(Exception):
    pass

class ClouderaManagerSshTunnel(SshTunnel):

    def __init__(self):
        super().__init__(
            hostname=config.cdh_master_2_hostname,
            username=config.jumpbox_username,
            path_to_key=config.jumpbox_key_path,
            port=config.cdh_manager_port,
            via_hostname=config.jumpbox_hostname
        )

class CdhMasterClient:

    def __init__(self, cdh_host_name):

        self.cdh_host_name = cdh_host_name
        ssh_no_host_checking = ["-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no"]
        key_path = config.jumpbox_key_path
        jumpbox_host = config.jumpbox_hostname
        jumpbox_username = config.jumpbox_username
        cdh_master_username = config.cdh_master_username

        self._output_path = "/tmp/{}".format(datetime.now().strftime("%Y%m%d_%H%M%S_%f"))
        self._prepare_commands = [["set", "-e"], ["rm", "-rf", self._output_path], ["mkdir", self._output_path]]
        self._ssh_command = ["ssh", "-tt"] + ssh_no_host_checking + \
                            ["-i", key_path, "{}@{}".format(jumpbox_username, jumpbox_host), "sudo", "ssh", "-tt"] + \
                            ssh_no_host_checking + ["{}@{}".format(cdh_master_username, self.cdh_host_name)]
        self._rsync_command = [
            "rsync", "-avz", "--delete", "-e",
            "ssh {}@{} {} -i {} sudo ssh {}".format(jumpbox_username, jumpbox_host, " ".join(ssh_no_host_checking),
                                                    key_path, " ".join(ssh_no_host_checking)),
            "{}@{}:{}/".format(cdh_master_username, self.cdh_host_name, self._output_path), self._output_path
        ]

    def exec_commands(self, commands):
        """
        :param commands: list of commands
        :return: list with std commands outputs (out and err)

        Example:
            exec_commands([["echo", "hello"], ["python", "-c", "import os; os.write(2, \"world\")"]])
            -> [["hello", ""], ["", "world"]]
        """
        process = subprocess.Popen(self._ssh_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                                   stderr=subprocess.STDOUT, universal_newlines=True)

        string_commands = self._send_commands_to_process(process, commands)

        while True:
            output = process.stdout.readline().strip()
            if output == '' and process.poll() is not None:
                break
            if output != '':
                logger.info(output)

        outputs = self._get_commands_outputs(string_commands)

        return_code = process.poll()
        if return_code != 0:
            raise CommandExecutionException(return_code, " ".join(self._ssh_command))

        return outputs

    @classmethod
    def _ensure_quote(cls, query):
        query = query.strip()
        if query[0] not in "'\"":
            query = "'{}'".format(query)
        return query

    def _send_commands_to_process(self, process, commands):
        for command in self._prepare_commands:
            process.stdin.write(" ".join(command + ["\n"]))

        string_commands = []

        for i, command in enumerate(commands):
            command = [self._ensure_quote(i) for i in command]
            string_command = " ".join(
                command + ["1>{}/{}_1".format(self._output_path, i), "2>{}/{}_2".format(self._output_path, i), "\n"])
            logger.info("Executing command: %s", string_command)
            process.stdin.write(string_command)
            string_commands.append(string_command)
        process.stdin.write("exit 0\n")
        process.stdin.close()

        return string_commands

    def _get_commands_outputs(self, string_commands):
        run(self._rsync_command)

        cmds_outputs = []
        for i, command in enumerate(string_commands):
            logger.info("Outputs from command: %s", command)
            cmd_outputs = []
            for fd_name, fd in [("stdout", 1), ("stderr", 2)]:
                logger.info("%s:", fd_name)
                path = "{}/{}_{}".format(self._output_path, i, fd)
                if os.path.exists(path):
                    with open(path) as f:
                        output = f.read().strip()
                else:
                    raise CommandExecutionException("No {} output of command {}".format(fd_name, command))
                cmd_outputs.append(output)
                logger.info(output)
            cmds_outputs.append(cmd_outputs)

        return cmds_outputs


# created by: Alex Clippinger
# e-mail: clippinger.a@northeastern.edu
# date: 2021-04-06

# the following script produces a summary file for the web layers of the ArcGIS Dashboard
# for the NEU IS office.
# execute the script to output an excel file to your working directory.

# import arcgis libraries and modules
import arcgis
from arcgis import GIS
from arcgis.mapping import WebMap

# initiate connection to ArcGIS Online
gis = GIS("Home")

# access web map used in the dashboard
web_map = gis.content.get('e52b2e79cf304c128aa0d9a61cf5077b')\

# create WebMap object
web_map = WebMap(web_map)

# get columns of layer names, ids, and layer types

# create empty lists
col1 = []
col2 = []
col3 = []

# iterate through layers in web map
for layer in web_map.layers:

    #append titles, ids, and layer types to fill empty lists
    col1.append(layer.title)
    col2.append(layer.itemId)
    col3.append(layer.layerType)

# get columns of Layer information (exists, status, owner, links)

# create empty lists
col4 = []
col5 = []
col6 = []
col7 = []

# iterate through itemIds from column 2 above
for uid in col2:

    # check if itemid returns a result.
    if len(gis.content.search(uid, outside_org = True)) > 0:

        # if the itemid returns any result, then it exists
        col4.append('Yes')

        # if itemid returns any result, then fetch the layer
        layer = gis.content.get(str(uid))

        # use the layer information to append owner and link to fill empty lists
        col6.append(layer.owner)
        col7.append(layer.homepage)

        # for layers that exist, check the layer status and append to fill empty list
        if layer.content_status == 'deprecated':
            col5.append('Layer has been deprecated')
        else:
            col5.append('Active')

    # return if the itemid does not return any result.
    else:
        col4.append('Layer does not exist.')

# import pandas to work with dataframes
import pandas as pd

# create columns
d = {'Layer Title': col1,
     'Layer ID': col2,
     'Layer Type': col3,
     'Layer Exists': col4,
     'Layer Status': col5,
     'Owner': col6,
     'Link': col7}

# create dataframe
df = pd.DataFrame(d)

# import datetime module
from datetime import datetime

# get today's date for file name
date = datetime.today().strftime('%Y-%m-%d')

# import the os module
import os

# create file path
path = os.path.join('Dashboard_Layers-Summary_' + date + '.xlsx')

# save dataframe to an excel file
df.to_excel(path,
            sheet_name = str(date))

# Note: this will be saved to your working directory.
# To check your working directory, use the code below:
# > import os
# > os.getcwd()
# To change your working directory, use the code below:
# > import os
# > os.chdir('new/file/path/here')