# Path: chemdataextractor/scrape/base.py
# class BaseEntity(six.with_metaclass(ABCMeta)):
#     """Abstract Entity class from which all Entities inherit."""
#     pass
# 
# class EntityMeta(ABCMeta):
#     """Metaclass for Entity."""
# 
#     def __new__(mcs, name, bases, attrs):
#         fields = {}
#         for attr_name, attr_value in six.iteritems(attrs):
#             if isinstance(attr_value, BaseField):
#                 # Set the name attribute on the field to the attribute name on the Entity
#                 attr_value.name = six.text_type(attr_name)
#                 fields[attr_name] = attr_value
#         #attrs['fields'] = fields
#         # Set default _meta values, then update with any custom definitions from meta
#         #attrs['_meta'] = {'root': None}
#         #attrs['_meta'].update(attrs.pop('meta', {}))
#         cls = super(EntityMeta, mcs).__new__(mcs, name, bases, attrs)
#         cls.fields = cls.fields.copy()
#         cls.fields.update(fields)
#         return cls
# 
# Path: chemdataextractor/scrape/fields.py
# class StringField(BaseField):
#     """A string field."""
# 
#     def __init__(self, selection, lower=False, upper=False, strip=False, **kwargs):
#         """
# 
#         :param bool lower: (Optional) Whether to lowercase the string. Default False.
#         :param bool upper: (Optional) Whether to uppercase the string. Default False.
#         :param bool strip: (Optional) Whether to strip whitespace from start/end. Default False.
#         """
#         super(StringField, self).__init__(selection, **kwargs)
#         self.lower = lower
#         self.upper = upper
#         self.strip = strip
# 
#     def process(self, value):
#         value = super(StringField, self).process(value)
#         if value is not None:
#             if self.strip:
#                 value = value.strip()
#             if self.lower:
#                 value = value.lower()
#             if self.upper:
#                 value = value.upper()
#         return value
# 
# class DateTimeField(BaseField):
#     """A datetime field. Depends on python-dateutil."""
# 
#     def process(self, value):
#         if value == '':
#             return None
#         try:
#             # Ignore year-only values
#             if 32 < float(value) < 9999:
#                 return None
#         except ValueError:
#             pass
#         try:
#             return dateutil.parser.parse(value)
#         except (TypeError, ValueError):
#             return None
# 
#     def serialize(self, value):
#         return six.text_type(value.isoformat())
# 
# class UrlField(StringField):
#     """A field with optional URL processing."""
# 
#     def __init__(self, selection, strip_querystring=False, **kwargs):
#         """
# 
#         :param strip_querystring: (Optional) Whether to remove the querystring. Default False.
#         """
#         self.strip_querystring = strip_querystring
#         super(UrlField, self).__init__(selection, **kwargs)
# 
#     def process(self, value):
#         value = super(UrlField, self).process(value)
#         if value is not None and self.strip_querystring:
#             value = strip_querystring(value)
#         return value
# 
# Path: chemdataextractor/text/normalize.py
# @abstractmethod
# def normalize(self, text):
#     """Normalize the text.
# 
#     :param string text: The text to normalize.
#     :returns: Normalized text.
#     :rtype: string
#     """
#     return text


from collections import Sequence
from .base import BaseEntity, EntityMeta
from .fields import StringField, DateTimeField, UrlField
from ..text.normalize import normalize
import json
import logging
import six


class EntityList(Sequence):
    """Wrapper around a list of Entities to facilitate operations on all at once."""

    def __init__(self, *entities):
        self.entities = list(entities)

    def __getitem__(self, index):
        return self.entities[index]

    def __len__(self):
        return len(self.entities)

    def serialize(self):
        """Serialize to a list of python dictionaries."""
        return [e.serialize() for e in self.entities]

    def to_json(self, *args, **kwargs):
        """Convert EntityList to JSON."""
        return json.dumps(self.serialize(), *args, **kwargs)


class DocumentEntity(Entity):
    """Generic document entity."""
    doi = StringField('//meta[@name="citation_doi"]/@content | //meta[@name="dc.identifier"]/@content | //meta[@name="DC.identifier"]/@content | //meta[@name="DC.Identifier"]/@content | //meta[@name="dc.Identifier"]/@content', xpath=True, lower=True)
    title = StringField('//meta[@name="citation_title"]/@content | //meta[@name="dc.title"]/@content | //meta[@name="DC.title"]/@content | //meta[@name="DC.Title"]/@content | //meta[@name="dc.Title"]/@content | //meta[@name="title"]/@content', xpath=True, strip=True)
    authors = StringField('//meta[@name="citation_author"]/@content | //meta[@name="dc.creator"]/@content | //meta[@name="DC.creator"]/@content | //meta[@name="DC.Creator"]/@content | //meta[@name="dc.Creator"]/@content', xpath=True, all=True)
    published_date = DateTimeField('//meta[@name="citation_publication_date"]/@content | //meta[@name="prism.publicationDate"]/@content | //meta[@name="citation_date"]/@content | //meta[@name="dc.date"]/@content | //meta[@name="DC.date"]/@content | //meta[@name="DC.Date"]/@content | //meta[@name="dc.Date"]/@content', xpath=True)
    online_date = DateTimeField('//meta[@name="citation_online_date"]/@content', xpath=True)

# Path: chemdataextractor/scrape/base.py
# class BaseEntity(six.with_metaclass(ABCMeta)):
#     """Abstract Entity class from which all Entities inherit."""
#     pass
# 
# class EntityMeta(ABCMeta):
#     """Metaclass for Entity."""
# 
#     def __new__(mcs, name, bases, attrs):
#         fields = {}
#         for attr_name, attr_value in six.iteritems(attrs):
#             if isinstance(attr_value, BaseField):
#                 # Set the name attribute on the field to the attribute name on the Entity
#                 attr_value.name = six.text_type(attr_name)
#                 fields[attr_name] = attr_value
#         #attrs['fields'] = fields
#         # Set default _meta values, then update with any custom definitions from meta
#         #attrs['_meta'] = {'root': None}
#         #attrs['_meta'].update(attrs.pop('meta', {}))
#         cls = super(EntityMeta, mcs).__new__(mcs, name, bases, attrs)
#         cls.fields = cls.fields.copy()
#         cls.fields.update(fields)
#         return cls
# 
# Path: chemdataextractor/scrape/fields.py
# class StringField(BaseField):
#     """A string field."""
# 
#     def __init__(self, selection, lower=False, upper=False, strip=False, **kwargs):
#         """
# 
#         :param bool lower: (Optional) Whether to lowercase the string. Default False.
#         :param bool upper: (Optional) Whether to uppercase the string. Default False.
#         :param bool strip: (Optional) Whether to strip whitespace from start/end. Default False.
#         """
#         super(StringField, self).__init__(selection, **kwargs)
#         self.lower = lower
#         self.upper = upper
#         self.strip = strip
# 
#     def process(self, value):
#         value = super(StringField, self).process(value)
#         if value is not None:
#             if self.strip:
#                 value = value.strip()
#             if self.lower:
#                 value = value.lower()
#             if self.upper:
#                 value = value.upper()
#         return value
# 
# class DateTimeField(BaseField):
#     """A datetime field. Depends on python-dateutil."""
# 
#     def process(self, value):
#         if value == '':
#             return None
#         try:
#             # Ignore year-only values
#             if 32 < float(value) < 9999:
#                 return None
#         except ValueError:
#             pass
#         try:
#             return dateutil.parser.parse(value)
#         except (TypeError, ValueError):
#             return None
# 
#     def serialize(self, value):
#         return six.text_type(value.isoformat())
# 
# class UrlField(StringField):
#     """A field with optional URL processing."""
# 
#     def __init__(self, selection, strip_querystring=False, **kwargs):
#         """
# 
#         :param strip_querystring: (Optional) Whether to remove the querystring. Default False.
#         """
#         self.strip_querystring = strip_querystring
#         super(UrlField, self).__init__(selection, **kwargs)
# 
#     def process(self, value):
#         value = super(UrlField, self).process(value)
#         if value is not None and self.strip_querystring:
#             value = strip_querystring(value)
#         return value
# 
# Path: chemdataextractor/text/normalize.py
# @abstractmethod
# def normalize(self, text):
#     """Normalize the text.
# 
#     :param string text: The text to normalize.
#     :returns: Normalized text.
#     :rtype: string
#     """
#     return text


# Path: chemdataextractor/scrape/entity.py
from collections import Sequence
from .base import BaseEntity, EntityMeta
from .fields import StringField, DateTimeField, UrlField
from ..text.normalize import normalize
import json
import logging
import six



class EntityList(Sequence):
    """Wrapper around a list of Entities to facilitate operations on all at once."""

    def __init__(self, *entities):
        self.entities = list(entities)

    def __getitem__(self, index):
        return self.entities[index]

    def __len__(self):
        return len(self.entities)

    def serialize(self):
        """Serialize to a list of python dictionaries."""
        return [e.serialize() for e in self.entities]

    def to_json(self, *args, **kwargs):
        """Convert EntityList to JSON."""
        return json.dumps(self.serialize(), *args, **kwargs)


class DocumentEntity(Entity):
    """Generic document entity."""
    doi = StringField('//meta[@name="citation_doi"]/@content | //meta[@name="dc.identifier"]/@content | //meta[@name="DC.identifier"]/@content | //meta[@name="DC.Identifier"]/@content | //meta[@name="dc.Identifier"]/@content', xpath=True, lower=True)
    title = StringField('//meta[@name="citation_title"]/@content | //meta[@name="dc.title"]/@content | //meta[@name="DC.title"]/@content | //meta[@name="DC.Title"]/@content | //meta[@name="dc.Title"]/@content | //meta[@name="title"]/@content', xpath=True, strip=True)
    authors = StringField('//meta[@name="citation_author"]/@content | //meta[@name="dc.creator"]/@content | //meta[@name="DC.creator"]/@content | //meta[@name="DC.Creator"]/@content | //meta[@name="dc.Creator"]/@content', xpath=True, all=True)
    published_date = DateTimeField('//meta[@name="citation_publication_date"]/@content | //meta[@name="prism.publicationDate"]/@content | //meta[@name="citation_date"]/@content | //meta[@name="dc.date"]/@content | //meta[@name="DC.date"]/@content | //meta[@name="DC.Date"]/@content | //meta[@name="dc.Date"]/@content', xpath=True)
    online_date = DateTimeField('//meta[@name="citation_online_date"]/@content', xpath=True)

    journal = StringField('//meta[@name="citation_journal_title"]/@content | //meta[@name="citation_journal_abbrev"]/@content | //meta[@name="prism.publicationName"]/@content | //meta[@name="dc.source"]/@content | //meta[@name="DC.source"]/@content | //meta[@name="DC.Source"]/@content', xpath=True, strip=True)