<commit_before>"""
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
  This file is part of the Smart Developer Hub Project:
    http://www.smartdeveloperhub.org

  Center for Open Middleware
        http://www.centeropenmiddleware.com/
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
  Copyright (C) 2015 Center for Open Middleware.
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
  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.
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
"""
import base64
import logging
import traceback
import uuid

from datetime import datetime

from abc import ABCMeta, abstractmethod
from agora.stoa.actions.core import RDF, STOA, FOAF, TYPES, XSD
from agora.stoa.actions.core.base import Request, Action, Response, Sink
from agora.stoa.actions.core.utils import CGraph
from agora.stoa.messaging.reply import reply
from agora.stoa.server import app
from agora.stoa.store import r
from rdflib import BNode, Literal, RDFS

__author__ = '<NAME>'

EXCHANGE_CONFIG = app.config['EXCHANGE']
exchange = EXCHANGE_CONFIG['exchange']
log = logging.getLogger('agora.stoa.actions.delivery')
AGENT_UUID = Literal(str(uuid.uuid4()), datatype=TYPES.UUID)


def _build_reply_templates():
    """
    :return: Accept and Failure message templates
    """
    accepted = CGraph()
    failure = CGraph()
    response_node = BNode()
    agent_node = BNode()
    accepted.add((response_node, RDF.type, STOA.Accepted))
    accepted.add((agent_node, RDF.type, FOAF.Agent))
    accepted.add((response_node, STOA.responseNumber, Literal("0", datatype=XSD.unsignedLong)))
    accepted.add((response_node, STOA.submittedBy, agent_node))
    accepted.add((response_node, STOA.submittedBy, agent_node))
    accepted.add(
        (agent_node, STOA.agentId, AGENT_UUID))
    accepted.bind('types', TYPES)
    accepted.bind('stoa', STOA)
    accepted.bind('foaf', FOAF)

    for triple in accepted:
        failure.add(triple)
    failure.set((response_node, RDF.type, STOA.Failure))
    for (prefix, ns) in accepted.namespaces():
        failure.bind(prefix, ns)

    return accepted, failure


def build_reply(template, reply_to, comment=None):
    """
    :param template: A specific message template graph
    :param reply_to: Recipient Agent UUID
    :param comment: Optional comment
    :return: The reply graph
    """
    reply_graph = CGraph()
    root_node = None
    for (s, p, o) in template:
        reply_graph.add((s, p, o))
        if p == RDF.type:
            root_node = s
    reply_graph.add((root_node, STOA.responseTo, Literal(reply_to, datatype=TYPES.UUID)))
    reply_graph.set((root_node, STOA.submittedOn, Literal(datetime.now())))
    reply_graph.set((root_node, STOA.messageId, Literal(str(uuid.uuid4()), datatype=TYPES.UUID)))
    if comment is not None:
        reply_graph.set((root_node, RDFS.comment, Literal(comment, datatype=XSD.string)))
    for (prefix, ns) in template.namespaces():
        reply_graph.bind(prefix, ns)
    return reply_graph


# Create both accept and failure templates
accepted_template, failure_template = _build_reply_templates()
log.info('Basic delivery templates created')


class DeliveryRequest(Request):
    def __init__(self):
        super(DeliveryRequest, self).__init__()

    def _extract_content(self, request_type=None):
        """
        Extracts delivery related data from message (delivery channel to reply to)
        """
        super(DeliveryRequest, self)._extract_content(request_type)

        q_res = self._graph.query("""SELECT ?node ?ex ?rk ?h ?p ?v WHERE {
                                  ?node stoa:replyTo [
                                        a stoa:DeliveryChannel;
                                        amqp:exchangeName ?ex;
                                        amqp:routingKey ?rk;
                                        amqp:broker [
                                           a amqp:Broker;
                                           amqp:host ?h;
                                           amqp:port ?p;
                                           amqp:virtualHost ?v
                                        ]
                                     ]
                                  } """)
        q_res = list(q_res)
        if len(q_res) != 1:
            raise SyntaxError('Invalid delivery request')

        request_fields = q_res.pop()

        if not any(request_fields):
            raise SyntaxError('Missing fields for delivery request')

        if request_fields[0] != self._request_node:
            raise SyntaxError('Request node does not match')

        delivery_data = {}

        (delivery_data['exchange'],
         delivery_data['routing_key'],
         delivery_data['host'],
         delivery_data['port'],
         delivery_data['vhost']) = request_fields[1:]
        log.debug("""Parsed attributes of a delivery action request:
                      -exchange name: {}
                      -routing key: {}
                      -host: {}
                      -port: {}
                      -virtual host: {}""".format(
            delivery_data['exchange'],
            delivery_data['routing_key'],
            delivery_data['host'], delivery_data['port'], delivery_data['vhost']))

        # Copy delivery data dictionary to the base request fields attribute
        self._fields['delivery'] = delivery_data.copy()

    @property
    def broker(self):
        """
        :return: Broker to which response must be addressed
        """
        broker_dict = {k: self._fields['delivery'][k].toPython() for k in ('host', 'port', 'vhost') if
                       k in self._fields['delivery']}
        broker_dict['port'] = int(broker_dict['port'])
        return broker_dict

    @property
    def channel(self):
        """
        :return: Delivery channel attributes
        """
        return {k: self._fields['delivery'][k].toPython() for k in ('exchange', 'routing_key') if
                k in self._fields['delivery']}

    @property
    def recipient(self):
        """
        :return: Broker and delivery channel data
        """
        recipient = self.broker.copy()
        recipient.update(self.channel)
        return recipient


class DeliveryAction(Action):
    __metaclass__ = ABCMeta

    def __init__(self, message):
        super(DeliveryAction, self).__init__(message)

    def __reply(self, template, reason=None):
        """
        Sends a protocol reply message to the submitter
        """
        graph = build_reply(template, self.request.message_id, reason)
        reply(graph.serialize(format='turtle'), exchange=exchange,
              routing_key='stoa.response.{}'.format(self.request.submitted_by),
              **self.request.broker)

    def __reply_accepted(self):
        """
        Sends an Accept message to the submitter
        """
        try:
            self.__reply(accepted_template)
        except Exception:
            # KeyError: Delivery channel data may be invalid
            # IOError: If the acceptance couldn't be sent, propagate the exception
            raise

    def _reply_failure(self, reason=None):
        """<commit_msg>- Fixed reply templates - Using response prefix on replies<commit_after>"""
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
  This file is part of the Smart Developer Hub Project:
    http://www.smartdeveloperhub.org

  Center for Open Middleware
        http://www.centeropenmiddleware.com/
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
  Copyright (C) 2015 Center for Open Middleware.
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
  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.
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
"""
import base64
import logging
import traceback
import uuid

from datetime import datetime

from abc import ABCMeta, abstractmethod
from agora.stoa.actions.core import RDF, STOA, FOAF, TYPES, XSD
from agora.stoa.actions.core.base import Request, Action, Response, Sink
from agora.stoa.actions.core.utils import CGraph
from agora.stoa.messaging.reply import reply
from agora.stoa.server import app
from agora.stoa.store import r
from rdflib import BNode, Literal, RDFS

__author__ = '<NAME>'

EXCHANGE_CONFIG = app.config['EXCHANGE']
exchange = EXCHANGE_CONFIG['exchange']
response_rk = EXCHANGE_CONFIG['response_rk']
log = logging.getLogger('agora.stoa.actions.delivery')
AGENT_UUID = Literal(str(uuid.uuid4()), datatype=TYPES.UUID)


def _build_reply_templates():
    """
    :return: Accept and Failure message templates
    """
    accepted = CGraph()
    failure = CGraph()
    response_node = BNode()
    agent_node = BNode()
    accepted.add((response_node, RDF.type, STOA.Root))
    accepted.add((response_node, RDF.type, STOA.Accepted))
    accepted.add((agent_node, RDF.type, FOAF.Agent))
    accepted.add((response_node, STOA.responseNumber, Literal("0", datatype=XSD.unsignedLong)))
    accepted.add((response_node, STOA.submittedBy, agent_node))
    accepted.add(
        (agent_node, STOA.agentId, AGENT_UUID))
    accepted.bind('types', TYPES)
    accepted.bind('stoa', STOA)
    accepted.bind('foaf', FOAF)

    for triple in accepted:
        failure.add(triple)
    failure.set((response_node, RDF.type, STOA.Failure))
    for (prefix, ns) in accepted.namespaces():
        failure.bind(prefix, ns)

    return accepted, failure


def build_reply(template, reply_to, comment=None):
    """
    :param template: A specific message template graph
    :param reply_to: Recipient Agent UUID
    :param comment: Optional comment
    :return: The reply graph
    """
    reply_graph = CGraph()
    root_node = None
    for (s, p, o) in template:
        if o == STOA.Root:
            root_node = s
        else:
            reply_graph.add((s, p, o))

    reply_graph.add((root_node, STOA.responseTo, Literal(reply_to, datatype=TYPES.UUID)))
    reply_graph.set((root_node, STOA.submittedOn, Literal(datetime.now())))
    reply_graph.set((root_node, STOA.messageId, Literal(str(uuid.uuid4()), datatype=TYPES.UUID)))
    if comment is not None:
        reply_graph.set((root_node, RDFS.comment, Literal(comment, datatype=XSD.string)))
    for (prefix, ns) in template.namespaces():
        reply_graph.bind(prefix, ns)
    return reply_graph


# Create both accept and failure templates
accepted_template, failure_template = _build_reply_templates()
log.info('Basic delivery templates created')


class DeliveryRequest(Request):
    def __init__(self):
        super(DeliveryRequest, self).__init__()

    def _extract_content(self, request_type=None):
        """
        Extracts delivery related data from message (delivery channel to reply to)
        """
        super(DeliveryRequest, self)._extract_content(request_type)

        q_res = self._graph.query("""SELECT ?node ?ex ?rk ?h ?p ?v WHERE {
                                  ?node stoa:replyTo [
                                        a stoa:DeliveryChannel;
                                        amqp:exchangeName ?ex;
                                        amqp:routingKey ?rk;
                                        amqp:broker [
                                           a amqp:Broker;
                                           amqp:host ?h;
                                           amqp:port ?p;
                                           amqp:virtualHost ?v
                                        ]
                                     ]
                                  } """)
        q_res = list(q_res)
        if len(q_res) != 1:
            raise SyntaxError('Invalid delivery request')

        request_fields = q_res.pop()

        if not any(request_fields):
            raise SyntaxError('Missing fields for delivery request')

        if request_fields[0] != self._request_node:
            raise SyntaxError('Request node does not match')

        delivery_data = {}

        (delivery_data['exchange'],
         delivery_data['routing_key'],
         delivery_data['host'],
         delivery_data['port'],
         delivery_data['vhost']) = request_fields[1:]
        log.debug("""Parsed attributes of a delivery action request:
                      -exchange name: {}
                      -routing key: {}
                      -host: {}
                      -port: {}
                      -virtual host: {}""".format(
            delivery_data['exchange'],
            delivery_data['routing_key'],
            delivery_data['host'], delivery_data['port'], delivery_data['vhost']))

        # Copy delivery data dictionary to the base request fields attribute
        self._fields['delivery'] = delivery_data.copy()

    @property
    def broker(self):
        """
        :return: Broker to which response must be addressed
        """
        broker_dict = {k: self._fields['delivery'][k].toPython() for k in ('host', 'port', 'vhost') if
                       k in self._fields['delivery']}
        broker_dict['port'] = int(broker_dict['port'])
        return broker_dict

    @property
    def channel(self):
        """
        :return: Delivery channel attributes
        """
        return {k: self._fields['delivery'][k].toPython() for k in ('exchange', 'routing_key') if
                k in self._fields['delivery']}

    @property
    def recipient(self):
        """
        :return: Broker and delivery channel data
        """
        recipient = self.broker.copy()
        recipient.update(self.channel)
        return recipient


class DeliveryAction(Action):
    __metaclass__ = ABCMeta

    def __init__(self, message):
        super(DeliveryAction, self).__init__(message)

    def __reply(self, template, reason=None):
        """
        Sends a protocol reply message to the submitter
        """
        graph = build_reply(template, self.request.message_id, reason)
        reply(graph.serialize(format='turtle'), exchange=exchange,
              routing_key='{}.{}'.format(response_rk, self.request.submitted_by),
              **self.request.broker)

    def __reply_accepted(self):
        """
        Sends an Accept message to the submitter
        """
        try:
            self.__reply(accepted_template)
        except Exception:
            # KeyError: Delivery channel data may be invalid
            # IOError: If the acceptance couldn't be sent, propagate the exception
            raise

    def _reply_failure(self, reason=None):
        """
{'fraction_non_alphanumeric': 0.13750401026628167, 'fraction_numerical': 0.002053256336220725, 'mean_word_length': 2.236295681063123, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 6, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 36, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6700685', 'n_tokens_mistral': 4493, 'n_tokens_neox': 4265, 'n_words': 1116}