<commit_before>import datetime
import xml.dom.minidom

import pytz

from .models import (
    Action, Bounds, Diff, Node, OSMElement, Relation,
    RelationMember, Tag, Way, WayNode)


class FileFormatError(Exception):
    pass

zulu_tz = pytz.timezone('Zulu')
def parse_iso8601(s):
    return zulu_tz.localize(datetime.datetime.strptime(s, "%Y-%m-%dT%H:%M:%SZ" ))

def getFirstNontextChild(element):
    """ Return the first non-text child

    :param element: the parent element to search in
    :type element: xml.dom.minidom.Element
    :rtype: xml.dom.minidom.Element
    :return: The first child element which is not a text node
    """
    for i in element.childNodes:
        if not isinstance(i, xml.dom.minidom.Text):
            return i


class AbstractXMLParser:
    """ Base class for Adiff XML node parsers
    """

    ELEMENT_TYPES = {
        OSMElement.NODE: Node,
        OSMElement.WAY: Way,
        OSMElement.RELATION: Relation,
    }

    # See at the end of the file for definition of :
    # PARSER_MAP = {...}

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


    @staticmethod
    def _dict_fetch(_dict, str_name):
        """ Given a str name, returns the matching parser
        """
        try:
            klass = _dict[str_name]
        except KeyError:
            raise FileFormatError(
                'Unknown member type : "{}"'.format(str_name))
        return klass

    @classmethod
    def get_model(cls, str_name):
        """ Given a str name, returns the matching model
        """
        return cls._dict_fetch(cls.ELEMENT_TYPES, str_name)

    @classmethod
    def get_parser(cls, str_name):
        """ Given a str name, returns the matching model
        """
        return cls._dict_fetch(cls.PARSER_MAP, str_name)

    def transform(self, node, name=None):
        """"Given a node/way/relation, creates a Django object in db

        :return: the db object.
        """
        klass = self.get_parser(node.localName)
        return klass(node).parse()

    def parse(self):
        """ Parse the XML node and retuns a model instance

        Can recurse, on sub-nodes calling other parsers, only the top-level
        node(s) is/are returned.

        :rtype: a OSMElement or a list of OSMElement
        """
        raise NotImplemented


class RelationMemberParser(AbstractXMLParser):
    """ <member> parser
    """
    def __init__(self, node, relation, order):
        self.node = node
        self.relation = relation
        self.order = order

    def parse(self):
        _type = self.node.attributes['type'].value


        # FIXME: factorize ? modify & use transform()
        if _type == OSMElement.NODE :
            parser = NodeParser(self.node)
        elif _type == OSMElement.WAY:
            parser = WayParser(self.node)
        elif _type == OSMElement.RELATION:
            parser = RelationParser(self.node)
        else:
            raise FileFormatError('Unknown member type : "{}"'.format(_type))
        element = parser.parse()

        return RelationMember.objects.create(
            relation=self.relation,
            element=element,
            order=self.order,
            role=self.node.attributes['role'].value)


class BoundsParser(AbstractXMLParser):
    BOUNDS_ATTRS = ('minlat', 'minlon', 'maxlat', 'maxlon')

    def parse(self):
        attrs = {k:self.node.attributes[k].value for k in self.BOUNDS_ATTRS}
        return Bounds.objects.create(**attrs)<commit_msg>Refactor using get_parser()<commit_after>import datetime
import xml.dom.minidom

import pytz

from .models import (
    Action, Bounds, Diff, Node, OSMElement, Relation,
    RelationMember, Tag, Way, WayNode)


class FileFormatError(Exception):
    pass

zulu_tz = pytz.timezone('Zulu')
def parse_iso8601(s):
    return zulu_tz.localize(datetime.datetime.strptime(s, "%Y-%m-%dT%H:%M:%SZ" ))

def getFirstNontextChild(element):
    """ Return the first non-text child

    :param element: the parent element to search in
    :type element: xml.dom.minidom.Element
    :rtype: xml.dom.minidom.Element
    :return: The first child element which is not a text node
    """
    for i in element.childNodes:
        if not isinstance(i, xml.dom.minidom.Text):
            return i


class AbstractXMLParser:
    """ Base class for Adiff XML node parsers
    """

    ELEMENT_TYPES = {
        OSMElement.NODE: Node,
        OSMElement.WAY: Way,
        OSMElement.RELATION: Relation,
    }

    # See at the end of the file for definition of :
    # PARSER_MAP = {...}

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


    @staticmethod
    def _dict_fetch(_dict, str_name):
        """ Given a str name, returns the matching parser
        """
        try:
            klass = _dict[str_name]
        except KeyError:
            raise FileFormatError(
                'Unknown member type : "{}"'.format(str_name))
        return klass

    @classmethod
    def get_model(cls, str_name):
        """ Given a str name, returns the matching model
        """
        return cls._dict_fetch(cls.ELEMENT_TYPES, str_name)

    @classmethod
    def get_parser(cls, str_name):
        """ Given a str name, returns the matching model
        """
        return cls._dict_fetch(cls.PARSER_MAP, str_name)

    def transform(self, node, name=None):
        """"Given a node/way/relation, creates a Django object in db

        :return: the db object.
        """
        klass = self.get_parser(node.localName)
        return klass(node).parse()

    def parse(self):
        """ Parse the XML node and retuns a model instance

        Can recurse, on sub-nodes calling other parsers, only the top-level
        node(s) is/are returned.

        :rtype: a OSMElement or a list of OSMElement
        """
        raise NotImplemented


class RelationMemberParser(AbstractXMLParser):
    """ <member> parser
    """
    def __init__(self, node, relation, order):
        self.node = node
        self.relation = relation
        self.order = order

    def parse(self):
        _type = self.node.attributes['type'].value

        ParserClass = self.get_parser(_type)
        parser = ParserClass(self.node)
        element = parser.parse()

        return RelationMember.objects.create(
            relation=self.relation,
            element=element,
            order=self.order,
            role=self.node.attributes['role'].value)


class BoundsParser(AbstractXMLParser):
    BOUNDS_ATTRS = ('minlat', 'minlon', 'maxlat', 'maxlon')

    def parse(self):
        attrs = {k:self.node.attributes[k].value for k in self.BOUNDS_ATTRS}
        return Bounds.objects.create(**attrs)
{'fraction_non_alphanumeric': 0.10465291371781359, 'fraction_numerical': 0.0012046378557446168, 'mean_word_length': 2.420185375901133, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 8}, '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': '3555244', 'n_tokens_mistral': 2019, 'n_tokens_neox': 1976, 'n_words': 543}