# Path: pupa/utils/generic.py
# def _make_pseudo_id(**kwargs):
#     """ pseudo ids are just JSON """
#     # ensure keys are sorted so that these are deterministic
#     return '~' + json.dumps(kwargs, sort_keys=True)
# 
# Path: pupa/scrape/base.py
# class BaseModel(object):
#     """
#     This is the base class for all the Open Civic objects. This contains
#     common methods and abstractions for OCD objects.
#     """
# 
#     # to be overridden by children. Something like "person" or "organization".
#     # Used in :func:`validate`.
#     _type = None
#     _schema = None
# 
#     def __init__(self):
#         super(BaseModel, self).__init__()
#         self._id = str(uuid.uuid1())
#         self._related = []
#         self.extras = {}
# 
#     # validation
# 
#     def validate(self, schema=None):
#         """
#         Validate that we have a valid object.
# 
#         On error, this will raise a `ScrapeValueError`
# 
#         This also expects that the schemas assume that omitting required
#         in the schema asserts the field is optional, not required. This is
#         due to upstream schemas being in JSON Schema v3, and not validictory's
#         modified syntax.
#         ^ TODO: FIXME
#         """
#         if schema is None:
#             schema = self._schema
# 
#         type_checker = Draft3Validator.TYPE_CHECKER.redefine(
#             "datetime", lambda c, d: isinstance(d, (datetime.date, datetime.datetime))
#         )
#         type_checker = type_checker.redefine(
#             "date", lambda c, d: (isinstance(d, datetime.date)
#                                   and not isinstance(d, datetime.datetime))
#         )
# 
#         ValidatorCls = jsonschema.validators.extend(Draft3Validator, type_checker=type_checker)
#         validator = ValidatorCls(schema, format_checker=FormatChecker())
# 
#         errors = [str(error) for error in validator.iter_errors(self.as_dict())]
#         if errors:
#             raise ScrapeValueError('validation of {} {} failed: {}'.format(
#                 self.__class__.__name__, self._id, '\n\t'+'\n\t'.join(errors)
#             ))
# 
#     def pre_save(self, jurisdiction_id):
#         pass
# 
#     def as_dict(self):
#         d = {}
#         for attr in self._schema['properties'].keys():
#             if hasattr(self, attr):
#                 d[attr] = getattr(self, attr)
#         d['_id'] = self._id
#         return d
# 
#     # operators
# 
#     def __setattr__(self, key, val):
#         if key[0] != '_' and key not in self._schema['properties'].keys():
#             raise ScrapeValueError('property "{}" not in {} schema'.format(key, self._type))
#         super(BaseModel, self).__setattr__(key, val)
# 
# def cleanup_list(obj, default):
#     if not obj:
#         obj = default
#     elif isinstance(obj, str):
#         obj = [obj]
#     elif not isinstance(obj, list):
#         obj = list(obj)
#     return obj
# 
# class SourceMixin(object):
#     def __init__(self):
#         super(SourceMixin, self).__init__()
#         self.sources = []
# 
#     def add_source(self, url, *, note=''):
#         """ Add a source URL from which data was collected """
#         new = {'url': url, 'note': note}
#         self.sources.append(new)
# 
# Path: pupa/scrape/popolo.py
# def pseudo_organization(organization, classification, default=None):
#     """ helper for setting an appropriate ID for organizations """
#     if organization and classification:
#         raise ScrapeValueError('cannot specify both classification and organization')
#     elif classification:
#         return _make_pseudo_id(classification=classification)
#     elif organization:
#         if isinstance(organization, Organization):
#             return organization._id
#         elif isinstance(organization, str):
#             return organization
#         else:
#             return _make_pseudo_id(**organization)
#     elif default is not None:
#         return _make_pseudo_id(classification=default)
#     else:
#         return None
# 
# Path: pupa/scrape/schemas/vote_event.py

# 
# Path: pupa/exceptions.py
# class ScrapeValueError(PupaError, ValueError):
#     """ An invalid value was passed to a pupa scrape object. """


from ..utils import _make_pseudo_id
from .base import BaseModel, cleanup_list, SourceMixin
from .bill import Bill
from .popolo import pseudo_organization
from .schemas.vote_event import schema
from pupa.exceptions import ScrapeValueError
import re

        if not self.legislative_session:
            raise ScrapeValueError('must set legislative_session or bill')

        self.organization = pseudo_organization(organization, chamber, 'legislature')
        self.votes = []
        self.counts = []

    def __str__(self):
        return '{0} - {1} - {2}'.format(self.legislative_session, self.start_date,
                                        self.motion_text)

    def set_bill(self, bill_or_identifier, *, chamber=None):
        if not bill_or_identifier:
            self.bill = None
        elif isinstance(bill_or_identifier, Bill):
            if chamber:
                raise ScrapeValueError("set_bill takes no arguments when using a `Bill` object")
            self.bill = bill_or_identifier._id
        else:
            if chamber is None:
                chamber = 'legislature'
            kwargs = {'identifier': bill_or_identifier,
                      'from_organization__classification': chamber,
                      'legislative_session__identifier': self.legislative_session
                      }
            self.bill = _make_pseudo_id(**kwargs)

    def vote(self, option, voter, *, note=''):
        self.votes.append({"option": option, "voter_name": voter,

# Path: pupa/utils/generic.py
# def _make_pseudo_id(**kwargs):
#     """ pseudo ids are just JSON """
#     # ensure keys are sorted so that these are deterministic
#     return '~' + json.dumps(kwargs, sort_keys=True)
# 
# Path: pupa/scrape/base.py
# class BaseModel(object):
#     """
#     This is the base class for all the Open Civic objects. This contains
#     common methods and abstractions for OCD objects.
#     """
# 
#     # to be overridden by children. Something like "person" or "organization".
#     # Used in :func:`validate`.
#     _type = None
#     _schema = None
# 
#     def __init__(self):
#         super(BaseModel, self).__init__()
#         self._id = str(uuid.uuid1())
#         self._related = []
#         self.extras = {}
# 
#     # validation
# 
#     def validate(self, schema=None):
#         """
#         Validate that we have a valid object.
# 
#         On error, this will raise a `ScrapeValueError`
# 
#         This also expects that the schemas assume that omitting required
#         in the schema asserts the field is optional, not required. This is
#         due to upstream schemas being in JSON Schema v3, and not validictory's
#         modified syntax.
#         ^ TODO: FIXME
#         """
#         if schema is None:
#             schema = self._schema
# 
#         type_checker = Draft3Validator.TYPE_CHECKER.redefine(
#             "datetime", lambda c, d: isinstance(d, (datetime.date, datetime.datetime))
#         )
#         type_checker = type_checker.redefine(
#             "date", lambda c, d: (isinstance(d, datetime.date)
#                                   and not isinstance(d, datetime.datetime))
#         )
# 
#         ValidatorCls = jsonschema.validators.extend(Draft3Validator, type_checker=type_checker)
#         validator = ValidatorCls(schema, format_checker=FormatChecker())
# 
#         errors = [str(error) for error in validator.iter_errors(self.as_dict())]
#         if errors:
#             raise ScrapeValueError('validation of {} {} failed: {}'.format(
#                 self.__class__.__name__, self._id, '\n\t'+'\n\t'.join(errors)
#             ))
# 
#     def pre_save(self, jurisdiction_id):
#         pass
# 
#     def as_dict(self):
#         d = {}
#         for attr in self._schema['properties'].keys():
#             if hasattr(self, attr):
#                 d[attr] = getattr(self, attr)
#         d['_id'] = self._id
#         return d
# 
#     # operators
# 
#     def __setattr__(self, key, val):
#         if key[0] != '_' and key not in self._schema['properties'].keys():
#             raise ScrapeValueError('property "{}" not in {} schema'.format(key, self._type))
#         super(BaseModel, self).__setattr__(key, val)
# 
# def cleanup_list(obj, default):
#     if not obj:
#         obj = default
#     elif isinstance(obj, str):
#         obj = [obj]
#     elif not isinstance(obj, list):
#         obj = list(obj)
#     return obj
# 
# class SourceMixin(object):
#     def __init__(self):
#         super(SourceMixin, self).__init__()
#         self.sources = []
# 
#     def add_source(self, url, *, note=''):
#         """ Add a source URL from which data was collected """
#         new = {'url': url, 'note': note}
#         self.sources.append(new)
# 
# Path: pupa/scrape/popolo.py
# def pseudo_organization(organization, classification, default=None):
#     """ helper for setting an appropriate ID for organizations """
#     if organization and classification:
#         raise ScrapeValueError('cannot specify both classification and organization')
#     elif classification:
#         return _make_pseudo_id(classification=classification)
#     elif organization:
#         if isinstance(organization, Organization):
#             return organization._id
#         elif isinstance(organization, str):
#             return organization
#         else:
#             return _make_pseudo_id(**organization)
#     elif default is not None:
#         return _make_pseudo_id(classification=default)
#     else:
#         return None
# 
# Path: pupa/scrape/schemas/vote_event.py

# 
# Path: pupa/exceptions.py
# class ScrapeValueError(PupaError, ValueError):
#     """ An invalid value was passed to a pupa scrape object. """


# Path: pupa/scrape/vote_event.py
from ..utils import _make_pseudo_id
from .base import BaseModel, cleanup_list, SourceMixin
from .bill import Bill
from .popolo import pseudo_organization
from .schemas.vote_event import schema
from pupa.exceptions import ScrapeValueError
import re


        if not self.legislative_session:
            raise ScrapeValueError('must set legislative_session or bill')

        self.organization = pseudo_organization(organization, chamber, 'legislature')
        self.votes = []
        self.counts = []

    def __str__(self):
        return '{0} - {1} - {2}'.format(self.legislative_session, self.start_date,
                                        self.motion_text)

    def set_bill(self, bill_or_identifier, *, chamber=None):
        if not bill_or_identifier:
            self.bill = None
        elif isinstance(bill_or_identifier, Bill):
            if chamber:
                raise ScrapeValueError("set_bill takes no arguments when using a `Bill` object")
            self.bill = bill_or_identifier._id
        else:
            if chamber is None:
                chamber = 'legislature'
            kwargs = {'identifier': bill_or_identifier,
                      'from_organization__classification': chamber,
                      'legislative_session__identifier': self.legislative_session
                      }
            self.bill = _make_pseudo_id(**kwargs)

    def vote(self, option, voter, *, note=''):
        self.votes.append({"option": option, "voter_name": voter,

                           "voter_id": _make_pseudo_id(name=voter), 'note': note})