def create_person_permissions_validator():
    return PersonPermissionsValidator(person_repo=create_person_repo()) class ExperienceRepo:

    def _decode_db_experience(self, db_experience, is_mine=False, is_saved=False):
        if not db_experience.picture:
            picture = None
        else:
            picture = Picture(small_url=db_experience.picture.small.url,
                              medium_url=db_experience.picture.medium.url,
                              large_url=db_experience.picture.large.url)

        return Experience(id=db_experience.id,
                          title=db_experience.title,
                          description=db_experience.description,
                          picture=picture,
                          author_id=db_experience.author.id,
                          author_username=db_experience.author.username,
                          is_mine=is_mine,
                          is_saved=is_saved)

    def get_all_experiences(self, logged_person_id, mine=False, saved=False):
        if saved:
            db_experiences = \
                [save.experience for save
                    in ORMSave.objects.select_related('experience').filter(person_id=logged_person_id)]
        else:
            all_db_experiences = ORMExperience.objects.select_related('author').all()
            if mine:
                db_experiences = all_db_experiences.filter(author_id=logged_person_id)
            else:
                saved_experience_ids = ORMSave.objects.values('experience_id').filter(person_id=logged_person_id)
                db_experiences = all_db_experiences.exclude(author_id=logged_person_id) \
                                                   .exclude(id__in=saved_experience_ids)

        experiences = []
        for db_experience in db_experiences:
            experiences.append(self._decode_db_experience(db_experience, mine, saved))
        return experiences

    def get_experience(self, id, logged_person_id=None):
        try:
            db_experience = ORMExperience.objects.select_related('author').get(id=id)
            return self._decode_db_experience(db_experience, is_mine=(logged_person_id == db_experience.author_id))
        except ORMExperience.DoesNotExist:
            raise EntityDoesNotExistException()

    def create_experience(self, experience):
        db_experience = ORMExperience.objects.create(title=experience.title,
                                                     description=experience.description,
                                                     author_id=experience.author_id)
        return self._decode_db_experience(db_experience, is_mine=True)

    def attach_picture_to_experience(self, experience_id, picture):
        experience = ORMExperience.objects.get(id=experience_id)
        experience.picture = picture
        experience.save()
        return self._decode_db_experience(experience, is_mine=True)

    def update_experience(self, experience):
        orm_experience = ORMExperience.objects.get(id=experience.id)

        orm_experience.title = experience.title
        orm_experience.description = experience.description

        orm_experience.save()
        return self._decode_db_experience(orm_experience, is_mine=True)

    def save_experience(self, person_id, experience_id):
        if not ORMSave.objects.filter(person_id=person_id, experience_id=experience_id).exists():
            ORMSave.objects.create(person_id=person_id, experience_id=experience_id)
        return True

    def unsave_experience(self, person_id, experience_id):
        ORMSave.objects.filter(person_id=person_id, experience_id=experience_id).delete()
        return True class ExperienceValidator:

    MIN_TITLE_LENGHT = 1
    MAX_TITLE_LENGHT = 30

    def validate_experience(self, experience):
        if experience.author_id is None:
            raise InvalidEntityException(source='author', code='empty_attribute', message='Author cannot be empty')
        if experience.title is None:
            raise InvalidEntityException(source='title', code='empty_attribute', message='Title cannot be empty')
        if type(experience.title) is not str:
            raise InvalidEntityException(source='title', code='wrong_type', message='Title must be string')
        if len(experience.title) < ExperienceValidator.MIN_TITLE_LENGHT or \
           len(experience.title) > ExperienceValidator.MAX_TITLE_LENGHT:
            raise InvalidEntityException(source='title', code='wrong_size',
                                         message='Title must be between 1 and 30 chars')

        if experience.description is not None and type(experience.description) is not str:
            raise InvalidEntityException(source='description', code='wrong_type', message='Description must be string')

        return True class ExperiencePermissionsValidator:

    def __init__(self, experience_repo, person_permissions_validator):
        self.experience_repo = experience_repo
        self.person_permissions_validator = person_permissions_validator

    def validate_permissions(self, logged_person_id, has_permissions_to_modify_experience=None):
        self.person_permissions_validator.validate_permissions(logged_person_id=logged_person_id)

        if has_permissions_to_modify_experience is not None:
            experience = self.experience_repo.get_experience(id=has_permissions_to_modify_experience)
            if experience.author_id != logged_person_id:
                raise NoPermissionException()

        return True class GetAllExperiencesInteractor:

    def __init__(self, experience_repo, permissions_validator):
        self.experience_repo = experience_repo
        self.permissions_validator = permissions_validator

    def set_params(self, mine, saved, logged_person_id):
        self.mine = mine
        self.saved = saved
        self.logged_person_id = logged_person_id
        return self

    def execute(self):
        self.permissions_validator.validate_permissions(logged_person_id=self.logged_person_id)

        return self.experience_repo.get_all_experiences(mine=self.mine, saved=self.saved,
                                                        logged_person_id=self.logged_person_id) class CreateNewExperienceInteractor:

    def __init__(self, experience_repo, experience_validator, permissions_validator):
        self.experience_repo = experience_repo
        self.experience_validator = experience_validator
        self.permissions_validator = permissions_validator

    def set_params(self, title, description, logged_person_id):
        self.title = title
        self.description = description
        self.logged_person_id = logged_person_id
        return self

    def execute(self):
        self.permissions_validator.validate_permissions(logged_person_id=self.logged_person_id,
                                                        wants_to_create_content=True)
        experience = Experience(title=self.title, description=self.description, author_id=self.logged_person_id)
        self.experience_validator.validate_experience(experience)
        return self.experience_repo.create_experience(experience) class ModifyExperienceInteractor:

    def __init__(self, experience_repo, experience_validator, permissions_validator):
        self.experience_repo = experience_repo
        self.experience_validator = experience_validator
        self.permissions_validator = permissions_validator

    def set_params(self, id, title, description, logged_person_id):
        self.id = id
        self.title = title
        self.description = description
        self.logged_person_id = logged_person_id
        return self

    def execute(self):
        self.permissions_validator.validate_permissions(logged_person_id=self.logged_person_id,
                                                        has_permissions_to_modify_experience=self.id)
        experience = self.experience_repo.get_experience(id=self.id, logged_person_id=self.logged_person_id)

        new_title = self.title if self.title is not None else experience.title
        new_description = self.description if self.description is not None else experience.description
        updated_experience = experience.builder().title(new_title).description(new_description).build()

        self.experience_validator.validate_experience(updated_experience)

        return self.experience_repo.update_experience(updated_experience) class UploadExperiencePictureInteractor:

    def __init__(self, experience_repo, permissions_validator):
        self.experience_repo = experience_repo
        self.permissions_validator = permissions_validator

    def set_params(self, experience_id, picture, logged_person_id):
        self.experience_id = experience_id
        self.picture = picture
        self.logged_person_id = logged_person_id
        return self

    def execute(self):
        self.permissions_validator.validate_permissions(logged_person_id=self.logged_person_id,
                                                        has_permissions_to_modify_experience=self.experience_id)
        return self.experience_repo.attach_picture_to_experience(experience_id=self.experience_id, picture=self.picture) class SaveUnsaveExperienceInteractor:

    class Action(Enum):
        SAVE = 1
        UNSAVE = 2

    def __init__(self, experience_repo, permissions_validator):
        self.experience_repo = experience_repo
        self.permissions_validator = permissions_validator

    def set_params(self, action, experience_id, logged_person_id):
        self.action = action
        self.experience_id = experience_id
        self.logged_person_id = logged_person_id
        return self

    def execute(self):
        self.permissions_validator.validate_permissions(logged_person_id=self.logged_person_id)

        experience = self.experience_repo.get_experience(id=self.experience_id)
        if experience.author_id == self.logged_person_id:
            raise ConflictException(source='experience', code='self_save',
                                    message='You cannot save your own experiences')

        if self.action is SaveUnsaveExperienceInteractor.Action.SAVE:
            self.experience_repo.save_experience(person_id=self.logged_person_id, experience_id=self.experience_id)
        elif self.action is SaveUnsaveExperienceInteractor.Action.UNSAVE:
            self.experience_repo.unsave_experience(person_id=self.logged_person_id, experience_id=self.experience_id)

        return True class ExperiencesView:

    def __init__(self, get_all_experiences_interactor=None, create_new_experience_interactor=None):
        self.get_all_experiences_interactor = get_all_experiences_interactor
        self.create_new_experience_interactor = create_new_experience_interactor

    @serialize_exceptions
    def get(self, mine='false', saved='false', logged_person_id=None):
        mine = (mine == 'true')
        saved = (saved == 'true')
        experiences = self.get_all_experiences_interactor.set_params(mine=mine, saved=saved,
                                                                     logged_person_id=logged_person_id).execute()

        body = MultipleExperiencesSerializer.serialize(experiences)
        status = 200
        return body, status

    @serialize_exceptions
    def post(self, title=None, description=None, logged_person_id=None):
        experience = self.create_new_experience_interactor \
                .set_params(title=title, description=description, logged_person_id=logged_person_id).execute()
        body = ExperienceSerializer.serialize(experience)
        status = 201
        return body, status class ExperienceView:

    def __init__(self, modify_experience_interactor=None):
        self.modify_experience_interactor = modify_experience_interactor

    @serialize_exceptions
    def patch(self, experience_id, title=None, description=None, logged_person_id=None):
        experience = self.modify_experience_interactor \
                .set_params(id=experience_id, title=title,
                            description=description, logged_person_id=logged_person_id).execute()
        body = ExperienceSerializer.serialize(experience)
        status = 200
        return body, status class UploadExperiencePictureView:

    def __init__(self, upload_experience_picture_interactor=None):
        self.upload_experience_picture_interactor = upload_experience_picture_interactor

    @serialize_exceptions
    def post(self, picture, experience_id, logged_person_id):
        experience = self.upload_experience_picture_interactor.set_params(experience_id=experience_id, picture=picture,
                                                                          logged_person_id=logged_person_id).execute()
        body = ExperienceSerializer.serialize(experience)
        status = 200
        return body, status class SaveExperienceView:

    def __init__(self, save_unsave_experience_interactor):
        self.save_unsave_experience_interactor = save_unsave_experience_interactor

    @serialize_exceptions
    def post(self, experience_id, logged_person_id):
        self.save_unsave_experience_interactor \
                .set_params(action=SaveUnsaveExperienceInteractor.Action.SAVE,
                            experience_id=experience_id, logged_person_id=logged_person_id).execute()
        body = ''
        status = 201
        return body, status

    @serialize_exceptions
    def delete(self, experience_id, logged_person_id):
        self.save_unsave_experience_interactor \
                .set_params(action=SaveUnsaveExperienceInteractor.Action.UNSAVE,
                            experience_id=experience_id, logged_person_id=logged_person_id).execute()
        body = None
        status = 204
        return body, status
from people.factories import create_person_permissions_validator
from .repositories import ExperienceRepo
from .validators import ExperienceValidator, ExperiencePermissionsValidator
from .interactors import GetAllExperiencesInteractor, CreateNewExperienceInteractor, \
        ModifyExperienceInteractor, UploadExperiencePictureInteractor, SaveUnsaveExperienceInteractor
from .views import ExperiencesView, ExperienceView, UploadExperiencePictureView, SaveExperienceView


def create_experience_repo():
    return ExperienceRepo()


def create_experience_validator():
    return ExperienceValidator()


def create_experience_permissions_validator():

    return ExperiencePermissionsValidator(experience_repo=create_experience_repo(),