<commit_before>"""
pynamodb attributes
"""
import six
import json
from base64 import b64encode, b64decode
from delorean import Delorean, parse
from pynamodb.constants import (
    STRING, NUMBER, BINARY, UTC, DATETIME_FORMAT, BINARY_SET, STRING_SET, NUMBER_SET,
    DEFAULT_ENCODING
)

class Attribute(object):
    """
    An attribute of a model
    """
    attr_name = None

    def __init__(self,
                 attr_type=str,
                 hash_key=False,
                 range_key=False,
                 null=False,
                 default=None
                 ):
        self.value = None
        self.default = default
        self.null = null
        self.attr_type = attr_type
        self.is_hash_key = hash_key
        self.is_range_key = range_key

    def __set__(self, instance, value):
        if isinstance(value, Attribute):
            return self
        if instance:
            instance.attribute_values[self.attr_name] = value
            self.value = value

    def __get__(self, instance, owner):
        if instance:
            return instance.attribute_values.get(self.attr_name, None)
        else:
            return self

    def serialize(self, value):
        """
        This method should return a dynamodb compatible value
        """
        return value

    def deserialize(self, value):
        """
        Performs any needed deserialization on the value
        """
        return value

class SetMixin(object):
    """
    Adds (de)serialization methods
    """
    def serialize(self, value):
        """
        Serializes a set

        Because dynamodb doesn't store empty attributes,
        empty sets return None
        """
        if value and len(value):
            return [json.dumps(val) for val in sorted(value)]
        else:
            return None

    def deserialize(self, value):
        """
        Deserializes a set
        """
        if value and len(value):
            return set([json.loads(val) for val in value])

class BinaryAttribute(Attribute):
    """
    A binary attribute
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', BINARY)
        super(BinaryAttribute, self).__init__(**kwargs)

    def serialize(self, value):
        """
        Returns a base64 encoded binary string
        """
        return b64encode(value).decode(DEFAULT_ENCODING)

    def deserialize(self, value):
        """
        Returns a decoded string from base64
        """
        return b64decode(value.encode(DEFAULT_ENCODING))

class BinarySetAttribute(SetMixin, Attribute):
    """
    A binary set
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', BINARY_SET)
        kwargs.setdefault('null', True)
        super(BinarySetAttribute, self).__init__(**kwargs)

    def serialize(self, value):
        """
        Returns a base64 encoded binary string
        """
        if value and len(value):
            return [b64encode(val).decode(DEFAULT_ENCODING) for val in sorted(value)]
        else:
            return None

    def deserialize(self, value):
        """
        Returns a decoded string from base64
        """
        if value and len(value):
            return set([b64decode(val.encode(DEFAULT_ENCODING)) for val in value])

class UnicodeSetAttribute(SetMixin, Attribute):
    """
    A unicode set
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', STRING_SET)
        kwargs.setdefault('null', True)
        super(UnicodeSetAttribute, self).__init__(**kwargs)

class UnicodeAttribute(Attribute):
    """
    A unicode attribute
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', STRING)
        super(UnicodeAttribute, self).__init__(**kwargs)

    def serialize(self, value):
        """
        Returns a unicode string
        """
        return six.u(value)

class NumberSetAttribute(SetMixin, Attribute):
    """
    A number set attribute
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', NUMBER_SET)
        kwargs.setdefault('null', True)
        super(NumberSetAttribute, self).__init__(**kwargs)

class NumberAttribute(Attribute):
    """
    A number attribute
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', NUMBER)
        super(NumberAttribute, self).__init__(**kwargs)

    def serialize(self, value):
        """
        Encode numbers as JSON
        """
        return json.dumps(value)

    def deserialize(self, value):
        """
        Decode numbers from JSON
        """
        return json.loads(value)

class UTCDateTimeAttribute(Attribute):
    """
    An attribute for storing a UTC Datetime
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', STRING)
        super(UTCDateTimeAttribute, self).__init__(**kwargs)

    def serialize(self, value):
        """
        Takes a datetime object and returns a string
        """
        fmt = Delorean(value, timezone=UTC).datetime.strftime(DATETIME_FORMAT)
        return six.u(fmt)

    def deserialize(self, value):
        """
        Takes a UTC datetime string and returns a datetime object
        """
        return parse(value).datetime
<commit_msg>Fixed unicode bug for Python 2<commit_after>"""
pynamodb attributes
"""
import six
import json
from base64 import b64encode, b64decode
from delorean import Delorean, parse
from pynamodb.constants import (
    STRING, NUMBER, BINARY, UTC, DATETIME_FORMAT, BINARY_SET, STRING_SET, NUMBER_SET,
    DEFAULT_ENCODING
)

class Attribute(object):
    """
    An attribute of a model
    """
    attr_name = None

    def __init__(self,
                 attr_type=str,
                 hash_key=False,
                 range_key=False,
                 null=False,
                 default=None
                 ):
        self.value = None
        self.default = default
        self.null = null
        self.attr_type = attr_type
        self.is_hash_key = hash_key
        self.is_range_key = range_key

    def __set__(self, instance, value):
        if isinstance(value, Attribute):
            return self
        if instance:
            instance.attribute_values[self.attr_name] = value
            self.value = value

    def __get__(self, instance, owner):
        if instance:
            return instance.attribute_values.get(self.attr_name, None)
        else:
            return self

    def serialize(self, value):
        """
        This method should return a dynamodb compatible value
        """
        return value

    def deserialize(self, value):
        """
        Performs any needed deserialization on the value
        """
        return value

class SetMixin(object):
    """
    Adds (de)serialization methods
    """
    def serialize(self, value):
        """
        Serializes a set

        Because dynamodb doesn't store empty attributes,
        empty sets return None
        """
        if value and len(value):
            return [json.dumps(val) for val in sorted(value)]
        else:
            return None

    def deserialize(self, value):
        """
        Deserializes a set
        """
        if value and len(value):
            return set([json.loads(val) for val in value])

class BinaryAttribute(Attribute):
    """
    A binary attribute
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', BINARY)
        super(BinaryAttribute, self).__init__(**kwargs)

    def serialize(self, value):
        """
        Returns a base64 encoded binary string
        """
        return b64encode(value).decode(DEFAULT_ENCODING)

    def deserialize(self, value):
        """
        Returns a decoded string from base64
        """
        return b64decode(value.encode(DEFAULT_ENCODING))

class BinarySetAttribute(SetMixin, Attribute):
    """
    A binary set
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', BINARY_SET)
        kwargs.setdefault('null', True)
        super(BinarySetAttribute, self).__init__(**kwargs)

    def serialize(self, value):
        """
        Returns a base64 encoded binary string
        """
        if value and len(value):
            return [b64encode(val).decode(DEFAULT_ENCODING) for val in sorted(value)]
        else:
            return None

    def deserialize(self, value):
        """
        Returns a decoded string from base64
        """
        if value and len(value):
            return set([b64decode(val.encode(DEFAULT_ENCODING)) for val in value])

class UnicodeSetAttribute(SetMixin, Attribute):
    """
    A unicode set
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', STRING_SET)
        kwargs.setdefault('null', True)
        super(UnicodeSetAttribute, self).__init__(**kwargs)

class UnicodeAttribute(Attribute):
    """
    A unicode attribute
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', STRING)
        super(UnicodeAttribute, self).__init__(**kwargs)

    def serialize(self, value):
        """
        Returns a unicode string
        """
        if isinstance(value, six.text_type):
            return value
        else:
            return six.u(value)

class NumberSetAttribute(SetMixin, Attribute):
    """
    A number set attribute
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', NUMBER_SET)
        kwargs.setdefault('null', True)
        super(NumberSetAttribute, self).__init__(**kwargs)

class NumberAttribute(Attribute):
    """
    A number attribute
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', NUMBER)
        super(NumberAttribute, self).__init__(**kwargs)

    def serialize(self, value):
        """
        Encode numbers as JSON
        """
        return json.dumps(value)

    def deserialize(self, value):
        """
        Decode numbers from JSON
        """
        return json.loads(value)

class UTCDateTimeAttribute(Attribute):
    """
    An attribute for storing a UTC Datetime
    """
    def __init__(self, **kwargs):
        kwargs.setdefault('attr_type', STRING)
        super(UTCDateTimeAttribute, self).__init__(**kwargs)

    def serialize(self, value):
        """
        Takes a datetime object and returns a string
        """
        fmt = Delorean(value, timezone=UTC).datetime.strftime(DATETIME_FORMAT)
        return six.u(fmt)

    def deserialize(self, value):
        """
        Takes a UTC datetime string and returns a datetime object
        """
        return parse(value).datetime
<|endoftext|>


<commit_before>#!/usr/bin/env python3.6
#!/usr/bin/env pypy3
"""Set qnames based on the curies defined for a given ontology.

Usage:
    qnamefix [options]
    qnamefix [options] (-x <prefix>)...
    qnamefix [options] <file>...
    qnamefix [options] (-x <prefix>)... <file>...

Options:
    -h --help       print this
    -k --keep       keep only existing used prefixes
    -x --exclude=X  do not include the prefix when rewriting, ALL will strip
    -v --verbose    do something fun!
    -s --slow       do not use a process pool
    -n --nowrite    parse the file and reserialize it but do not write changes

"""

import os
import sys
from glob import glob
import rdflib
from docopt import docopt
import ttlser.ttlfmt
from pyontutils.core import makeGraph, cull_prefixes
from ttlser.utils import readFromStdIn
from ttlser.ttlfmt import parse, prepare
from pyontutils.namespaces import PREFIXES as uPREFIXES

PREFIXES = {k:v for k, v in uPREFIXES.items() if k != 'NIFTTL'}

exclude = 'generated/swanson_hierarchies.ttl', 'generated/NIF-NIFSTD-mapping.ttl'

def serialize(graph, outpath):
    def prefix_cleanup(ps, graph):
        if 'parcellation/' in outpath:
            nsl = {p:n for p, n in graph.namespaces()}
            if '' in nsl:
                ps[''] = nsl['']
            elif 'hbaslim' in outpath:
                ps['HBA'] = nsl['HBA']
            elif 'mbaslim' in outpath:
                ps['MBA'] = nsl['MBA']
            elif 'cocomac' in outpath:
                ps['cocomac'] = nsl['cocomac']

        # special cases for NIFORG, NIFINV, NIFRET where there identifiers in
        # annotation properties that share the prefix, so we warn incase
        # at some point in the future for some reason want them again...
        if outpath == 'NIF-Organism.ttl':
            print('WARNING: special case for NIFORG')
            ps.pop('NIFORG')
        elif outpath == 'NIF-Investigation.ttl':
            print('WARNING: special case for NIFINV')
            ps.pop('NIFINV')
        elif outpath == 'unused/NIF-Retired.ttl':
            print('WARNING: special case for NIFRET')
            ps.pop('NIFGA')

    pc = prefix_cleanup if isinstance(outpath, str) else lambda a, b: None
    graph = cull_prefixes(graph, cleanup=pc)

    out = graph.g.serialize(format='nifttl', gen_prefix=bool(PREFIXES))
    if not isinstance(outpath, str):  # FIXME not a good test that it is stdout
        outpath.buffer.write(out)
    else:
        with open(outpath, 'wb') as f:
            f.write(out)

def convert(file_or_stream, stream=False, infmt=None):
    if file_or_stream in exclude:
        print('skipping', file)
        return file
    serialize(*parse(**prepare(file_or_stream, stream=stream), infmt=infmt))

def main():
    global PREFIXES
    args = docopt(__doc__, version = "qnamefix 0")
    infmt = args['--format'] = 'turtle'
    ttlser.ttlfmt.args = args
    if args['--exclude'] == ['ALL']:
        for k in list(PREFIXES):
            PREFIXES.pop(k)
    else:
        for x in args['--exclude'] and x in PREFIXES:
            PREFIXES.pop(x)
    if not args['<file>']:
        stdin = readFromStdIn(sys.stdin)
        if stdin is not None:
            convert(stdin, stream=True, infmt=infmt)
        else:
            print(__doc__)
    else:
        if args['--slow'] or len(args['<file>']) == 1:
            [convert(f, infmt=infmt) for f in args['<file>']]
        else:
            from joblib import Parallel, delayed
            Parallel(n_jobs=9)(delayed(convert)(f, infmt=infmt) for f in args['<file>'])

if __name__ == '__main__':
    main()
<commit_msg>qnamefix fix excluding prefixes<commit_after>#!/usr/bin/env python3.6
#!/usr/bin/env pypy3
"""Set qnames based on the curies defined for a given ontology.

Usage:
    qnamefix [options]
    qnamefix [options] (-x <prefix>)...
    qnamefix [options] <file>...
    qnamefix [options] (-x <prefix>)... <file>...

Options:
    -h --help       print this
    -k --keep       keep only existing used prefixes
    -x --exclude=X  do not include the prefix when rewriting, ALL will strip
    -v --verbose    do something fun!
    -s --slow       do not use a process pool
    -n --nowrite    parse the file and reserialize it but do not write changes

"""

import os
import sys
from glob import glob
import rdflib
from docopt import docopt
import ttlser.ttlfmt
from pyontutils.core import makeGraph, cull_prefixes
from ttlser.utils import readFromStdIn
from ttlser.ttlfmt import parse, prepare
from pyontutils.namespaces import PREFIXES as uPREFIXES

PREFIXES = {k:v for k, v in uPREFIXES.items() if k != 'NIFTTL'}

exclude = 'generated/swanson_hierarchies.ttl', 'generated/NIF-NIFSTD-mapping.ttl'

def serialize(graph, outpath):
    def prefix_cleanup(ps, graph):
        if 'parcellation/' in outpath:
            nsl = {p:n for p, n in graph.namespaces()}
            if '' in nsl:
                ps[''] = nsl['']
            elif 'hbaslim' in outpath:
                ps['HBA'] = nsl['HBA']
            elif 'mbaslim' in outpath:
                ps['MBA'] = nsl['MBA']
            elif 'cocomac' in outpath:
                ps['cocomac'] = nsl['cocomac']

        # special cases for NIFORG, NIFINV, NIFRET where there identifiers in
        # annotation properties that share the prefix, so we warn incase
        # at some point in the future for some reason want them again...
        if outpath == 'NIF-Organism.ttl':
            print('WARNING: special case for NIFORG')
            ps.pop('NIFORG')
        elif outpath == 'NIF-Investigation.ttl':
            print('WARNING: special case for NIFINV')
            ps.pop('NIFINV')
        elif outpath == 'unused/NIF-Retired.ttl':
            print('WARNING: special case for NIFRET')
            ps.pop('NIFGA')

    pc = prefix_cleanup if isinstance(outpath, str) else lambda a, b: None
    graph = cull_prefixes(graph, cleanup=pc, prefixes=PREFIXES)

    out = graph.g.serialize(format='nifttl', gen_prefix=bool(PREFIXES))
    if not isinstance(outpath, str):  # FIXME not a good test that it is stdout
        outpath.buffer.write(out)
    else:
        with open(outpath, 'wb') as f:
            f.write(out)

def convert(file_or_stream, stream=False, infmt=None):
    if file_or_stream in exclude:
        print('skipping', file)
        return file
    serialize(*parse(**prepare(file_or_stream, stream=stream), infmt=infmt))

def main():
    global PREFIXES
    args = docopt(__doc__, version = "qnamefix 0")
    infmt = args['--format'] = 'turtle'
    ttlser.ttlfmt.args = args
    if args['--exclude'] == ['ALL']:
        for k in list(PREFIXES):
            PREFIXES.pop(k)
    else:
        for x in args['--exclude']:
            if x in PREFIXES:
                PREFIXES.pop(x)
    if not args['<file>']:
        stdin = readFromStdIn(sys.stdin)
        if stdin is not None:
            convert(stdin, stream=True, infmt=infmt)
        else:
            print(__doc__)
    else:
        if args['--slow'] or len(args['<file>']) == 1:
            [convert(f, infmt=infmt) for f in args['<file>']]
        else:
            from joblib import Parallel, delayed
            Parallel(n_jobs=9)(delayed(convert)(f, infmt=infmt) for f in args['<file>'])

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>import inspect
import logging
import projex.text
import textwrap

from collections import defaultdict
from pyramid.view import view_config
from pyramid.httpexceptions import HTTPNotFound, HTTPForbidden, HTTPException
from pyramid.response import Response

from .documentation import Documentation, SectionGroup, Section
from .services import *

log = logging.getLogger(__name__)

class ApiFactory(dict):
    def __init__(self,
                 version='1.0.0',
                 application='pyramid_restful',
                 documentation_package='pyramid_restful',
                 documentation_folder='templates',
                 documentation_template='documentation.html.jinja'):
        super(ApiFactory, self).__init__()

        # private properties
        self.__documentation = Documentation(documentation_package, documentation_folder, documentation_template)

        # public properties
        self.version = version
        self.application = application
        self.services = {}

    def collect_documentation(self, name, service_info):
        service, service_object = service_info

        try:
            is_module = issubclass(service, ModuleService)
        except StandardError:
            is_module = False

        if is_module:
            module_name = service_object.__name__.split('.')[-1]
            group_name = getattr(service_object, '__group__', 'Topics')
            docs = getattr(service_object, '__doc__', '') or ''

            methods = [(docs, '')]

            for method in vars(service_object).values():
                if isinstance(service, Endpoint):
                    method_doc = '## {0}\n\n'.format(method.__name__)
                    method_doc += textwrap.dedent(getattr(method, '__doc__', '')) or ''
                    methods.append((method_doc, ''))

            section = Section(
                id=module_name,
                name=getattr(service_object, '__title__', module_name),
                methods=methods
            )
            yield group_name, section

    def cors_setup(self, request):
        response = Response()
        if request.is_xhr:
            response.headerlist = []
            response.headerlist.extend(
                (
                    ('Access-Control-Allow-Origin', '*'),
                    ('Content-Type', 'application/json')
                )
            )
        return response

    def factory(self, request, parent=None, name=None):
        """
        Returns a new service for the given request.

        :param      request | <pyramid.request.Request>

        :return     <pyramid_restful.services.AbstractService>
        """
        # show documentation at the root path
        if not request.matchdict['traverse']:
            return {}
        else:
            name = name or request.matchdict['traverse'][0]

            service = {}
            try:
                service_type, service_object = self.services[name]
            except KeyError:
                raise HTTPNotFound()
            else:
                if isinstance(service_type, Endpoint):
                    service[name] = service_type
                elif service_object is None:
                    service[name] = service_type(request)
                else:
                    service[name] = service_type(request, service_object)

            request.api_service = service
            return service

    def process(self, request):
        if request.method.lower() == 'options':
            return self.cors_setup(request)

        # look for a request to the root of the API, this will generate the
        # help information for the system
        elif not request.traversed:
            try:
                accept = request.accept.header_value
            except StandardError:
                accept = 'text/html'

            if 'application/json' not in accept:
                body = self.__documentation.render(self, request)
                return Response(body=body)
            else:
                return {'application': self.application, 'version': self.version}

        # otherwise, process the request context
        else:
            caller = request.context
            method = request.method.lower()

            # process an endpoint function
            if isinstance(caller, Endpoint):
                method = request.method.lower()
                try:
                    permit = caller.permissions[method]
                    callable = caller.callables[method]
                except KeyError:
                    raise HTTPNotFound()
                else:
                    print permit, request.has_permission(permit)
                    if permit and not request.has_permission(permit):
                        raise HTTPForbidden()
                    else:
                        return callable(request)

            # process an endpoint method
            elif inspect.ismethod(caller) and isinstance(getattr(caller.im_func, 'endpoint', None), Endpoint):
                try:
                    permit = caller.im_func.endpoint.permissions[method]
                except KeyError:
                    raise HTTPNotFound()
                else:
                    if permit and not request.has_permission(permit):
                        raise HTTPForbidden()
                    else:
                        return caller()

            # check if the caller has its own built-in process
            elif hasattr(caller, 'process'):
                return caller.process()

            else:
                raise HTTPNotFound()

    def register(self, service, name=''):
        """
        Exposes a given service to this API.
        """
        # expose a sub-factory
        if isinstance(service, ApiFactory):
            self.services[name] = (service.factory, None)

        # expose a module dynamically as a service
        elif inspect.ismodule(service):
            name = name or service.__name__.split('.')[-1]
            self.services[name] = (ModuleService, service)

        # expose a class dynamically as a service
        elif inspect.isclass(service):
            name = name or service.__name__
            self.services[name] = (ClassService, service)

        # expose an endpoint directly
        elif isinstance(getattr(service, 'endpoint', None), Endpoint):
            self.services[service.endpoint.name] = (service.endpoint, None)

        # expose a service directly
        else:
            raise StandardError('Invalid service provide: {0} ({1}).'.format(service, type(service)))

    @view_config(context=StandardError, accept='application/json', renderer='json2')
    @view_config(context=HTTPException, accept='application/json', renderer='json2')
    def handle_error(self, request):
        err = request.exception

        log.exception(err)

        status = getattr(err, 'status', projex.text.pretty(type(err).__name__))
        code = getattr(err, 'code', 500)

        request.response.status = '{0} {1}'.format(code, status)

        # for 500 errors, only return server error
        if code / 100 == 5:
            return {
                'type': 'server_error',
                'error': 'An unknown server error occurred.'
            }
        else:
            return {
                'type': projex.text.underscore(projex.text.underscore(type(err).__name__)),
                'error': projex.text.nativestring(err)
            }

    def section_groups(self, request):
        intro = self.__documentation.introduction(self, request)

        section_group = SectionGroup(
            name='Introduction',
            sections=[intro]
        )

        section_groups = [section_group]
        sections = defaultdict(list)

        for name, service_info in sorted(self.services.items()):
            for group_name, section in self.collect_documentation(name, service_info):
                sections[group_name].append(section)

        # show topics first
        topics = sections.pop('Topics', [])
        if topics:
            section_groups.append(SectionGroup(
                name='Topics',
                sections=topics
            ))

        # show core resources second
        resources = sections.pop('Core Resources', [])
        if resources:
            section_groups.append(SectionGroup(
                name='Core Resources',
                sections=resources
            ))

        # show all other topics
        for group_name, sections in sorted(sections.items()):
            section_groups.append(SectionGroup(
                name=group_name,
                sections=sections
            ))

        return section_groups

    def serve(self, config, path, route_name=None, **view_options):
        """
        Serves this API from the inputted root path
        """
        route_name = route_name or path.replace('/', '.').strip('.')
        path = path.strip('/') + '*traverse'

        self.route_name = route_name

        # configure the route and the path
        config.add_route(route_name, path, factory=self.factory)
        config.add_view(
            self.process,
            route_name=route_name,
            renderer='json2',
            **view_options
        )
        config.scan()

<commit_msg>remove print, was messing up None permissions<commit_after>import inspect
import logging
import projex.text
import textwrap

from collections import defaultdict
from pyramid.view import view_config
from pyramid.httpexceptions import HTTPNotFound, HTTPForbidden, HTTPException
from pyramid.response import Response

from .documentation import Documentation, SectionGroup, Section
from .services import *

log = logging.getLogger(__name__)

class ApiFactory(dict):
    def __init__(self,
                 version='1.0.0',
                 application='pyramid_restful',
                 documentation_package='pyramid_restful',
                 documentation_folder='templates',
                 documentation_template='documentation.html.jinja'):
        super(ApiFactory, self).__init__()

        # private properties
        self.__documentation = Documentation(documentation_package, documentation_folder, documentation_template)

        # public properties
        self.version = version
        self.application = application
        self.services = {}

    def collect_documentation(self, name, service_info):
        service, service_object = service_info

        try:
            is_module = issubclass(service, ModuleService)
        except StandardError:
            is_module = False

        if is_module:
            module_name = service_object.__name__.split('.')[-1]
            group_name = getattr(service_object, '__group__', 'Topics')
            docs = getattr(service_object, '__doc__', '') or ''

            methods = [(docs, '')]

            for method in vars(service_object).values():
                if isinstance(service, Endpoint):
                    method_doc = '## {0}\n\n'.format(method.__name__)
                    method_doc += textwrap.dedent(getattr(method, '__doc__', '')) or ''
                    methods.append((method_doc, ''))

            section = Section(
                id=module_name,
                name=getattr(service_object, '__title__', module_name),
                methods=methods
            )
            yield group_name, section

    def cors_setup(self, request):
        response = Response()
        if request.is_xhr:
            response.headerlist = []
            response.headerlist.extend(
                (
                    ('Access-Control-Allow-Origin', '*'),
                    ('Content-Type', 'application/json')
                )
            )
        return response

    def factory(self, request, parent=None, name=None):
        """
        Returns a new service for the given request.

        :param      request | <pyramid.request.Request>

        :return     <pyramid_restful.services.AbstractService>
        """
        # show documentation at the root path
        if not request.matchdict['traverse']:
            return {}
        else:
            name = name or request.matchdict['traverse'][0]

            service = {}
            try:
                service_type, service_object = self.services[name]
            except KeyError:
                raise HTTPNotFound()
            else:
                if isinstance(service_type, Endpoint):
                    service[name] = service_type
                elif service_object is None:
                    service[name] = service_type(request)
                else:
                    service[name] = service_type(request, service_object)

            request.api_service = service
            return service

    def process(self, request):
        if request.method.lower() == 'options':
            return self.cors_setup(request)

        # look for a request to the root of the API, this will generate the
        # help information for the system
        elif not request.traversed:
            try:
                accept = request.accept.header_value
            except StandardError:
                accept = 'text/html'

            if 'application/json' not in accept:
                body = self.__documentation.render(self, request)
                return Response(body=body)
            else:
                return {'application': self.application, 'version': self.version}

        # otherwise, process the request context
        else:
            caller = request.context
            method = request.method.lower()

            # process an endpoint function
            if isinstance(caller, Endpoint):
                method = request.method.lower()
                try:
                    permit = caller.permissions[method]
                    callable = caller.callables[method]
                except KeyError:
                    raise HTTPNotFound()
                else:
                    if permit and not request.has_permission(permit):
                        raise HTTPForbidden()
                    else:
                        return callable(request)

            # process an endpoint method
            elif inspect.ismethod(caller) and isinstance(getattr(caller.im_func, 'endpoint', None), Endpoint):
                try:
                    permit = caller.im_func.endpoint.permissions[method]
                except KeyError:
                    raise HTTPNotFound()
                else:
                    if permit and not request.has_permission(permit):
                        raise HTTPForbidden()
                    else:
                        return caller()

            # check if the caller has its own built-in process
            elif hasattr(caller, 'process'):
                return caller.process()

            else:
                raise HTTPNotFound()

    def register(self, service, name=''):
        """
        Exposes a given service to this API.
        """
        # expose a sub-factory
        if isinstance(service, ApiFactory):
            self.services[name] = (service.factory, None)

        # expose a module dynamically as a service
        elif inspect.ismodule(service):
            name = name or service.__name__.split('.')[-1]
            self.services[name] = (ModuleService, service)

        # expose a class dynamically as a service
        elif inspect.isclass(service):
            name = name or service.__name__
            self.services[name] = (ClassService, service)

        # expose an endpoint directly
        elif isinstance(getattr(service, 'endpoint', None), Endpoint):
            self.services[service.endpoint.name] = (service.endpoint, None)

        # expose a service directly
        else:
            raise StandardError('Invalid service provide: {0} ({1}).'.format(service, type(service)))

    @view_config(context=StandardError, accept='application/json', renderer='json2')
    @view_config(context=HTTPException, accept='application/json', renderer='json2')
    def handle_error(self, request):
        err = request.exception

        log.exception(err)

        status = getattr(err, 'status', projex.text.pretty(type(err).__name__))
        code = getattr(err, 'code', 500)

        request.response.status = '{0} {1}'.format(code, status)

        # for 500 errors, only return server error
        if code / 100 == 5:
            return {
                'type': 'server_error',
                'error': 'An unknown server error occurred.'
            }
        else:
            return {
                'type': projex.text.underscore(projex.text.underscore(type(err).__name__)),
                'error': projex.text.nativestring(err)
            }

    def section_groups(self, request):
        intro = self.__documentation.introduction(self, request)

        section_group = SectionGroup(
            name='Introduction',
            sections=[intro]
        )

        section_groups = [section_group]
        sections = defaultdict(list)

        for name, service_info in sorted(self.services.items()):
            for group_name, section in self.collect_documentation(name, service_info):
                sections[group_name].append(section)

        # show topics first
        topics = sections.pop('Topics', [])
        if topics:
            section_groups.append(SectionGroup(
                name='Topics',
                sections=topics
            ))

        # show core resources second
        resources = sections.pop('Core Resources', [])
        if resources:
            section_groups.append(SectionGroup(
                name='Core Resources',
                sections=resources
            ))

        # show all other topics
        for group_name, sections in sorted(sections.items()):
            section_groups.append(SectionGroup(
                name=group_name,
                sections=sections
            ))

        return section_groups

    def serve(self, config, path, route_name=None, **view_options):
        """
        Serves this API from the inputted root path
        """
        route_name = route_name or path.replace('/', '.').strip('.')
        path = path.strip('/') + '*traverse'

        self.route_name = route_name

        # configure the route and the path
        config.add_route(route_name, path, factory=self.factory)
        config.add_view(
            self.process,
            route_name=route_name,
            renderer='json2',
            **view_options
        )
        config.scan()

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
import os
import sys
import json
import warnings

# Python 3 comparability
if sys.version_info >= (3, 0):
    import configparser as ConfigParser
else:
    import ConfigParser

__LOCAL__ = ['sensor', 'projection', 'orbit', 'polarizations', 'acquisition_mode',
             'start', 'stop', 'product', 'spacing', 'samples', 'lines']

# a pattern to search for pyroSAR processing products and extract metadata attributes from the file name
product_pattern = r'(?:.*[/\\]|)' \
                  r'(?P<outname_base>' \
                  r'(?P<sensor>[A-Z0-9]{1,4})_+' \
                  r'(?P<acquisition_mode>[A-Z0-9]{1,4})_+' \
                  r'(?P<orbit>[AD])_' \
                  r'(?P<start>[0-9T]{15})' \
                  r'(?:_(?P<extensions>\w*)_|)' \
                  r')_' \
                  r'(?P<polarization>[HV]{2})_' \
                  r'(?P<proc_steps>\w*).tif$'

class Storage(dict):
    """
    Dict class with point access to store the lookups, pattern and URLs

    Attributes
    ----------
    STORAGE.LOOKUP : Storage
        All lookup table merged in a Storage class instance:
            * snap : SNAP process.
            * attributes : Attributes for different sensors.
    STORAGE.URL : dict (with point access)
        All URLs for DEMs, orbit files etc.:
            * dem : URL to download specific DEMs:
                * strm3
                * ace2
                * strm3_FTP
                * strm1HGT
                * ace
            * orbit : URL to download the orbit files:
                * ers1
                * ers2
                * s1_poe
                * s1_pres
                * doris
            * auxcal : URL to download the auxcal data:
                * s1
                * envisat
                * ers

    Note
    ----
    There may be additional attributes not listed above depending of the
    specific solver. Since this class is essentially a subclass of dict
    with attribute accessors, one can see which attributes are available
    using the `keys()` method.
    """

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __repr__(self):
        if self.keys():
            m = max(map(len, list(self.keys()))) + 1
            return '\n'.join([k.rjust(m) + ': ' + repr(v)
                              for k, v in sorted(self.items())])
        else:
            return self.__class__.__name__ + '()'

    def __dir__(self):
        return list(self.keys())

# ==============================================================================
# LOOKUP
# ==============================================================================
snap_suffix = {'Apply-Orbit-File': 'Orb',
               'Calibration': 'Cal',
               'Cross-Correlation': '',
               'LinearToFromdB': 'dB',
               'Multilook': 'ML',
               'Read': '',
               'Remove-GRD-Border-Noise': 'bnr',
               'SAR-Simulation': 'Sim',
               'SARSim-Terrain-Correction': 'TC',
               'Subset': '',
               'Terrain-Correction': 'TC',
               'Terrain-Flattening': 'TF',
               'ThermalNoiseRemoval': 'tnr',
               'Write': '',
               'Write (2)': ''}

snap = Storage(suffix=snap_suffix)

LOOKUP = Storage(snap=snap,

                 attributes={'sensor': 'TEXT',
                             'orbit': 'TEXT',
                             'acquisition_mode': 'TEXT',
                             'start': 'TEXT',
                             'stop': 'TEXT',
                             'product': 'TEXT',
                             'samples': 'INTEGER',
                             'lines': 'INTEGER',
                             'outname_base': 'TEXT PRIMARY KEY',
                             'scene': 'TEXT',
                             'hh': 'INTEGER',
                             'vv': 'INTEGER',
                             'hv': 'INTEGER',
                             'vh': 'INTEGER'})

# ==============================================================================
# URL
# ==============================================================================
dem = Storage(ace2='https://step.esa.int/auxdata/dem/ACE2/5M/',
              ace='https://step.esa.int/auxdata/dem/ACE30/',
              srtm3_FTP='xftp.jrc.it',
              srtm3='http://srtm.csi.cgiar.org/SRT-ZIP/SRTM_V41/SRTM_Data_GeoTiff/',
              srtm1Hgt='https://step.esa.int/auxdata/dem/SRTMGL1/', )

orbit = Storage(doris='https://step.esa.int/auxdata/orbits/Doris/vor',
                ers1='https://step.esa.int/auxdata/orbits/ers_precise_orb/ERS1',
                ers2='https://step.esa.int/auxdata/orbits/ers_precise_orb/ERS2',
                s1_poe='https://step.esa.int/auxdata/orbits/Sentinel-1/POEORB/',
                s1_res='https://step.esa.int/auxdata/orbits/Sentinel-1/RESORB/')

auxcal = Storage(s1='https://step.esa.int/auxdata/auxcal/S1/',
                 envisat='https://step.esa.int/auxdata/auxcal/ENVISAT/',
                 ers='https://step.esa.int/auxdata/auxcal/ERS/')

URL = Storage(dem=dem,
              orbit=orbit,
              auxcal=auxcal)

# ==============================================================================
# Merge
# ==============================================================================
STORAGE = Storage(URL=URL,
                  LOOKUP=LOOKUP)

class ConfigHandler(object):
    """
    ConfigHandler is a configuration handler for pyroSAR. It is intended to be called by a class's '__init__' and
    set or get the configuration parameters throughout an entire package.
    The primary goal with ConfigHandler is to load a single, consistent configuration environment to be passed
    amongst ALL objects within a package.

    ConfigHandler is a SINGLETON, meaning once instantiated, THE SAME OBJECT
    will be returned to every class object calling it.

    Parameters
    ----------
    path : str or None
        A path where the .pyrosar directory will be created. If None (default) it will be created in the user home
        directory.
    config_fname : str
        Name of the config file. Default is 'config.ini'.

    Methods
    -------
    make_dir : Create a .pyrosar directory in home directory.
    create_config : Create a config.ini file in .pyrosar directory.
    open : Open the config.ini file.
    add_section : Create a new section in the configuration.
    set : Set an option in the configuration.
    remove_option : Remove an option in the configuration.

    Notes
    -----
    The syntax is the same as in ConfigParser. Here, keys are called options.

    """

    # Define __setter to control changeable keys (optional)
    # __setter = ["etc", "auxdata"]

    def __init__(self, path=None, config_fname='config.ini'):

        path = os.path.expanduser('~') if path is None else os.path.realpath(path)
        path = os.path.join(path, '.pyrosar')

        self.__GLOBAL = {
            'path': path,
            'config_fname': config_fname,
            'config': os.path.join(path, config_fname),
        }

        if not os.path.isfile(self.__GLOBAL['config']):
            self.__create_config()

        self.parser = ConfigParser.RawConfigParser(allow_no_value=True)
        self.parser.optionxform = str
        self.parser.read(self.__GLOBAL['config'])

    def __create_config(self):
        """
        Create a config.ini file in .pyrosar directory.

        Returns
        -------
        None
        """

        if not os.path.exists(self.__GLOBAL['path']):
            os.makedirs(self.__GLOBAL['path'])

        with open(self.__GLOBAL['config'], 'w'):
            pass

    def __str__(self):
        items = []
        for section in self.parser.sections():
            items.append('  Section: {0}\n'.format(section))

            for options in self.parser.options(section):
                items.append('    x {0} :: {1} :: {2}\n'
                             .format(options,
                                     self.parser.get(section, options),
                                     str(type(options))))
        out = 'Class    : Config\n' \
              'Path     : {0}\n' \
              'Sections : {1}\n' \
              'Contents : \n{2}' \
            .format(self.__GLOBAL['config'],
                    len(self.parser.sections()),
                    ''.join(items))

        return out

    def __getitem__(self, section):
        if not self.parser.has_section(section):
            raise AttributeError('Section {0} does not exist.'.format(str(section)))
        return dict(self.parser.items(section))

    @property
    def sections(self):
        return self.parser.sections()

    def keys(self, section):
        """
        Get all keys (options) of a section.

        Parameters
        ----------
        section : str
            Section name.

        Returns
        -------
        list : options (keys) of a section.

        """
        return self.parser.options(section)

    def open(self):
        """
        Open the config.ini file. This method will open the config.ini file in a external standard app (text editor).

        Returns
        -------
        os.startfile

        """

        os.startfile(self.__GLOBAL['config'])

    def add_section(self, section='SNAP'):
        """
        Create a new section in the configuration.

        Parameters
        ----------
        section : str
            Section name

        Returns
        -------
        None

        """
        if not self.parser.has_section(section):
            self.parser.add_section(section)
            self.write()

    def set(self, section, key, value, overwrite=False):
        """
        Set an option.

        Parameters
        ----------
        section : str
            Section name.
        key : str
            the attribute name
        value :
            the attribute value
        overwrite : bool
            If True and the defined key exists the value will be overwritten.

        Returns
        -------

        """
        if not self.parser.has_section(section):
            raise AttributeError('Section {0} does not exist.'.format(str(section)))

        if isinstance(value, list):
            value = json.dumps(value)

        if key in self.parser.options(section) and not overwrite:
            raise RuntimeError('Value already exists.')

        self.parser.set(section, key, value)
        self.write()

    def remove_option(self, section, key):
        """
        Remove an option and key.

        Parameters
        ----------
        section : str
            Section name.
        key : str
            Key value.

        Returns
        -------

        """
        if not self.parser.has_section(section):
            raise AttributeError('Section {0} does not exist.'.format(str(section)))

        if key not in self.parser.options(section):
            raise AttributeError('Key {0} does not exist.'.format(str(key)))

        self.parser.remove_option(section, key)
        self.write()

    def write(self):
        if sys.version_info >= (3, 0):
            with open(self.__GLOBAL['config'], 'w', encoding='utf8') as out:
                self.parser.write(out)
        else:
            with open(self.__GLOBAL['config'], 'w') as out:
                self.parser.write(out)
<commit_msg>[_dev_config.ConfigHandler.file] new property method<commit_after># -*- coding: utf-8 -*-
import os
import sys
import json
import warnings

# Python 3 comparability
if sys.version_info >= (3, 0):
    import configparser as ConfigParser
else:
    import ConfigParser

__LOCAL__ = ['sensor', 'projection', 'orbit', 'polarizations', 'acquisition_mode',
             'start', 'stop', 'product', 'spacing', 'samples', 'lines']

# a pattern to search for pyroSAR processing products and extract metadata attributes from the file name
product_pattern = r'(?:.*[/\\]|)' \
                  r'(?P<outname_base>' \
                  r'(?P<sensor>[A-Z0-9]{1,4})_+' \
                  r'(?P<acquisition_mode>[A-Z0-9]{1,4})_+' \
                  r'(?P<orbit>[AD])_' \
                  r'(?P<start>[0-9T]{15})' \
                  r'(?:_(?P<extensions>\w*)_|)' \
                  r')_' \
                  r'(?P<polarization>[HV]{2})_' \
                  r'(?P<proc_steps>\w*).tif$'

class Storage(dict):
    """
    Dict class with point access to store the lookups, pattern and URLs

    Attributes
    ----------
    STORAGE.LOOKUP : Storage
        All lookup table merged in a Storage class instance:
            * snap : SNAP process.
            * attributes : Attributes for different sensors.
    STORAGE.URL : dict (with point access)
        All URLs for DEMs, orbit files etc.:
            * dem : URL to download specific DEMs:
                * strm3
                * ace2
                * strm3_FTP
                * strm1HGT
                * ace
            * orbit : URL to download the orbit files:
                * ers1
                * ers2
                * s1_poe
                * s1_pres
                * doris
            * auxcal : URL to download the auxcal data:
                * s1
                * envisat
                * ers

    Note
    ----
    There may be additional attributes not listed above depending of the
    specific solver. Since this class is essentially a subclass of dict
    with attribute accessors, one can see which attributes are available
    using the `keys()` method.
    """

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __repr__(self):
        if self.keys():
            m = max(map(len, list(self.keys()))) + 1
            return '\n'.join([k.rjust(m) + ': ' + repr(v)
                              for k, v in sorted(self.items())])
        else:
            return self.__class__.__name__ + '()'

    def __dir__(self):
        return list(self.keys())

# ==============================================================================
# LOOKUP
# ==============================================================================
snap_suffix = {'Apply-Orbit-File': 'Orb',
               'Calibration': 'Cal',
               'Cross-Correlation': '',
               'LinearToFromdB': 'dB',
               'Multilook': 'ML',
               'Read': '',
               'Remove-GRD-Border-Noise': 'bnr',
               'SAR-Simulation': 'Sim',
               'SARSim-Terrain-Correction': 'TC',
               'Subset': '',
               'Terrain-Correction': 'TC',
               'Terrain-Flattening': 'TF',
               'ThermalNoiseRemoval': 'tnr',
               'Write': '',
               'Write (2)': ''}

snap = Storage(suffix=snap_suffix)

LOOKUP = Storage(snap=snap,

                 attributes={'sensor': 'TEXT',
                             'orbit': 'TEXT',
                             'acquisition_mode': 'TEXT',
                             'start': 'TEXT',
                             'stop': 'TEXT',
                             'product': 'TEXT',
                             'samples': 'INTEGER',
                             'lines': 'INTEGER',
                             'outname_base': 'TEXT PRIMARY KEY',
                             'scene': 'TEXT',
                             'hh': 'INTEGER',
                             'vv': 'INTEGER',
                             'hv': 'INTEGER',
                             'vh': 'INTEGER'})

# ==============================================================================
# URL
# ==============================================================================
dem = Storage(ace2='https://step.esa.int/auxdata/dem/ACE2/5M/',
              ace='https://step.esa.int/auxdata/dem/ACE30/',
              srtm3_FTP='xftp.jrc.it',
              srtm3='http://srtm.csi.cgiar.org/SRT-ZIP/SRTM_V41/SRTM_Data_GeoTiff/',
              srtm1Hgt='https://step.esa.int/auxdata/dem/SRTMGL1/', )

orbit = Storage(doris='https://step.esa.int/auxdata/orbits/Doris/vor',
                ers1='https://step.esa.int/auxdata/orbits/ers_precise_orb/ERS1',
                ers2='https://step.esa.int/auxdata/orbits/ers_precise_orb/ERS2',
                s1_poe='https://step.esa.int/auxdata/orbits/Sentinel-1/POEORB/',
                s1_res='https://step.esa.int/auxdata/orbits/Sentinel-1/RESORB/')

auxcal = Storage(s1='https://step.esa.int/auxdata/auxcal/S1/',
                 envisat='https://step.esa.int/auxdata/auxcal/ENVISAT/',
                 ers='https://step.esa.int/auxdata/auxcal/ERS/')

URL = Storage(dem=dem,
              orbit=orbit,
              auxcal=auxcal)

# ==============================================================================
# Merge
# ==============================================================================
STORAGE = Storage(URL=URL,
                  LOOKUP=LOOKUP)

class ConfigHandler(object):
    """
    ConfigHandler is a configuration handler for pyroSAR. It is intended to be called by a class's '__init__' and
    set or get the configuration parameters throughout an entire package.
    The primary goal with ConfigHandler is to load a single, consistent configuration environment to be passed
    amongst ALL objects within a package.

    ConfigHandler is a SINGLETON, meaning once instantiated, THE SAME OBJECT
    will be returned to every class object calling it.

    Parameters
    ----------
    path : str or None
        A path where the .pyrosar directory will be created. If None (default) it will be created in the user home
        directory.
    config_fname : str
        Name of the config file. Default is 'config.ini'.

    Methods
    -------
    make_dir : Create a .pyrosar directory in home directory.
    create_config : Create a config.ini file in .pyrosar directory.
    open : Open the config.ini file.
    add_section : Create a new section in the configuration.
    set : Set an option in the configuration.
    remove_option : Remove an option in the configuration.

    Notes
    -----
    The syntax is the same as in ConfigParser. Here, keys are called options.

    """

    # Define __setter to control changeable keys (optional)
    # __setter = ["etc", "auxdata"]

    def __init__(self, path=None, config_fname='config.ini'):

        path = os.path.expanduser('~') if path is None else os.path.realpath(path)
        path = os.path.join(path, '.pyrosar')

        self.__GLOBAL = {
            'path': path,
            'config_fname': config_fname,
            'config': os.path.join(path, config_fname),
        }

        if not os.path.isfile(self.__GLOBAL['config']):
            self.__create_config()

        self.parser = ConfigParser.RawConfigParser(allow_no_value=True)
        self.parser.optionxform = str
        self.parser.read(self.__GLOBAL['config'])

    def __create_config(self):
        """
        Create a config.ini file in .pyrosar directory.

        Returns
        -------
        None
        """

        if not os.path.exists(self.__GLOBAL['path']):
            os.makedirs(self.__GLOBAL['path'])

        with open(self.__GLOBAL['config'], 'w'):
            pass

    def __str__(self):
        items = []
        for section in self.parser.sections():
            items.append('  Section: {0}\n'.format(section))

            for options in self.parser.options(section):
                items.append('    x {0} :: {1} :: {2}\n'
                             .format(options,
                                     self.parser.get(section, options),
                                     str(type(options))))
        out = 'Class    : Config\n' \
              'Path     : {0}\n' \
              'Sections : {1}\n' \
              'Contents : \n{2}' \
            .format(self.__GLOBAL['config'],
                    len(self.parser.sections()),
                    ''.join(items))

        return out

    def __getitem__(self, section):
        if not self.parser.has_section(section):
            raise AttributeError('Section {0} does not exist.'.format(str(section)))
        return dict(self.parser.items(section))

    @property
    def sections(self):
        return self.parser.sections()

    def keys(self, section):
        """
        Get all keys (options) of a section.

        Parameters
        ----------
        section : str
            Section name.

        Returns
        -------
        list : options (keys) of a section.

        """
        return self.parser.options(section)

    def open(self):
        """
        Open the config.ini file. This method will open the config.ini file in a external standard app (text editor).

        Returns
        -------
        os.startfile

        """

        os.startfile(self.__GLOBAL['config'])

    def add_section(self, section='SNAP'):
        """
        Create a new section in the configuration.

        Parameters
        ----------
        section : str
            Section name

        Returns
        -------
        None

        """
        if not self.parser.has_section(section):
            self.parser.add_section(section)
            self.write()

    @property
    def file(self):
        return self.__GLOBAL['config']

    def set(self, section, key, value, overwrite=False):
        """
        Set an option.

        Parameters
        ----------
        section : str
            Section name.
        key : str
            the attribute name
        value :
            the attribute value
        overwrite : bool
            If True and the defined key exists the value will be overwritten.

        Returns
        -------

        """
        if not self.parser.has_section(section):
            raise AttributeError('Section {0} does not exist.'.format(str(section)))

        if isinstance(value, list):
            value = json.dumps(value)

        if key in self.parser.options(section) and not overwrite:
            raise RuntimeError('Value already exists.')

        self.parser.set(section, key, value)
        self.write()

    def remove_option(self, section, key):
        """
        Remove an option and key.

        Parameters
        ----------
        section : str
            Section name.
        key : str
            Key value.

        Returns
        -------

        """
        if not self.parser.has_section(section):
            raise AttributeError('Section {0} does not exist.'.format(str(section)))

        if key not in self.parser.options(section):
            raise AttributeError('Key {0} does not exist.'.format(str(key)))

        self.parser.remove_option(section, key)
        self.write()

    def write(self):
        if sys.version_info >= (3, 0):
            with open(self.__GLOBAL['config'], 'w', encoding='utf8') as out:
                self.parser.write(out)
        else:
            with open(self.__GLOBAL['config'], 'w') as out:
                self.parser.write(out)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
import sys

# Python 3 comparability
if sys.version_info >= (3, 0):
    import configparser as ConfigParser
else:
    import ConfigParser

import os
import warnings

from os.path import expanduser

__LOCAL__ = ['sensor', 'projection', 'orbit', 'polarizations', 'acquisition_mode',
             'start', 'stop', 'product', 'spacing', 'samples', 'lines']

# a pattern to search for pyroSAR processing products
product_pattern = r'(?:.*[/\\]|)' \
                  r'(?P<sensor>[A-Z0-9]{1,4})_+' \
                  r'(?P<acquisition_mode>[A-Z0-9]{1,4})_+' \
                  r'(?P<orbit>[AZ])_' \
                  r'(?P<start>[0-9T]{15})_' \
                  r'(?P<extensions>[A-Z0-9_]*_|)' \
                  r'(?P<polarization>[HV]{2})_' \
                  r'(?P<proc_steps>[a-zA-Z0-9_]*).tif$'

class Storage(dict):
    """
    Dict class with point access to store the lookups, pattern and URLs

    Attributes
    ----------
    STORAGE.LOOKUP : Storage
        All lookup table merged in a Storage class instance:
            * snap : SNAP process.
            * attributes : Attributes for different sensors.
    STORAGE.URL : dict (with point access)
        All URLs for DEMs, orbit files etc.:
            * dem : URL to download specific DEMs:
                * strm3
                * ace2
                * strm3_FTP
                * strm1HGT
                * ace
            * orbit : URL to download the orbit files:
                * ers1
                * ers2
                * s1_poe
                * s1_pres
                * doris
            * auxcal : URL to download the auxcal data:
                * s1
                * envisat
                * ers

    Note
    ----
    There may be additional attributes not listed above depending of the
    specific solver. Since this class is essentially a subclass of dict
    with attribute accessors, one can see which attributes are available
    using the `keys()` method.
    """

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __repr__(self):
        if self.keys():
            m = max(map(len, list(self.keys()))) + 1
            return '\n'.join([k.rjust(m) + ': ' + repr(v)
                              for k, v in sorted(self.items())])
        else:
            return self.__class__.__name__ + "()"

    def __dir__(self):
        return list(self.keys())

# ==============================================================================
# LOOKUP
# ==============================================================================
snap_suffix = {'Apply-Orbit-File': 'Orb',
               'Calibration': 'Cal',
               'Cross-Correlation': '',
               'LinearToFromdB': 'dB',
               'Remove-GRD-Border-Noise': 'bnr',
               'SAR-Simulation': 'Sim',
               'SARSim-Terrain-Correction': 'TC',
               'Subset': '',
               'Terrain-Correction': 'TC',
               'Terrain-Flattening': 'TF',
               'Read': '',
               'Write': ''}

snap = Storage(suffix=snap_suffix)

LOOKUP = Storage(snap=snap,

                 attributes={'sensor': 'TEXT',
                             'orbit': 'TEXT',
                             'acquisition_mode': 'TEXT',
                             'start': 'TEXT',
                             'stop': 'TEXT',
                             'product': 'TEXT',
                             'samples': 'INTEGER',
                             'lines': 'INTEGER',
                             'outname_base': 'TEXT PRIMARY KEY',
                             'scene': 'TEXT',
                             'hh': 'INTEGER',
                             'vv': 'INTEGER',
                             'hv': 'INTEGER',
                             'vh': 'INTEGER'})

# ==============================================================================
# URL
# ==============================================================================
dem = Storage(ace2='http://step.esa.int/auxdata/dem/ACE2/5M/',
              ace='http://step.esa.int/auxdata/dem/ACE30/',
              srtm3_FTP='xftp.jrc.it',
              srtm3='http://srtm.csi.cgiar.org/SRT-ZIP/SRTM_V41/SRTM_Data_GeoTiff/',
              srtm1Hgt='http://step.esa.int/auxdata/dem/SRTMGL1/', )

orbit = Storage(doris='http://step.esa.int/auxdata/orbits/Doris/vor',
                ers1='http://step.esa.int/auxdata/orbits/ers_precise_orb/ERS1',
                ers2='http://step.esa.int/auxdata/orbits/ers_precise_orb/ERS2',
                s1_poe='http://step.esa.int/auxdata/orbits/Sentinel-1/POEORB/',
                s1_res='http://step.esa.int/auxdata/orbits/Sentinel-1/RESORB/')

auxcal = Storage(s1='http://step.esa.int/auxdata/auxcal/S1/',
                 envisat='http://step.esa.int/auxdata/auxcal/ENVISAT/',
                 ers='http://step.esa.int/auxdata/auxcal/ERS/')

URL = Storage(dem=dem,
              orbit=orbit,
              auxcal=auxcal)

# ==============================================================================
# Merge
# ==============================================================================
STORAGE = Storage(URL=URL,
                  LOOKUP=LOOKUP)

class ConfigHandler(object):
    """
    ConfigHandler is a configuration handler for pyroSAR. It is intended to be called by a class's "__init__" and
    set or get the configuration parameters throughout an entire package.
    The primary goal with ConfigHandler is to load a single, consistent configuration environment to be passed
    amongst ALL objects within a package.

    ConfigHandler is a SINGLETON, meaning once instantiated, THE SAME OBJECT
    will be returned to every class object calling it.

    Parameters
    ----------
    path : str or None
        A path where the .pyrosar directory will be created. If None (default) it will be created in the user home
        directory.
    config_fname : str
        Name of the config file. Default is 'config.ini'.

    Methods
    -------
    make_dir : Create a .pyrosar directory in home directory.
    create_config : Create a config.ini file in .pyrosar directory.
    open : Open the config.ini file.
    add_section : Create a new section in the configuration.
    set : Set an option in the configuration.
    remove_option : Remove an option in the configuration.

    Notes
    -----
    The syntax is the same as in ConfigParser. Here, keys are called options.

    """

    # ---- Define Global Variables ----

    __KEYS = ['auxdata',
              'auxdatapath',
              'demPath',
              'etc',
              'gpt',
              'path',
              'properties',
              'DEM.AsterDEMDataPath',
              'LandCover.globcoverDataPath',
              'DEM.ace2_5MinDEMDataPath',
              'OrbitFiles.delftFTP_ERS2_precise_remotePath',
              'DEM.gtopo30DEMDataPath',
              'DEM.srtm3GeoTiffDEM_HTTP',
              'DEM.aceDEMDataPath',
              'DEM.srtm3GeoTiffDEM_remotePath',
              'AuxCal.ENVISAT.remotePath',
              'DEM.ace2_5MinDEM_HTTP',
              'DEM.aceDEM_HTTP',
              'OrbitFiles.prareHTTP_ERS1_remotePath',
              'OrbitFiles.delftERS1OrbitPath',
              'OrbitFiles.delftFTP_ERS1_precise_remotePath',
              'OrbitFiles.sentinel1POEOrbit_remotePath',
              'AuxCal.ERS.remotePath',
              'DEM.CDEM_HTTP',
              'OrbitFiles.sentinel1RESOrbitPath',
              'OrbitFiles.dorisHTTP_vor_remotePath',
              'OrbitFiles.dorisVOROrbitPath',
              'OrbitFiles.sentinel1POEOrbitPath',
              'AuxCal.Sentinel1.remotePath',
              'OrbitFiles.delftFTP',
              'OrbitFiles.delftEnvisatOrbitPath',
              'OrbitFiles.delftERS2OrbitPath',
              'DEM.srtm1GridDEMDataPath',
              'DEM.srtm3GeoTiffDEMDataPath',
              'OrbitFiles.delftFTP_ENVISAT_precise_remotePath',
              'OrbitFiles.sentinel1RESOrbit_remotePath',
              'DEM.srtm3GeoTiffDEM_FTP',
              'LandCover.glc2000DataPath',
              'OrbitFiles.prareHTTP_ERS2_remotePath',
              'DEM.srtm1HgtDEM_HTTP',
              'OrbitFiles.prareERS1OrbitPath',
              'landCoverPath',
              'executable',
              'OrbitFiles.dorisPOROrbitPath',
              'OrbitFiles.prareERS2OrbitPath',
              'DEM.Getasse30DEMDataPath']

    __SECTIONS = {
        "snap": "SNAP",
        "outputpaths": "OUTPUT",
        "url": "URL"
    }

    ___AUXDATANAMES = {
        "demPath": "${AuxDataPath}/dem",
        "landCoverPath": "${AuxDataPath}/LandCover",
    }

    # Define __setter to control changeable keys (optional)
    # __setter = ["etc", "auxdata"]

    def __init__(self, path=None, config_fname='config.ini'):

        path = os.path.join(expanduser("~"), '.pyrosar') if path is None else os.path.join(path, '.pyrosar')

        self.__GLOBAL = {
            "path": path,
            "config_fname": config_fname,
            "config": os.path.join(os.path.join(path, config_fname)),
        }

        if os.path.isfile(self.__GLOBAL['config']):
            self.parser = ConfigParser.RawConfigParser(allow_no_value=True)
            self.parser.read(self.__GLOBAL['config'])

        else:
            self.create_config()
            self.parser = ConfigParser.RawConfigParser()
            self.parser.optionxform = str
            self.parser.read(self.__GLOBAL['config'])

    def make_dir(self):
        """
        Create a .pyrosar directory in home directory.

        Returns
        -------
        None
        """

        if not os.path.exists(self.__GLOBAL['path']):
            os.makedirs(self.__GLOBAL['path'])

        else:
            pass

    def create_config(self):
        """
        Create a config.ini file in .pyrosar directory.

        Returns
        -------
        None
        """

        self.make_dir()

        if not os.path.isfile(self.__GLOBAL['config']):
            with open(self.__GLOBAL['config'], 'w'):
                pass
        else:
            pass

    def __str__(self):

        string_literal = 'Class    : Config\n' \
                         'Path     : {0}\n' \
                         'Sections : {1}\n'.format(self.__GLOBAL['config'], len(self.parser.sections()))

        print(string_literal)
        print("Contents:")

        for section in self.parser.sections():
            print("\n Section: {0}".format(section))

            for options in self.parser.options(section):
                print("\t x {0} :: {1} :: {2}".format(options, self.parser.get(section, options), str(type(options))))

        return ''

    def __getattr__(self, section):
        try:
            return dict(self.parser.items(section))
        except ConfigParser.NoSectionError:
            raise AttributeError

    @property
    def sections(self):
        return self.parser.sections()

    def keys(self, section):
        """
        Get all keys (options) of a section.

        Parameters
        ----------
        section : str
            Section name.

        Returns
        -------
        list : options (keys) of a section.

        """
        return self.parser.options(section)

    def open(self):
        """
        Open the config.ini file. This method will open the config.ini file in a external standard app (text editor).

        Returns
        -------
        os.startfile

        """

        os.startfile(self.__GLOBAL['config'])

    def add_section(self, section='SNAP'):
        """
        Create a new section in the configuration.

        Parameters
        ----------
        section : str
            Section name

        Returns
        -------
        None

        """
        if self.parser.has_section(section):
            pass

        elif section not in ConfigHandler.__SECTIONS.values():
            raise AttributeError(
                "Only the following sections are allowed: {0}.".format(str(ConfigHandler.__SECTIONS.values())))

        else:
            self.parser.add_section(section)
            if sys.version_info >= (3, 0):
                with open(self.__GLOBAL['config'], 'w', encoding='utf8') as item:
                    self.parser.write(item)
            else:
                with open(self.__GLOBAL['config'], 'w') as item:
                    self.parser.write(item)

            # with open(self.__GLOBAL['config'], 'wb') as item:
            #     self.parser.write(item)

    def set(self, section, key, value, overwrite=False):
        """
        Set an option.

        Parameters
        ----------
        section : str
            Section name.
        key : str
            Key value.
        value :
            Value of the key.
        overwrite : bool
            If True and the defined key exists the value will be overwritten.

        Returns
        -------
        None
        """
        if not self.parser.has_section(section):
            raise AttributeError("Section {0} does not exist.".format(str(section)))

        elif key not in ConfigHandler.__KEYS:
            raise AttributeError(
                "Your key is {0}. Only the following keys are allowed: {1}.".format(str(key),
                                                                                str(ConfigHandler.__KEYS)))

        else:
            if key in self.parser.options(section):

                if overwrite:
                    self.parser.set(section, key, value)
                    if sys.version_info >= (3, 0):
                        with open(self.__GLOBAL['config'], 'w', encoding='utf8') as item:
                            self.parser.write(item)
                    else:
                        with open(self.__GLOBAL['config'], 'w') as item:
                            self.parser.write(item)

                else:
                    pass
                    # warnings.warn("Value already exists.")

            else:
                self.parser.set(section, key, value)
                if sys.version_info >= (3, 0):
                    with open(self.__GLOBAL['config'], 'w', encoding='utf8') as item:
                        self.parser.write(item)
                else:
                    with open(self.__GLOBAL['config'], 'w') as item:
                        self.parser.write(item)

    def remove_option(self, section, key):
        """
        Remove an option and key.

        Parameters
        ----------
        section : str
            Section name.
        key : str
            Key value.

        Returns
        -------
        None
        """
        if not self.parser.has_section(section):
            raise AttributeError("Section {0} does not exist.".format(str(section)))

        elif key not in self.parser.options(section):
            raise AttributeError("Key {0} does not exist.".format(str(key)))

        else:
            self.parser.remove_option(section, key)

            # write changes back to the config file
            if sys.version_info >= (3, 0):
                with open(self.__GLOBAL['config'], 'w', encoding='utf8') as item:
                    self.parser.write(item)
            else:
                with open(self.__GLOBAL['config'], 'w') as item:
                    self.parser.write(item)

    def get(self, section, key=None):
        """
        Get all options (keys) and values of a section.

        Parameters
        ----------
        section : str
            Section name.

        key : str or None
            If not None it returns the specific value of the desired key. Else (default) it will return all
            keys and values.

        Returns
        -------
        dict : keys and values of a section.

        """
        if not self.parser.has_section(section):
            raise AttributeError("Section {0} does not exist.".format(str(section)))
        else:
            if key is None:
                return dict(self.parser.items(section))

            else:
                items = dict(self.parser.items(section))
                return items[key]
<commit_msg>[_dev_config] improved parameter product_pattern<commit_after># -*- coding: utf-8 -*-
import sys

# Python 3 comparability
if sys.version_info >= (3, 0):
    import configparser as ConfigParser
else:
    import ConfigParser

import os
import warnings

from os.path import expanduser

__LOCAL__ = ['sensor', 'projection', 'orbit', 'polarizations', 'acquisition_mode',
             'start', 'stop', 'product', 'spacing', 'samples', 'lines']

# a pattern to search for pyroSAR processing products and extract metadata attributes from the file name
product_pattern = r'(?:.*[/\\]|)' \
                  r'(?P<sensor>[A-Z0-9]{1,4})_+' \
                  r'(?P<acquisition_mode>[A-Z0-9]{1,4})_+' \
                  r'(?P<orbit>[AD])_' \
                  r'(?P<start>[0-9T]{15})_' \
                  r'(?P<extensions>\w*_|)' \
                  r'(?P<polarization>[HV]{2})_' \
                  r'(?P<proc_steps>\w*).tif$'

class Storage(dict):
    """
    Dict class with point access to store the lookups, pattern and URLs

    Attributes
    ----------
    STORAGE.LOOKUP : Storage
        All lookup table merged in a Storage class instance:
            * snap : SNAP process.
            * attributes : Attributes for different sensors.
    STORAGE.URL : dict (with point access)
        All URLs for DEMs, orbit files etc.:
            * dem : URL to download specific DEMs:
                * strm3
                * ace2
                * strm3_FTP
                * strm1HGT
                * ace
            * orbit : URL to download the orbit files:
                * ers1
                * ers2
                * s1_poe
                * s1_pres
                * doris
            * auxcal : URL to download the auxcal data:
                * s1
                * envisat
                * ers

    Note
    ----
    There may be additional attributes not listed above depending of the
    specific solver. Since this class is essentially a subclass of dict
    with attribute accessors, one can see which attributes are available
    using the `keys()` method.
    """

    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            raise AttributeError(name)

    __setattr__ = dict.__setitem__
    __delattr__ = dict.__delitem__

    def __repr__(self):
        if self.keys():
            m = max(map(len, list(self.keys()))) + 1
            return '\n'.join([k.rjust(m) + ': ' + repr(v)
                              for k, v in sorted(self.items())])
        else:
            return self.__class__.__name__ + "()"

    def __dir__(self):
        return list(self.keys())

# ==============================================================================
# LOOKUP
# ==============================================================================
snap_suffix = {'Apply-Orbit-File': 'Orb',
               'Calibration': 'Cal',
               'Cross-Correlation': '',
               'LinearToFromdB': 'dB',
               'Remove-GRD-Border-Noise': 'bnr',
               'SAR-Simulation': 'Sim',
               'SARSim-Terrain-Correction': 'TC',
               'Subset': '',
               'Terrain-Correction': 'TC',
               'Terrain-Flattening': 'TF',
               'Read': '',
               'Write': ''}

snap = Storage(suffix=snap_suffix)

LOOKUP = Storage(snap=snap,

                 attributes={'sensor': 'TEXT',
                             'orbit': 'TEXT',
                             'acquisition_mode': 'TEXT',
                             'start': 'TEXT',
                             'stop': 'TEXT',
                             'product': 'TEXT',
                             'samples': 'INTEGER',
                             'lines': 'INTEGER',
                             'outname_base': 'TEXT PRIMARY KEY',
                             'scene': 'TEXT',
                             'hh': 'INTEGER',
                             'vv': 'INTEGER',
                             'hv': 'INTEGER',
                             'vh': 'INTEGER'})

# ==============================================================================
# URL
# ==============================================================================
dem = Storage(ace2='http://step.esa.int/auxdata/dem/ACE2/5M/',
              ace='http://step.esa.int/auxdata/dem/ACE30/',
              srtm3_FTP='xftp.jrc.it',
              srtm3='http://srtm.csi.cgiar.org/SRT-ZIP/SRTM_V41/SRTM_Data_GeoTiff/',
              srtm1Hgt='http://step.esa.int/auxdata/dem/SRTMGL1/', )

orbit = Storage(doris='http://step.esa.int/auxdata/orbits/Doris/vor',
                ers1='http://step.esa.int/auxdata/orbits/ers_precise_orb/ERS1',
                ers2='http://step.esa.int/auxdata/orbits/ers_precise_orb/ERS2',
                s1_poe='http://step.esa.int/auxdata/orbits/Sentinel-1/POEORB/',
                s1_res='http://step.esa.int/auxdata/orbits/Sentinel-1/RESORB/')

auxcal = Storage(s1='http://step.esa.int/auxdata/auxcal/S1/',
                 envisat='http://step.esa.int/auxdata/auxcal/ENVISAT/',
                 ers='http://step.esa.int/auxdata/auxcal/ERS/')

URL = Storage(dem=dem,
              orbit=orbit,
              auxcal=auxcal)

# ==============================================================================
# Merge
# ==============================================================================
STORAGE = Storage(URL=URL,
                  LOOKUP=LOOKUP)

class ConfigHandler(object):
    """
    ConfigHandler is a configuration handler for pyroSAR. It is intended to be called by a class's "__init__" and
    set or get the configuration parameters throughout an entire package.
    The primary goal with ConfigHandler is to load a single, consistent configuration environment to be passed
    amongst ALL objects within a package.

    ConfigHandler is a SINGLETON, meaning once instantiated, THE SAME OBJECT
    will be returned to every class object calling it.

    Parameters
    ----------
    path : str or None
        A path where the .pyrosar directory will be created. If None (default) it will be created in the user home
        directory.
    config_fname : str
        Name of the config file. Default is 'config.ini'.

    Methods
    -------
    make_dir : Create a .pyrosar directory in home directory.
    create_config : Create a config.ini file in .pyrosar directory.
    open : Open the config.ini file.
    add_section : Create a new section in the configuration.
    set : Set an option in the configuration.
    remove_option : Remove an option in the configuration.

    Notes
    -----
    The syntax is the same as in ConfigParser. Here, keys are called options.

    """

    # ---- Define Global Variables ----

    __KEYS = ['auxdata',
              'auxdatapath',
              'demPath',
              'etc',
              'gpt',
              'path',
              'properties',
              'DEM.AsterDEMDataPath',
              'LandCover.globcoverDataPath',
              'DEM.ace2_5MinDEMDataPath',
              'OrbitFiles.delftFTP_ERS2_precise_remotePath',
              'DEM.gtopo30DEMDataPath',
              'DEM.srtm3GeoTiffDEM_HTTP',
              'DEM.aceDEMDataPath',
              'DEM.srtm3GeoTiffDEM_remotePath',
              'AuxCal.ENVISAT.remotePath',
              'DEM.ace2_5MinDEM_HTTP',
              'DEM.aceDEM_HTTP',
              'OrbitFiles.prareHTTP_ERS1_remotePath',
              'OrbitFiles.delftERS1OrbitPath',
              'OrbitFiles.delftFTP_ERS1_precise_remotePath',
              'OrbitFiles.sentinel1POEOrbit_remotePath',
              'AuxCal.ERS.remotePath',
              'DEM.CDEM_HTTP',
              'OrbitFiles.sentinel1RESOrbitPath',
              'OrbitFiles.dorisHTTP_vor_remotePath',
              'OrbitFiles.dorisVOROrbitPath',
              'OrbitFiles.sentinel1POEOrbitPath',
              'AuxCal.Sentinel1.remotePath',
              'OrbitFiles.delftFTP',
              'OrbitFiles.delftEnvisatOrbitPath',
              'OrbitFiles.delftERS2OrbitPath',
              'DEM.srtm1GridDEMDataPath',
              'DEM.srtm3GeoTiffDEMDataPath',
              'OrbitFiles.delftFTP_ENVISAT_precise_remotePath',
              'OrbitFiles.sentinel1RESOrbit_remotePath',
              'DEM.srtm3GeoTiffDEM_FTP',
              'LandCover.glc2000DataPath',
              'OrbitFiles.prareHTTP_ERS2_remotePath',
              'DEM.srtm1HgtDEM_HTTP',
              'OrbitFiles.prareERS1OrbitPath',
              'landCoverPath',
              'executable',
              'OrbitFiles.dorisPOROrbitPath',
              'OrbitFiles.prareERS2OrbitPath',
              'DEM.Getasse30DEMDataPath']

    __SECTIONS = {
        "snap": "SNAP",
        "outputpaths": "OUTPUT",
        "url": "URL"
    }

    ___AUXDATANAMES = {
        "demPath": "${AuxDataPath}/dem",
        "landCoverPath": "${AuxDataPath}/LandCover",
    }

    # Define __setter to control changeable keys (optional)
    # __setter = ["etc", "auxdata"]

    def __init__(self, path=None, config_fname='config.ini'):

        path = os.path.join(expanduser("~"), '.pyrosar') if path is None else os.path.join(path, '.pyrosar')

        self.__GLOBAL = {
            "path": path,
            "config_fname": config_fname,
            "config": os.path.join(os.path.join(path, config_fname)),
        }

        if os.path.isfile(self.__GLOBAL['config']):
            self.parser = ConfigParser.RawConfigParser(allow_no_value=True)
            self.parser.read(self.__GLOBAL['config'])

        else:
            self.create_config()
            self.parser = ConfigParser.RawConfigParser()
            self.parser.optionxform = str
            self.parser.read(self.__GLOBAL['config'])

    def make_dir(self):
        """
        Create a .pyrosar directory in home directory.

        Returns
        -------
        None
        """

        if not os.path.exists(self.__GLOBAL['path']):
            os.makedirs(self.__GLOBAL['path'])

        else:
            pass

    def create_config(self):
        """
        Create a config.ini file in .pyrosar directory.

        Returns
        -------
        None
        """

        self.make_dir()

        if not os.path.isfile(self.__GLOBAL['config']):
            with open(self.__GLOBAL['config'], 'w'):
                pass
        else:
            pass

    def __str__(self):

        string_literal = 'Class    : Config\n' \
                         'Path     : {0}\n' \
                         'Sections : {1}\n'.format(self.__GLOBAL['config'], len(self.parser.sections()))

        print(string_literal)
        print("Contents:")

        for section in self.parser.sections():
            print("\n Section: {0}".format(section))

            for options in self.parser.options(section):
                print("\t x {0} :: {1} :: {2}".format(options, self.parser.get(section, options), str(type(options))))

        return ''

    def __getattr__(self, section):
        try:
            return dict(self.parser.items(section))
        except ConfigParser.NoSectionError:
            raise AttributeError

    @property
    def sections(self):
        return self.parser.sections()

    def keys(self, section):
        """
        Get all keys (options) of a section.

        Parameters
        ----------
        section : str
            Section name.

        Returns
        -------
        list : options (keys) of a section.

        """
        return self.parser.options(section)

    def open(self):
        """
        Open the config.ini file. This method will open the config.ini file in a external standard app (text editor).

        Returns
        -------
        os.startfile

        """

        os.startfile(self.__GLOBAL['config'])

    def add_section(self, section='SNAP'):
        """
        Create a new section in the configuration.

        Parameters
        ----------
        section : str
            Section name

        Returns
        -------
        None

        """
        if self.parser.has_section(section):
            pass

        elif section not in ConfigHandler.__SECTIONS.values():
            raise AttributeError(
                "Only the following sections are allowed: {0}.".format(str(ConfigHandler.__SECTIONS.values())))

        else:
            self.parser.add_section(section)
            if sys.version_info >= (3, 0):
                with open(self.__GLOBAL['config'], 'w', encoding='utf8') as item:
                    self.parser.write(item)
            else:
                with open(self.__GLOBAL['config'], 'w') as item:
                    self.parser.write(item)

            # with open(self.__GLOBAL['config'], 'wb') as item:
            #     self.parser.write(item)

    def set(self, section, key, value, overwrite=False):
        """
        Set an option.

        Parameters
        ----------
        section : str
            Section name.
        key : str
            Key value.
        value :
            Value of the key.
        overwrite : bool
            If True and the defined key exists the value will be overwritten.

        Returns
        -------
        None
        """
        if not self.parser.has_section(section):
            raise AttributeError("Section {0} does not exist.".format(str(section)))

        elif key not in ConfigHandler.__KEYS:
            raise AttributeError(
                "Your key is {0}. Only the following keys are allowed: {1}.".format(str(key),
                                                                                str(ConfigHandler.__KEYS)))

        else:
            if key in self.parser.options(section):

                if overwrite:
                    self.parser.set(section, key, value)
                    if sys.version_info >= (3, 0):
                        with open(self.__GLOBAL['config'], 'w', encoding='utf8') as item:
                            self.parser.write(item)
                    else:
                        with open(self.__GLOBAL['config'], 'w') as item:
                            self.parser.write(item)

                else:
                    pass
                    # warnings.warn("Value already exists.")

            else:
                self.parser.set(section, key, value)
                if sys.version_info >= (3, 0):
                    with open(self.__GLOBAL['config'], 'w', encoding='utf8') as item:
                        self.parser.write(item)
                else:
                    with open(self.__GLOBAL['config'], 'w') as item:
                        self.parser.write(item)

    def remove_option(self, section, key):
        """
        Remove an option and key.

        Parameters
        ----------
        section : str
            Section name.
        key : str
            Key value.

        Returns
        -------
        None
        """
        if not self.parser.has_section(section):
            raise AttributeError("Section {0} does not exist.".format(str(section)))

        elif key not in self.parser.options(section):
            raise AttributeError("Key {0} does not exist.".format(str(key)))

        else:
            self.parser.remove_option(section, key)

            # write changes back to the config file
            if sys.version_info >= (3, 0):
                with open(self.__GLOBAL['config'], 'w', encoding='utf8') as item:
                    self.parser.write(item)
            else:
                with open(self.__GLOBAL['config'], 'w') as item:
                    self.parser.write(item)

    def get(self, section, key=None):
        """
        Get all options (keys) and values of a section.

        Parameters
        ----------
        section : str
            Section name.

        key : str or None
            If not None it returns the specific value of the desired key. Else (default) it will return all
            keys and values.

        Returns
        -------
        dict : keys and values of a section.

        """
        if not self.parser.has_section(section):
            raise AttributeError("Section {0} does not exist.".format(str(section)))
        else:
            if key is None:
                return dict(self.parser.items(section))

            else:
                items = dict(self.parser.items(section))
                return items[key]
<|endoftext|>


<commit_before>##############################################################
# Functionality for masking SAR image border noise
# John Truckenbrodt, 2017
##############################################################
"""
Notice: these functions are not intended for direct application. Please use the functionality provided by pyroSAR instead
"""

import numpy as np
from osgeo import ogr
from polysimplify import VWSimplifier

def simplify(x, y, maxpoints=20):
    x = map(float, x)
    y = map(float, y)
    pts = np.array(zip(x, y))
    simplifier = VWSimplifier(pts)
    sqd = []
    iter_range = range(2, maxpoints + 1)
    for i in iter_range:
        VWpts = simplifier.from_number(i)
        xn, yn = zip(*VWpts)
        out = np.sum((y - np.interp(x, xn, yn)) ** 2)
        sqd.append(out)
    sqd /= max(sqd)
    iter = (np.array(iter_range) - 2) / (maxpoints - 2.)
    # plt.plot(iter_range, sqd, label='residual')
    # plt.plot(iter_range, iter, color='r', label='iteration')
    # plt.plot(iter_range, iter + sqd, color='g', label='residual+iteration')
    # plt.legend(loc='upper center', shadow=True)
    # plt.show()
    npoints = np.argmin(iter + sqd) + 2
    VWpts = simplifier.from_number(npoints)
    return VWpts

def createPoly(xn, yn, xmax, ymax):
    ring = ogr.Geometry(ogr.wkbLinearRing)
    ring.AddPoint(0, 0)
    for item in zip(xn, yn):
        item = map(int, item)
        if item != [0, 0] and item != [xmax, ymax]:
            ring.AddPoint(item[0], item[1])
    ring.AddPoint(xmax, ymax)
    ring.AddPoint(xmax, 0)
    ring.CloseRings()
    poly = ogr.Geometry(ogr.wkbPolygon)
    poly.AddGeometry(ring)
    return poly

def crop(seq, maxpoints=20, proximity=100, straighten=False):
    x = map(float, range(0, len(seq)))
    VWpts = simplify(x, seq, maxpoints)
    xn, yn = map(list, zip(*VWpts))
    simple = np.interp(x, xn, yn)
    seq[abs(seq - simple) > proximity] = simple[abs(seq - simple) > proximity]
    points = []
    for xi, yi in enumerate(seq):
        point = ogr.Geometry(ogr.wkbPoint)
        point.AddPoint(xi, yi)
        points.append(point)
    points = np.array(points)
    while True:
        poly = createPoly(xn, yn, len(seq), max(seq))
        line = ogr.Geometry(ogr.wkbLineString)
        for xi, yi in zip(xn, yn):
            line.AddPoint(xi, yi)
        dists = np.array([line.Distance(point) for point in points])
        contain = np.array([point.Within(poly) for point in points])
        dists[~contain] = 0
        points = points[(dists > 0)]
        dists = dists[(dists > 0)]
        if len(dists) == 0:
            break
        candidate = points[np.argmax(dists)]
        cp = candidate.GetPoint()
        index = np.argmin(np.array(xn) < cp[0])
        xn.insert(index, cp[0])
        yn.insert(index, cp[1])
    if straighten:
        indices = [i for i in range(0, len(xn)) if (xn[i], yn[i]) in VWpts]
        for i, j in enumerate(indices):
            if i < (len(indices)-1):
                if indices[i+1] > j+1:
                    dx = abs(xn[j] - xn[indices[i + 1]])
                    dy = abs(yn[j] - yn[indices[i + 1]])
                    if dx > dy:
                        seg_y = yn[j:indices[i + 1]+1]
                        print seg_y
                        for k in range(j, indices[i + 1]+1):
                            yn[k] = min(seg_y)
    return np.interp(x, xn, yn)<commit_msg>switched to print function, adjusted import of polysimplify<commit_after>##############################################################
# Functionality for masking SAR image border noise
# John Truckenbrodt, 2017
##############################################################
"""
Notice: these functions are not intended for direct application. Please use the functionality provided by pyroSAR instead
"""

import numpy as np
from osgeo import ogr
from .polysimplify import VWSimplifier

def simplify(x, y, maxpoints=20):
    x = map(float, x)
    y = map(float, y)
    pts = np.array(zip(x, y))
    simplifier = VWSimplifier(pts)
    sqd = []
    iter_range = range(2, maxpoints + 1)
    for i in iter_range:
        VWpts = simplifier.from_number(i)
        xn, yn = zip(*VWpts)
        out = np.sum((y - np.interp(x, xn, yn)) ** 2)
        sqd.append(out)
    sqd /= max(sqd)
    iter = (np.array(iter_range) - 2) / (maxpoints - 2.)
    # plt.plot(iter_range, sqd, label='residual')
    # plt.plot(iter_range, iter, color='r', label='iteration')
    # plt.plot(iter_range, iter + sqd, color='g', label='residual+iteration')
    # plt.legend(loc='upper center', shadow=True)
    # plt.show()
    npoints = np.argmin(iter + sqd) + 2
    VWpts = simplifier.from_number(npoints)
    return VWpts

def createPoly(xn, yn, xmax, ymax):
    ring = ogr.Geometry(ogr.wkbLinearRing)
    ring.AddPoint(0, 0)
    for item in zip(xn, yn):
        item = map(int, item)
        if item != [0, 0] and item != [xmax, ymax]:
            ring.AddPoint(item[0], item[1])
    ring.AddPoint(xmax, ymax)
    ring.AddPoint(xmax, 0)
    ring.CloseRings()
    poly = ogr.Geometry(ogr.wkbPolygon)
    poly.AddGeometry(ring)
    return poly

def crop(seq, maxpoints=20, proximity=100, straighten=False):
    x = map(float, range(0, len(seq)))
    VWpts = simplify(x, seq, maxpoints)
    xn, yn = map(list, zip(*VWpts))
    simple = np.interp(x, xn, yn)
    seq[abs(seq - simple) > proximity] = simple[abs(seq - simple) > proximity]
    points = []
    for xi, yi in enumerate(seq):
        point = ogr.Geometry(ogr.wkbPoint)
        point.AddPoint(xi, yi)
        points.append(point)
    points = np.array(points)
    while True:
        poly = createPoly(xn, yn, len(seq), max(seq))
        line = ogr.Geometry(ogr.wkbLineString)
        for xi, yi in zip(xn, yn):
            line.AddPoint(xi, yi)
        dists = np.array([line.Distance(point) for point in points])
        contain = np.array([point.Within(poly) for point in points])
        dists[~contain] = 0
        points = points[(dists > 0)]
        dists = dists[(dists > 0)]
        if len(dists) == 0:
            break
        candidate = points[np.argmax(dists)]
        cp = candidate.GetPoint()
        index = np.argmin(np.array(xn) < cp[0])
        xn.insert(index, cp[0])
        yn.insert(index, cp[1])
    if straighten:
        indices = [i for i in range(0, len(xn)) if (xn[i], yn[i]) in VWpts]
        for i, j in enumerate(indices):
            if i < (len(indices)-1):
                if indices[i+1] > j+1:
                    dx = abs(xn[j] - xn[indices[i + 1]])
                    dy = abs(yn[j] - yn[indices[i + 1]])
                    if dx > dy:
                        seg_y = yn[j:indices[i + 1]+1]
                        print(seg_y)
                        for k in range(j, indices[i + 1]+1):
                            yn[k] = min(seg_y)
    return np.interp(x, xn, yn)<|endoftext|>