Crystalnix/termius-cli

View on GitHub
termius/core/storage/strategies.py

Summary

Maintainability
A
0 mins
Test Coverage
# -*- coding: utf-8 -*-
"""Module with strategies.

Strategy is some additional behaviour for storage.
"""
import six
from ..models.base import DeleteSets


# pylint: disable=too-few-public-methods
class Strategy(object):
    """Base class for any strategy."""

    def __init__(self, storage):
        """Construct strategy, keep assigned storage."""
        self.storage = storage


class SaveStrategy(Strategy):
    """Saver strategy that saves relations on storage."""

    # pylint: disable=no-self-use,unused-argument
    def save_submodel(self, submodel, mapping):
        """Save submodels of model."""
        if isinstance(submodel, six.integer_types):
            return submodel
        assert submodel.id
        return submodel.id

    def serialize_relation(self, field, mapping):
        """Transform relation field.

        Use it to save relations.
        """
        return field and self.save_submodel(field, mapping)

    def mark_model(self, model):
        """Change model state before saving."""
        model.mark_updated()

    def save(self, model):
        """Do extra action when model saved.

        Save it's relations.
        """
        model_copy = model.copy()
        fk_fields = model.fk_field_names()
        for field in fk_fields:
            mapping = model.fields[field]
            saved_submodel = self.serialize_relation(
                getattr(model, field), mapping
            )
            setattr(model_copy, field, saved_submodel)
        return model_copy


class RelatedSaveStrategy(SaveStrategy):
    """Saver strategy that does not save relations."""

    def save_submodel(self, submodel, mapping):
        """Do not save any, barely return relation id."""
        return self.storage.save(submodel).id


class SyncSaveStrategy(SaveStrategy):
    """Saver strategy for synced models."""

    def mark_model(self, model):
        """Change model state before saving."""
        model.mark_synced()


class GetStrategy(Strategy):
    """Getter strategy that simply get model."""

    # pylint: disable=no-self-use
    def get(self, model):
        """Return what it gets."""
        return model


class RelatedGetStrategy(GetStrategy):
    """Getter strategy that get model's relation tree."""

    def get(self, model):
        """Return model with whole relation tree."""
        result = super(RelatedGetStrategy, self).get(model)
        fk_fields = model.fk_field_names()
        for field in fk_fields:
            mapping = model.fields[field]
            submodel_id = getattr(result, field)
            if submodel_id:
                submodel = self.storage.get_single_by_id(
                    mapping.model, submodel_id
                )
                setattr(result, field, submodel)
        return result


class DeleteStrategy(Strategy):
    """Deleter strategy that completely delete model."""

    # pylint: disable=no-self-use
    def get_delete_sets(self):
        """Return delete objects."""
        return {}

    # pylint: disable=no-self-use
    def delete(self, model):
        """Return what it gets."""
        return model

    def remove_intersection(self, deleted_sets):
        """Confirm delete (Need to create more suitable description)."""


class SoftDeleteStrategy(DeleteStrategy):
    """Deleter strategy that delete model and do small extra action.

    Extra action is that store model remote id in deleted_sets if any.
    """

    delete_sets_class = DeleteSets

    def get_delete_sets(self):
        """Retrieve delete objects from storage."""
        try:
            data = self.storage.low_get(self.delete_sets_class.set_name)
        except KeyError:
            data = {}
        model = self.delete_sets_class(data)
        return model

    def set_delete_sets(self, deleted):
        """Store delete sets into the storage."""
        self.storage.low_set(self.delete_sets_class.set_name, deleted)

    def delete(self, model):
        """Store remote_id of model in delete sets."""
        delete_sets = self.get_delete_sets()
        delete_sets.store(model)
        self.set_delete_sets(delete_sets)
        return model

    def remove_intersection(self, deleted_sets):
        """Remove from deleted_sets intersection with sets passed."""
        delete_sets = self.get_delete_sets()
        for set_name, id_list in deleted_sets.items():
            delete_sets.remove_all(set_name, id_list)
        self.set_delete_sets(delete_sets)