<commit_before>from __future__ import unicode_literals

import hashlib
import json
from abc import ABCMeta, abstractmethod

import six

from eventsourcing.exceptions import DataIntegrityError
from eventsourcing.utils.topic import get_topic, resolve_topic
from eventsourcing.infrastructure.cipher.base import AbstractCipher
from eventsourcing.infrastructure.sequenceditem import SequencedItem, SequencedItemFieldNames
from eventsourcing.utils.transcoding import ObjectJSONDecoder, ObjectJSONEncoder


class AbstractSequencedItemMapper(six.with_metaclass(ABCMeta)):
    @abstractmethod
    def to_sequenced_item(self, domain_event):
        """
        Returns sequenced item for given domain event.
        """

    @abstractmethod
    def from_sequenced_item(self, sequenced_item):
        """
        Return domain event from given sequenced item.
        """


class SequencedItemMapper(AbstractSequencedItemMapper):
    """
    Uses JSON to transcode domain events.
    """

    def __init__(self, sequenced_item_class=SequencedItem, sequence_id_attr_name=None, position_attr_name=None,
                 json_encoder_class=ObjectJSONEncoder, json_decoder_class=ObjectJSONDecoder,
                 always_encrypt=False, cipher=None, other_attr_names=(), with_data_integrity=False):
        self.sequenced_item_class = sequenced_item_class
        self.json_encoder_class = json_encoder_class
        self.json_decoder_class = json_decoder_class
        self.cipher = cipher
        self.always_encrypt = always_encrypt
        self.field_names = SequencedItemFieldNames(self.sequenced_item_class)
        self.sequence_id_attr_name = sequence_id_attr_name or self.field_names.sequence_id
        self.position_attr_name = position_attr_name or self.field_names.position
        self.other_attr_names = other_attr_names or self.field_names[4:]
        self.with_data_integrity = with_data_integrity

    def to_sequenced_item(self, domain_event):
        """
        Constructs a sequenced item from a domain event.
        """
        item_args = self.construct_item_args(domain_event)
        return self.construct_sequenced_item(item_args)

    def construct_item_args(self, domain_event):
        """
        Constructs attributes of a sequenced item from the given domain event.
        """
        # Construct the topic from the event class.
        topic = get_topic(domain_event.__class__)

        # Copy the state of the event.
        event_attrs = domain_event.__dict__.copy()

        # Pop the sequence ID.
        sequence_id = event_attrs.pop(self.sequence_id_attr_name)

        # Pop the position in the sequence.
        position = event_attrs.pop(self.position_attr_name)

        # Decide if this event will be encrypted.
        is_encrypted = self.is_encrypted(domain_event.__class__)

        # Serialise the remaining event attribute values.
        data = self.serialize_event_attrs(event_attrs, is_encrypted=is_encrypted)

        if self.with_data_integrity:
            algorithm = 'sha256'
            hash = self.hash(algorithm, sequence_id, position, data)
            data = '{}:{}:{}'.format(algorithm, hash, data)

        # Get the 'other' args.
        # - these are meant to be derivative of the other attributes,
        #   to populate database fields, and shouldn't affect the hash.
        other_args = tuple((getattr(domain_event, name) for name in self.other_attr_names))

        return (sequence_id, position, topic, data) + other_args

    def hash(self, algorithm, *args):
        if algorithm == 'sha256':
            return hashlib.sha256(self.json_dumps(args).encode()).hexdigest()
        else:
            raise ValueError('Algorithm not supported: {}'.format(algorithm))

    def json_dumps(self, obj):
        return json.dumps(
            obj,
            separators=(',', ':'),
            sort_keys=True,
            cls=self.json_encoder_class,
        )

    def construct_sequenced_item(self, item_args):
        return self.sequenced_item_class(*item_args)

    def from_sequenced_item(self, sequenced_item):
        """
        Reconstructs domain event from stored event topic and
        event attrs. Used in the event store when getting domain events.
        """
        assert isinstance(sequenced_item, self.sequenced_item_class), (self.sequenced_item_class, type(sequenced_item))

        # Get the domain event class from the topic.
        topic = getattr(sequenced_item, self.field_names.topic)
        domain_event_class = resolve_topic(topic)

        # Deserialize, optionally with decryption.
        is_encrypted = self.is_encrypted(domain_event_class)
        data = getattr(sequenced_item, self.field_names.data)

        hash = None
        algorithm = None<commit_msg>Removed duplicate method.<commit_after>from __future__ import unicode_literals

import hashlib
import json
from abc import ABCMeta, abstractmethod

import six

from eventsourcing.exceptions import DataIntegrityError
from eventsourcing.utils.topic import get_topic, resolve_topic
from eventsourcing.infrastructure.cipher.base import AbstractCipher
from eventsourcing.infrastructure.sequenceditem import SequencedItem, SequencedItemFieldNames
from eventsourcing.utils.transcoding import ObjectJSONDecoder, ObjectJSONEncoder


class AbstractSequencedItemMapper(six.with_metaclass(ABCMeta)):
    @abstractmethod
    def to_sequenced_item(self, domain_event):
        """
        Returns sequenced item for given domain event.
        """

    @abstractmethod
    def from_sequenced_item(self, sequenced_item):
        """
        Return domain event from given sequenced item.
        """


class SequencedItemMapper(AbstractSequencedItemMapper):
    """
    Uses JSON to transcode domain events.
    """

    def __init__(self, sequenced_item_class=SequencedItem, sequence_id_attr_name=None, position_attr_name=None,
                 json_encoder_class=ObjectJSONEncoder, json_decoder_class=ObjectJSONDecoder,
                 always_encrypt=False, cipher=None, other_attr_names=(), with_data_integrity=False):
        self.sequenced_item_class = sequenced_item_class
        self.json_encoder_class = json_encoder_class
        self.json_decoder_class = json_decoder_class
        self.cipher = cipher
        self.always_encrypt = always_encrypt
        self.field_names = SequencedItemFieldNames(self.sequenced_item_class)
        self.sequence_id_attr_name = sequence_id_attr_name or self.field_names.sequence_id
        self.position_attr_name = position_attr_name or self.field_names.position
        self.other_attr_names = other_attr_names or self.field_names[4:]
        self.with_data_integrity = with_data_integrity

    def to_sequenced_item(self, domain_event):
        """
        Constructs a sequenced item from a domain event.
        """
        item_args = self.construct_item_args(domain_event)
        return self.construct_sequenced_item(item_args)

    def construct_item_args(self, domain_event):
        """
        Constructs attributes of a sequenced item from the given domain event.
        """
        # Construct the topic from the event class.
        topic = get_topic(domain_event.__class__)

        # Copy the state of the event.
        event_attrs = domain_event.__dict__.copy()

        # Pop the sequence ID.
        sequence_id = event_attrs.pop(self.sequence_id_attr_name)

        # Pop the position in the sequence.
        position = event_attrs.pop(self.position_attr_name)

        # Decide if this event will be encrypted.
        is_encrypted = self.is_encrypted(domain_event.__class__)

        # Serialise the remaining event attribute values.
        data = self.serialize_event_attrs(event_attrs, is_encrypted=is_encrypted)

        if self.with_data_integrity:
            algorithm = 'sha256'
            hash = self.hash(algorithm, sequence_id, position, data)
            data = '{}:{}:{}'.format(algorithm, hash, data)

        # Get the 'other' args.
        # - these are meant to be derivative of the other attributes,
        #   to populate database fields, and shouldn't affect the hash.
        other_args = tuple((getattr(domain_event, name) for name in self.other_attr_names))

        return (sequence_id, position, topic, data) + other_args

    def hash(self, algorithm, *args):
        if algorithm == 'sha256':
            return hashlib.sha256(self.json_dumps(args).encode()).hexdigest()
        else:
            raise ValueError('Algorithm not supported: {}'.format(algorithm))

    def construct_sequenced_item(self, item_args):
        return self.sequenced_item_class(*item_args)

    def from_sequenced_item(self, sequenced_item):
        """
        Reconstructs domain event from stored event topic and
        event attrs. Used in the event store when getting domain events.
        """
        assert isinstance(sequenced_item, self.sequenced_item_class), (self.sequenced_item_class, type(sequenced_item))

        # Get the domain event class from the topic.
        topic = getattr(sequenced_item, self.field_names.topic)
        domain_event_class = resolve_topic(topic)

        # Deserialize, optionally with decryption.
        is_encrypted = self.is_encrypted(domain_event_class)
        data = getattr(sequenced_item, self.field_names.data)

        hash = None
        algorithm = None
{'fraction_non_alphanumeric': 0.07280720566159125, 'fraction_numerical': 0.002144542140253056, 'mean_word_length': 3.1900269541778976, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4734491', 'n_tokens_mistral': 2622, 'n_tokens_neox': 2517, 'n_words': 624}