<commit_before># cerbero - a multi-platform build system for Open Source software
# Copyright (C) 2012 Andoni Morales Alastruey <ylatuya@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

import os
import itertools

from cerbero.utils import etree, shell

class PMDocXML(object):

    TAG_CONTENTS = 'contents'
    TAG_DESCRIPTION = 'description'
    TAG_FLAGS = 'flags'
    TAG_MOD = 'mod'
    TAG_PKGREF = 'pkgref'
    TAG_SCRIPTS = 'scripts'
    TAG_TITLE = 'title'
    TAG_VERSION = 'version'
    SPEC_VERSION = '1.12'

    def render(self):
        return etree.tostring(self.root)

    def _add_mods(self, parent, mods):
        for mod in mods:
            el = etree.SubElement(parent, self.TAG_MOD)
            el.text = mod

    def _subelement_text(self, parent, tag, text, **attrib):
        el = etree.SubElement(parent, tag, **attrib)
        el.text = text
        return el

class Index(PMDocXML):
    ''' Index file for PackageMake document '''

    DOCUMENT_TAG = 'pkmdoc'
    MIN_SPEC = '1.000000'
    PROP_USER_SEES = 'custom'
    PROP_MIN_TARGET = '2'
    PROP_DOMAIN = 'anywhere'
    ATTR_MIN_SPEC = 'min-spec'
    TAG_BUILD = 'build'
    TAG_CHOICE = 'choice'
    TAG_DISTRIBUTION = 'ditribution'
    TAG_DOMAIN = 'domain'
    TAG_ITEM = 'item'
    TAG_MIN_TARGET = 'min-target'
    TAG_MOD = 'mod'
    TAG_ORGANIZATION = 'organization'
    TAG_PROPERTIES = 'properties'
    TAG_PKGREF = 'pkgref'
    TAG_SCRIPTS = 'scripts'
    TAG_USER_SEES = 'userSees'

    def __init__(self, package, store, out_dir, fill=True):
        self.package = package
        self.store = store
        self.out_dir = out_dir
        if fill:
            self._fill()

    def _fill(self):
        self._add_root()
        self._add_properties()
        self._add_distribution()
        self._add_description()
        self._add_mod()
        self._add_flags()
        self._add_contents()

    def _add_root(self):
        self.root = etree.Element(self.DOCUMENT_TAG, spec=self.SPEC_VERSION)

    def _add_properties(self):
        properties = etree.SubElement(self.root, self.TAG_PROPERTIES)
        org = etree.SubElement(properties, self.TAG_ORGANIZATION)
        org.text = self.package.org
        title = etree.SubElement(properties, self.TAG_TITLE)
        title.text = self.package.title
        build = etree.SubElement(properties, self.TAG_BUILD)
        build.text = os.path.join(self.out_dir, "%s.pkg" % self.package.name)

        for e, k, v in [(self.TAG_USER_SEES, 'ui', self.PROP_USER_SEES),
                (self.TAG_MIN_TARGET, 'os', self.PROP_MIN_TARGET),
                (self.TAG_DOMAIN, 'anywhere', self.PROP_DOMAIN)]:
            node = etree.SubElement(properties, e)
            self._set(node, **{k:v})

    def _add_distribution(self):
        distribution = etree.SubElement(self.root, self.TAG_DISTRIBUTION)
        scripts = etree.SubElement(distribution, self.TAG_SCRIPTS)
        version = etree.SubElement(distribution, self.TAG_VERSION)
        version.set(self.ATTR_MIN_SPEC, self.MIN_SPEC)

    def _add_description(self):
        desc = etree.SubElement(self.root, self.TAG_DESCRIPTION)
        desc.text = self.package.shortdesc

    def _add_mod(self):
        self._add_mods(self.root, ['description', 'properties.title',
            'properties.customizeOption'])

    def _add_flags(self):
        etree.SubElement(self.root, self.TAG_FLAGS)

    def _add_contents(self):
        contents = etree.SubElement(self.root, self.TAG_CONTENTS)

        # With PackageMaker choices can't be nested so we group all packages
        # no matter the feature they belong to.
        packages = itertools.chain(*self.package.packages.values())

        for p, required, selected in packages:
            package = self.store.get_package(p)
            self._add_choice(package, not required, selected, contents)

    def _add_choice(self, package, enabled, selected, contents):
        choice = etree.SubElement(contents, self.TAG_CHOICE,
            title=package.shortdesc, id=package.name,
            starts_selected=self._boolstr(selected),
            starts_enabled=self._boolstr(enabled), start_hidden='false')
        packages = [package.name] + package.deps
        for p_name in packages:
            etree.SubElement(choice, self.TAG_PKGREF, id=p_name)
            item = etree.SubElement(self.root, self.TAG_ITEM, type='pkgref')
            item.text = '%s.xml' % p_name

    def _boolstr(self, boolean):
        return boolean and 'true' or 'false'

    def _set(self, node, **kwargs):
        for key in sorted(kwargs.keys()):
            value = kwargs[key]
            if value is None or value is '':
                continue
            node.set(key, value)

class PkgRef(PMDocXML):
    ''' PkgRef for packages in a PackageMaker document '''

    TAG_CONFIG = 'config'
    TAG_IDENTIFIER = 'identifier'
    TAG_POST_INSTALL = 'post-install'
    TAG_REQ_AUTH = 'requireAuthorization'
    TAG_INSTALL_TO = 'intallTo'
    TAG_FOLLOW_SYMLINKS = 'followSymbolicLinks'
    TAG_PACKAGE_STORE = 'packageStore'
    TAG_FILE_LIST = 'file-list'
    TAG_FILTER = 'filter'
    TAG_EXTRA = 'extra'
    TAG_PACKAGE_PATH = 'packagePath'
    TAG_SCRIPTS_DIR = 'scripts-dir'

    def __init__(self, package, package_path, fill=True):
        self.package = package
        self.package_path = package_path
        if fill:
            self._fill()

    def _fill(self):
        self._add_root()
        self._add_config()
        self._add_scripts()
        self._add_contents()
        self._add_extra()

    def _add_root(self):
        if self.package.uuid is None:
            raise FatalError("uuid for package '%s' is None" % self.package.name)
        self.root = etree.Element(self.TAG_PKGREF, spec=self.SPEC_VERSION,
                                  uuid=self.package.uuid)

    def _add_config(self):
        config = etree.SubElement(self.root, self.TAG_CONFIG)
        self._subelement_text(config, self.TAG_VERSION, '1.0')
        self._subelement_text(config, self.TAG_IDENTIFIER, self.package.name)
        self._subelement_text(config, self.TAG_DESCRIPTION, self.package.shortdesc)
        etree.SubElement(config, self.TAG_POST_INSTALL, type="none")
        etree.SubElement(config, self.TAG_REQ_AUTH)
        self._subelement_text(config, self.TAG_INSTALL_TO, '.',
                              relative="true", mod="true")
        etree.SubElement(config, self.TAG_PACKAGE_STORE, type="internal")
        self._add_mods(config, ['installTo.isAbsoluteType', 'installTo.path',
            'parent', 'installTo.isRelativeType', 'installTo'])
        flags = etree.SubElement(config, self.TAG_FLAGS)
        etree.SubElement(flags, self.TAG_FOLLOW_SYMLINKS)

    def _add_scripts(self):
        scripts = etree.SubElement(self.root, self.TAG_SCRIPTS)
        self._subelement_text(scripts, self.TAG_SCRIPTS_DIR,
                os.path.join(self.package_path, 'Contents', 'Resources'))

    def _add_contents(self):
        contents = etree.SubElement(self.root, self.TAG_CONTENTS)
        self._subelement_text(contents, self.TAG_FILE_LIST,
                              "%s-contents.xml" % self.package.name)
        for f in ['.DS_Store$']:
            self._subelement_text(contents, self.TAG_FILTER, f)

    def _add_extra(self):
        extra = etree.SubElement(self.root, self.TAG_EXTRA)
        self._subelement_text(extra, self.TAG_TITLE, self.package.shortdesc)
        self._subelement_text(extra, self.TAG_PACKAGE_PATH, self.package_path)

class PkgContents(PMDocXML):
    '''
    Creates a contents.xml files list for PackageMaker documents,
    using lsbom to list the files inside a packages.
    '''

    TAG_PKG_CONTENTS = 'pkg-contents'
    TAG_F = 'f'
    OWNER = 'root'
    GROUP = 'admin'

    def __init__(self, package_path, fill=True):
        self.package_path = package_path
        self.bom_path = os.path.join(self.package_path, 'Contents',
                                     'Archive.bom')
        if fill:
            self._fill()

    def _fill(self):
        self._add_root()
        self._list_directories()
        self._list_files()
        self._add_dirs()
        self._add_files()

    def _list_bom_dirs(self):
        return shell.check_call('lsbom %s -s -d' % self.bom_path)

    def _list_bom_files(self):
        return shell.check_call('lsbom %s -p fm' % self.bom_path)

    def _list_directories(self):
        self.dirs = self._list_bom_dirs().split('\n')
        try:
            self.dirs.remove('')
        except:
            pass

    def _list_files(self):
        self.files_modes = dict()
        for line in self._list_bom_files().split('\n')[1:]:
            if line == '':
                continue
            path, mode = line.split('\t')
            self.files_modes[path] = mode
        try:
            del self.files_modes['']
        except:
            pass

    def _add_root(self):
        self.root = etree.Element(self.TAG_PKG_CONTENTS, spec=self.SPEC_VERSION)

    def _add_package_root(self):
        self.proot = etree.SubElement(self.root, self.TAG_F,
            n='PackageRoot', o=self.OWNER, g=self.GROUP, pt='.', m='true',
            t='bom')
        for mod in ['name']:
            self._subelement_text(self.proot, self.TAG_MOD, mod)
        return self.proot

    def _add_dirs(self):
        self.dirs_nodes = dict()
        for path in self.dirs:
            parent, dirname = os.path.split(path)
            if dirname == '.':
                self.dirs_nodes['.'] = self._add_package_root()
            else:
                # all directories are listed in the correct order, so we assume
                # that the parent has already been created
                parent_node = self.dirs_nodes[parent]
                self.dirs_nodes[path] = self._add_entry(parent_node, dirname,
                        self.files_modes[path])

    def _add_files(self):
        for f, m in self.files_modes.iteritems():
            if f in self.dirs:
                continue
            parent, filename = os.path.split(f)
            self._add_entry(self.dirs_nodes[parent], filename, m)

    def _add_entry(self, parent, name, mode):
        return etree.SubElement(parent, self.TAG_F, n=name, o=self.OWNER,
                g=self.GROUP, p=mode)

<commit_msg>Add function to save the xml to disk<commit_after># cerbero - a multi-platform build system for Open Source software
# Copyright (C) 2012 Andoni Morales Alastruey <ylatuya@gmail.com>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

import os
import itertools

from cerbero.utils import etree, shell

class PMDocXML(object):

    TAG_CONTENTS = 'contents'
    TAG_DESCRIPTION = 'description'
    TAG_FLAGS = 'flags'
    TAG_MOD = 'mod'
    TAG_PKGREF = 'pkgref'
    TAG_SCRIPTS = 'scripts'
    TAG_TITLE = 'title'
    TAG_VERSION = 'version'
    SPEC_VERSION = '1.12'

    def render(self):
        return etree.tostring(self.root)

    def write(self, path):
        tree = etree.ElementTree(self.root)
        tree.write(path, encoding='utf-8')

    def _add_mods(self, parent, mods):
        for mod in mods:
            el = etree.SubElement(parent, self.TAG_MOD)
            el.text = mod

    def _subelement_text(self, parent, tag, text, **attrib):
        el = etree.SubElement(parent, tag, **attrib)
        el.text = text
        return el

class Index(PMDocXML):
    ''' Index file for PackageMake document '''

    DOCUMENT_TAG = 'pkmdoc'
    MIN_SPEC = '1.000000'
    PROP_USER_SEES = 'custom'
    PROP_MIN_TARGET = '2'
    PROP_DOMAIN = 'anywhere'
    ATTR_MIN_SPEC = 'min-spec'
    TAG_BUILD = 'build'
    TAG_CHOICE = 'choice'
    TAG_DISTRIBUTION = 'ditribution'
    TAG_DOMAIN = 'domain'
    TAG_ITEM = 'item'
    TAG_MIN_TARGET = 'min-target'
    TAG_MOD = 'mod'
    TAG_ORGANIZATION = 'organization'
    TAG_PROPERTIES = 'properties'
    TAG_PKGREF = 'pkgref'
    TAG_SCRIPTS = 'scripts'
    TAG_USER_SEES = 'userSees'

    def __init__(self, package, store, out_dir, fill=True):
        self.package = package
        self.store = store
        self.out_dir = out_dir
        if fill:
            self._fill()

    def _fill(self):
        self._add_root()
        self._add_properties()
        self._add_distribution()
        self._add_description()
        self._add_mod()
        self._add_flags()
        self._add_contents()

    def _add_root(self):
        self.root = etree.Element(self.DOCUMENT_TAG, spec=self.SPEC_VERSION)

    def _add_properties(self):
        properties = etree.SubElement(self.root, self.TAG_PROPERTIES)
        org = etree.SubElement(properties, self.TAG_ORGANIZATION)
        org.text = self.package.org
        title = etree.SubElement(properties, self.TAG_TITLE)
        title.text = self.package.title
        build = etree.SubElement(properties, self.TAG_BUILD)
        build.text = os.path.join(self.out_dir, "%s.pkg" % self.package.name)

        for e, k, v in [(self.TAG_USER_SEES, 'ui', self.PROP_USER_SEES),
                (self.TAG_MIN_TARGET, 'os', self.PROP_MIN_TARGET),
                (self.TAG_DOMAIN, 'anywhere', self.PROP_DOMAIN)]:
            node = etree.SubElement(properties, e)
            self._set(node, **{k:v})

    def _add_distribution(self):
        distribution = etree.SubElement(self.root, self.TAG_DISTRIBUTION)
        scripts = etree.SubElement(distribution, self.TAG_SCRIPTS)
        version = etree.SubElement(distribution, self.TAG_VERSION)
        version.set(self.ATTR_MIN_SPEC, self.MIN_SPEC)

    def _add_description(self):
        desc = etree.SubElement(self.root, self.TAG_DESCRIPTION)
        desc.text = self.package.shortdesc

    def _add_mod(self):
        self._add_mods(self.root, ['description', 'properties.title',
            'properties.customizeOption'])

    def _add_flags(self):
        etree.SubElement(self.root, self.TAG_FLAGS)

    def _add_contents(self):
        contents = etree.SubElement(self.root, self.TAG_CONTENTS)

        # With PackageMaker choices can't be nested so we group all packages
        # no matter the feature they belong to.
        packages = itertools.chain(*self.package.packages.values())

        for p, required, selected in packages:
            package = self.store.get_package(p)
            self._add_choice(package, not required, selected, contents)

    def _add_choice(self, package, enabled, selected, contents):
        choice = etree.SubElement(contents, self.TAG_CHOICE,
            title=package.shortdesc, id=package.name,
            starts_selected=self._boolstr(selected),
            starts_enabled=self._boolstr(enabled), start_hidden='false')
        packages = [package.name] + package.deps
        for p_name in packages:
            etree.SubElement(choice, self.TAG_PKGREF, id=p_name)
            item = etree.SubElement(self.root, self.TAG_ITEM, type='pkgref')
            item.text = '%s.xml' % p_name

    def _boolstr(self, boolean):
        return boolean and 'true' or 'false'

    def _set(self, node, **kwargs):
        for key in sorted(kwargs.keys()):
            value = kwargs[key]
            if value is None or value is '':
                continue
            node.set(key, value)

class PkgRef(PMDocXML):
    ''' PkgRef for packages in a PackageMaker document '''

    TAG_CONFIG = 'config'
    TAG_IDENTIFIER = 'identifier'
    TAG_POST_INSTALL = 'post-install'
    TAG_REQ_AUTH = 'requireAuthorization'
    TAG_INSTALL_TO = 'intallTo'
    TAG_FOLLOW_SYMLINKS = 'followSymbolicLinks'
    TAG_PACKAGE_STORE = 'packageStore'
    TAG_FILE_LIST = 'file-list'
    TAG_FILTER = 'filter'
    TAG_EXTRA = 'extra'
    TAG_PACKAGE_PATH = 'packagePath'
    TAG_SCRIPTS_DIR = 'scripts-dir'

    def __init__(self, package, package_path, fill=True):
        self.package = package
        self.package_path = package_path
        if fill:
            self._fill()

    def _fill(self):
        self._add_root()
        self._add_config()
        self._add_scripts()
        self._add_contents()
        self._add_extra()

    def _add_root(self):
        if self.package.uuid is None:
            raise FatalError("uuid for package '%s' is None" % self.package.name)
        self.root = etree.Element(self.TAG_PKGREF, spec=self.SPEC_VERSION,
                                  uuid=self.package.uuid)

    def _add_config(self):
        config = etree.SubElement(self.root, self.TAG_CONFIG)
        self._subelement_text(config, self.TAG_VERSION, '1.0')
        self._subelement_text(config, self.TAG_IDENTIFIER, self.package.name)
        self._subelement_text(config, self.TAG_DESCRIPTION, self.package.shortdesc)
        etree.SubElement(config, self.TAG_POST_INSTALL, type="none")
        etree.SubElement(config, self.TAG_REQ_AUTH)
        self._subelement_text(config, self.TAG_INSTALL_TO, '.',
                              relative="true", mod="true")
        etree.SubElement(config, self.TAG_PACKAGE_STORE, type="internal")
        self._add_mods(config, ['installTo.isAbsoluteType', 'installTo.path',
            'parent', 'installTo.isRelativeType', 'installTo'])
        flags = etree.SubElement(config, self.TAG_FLAGS)
        etree.SubElement(flags, self.TAG_FOLLOW_SYMLINKS)

    def _add_scripts(self):
        scripts = etree.SubElement(self.root, self.TAG_SCRIPTS)
        self._subelement_text(scripts, self.TAG_SCRIPTS_DIR,
                os.path.join(self.package_path, 'Contents', 'Resources'))

    def _add_contents(self):
        contents = etree.SubElement(self.root, self.TAG_CONTENTS)
        self._subelement_text(contents, self.TAG_FILE_LIST,
                              "%s-contents.xml" % self.package.name)
        for f in ['.DS_Store$']:
            self._subelement_text(contents, self.TAG_FILTER, f)

    def _add_extra(self):
        extra = etree.SubElement(self.root, self.TAG_EXTRA)
        self._subelement_text(extra, self.TAG_TITLE, self.package.shortdesc)
        self._subelement_text(extra, self.TAG_PACKAGE_PATH, self.package_path)

class PkgContents(PMDocXML):
    '''
    Creates a contents.xml files list for PackageMaker documents,
    using lsbom to list the files inside a packages.
    '''

    TAG_PKG_CONTENTS = 'pkg-contents'
    TAG_F = 'f'
    OWNER = 'root'
    GROUP = 'admin'

    def __init__(self, package_path, fill=True):
        self.package_path = package_path
        self.bom_path = os.path.join(self.package_path, 'Contents',
                                     'Archive.bom')
        if fill:
            self._fill()

    def _fill(self):
        self._add_root()
        self._list_directories()
        self._list_files()
        self._add_dirs()
        self._add_files()

    def _list_bom_dirs(self):
        return shell.check_call('lsbom %s -s -d' % self.bom_path)

    def _list_bom_files(self):
        return shell.check_call('lsbom %s -p fm' % self.bom_path)

    def _list_directories(self):
        self.dirs = self._list_bom_dirs().split('\n')
        try:
            self.dirs.remove('')
        except:
            pass

    def _list_files(self):
        self.files_modes = dict()
        for line in self._list_bom_files().split('\n')[1:]:
            if line == '':
                continue
            path, mode = line.split('\t')
            self.files_modes[path] = mode
        try:
            del self.files_modes['']
        except:
            pass

    def _add_root(self):
        self.root = etree.Element(self.TAG_PKG_CONTENTS, spec=self.SPEC_VERSION)

    def _add_package_root(self):
        self.proot = etree.SubElement(self.root, self.TAG_F,
            n='PackageRoot', o=self.OWNER, g=self.GROUP, pt='.', m='true',
            t='bom')
        for mod in ['name']:
            self._subelement_text(self.proot, self.TAG_MOD, mod)
        return self.proot

    def _add_dirs(self):
        self.dirs_nodes = dict()
        for path in self.dirs:
            parent, dirname = os.path.split(path)
            if dirname == '.':
                self.dirs_nodes['.'] = self._add_package_root()
            else:
                # all directories are listed in the correct order, so we assume
                # that the parent has already been created
                parent_node = self.dirs_nodes[parent]
                self.dirs_nodes[path] = self._add_entry(parent_node, dirname,
                        self.files_modes[path])

    def _add_files(self):
        for f, m in self.files_modes.iteritems():
            if f in self.dirs:
                continue
            parent, filename = os.path.split(f)
            self._add_entry(self.dirs_nodes[parent], filename, m)

    def _add_entry(self, parent, name, mode):
        return etree.SubElement(parent, self.TAG_F, n=name, o=self.OWNER,
                g=self.GROUP, p=mode)

<|endoftext|>


<commit_before># coding: utf-8
from __future__ import unicode_literals, division, absolute_import, print_function

from asn1crypto import pem, x509
from oscrypto import trust_list

from ._errors import pretty_message
from ._types import byte_cls, type_name
from .errors import PathBuildingError, DuplicateCertificateError
from .path import ValidationPath

class CertificateRegistry():
    """
    Contains certificate lists used to build validation paths
    """

    # A dict with keys being asn1crypto.x509.Certificate.Name.hashable byte
    # string. Each value is a list of asn1crypto.x509.Certificate objects.
    _subject_map = None

    # A dict with keys being asn1crypto.x509.Certificate.key_identifier byte
    # string. Each value is an asn1crypto.x509.Certificate object.
    _key_identifier_map = None

    # A dict with keys being asn1crypto.x509.Certificate.signature byte string.
    # Each value is a bool - if the certificate is a CA cert.
    _ca_lookup = None

    def __init__(self, trust_roots=None, extra_trust_roots=None, other_certs=None):
        """
        :param trust_roots:
            If the operating system's trust list should not be used, instead
            pass a list of byte strings containing DER or PEM-encoded X.509
            certificates, or asn1crypto.x509.Certificate objects. These
            certificates will be used as the trust roots for the path being
            built.

        :param extra_trust_roots:
            If the operating system's trust list should be used, but augmented
            with one or more extra certificates. This should be a list of byte
            strings containing DER or PEM-encoded X.509 certificates, or
            asn1crypto.x509.Certificate objects.

        :param other_certs:
            A list of byte strings containing DER or PEM-encoded X.509
            certificates, or a list of asn1crypto.x509.Certificate objects.
            These other certs are usually provided by the service/item being
            validated. In SSL, these would be intermediate chain certs.
        """

        if trust_roots is not None and not isinstance(trust_roots, list):
            raise TypeError(pretty_message(
                '''
                trust_roots must be a list of byte strings or
                asn1crypto.x509.Certificate objects, not %s
                ''',
                type_name(trust_roots)
            ))

        if extra_trust_roots is not None and not isinstance(extra_trust_roots, list):
            raise TypeError(pretty_message(
                '''
                extra_trust_roots must be a list of byte strings or
                asn1crypto.x509.Certificate objects, not %s
                ''',
                type_name(extra_trust_roots)
            ))

        if other_certs is not None and not isinstance(other_certs, list):
            raise TypeError(pretty_message(
                '''
                other_certs must be a list of byte strings or
                asn1crypto.x509.Certificate objects, not %s
                ''',
                type_name(other_certs)
            ))

        if other_certs is None:
            other_certs = []
        else:
            other_certs = self._validate_unarmor(other_certs, 'other_certs')

        if trust_roots is None:
            trust_roots = trust_list.get_list()
        else:
            trust_roots = self._validate_unarmor(trust_roots, 'trust_roots')

        if extra_trust_roots is not None:
            trust_roots.extend(self._validate_unarmor(extra_trust_roots, 'extra_trust_roots'))

        self._subject_map = {}
        self._key_identifier_map = {}
        self._ca_lookup = {}

        for trust_root in trust_roots:
            hashable = trust_root.subject.hashable
            if hashable not in self._subject_map:
                self._subject_map[hashable] = []
            self._subject_map[hashable].append(trust_root)
            if trust_root.key_identifier:
                self._key_identifier_map[trust_root.key_identifier] = trust_root
            self._ca_lookup[trust_root.signature] = True

        for other_cert in other_certs:
            hashable = other_cert.subject.hashable
            if hashable not in self._subject_map:
                self._subject_map[hashable] = []
            self._subject_map[hashable].append(other_cert)
            if other_cert.key_identifier:
                self._key_identifier_map[other_cert.key_identifier] = other_cert

    def _validate_unarmor(self, certs, var_name):
        """
        Takes a list of byte strings or asn1crypto.x509.Certificates objects,
        validates and loads them while unarmoring any PEM-encoded contents

        :param certs:
            A list of byte strings or asn1crypto.x509.Certificate objects

        :param var_name:
            A unicode variable name to use in any TypeError exceptions

        :return:
            A list of asn1crypto.x509.Certificate objects
        """

        output = []
        for cert in certs:
            if isinstance(cert, x509.Certificate):
                output.append(cert)
            else:
                if not isinstance(cert, byte_cls):
                    raise TypeError(pretty_message(
                        '''
                        %s must contain only byte strings or
                        asn1crypto.x509.Certificate objects, not %s
                        ''',
                        var_name,
                        type_name(cert)
                    ))
                if pem.detect(cert):
                    _, _, cert = pem.unarmor(cert)
                output.append(x509.Certificate.load(cert))
        return output

    def is_ca(self, cert):
        """
        Checks if a certificate is in the list of CA certs in this registry

        :param cert:
            An asn1crypto.x509.Certificate object

        :return:
            A boolean - if the certificate is in the CA list
        """

        return self._ca_lookup.get(cert.signature, False)

    def add_other_cert(self, cert):
        """
        Allows adding an "other" cert that is obtained from doing revocation
        check via OCSP or CRL, or some other method

        :param cert:
            An asn1crypto.x509.Certificate object or a byte string of a DER or
            PEM-encoded certificate

        :return:
            A boolean indicating if the certificate was added - will return
            False if the certificate was already present
        """

        if not isinstance(cert, x509.Certificate):
            if not isinstance(cert, byte_cls):
                raise TypeError(pretty_message(
                    '''
                    cert must be a byte string or an instance of
                    asn1crypto.x509.Certificate, not %s
                    ''',
                    type_name(cert)
                ))
            if pem.detect(cert):
                _, _, cert = pem.unarmor(cert)
            cert = x509.Certificate.load(cert)

        hashable = cert.subject.hashable
        if hashable not in self._subject_map:
            self._subject_map[hashable] = []

        # Don't add the cert if we already have it
        else:
            serial_number = cert.serial_number
            for existing_cert in self._subject_map[hashable]:
                if existing_cert.serial_number == serial_number:
                    return False

        self._subject_map[hashable].append(cert)
        if cert.key_identifier:
            self._key_identifier_map[cert.key_identifier] = cert
        else:
            self._key_identifier_map[cert.public_key.sha1] = cert

        return True

    def retrieve_by_key_identifier(self, key_identifier):
        """
        Retrieves a cert via its key identifier

        :param key_identifier:
            A byte string of the key identifier

        :return:
            None or an asn1crypto.x509.Certificate object
        """

        if not isinstance(key_identifier, byte_cls):
            raise TypeError(pretty_message(
                '''
                key_identifier must be a byte string, not %s
                ''',
                type_name(key_identifier)
            ))

        return self._key_identifier_map.get(key_identifier)

    def retrieve_by_name(self, name, first_certificate=None):
        """
        Retrieves a list certs via their subject name

        :param name:
            An asn1crypto.x509.Name object

        :param first_certificate:
            An asn1crypto.x509.Certificate object that if found, should be
            placed first in the result list

        :return:
            A list of asn1crypto.x509.Certificate objects
        """

        if not isinstance(name, x509.Name):
            raise TypeError(pretty_message(
                '''
                name must be an instance of asn1crypto.x509.Name, not %s
                ''',
                type_name(name)
            ))

        if first_certificate and not isinstance(first_certificate, x509.Certificate):
            raise TypeError(pretty_message(
                '''
                first_certificate must be an instance of
                asn1crypto.x509.Certificate, not %s
                ''',
                type_name(first_certificate)
            ))

        hashable = name.hashable

        if hashable not in self._subject_map:
            return []

        certs = self._subject_map[hashable]
        first = None
        output = []
        for cert in certs:
            if first_certificate and first_certificate.sha256 == cert.sha256:
                first = cert
            else:
                output.append(cert)
        if first:
            output.insert(0, first)
        return output

    def build_paths(self, end_entity_cert):
        """
        Builds a list of ValidationPath objects from a certificate in the
        operating system trust store to the end-entity certificate

        :param end_entity_cert:
            A byte string of a DER or PEM-encoded X.509 certificate, or an
            instance of asn1crypto.x509.Certificate

        :return:
            A list of certvalidator.path.ValidationPath objects that represent
            the possible paths from the end-entity certificate to one of the CA
            certs.
        """

        if not isinstance(end_entity_cert, byte_cls) and not isinstance(end_entity_cert, x509.Certificate):
            raise TypeError(pretty_message(
                '''
                end_entity_cert must be a byte string or an instance of
                asn1crypto.x509.Certificate, not %s
                ''',
                type_name(end_entity_cert)
            ))

        if isinstance(end_entity_cert, byte_cls):
            if pem.detect(end_entity_cert):
                _, _, end_entity_cert = pem.unarmor(end_entity_cert)
            end_entity_cert = x509.Certificate.load(end_entity_cert)

        path = ValidationPath(end_entity_cert)
        paths = []
        failed_paths = []

        self._walk_issuers(path, paths, failed_paths)

        if len(paths) == 0:
            cert_name = end_entity_cert.subject.human_friendly
            missing_issuer_name = failed_paths[0].first.issuer.human_friendly
            raise PathBuildingError(pretty_message(
                '''
                Unable to build a validation path for the certificate "%s" - no
                issuer matching "%s" was found
                ''',
                cert_name,
                missing_issuer_name
            ))

        return paths

    def _walk_issuers(self, path, paths, failed_paths):
        """
        Recursively looks through the list of known certificates for the issuer
        of the certificate specified, stopping once the certificate in question
        is one contained within the CA certs list

        :param path:
            A ValidationPath object representing the current traversal of
            possible paths

        :param paths:
            A list of completed ValidationPath objects. This is mutated as
            results are found.

        :param failed_paths:
            A list of certvalidator.path.ValidationPath objects that failed due
            to no matching issuer before reaching a certificate from the CA
            certs list
        """

        if path.first.signature in self._ca_lookup:
            paths.append(path)
            return

        new_branches = 0
        for issuer in self._possible_issuers(path.first):
            try:
                self._walk_issuers(path.copy().prepend(issuer), paths, failed_paths)
                new_branches += 1
            except (DuplicateCertificateError):
                pass

        if not new_branches:
            failed_paths.append(path)

    def _possible_issuers(self, cert):
        """
        Returns a generator that will list all possible issuers for the cert

        :param cert:
            An asn1crypto.x509.Certificate object to find the issuer of
        """

        issuer_hashable = cert.issuer.hashable
        if issuer_hashable not in self._subject_map:
            return

        for issuer in self._subject_map[issuer_hashable]:
            # Info from the authority key identifier extension can be used to
            # eliminate possible options when multiple keys with the same
            # subject exist, such as during a transition, or with cross-signing.
            if cert.authority_key_identifier and issuer.key_identifier:
                if cert.authority_key_identifier != issuer.key_identifier:
                    continue
            elif cert.authority_issuer_serial:
                if cert.authority_issuer_serial != issuer.issuer_serial:
                    continue

            yield issuer
<commit_msg>Fixed compatiblity with oscrypto 0.16.0<commit_after># coding: utf-8
from __future__ import unicode_literals, division, absolute_import, print_function

from asn1crypto import pem, x509
from oscrypto import trust_list

from ._errors import pretty_message
from ._types import byte_cls, type_name
from .errors import PathBuildingError, DuplicateCertificateError
from .path import ValidationPath

class CertificateRegistry():
    """
    Contains certificate lists used to build validation paths
    """

    # A dict with keys being asn1crypto.x509.Certificate.Name.hashable byte
    # string. Each value is a list of asn1crypto.x509.Certificate objects.
    _subject_map = None

    # A dict with keys being asn1crypto.x509.Certificate.key_identifier byte
    # string. Each value is an asn1crypto.x509.Certificate object.
    _key_identifier_map = None

    # A dict with keys being asn1crypto.x509.Certificate.signature byte string.
    # Each value is a bool - if the certificate is a CA cert.
    _ca_lookup = None

    def __init__(self, trust_roots=None, extra_trust_roots=None, other_certs=None):
        """
        :param trust_roots:
            If the operating system's trust list should not be used, instead
            pass a list of byte strings containing DER or PEM-encoded X.509
            certificates, or asn1crypto.x509.Certificate objects. These
            certificates will be used as the trust roots for the path being
            built.

        :param extra_trust_roots:
            If the operating system's trust list should be used, but augmented
            with one or more extra certificates. This should be a list of byte
            strings containing DER or PEM-encoded X.509 certificates, or
            asn1crypto.x509.Certificate objects.

        :param other_certs:
            A list of byte strings containing DER or PEM-encoded X.509
            certificates, or a list of asn1crypto.x509.Certificate objects.
            These other certs are usually provided by the service/item being
            validated. In SSL, these would be intermediate chain certs.
        """

        if trust_roots is not None and not isinstance(trust_roots, list):
            raise TypeError(pretty_message(
                '''
                trust_roots must be a list of byte strings or
                asn1crypto.x509.Certificate objects, not %s
                ''',
                type_name(trust_roots)
            ))

        if extra_trust_roots is not None and not isinstance(extra_trust_roots, list):
            raise TypeError(pretty_message(
                '''
                extra_trust_roots must be a list of byte strings or
                asn1crypto.x509.Certificate objects, not %s
                ''',
                type_name(extra_trust_roots)
            ))

        if other_certs is not None and not isinstance(other_certs, list):
            raise TypeError(pretty_message(
                '''
                other_certs must be a list of byte strings or
                asn1crypto.x509.Certificate objects, not %s
                ''',
                type_name(other_certs)
            ))

        if other_certs is None:
            other_certs = []
        else:
            other_certs = self._validate_unarmor(other_certs, 'other_certs')

        if trust_roots is None:
            trust_roots = [e[0] for e in trust_list.get_list()]
        else:
            trust_roots = self._validate_unarmor(trust_roots, 'trust_roots')

        if extra_trust_roots is not None:
            trust_roots.extend(self._validate_unarmor(extra_trust_roots, 'extra_trust_roots'))

        self._subject_map = {}
        self._key_identifier_map = {}
        self._ca_lookup = {}

        for trust_root in trust_roots:
            hashable = trust_root.subject.hashable
            if hashable not in self._subject_map:
                self._subject_map[hashable] = []
            self._subject_map[hashable].append(trust_root)
            if trust_root.key_identifier:
                self._key_identifier_map[trust_root.key_identifier] = trust_root
            self._ca_lookup[trust_root.signature] = True

        for other_cert in other_certs:
            hashable = other_cert.subject.hashable
            if hashable not in self._subject_map:
                self._subject_map[hashable] = []
            self._subject_map[hashable].append(other_cert)
            if other_cert.key_identifier:
                self._key_identifier_map[other_cert.key_identifier] = other_cert

    def _validate_unarmor(self, certs, var_name):
        """
        Takes a list of byte strings or asn1crypto.x509.Certificates objects,
        validates and loads them while unarmoring any PEM-encoded contents

        :param certs:
            A list of byte strings or asn1crypto.x509.Certificate objects

        :param var_name:
            A unicode variable name to use in any TypeError exceptions

        :return:
            A list of asn1crypto.x509.Certificate objects
        """

        output = []
        for cert in certs:
            if isinstance(cert, x509.Certificate):
                output.append(cert)
            else:
                if not isinstance(cert, byte_cls):
                    raise TypeError(pretty_message(
                        '''
                        %s must contain only byte strings or
                        asn1crypto.x509.Certificate objects, not %s
                        ''',
                        var_name,
                        type_name(cert)
                    ))
                if pem.detect(cert):
                    _, _, cert = pem.unarmor(cert)
                output.append(x509.Certificate.load(cert))
        return output

    def is_ca(self, cert):
        """
        Checks if a certificate is in the list of CA certs in this registry

        :param cert:
            An asn1crypto.x509.Certificate object

        :return:
            A boolean - if the certificate is in the CA list
        """

        return self._ca_lookup.get(cert.signature, False)

    def add_other_cert(self, cert):
        """
        Allows adding an "other" cert that is obtained from doing revocation
        check via OCSP or CRL, or some other method

        :param cert:
            An asn1crypto.x509.Certificate object or a byte string of a DER or
            PEM-encoded certificate

        :return:
            A boolean indicating if the certificate was added - will return
            False if the certificate was already present
        """

        if not isinstance(cert, x509.Certificate):
            if not isinstance(cert, byte_cls):
                raise TypeError(pretty_message(
                    '''
                    cert must be a byte string or an instance of
                    asn1crypto.x509.Certificate, not %s
                    ''',
                    type_name(cert)
                ))
            if pem.detect(cert):
                _, _, cert = pem.unarmor(cert)
            cert = x509.Certificate.load(cert)

        hashable = cert.subject.hashable
        if hashable not in self._subject_map:
            self._subject_map[hashable] = []

        # Don't add the cert if we already have it
        else:
            serial_number = cert.serial_number
            for existing_cert in self._subject_map[hashable]:
                if existing_cert.serial_number == serial_number:
                    return False

        self._subject_map[hashable].append(cert)
        if cert.key_identifier:
            self._key_identifier_map[cert.key_identifier] = cert
        else:
            self._key_identifier_map[cert.public_key.sha1] = cert

        return True

    def retrieve_by_key_identifier(self, key_identifier):
        """
        Retrieves a cert via its key identifier

        :param key_identifier:
            A byte string of the key identifier

        :return:
            None or an asn1crypto.x509.Certificate object
        """

        if not isinstance(key_identifier, byte_cls):
            raise TypeError(pretty_message(
                '''
                key_identifier must be a byte string, not %s
                ''',
                type_name(key_identifier)
            ))

        return self._key_identifier_map.get(key_identifier)

    def retrieve_by_name(self, name, first_certificate=None):
        """
        Retrieves a list certs via their subject name

        :param name:
            An asn1crypto.x509.Name object

        :param first_certificate:
            An asn1crypto.x509.Certificate object that if found, should be
            placed first in the result list

        :return:
            A list of asn1crypto.x509.Certificate objects
        """

        if not isinstance(name, x509.Name):
            raise TypeError(pretty_message(
                '''
                name must be an instance of asn1crypto.x509.Name, not %s
                ''',
                type_name(name)
            ))

        if first_certificate and not isinstance(first_certificate, x509.Certificate):
            raise TypeError(pretty_message(
                '''
                first_certificate must be an instance of
                asn1crypto.x509.Certificate, not %s
                ''',
                type_name(first_certificate)
            ))

        hashable = name.hashable

        if hashable not in self._subject_map:
            return []

        certs = self._subject_map[hashable]
        first = None
        output = []
        for cert in certs:
            if first_certificate and first_certificate.sha256 == cert.sha256:
                first = cert
            else:
                output.append(cert)
        if first:
            output.insert(0, first)
        return output

    def build_paths(self, end_entity_cert):
        """
        Builds a list of ValidationPath objects from a certificate in the
        operating system trust store to the end-entity certificate

        :param end_entity_cert:
            A byte string of a DER or PEM-encoded X.509 certificate, or an
            instance of asn1crypto.x509.Certificate

        :return:
            A list of certvalidator.path.ValidationPath objects that represent
            the possible paths from the end-entity certificate to one of the CA
            certs.
        """

        if not isinstance(end_entity_cert, byte_cls) and not isinstance(end_entity_cert, x509.Certificate):
            raise TypeError(pretty_message(
                '''
                end_entity_cert must be a byte string or an instance of
                asn1crypto.x509.Certificate, not %s
                ''',
                type_name(end_entity_cert)
            ))

        if isinstance(end_entity_cert, byte_cls):
            if pem.detect(end_entity_cert):
                _, _, end_entity_cert = pem.unarmor(end_entity_cert)
            end_entity_cert = x509.Certificate.load(end_entity_cert)

        path = ValidationPath(end_entity_cert)
        paths = []
        failed_paths = []

        self._walk_issuers(path, paths, failed_paths)

        if len(paths) == 0:
            cert_name = end_entity_cert.subject.human_friendly
            missing_issuer_name = failed_paths[0].first.issuer.human_friendly
            raise PathBuildingError(pretty_message(
                '''
                Unable to build a validation path for the certificate "%s" - no
                issuer matching "%s" was found
                ''',
                cert_name,
                missing_issuer_name
            ))

        return paths

    def _walk_issuers(self, path, paths, failed_paths):
        """
        Recursively looks through the list of known certificates for the issuer
        of the certificate specified, stopping once the certificate in question
        is one contained within the CA certs list

        :param path:
            A ValidationPath object representing the current traversal of
            possible paths

        :param paths:
            A list of completed ValidationPath objects. This is mutated as
            results are found.

        :param failed_paths:
            A list of certvalidator.path.ValidationPath objects that failed due
            to no matching issuer before reaching a certificate from the CA
            certs list
        """

        if path.first.signature in self._ca_lookup:
            paths.append(path)
            return

        new_branches = 0
        for issuer in self._possible_issuers(path.first):
            try:
                self._walk_issuers(path.copy().prepend(issuer), paths, failed_paths)
                new_branches += 1
            except (DuplicateCertificateError):
                pass

        if not new_branches:
            failed_paths.append(path)

    def _possible_issuers(self, cert):
        """
        Returns a generator that will list all possible issuers for the cert

        :param cert:
            An asn1crypto.x509.Certificate object to find the issuer of
        """

        issuer_hashable = cert.issuer.hashable
        if issuer_hashable not in self._subject_map:
            return

        for issuer in self._subject_map[issuer_hashable]:
            # Info from the authority key identifier extension can be used to
            # eliminate possible options when multiple keys with the same
            # subject exist, such as during a transition, or with cross-signing.
            if cert.authority_key_identifier and issuer.key_identifier:
                if cert.authority_key_identifier != issuer.key_identifier:
                    continue
            elif cert.authority_issuer_serial:
                if cert.authority_issuer_serial != issuer.issuer_serial:
                    continue

            yield issuer
<|endoftext|>


<commit_before>#!/usr/bin/python3
import sys
import json
import os
import configparser
import urllib.request, urllib.parse, urllib.error

LIBDIR = 'assets/bibs/'
TYPES = ['NONE', 'BOOK', 'CD', 'CD_SOFTWARE', 'CD_MUSIC', 'DVD', 'MOVIE', 'AUDIOBOOK', 'PACKAGE',
		'GAME_CONSOLE', 'EBOOK', 'SCORE_MUSIC', 'PACKAGE_BOOKS', 'UNKNOWN', 'NEWSPAPER',
		'BOARDGAME', 'SCHOOL_VERSION', 'MAP', 'BLURAY', 'AUDIO_CASSETTE', 'ART', 'MAGAZINE',
		'GAME_CONSOLE_WII', 'GAME_CONSOLE_NINTENDO', 'GAME_CONSOLE_PLAYSTATION',
		'GAME_CONSOLE_XBOX', 'LP_RECORD', 'MP3', 'URL']

def getInput(required = False, default = None):
	if default is not None:
		print("[Standard: %s]" % default, end=" ")
	print("> ", end="")
	inp = input().strip()
	if default is not None and (inp is None or inp == ''):
		return default
	if required and (inp is None or inp == ''):
		print("Feld muss gefüllt werden.")
		return getInput(required=required, default=default)
	if inp is None or inp == '':
		return None
	return inp

def loadGeoPossibilities(city):
	uri = 'https://maps.googleapis.com/maps/api/geocode/json?'+urllib.parse.urlencode({'address':city,'sensor':'false'})
	jsoncontent = urllib.request.urlopen(uri).read().decode()
	geocode = json.loads(jsoncontent)

	if geocode['status'] != 'OK':
		print("ERROR! %s" %filename)
		return False

	possibilities = []

	for res in geocode['results']:
		possibilities.append(
				(
					", ".join([a["long_name"] for a in res['address_components']]),
					[float(res['geometry']['location']['lat']), float(res['geometry']['location']['lng'])]
				)
			)
	return possibilities

class Api:

	def getDefaultSupportString(self):
		return 'Katalogsuche und Konto'

	def prompt(self, data):
		return data

class Bibliotheca(Api):

	def getDefaultSupportString(self):
		return 'Katalogsuche und Konto'

	def prompt(self, data):
		datadata = data['data']
		global TYPES

		print("Muss eine bestimmte Datenbank geladen werden?")
		print("Enter drücken, wenn nicht benötigt.")
		inp = getInput(required=False)
		suffix = ''
		if inp is not None:
			datadata['db'] = inp

			print("Welchen Suffix hat diese Datenbank im System? (Bsp. _DB1)")
			inp = getInput(required=False, default='')
			if inp is not None:
				suffix = inp

		fetched = None

		try:
			fetched = self._fetchData(datadata['baseurl'], suffix)
			datadata['accounttable'] = fetched['accounttable']
			datadata['reservationtable'] = fetched['reservationtable']
			datadata['copiestable'] = fetched['copiestable']
		except:
			print("WARNUNG! Konfiguration konnte nicht ausgelesen werden. HANDARBEIT NÖTIG!")
			print("Mehr Informationen:")
			print("https://github.com/raphaelm/opacclient/wiki/Supported-library-types#bibliotheca")

		if fetched is not None:
			if len(fetched['mediatypes']) > 0:
				print("Bitte weise die Medientypen ihren Entsprechungen in der App zu.")
				print("Verfügbar sind:")
				print(" ".join(TYPES))
				print("")
				datadata['mediatypes'] = {}
				for k, v in fetched['mediatypes'].items():
					inp = ''
					while inp not in TYPES:
						print("'%s' ('%s')?" % (v, k))
						inp = getInput(required=True)
						datadata['mediatypes'][k] = inp

		data['data'] = datadata
		return data

	def _fetchData(self, url, suff = ''):
		config = configparser.RawConfigParser(allow_no_value=True)
		config.read_string(urllib.request.urlopen(url+'/w3oini.txt').read().decode('iso-8859-1'))
		data = {
			'accounttable': [-1]*8,
			'reservationtable': [-1]*6,
			'copiestable': [-1]*7
		}
		i_acc = 0
		i_res = 0
		for i in range(1,21):
			conf = config.get("ANZEIGEKONTOFELDER", "konto"+str(i))
			if conf == '': continue
			key = conf.split("#")[0]

			if key == 'buchungsnr':
				data['accounttable'][0] = i_acc
				i_acc += 1
			elif key == 'verf':
				data['accounttable'][1] = i_acc
				data['reservationtable'][0] = i_res
				i_acc += 1
				i_res += 1
			elif key == 'titel':
				data['accounttable'][2] = i_acc
				data['reservationtable'][1] = i_res
				i_acc += 1
				i_res += 1
			elif key == 'frist':
				data['accounttable'][3] = i_acc
				i_acc += 1
			elif key == 'bereit':
				data['reservationtable'][2] = i_res
				i_res += 1
			elif key == 'ausleihstatus':
				data['accounttable'][4] = i_acc
				i_acc += 1
			elif key == 'zwst':
				data['accounttable'][5] = i_acc
				data['reservationtable'][3] = i_res
				i_acc += 1
				i_res += 1
			elif key == 'ausleihstelle':
				data['accounttable'][6] = i_acc
				i_acc += 1
			elif key == 'mediengrp':
				i_acc += 1
			elif key == 'bereit bis':
				data['reservationtable'][5] = i_res
				i_res += 1
			else:
				print("WARNING! NOT COUNTING ", key, url)
				sys.exit(0)
		data['accounttable'][-1] = i_acc
		data['reservationtable'][-2] = i_res
		if data['accounttable'][6] == -1 and data['accounttable'][5] > 0:
			data['accounttable'][6] = data['accounttable'][5]

		i_copy = 0
		for i in range(1,11):
			conf = config.get("ANZEIGE_EXEMPLAR"+suff, "AE"+str(i))
			if conf == '': continue
			key = conf.split("#")[1]
			if key == 'buchungsnr':
				data['copiestable'][0] = i_copy
			elif key == 'zweigstelle':
				data['copiestable'][1] = i_copy
			elif key == 'standort2':
				data['copiestable'][2] = i_copy
			elif key == 'standort':
				data['copiestable'][3] = i_copy
			elif key == 'exemplarstatus':
				data['copiestable'][4] = i_copy
			elif key == 'rueckgabedatum':
				data['copiestable'][5] = i_copy
			elif key == 'Auslanzvorbestakt':
				data['copiestable'][6] = i_copy
			i_copy += 1

		data['mediatypes'] = {}
		for i in range(1,100):
			conf = config.get("ANZEIGE_MEDIGRPPIC", "MEDIGRPPIC"+str(i))
			if conf == '': continue
			split = conf.split("#")
			data['mediatypes'][split[1]] = split[2]
		return data

class Sisis(Api):

	def getDefaultSupportString(self):
		return 'Katalogsuche und Konto'

	def prompt(self, data):
		print("Sind zusätzliche Parameter nötig?")
		print("Ein häufiges Beispiel wäre sowas wie 'Login=opsb'")
		inp = getInput(required=False)
		if inp is not None:
			data['data']['startparams'] = inp
		return data

class Biber1992(Api):

	def getDefaultSupportString(self):
		return 'Katalogsuche'

	def prompt(self, data):
		print("WARNUNG! Konfiguration kann nicht ausgelesen werden. HANDARBEIT NÖTIG!")
		return data

class Zones22(Api):

	def getDefaultSupportString(self):
		return 'Katalogsuche'

APIS = {
		'bibliotheca' : Bibliotheca,
		'sisis'       : Sisis,
		'biber1992'   : Biber1992,
		'zones22'     : Zones22
	}

data = {}

if __name__ == '__main__':

	print("Hallo! Dieses Skript hilft dir, eine neue Bibliothek hinzuzufügen")

	if not os.path.isdir(LIBDIR):
		print("Bitte wechsle in das Verzeichnis, in dem die App liegt.")
		sys.exit(0)

	print("In welcher Stadt befindet sich die Bibliothek?")
	print("Suffixe wie 'Frankfurt (Main)' werden in Klammern gesetzt.")

	data['city'] = getInput(required=True)

	print("Lade Geodaten…")

	geo = loadGeoPossibilities(data['city'])
	for k, g in enumerate(geo):
		print("[%d]	%s" % (k+1, g[0]))

	print("Welche dieser Positionen trifft am besten zu? 0 für keine.")
	print("Nummer", end=" ")
	geokey = int(getInput(default="0"))
	if geokey > 0:
		data['geo'] = geo[geokey-1][1]

	print("Zu welcher Gruppe soll die Bibliothek gehören?")
	print("Aktuell benutzen wir für die Gruppennamen die deutschen Bundesländer sowie 'Österreich' und 'Schweiz'")

	data['group'] = getInput(required=True)

	print("Wie heißt die Bibliothek?")
	print("Dies sollte etwas in dieser Stadt eindeutiges sein wie 'Stadtbibliothek', 'Unibibliothek' oder 'Ruprecht-Karls-Universität'. Der Name der Stadt soll nicht erneut vorkommen!")

	data['title'] = getInput(default="Stadtbibliothek")

	print("Welche API-Implementierung wird genutzt?")
	print("Verfügbar sind: " + " ".join(APIS.keys()))

	data['api'] = ''
	while data['api'] not in APIS.keys():
		data['api'] = getInput(required=True)

	print("URL zum OPAC")
	print("Ohne abschließenden /")

	data['data'] = {}
	data['data']['baseurl'] = getInput(required=True)

	print("URL zu einer Informationsseite")
	print("Sollte Öffnungszeiten u.ä. enthalten")

	data['data']['information'] = getInput(required=True)

	api = APIS[data['api']]()
	data = api.prompt(data)

	print("Grad der Unterstützung")

	data['support'] = getInput(required=False, default=api.getDefaultSupportString())

	print("Dateiname")
	print("Sowas wie 'Mannheim' oder 'Heidelberg_Uni'. Möglichst keine Leerzeichen und Umlaute.")

	ident = getInput(required=True)

	print(json.dumps(data, indent=4, sort_keys=True), end="\n\n")
	json.dump(data, open(LIBDIR+ident+'.json', 'w'), sort_keys=True, indent=4)
	print("In Datei %s geschrieben." % (LIBDIR+ident+'.json'))
<commit_msg>add_library.py: Enable skipping media types<commit_after>#!/usr/bin/python3
import sys
import json
import os
import configparser
import urllib.request, urllib.parse, urllib.error

LIBDIR = 'assets/bibs/'
TYPES = ['NONE', 'BOOK', 'CD', 'CD_SOFTWARE', 'CD_MUSIC', 'DVD', 'MOVIE', 'AUDIOBOOK', 'PACKAGE',
		'GAME_CONSOLE', 'EBOOK', 'SCORE_MUSIC', 'PACKAGE_BOOKS', 'UNKNOWN', 'NEWSPAPER',
		'BOARDGAME', 'SCHOOL_VERSION', 'MAP', 'BLURAY', 'AUDIO_CASSETTE', 'ART', 'MAGAZINE',
		'GAME_CONSOLE_WII', 'GAME_CONSOLE_NINTENDO', 'GAME_CONSOLE_PLAYSTATION',
		'GAME_CONSOLE_XBOX', 'LP_RECORD', 'MP3', 'URL']

def getInput(required = False, default = None):
	if default is not None:
		print("[Standard: %s]" % default, end=" ")
	print("> ", end="")
	inp = input().strip()
	if default is not None and (inp is None or inp == ''):
		return default
	if required and (inp is None or inp == ''):
		print("Feld muss gefüllt werden.")
		return getInput(required=required, default=default)
	if inp is None or inp == '':
		return None
	return inp

def loadGeoPossibilities(city):
	uri = 'https://maps.googleapis.com/maps/api/geocode/json?'+urllib.parse.urlencode({'address':city,'sensor':'false'})
	jsoncontent = urllib.request.urlopen(uri).read().decode()
	geocode = json.loads(jsoncontent)

	if geocode['status'] != 'OK':
		print("ERROR! %s" %filename)
		return False

	possibilities = []

	for res in geocode['results']:
		possibilities.append(
				(
					", ".join([a["long_name"] for a in res['address_components']]),
					[float(res['geometry']['location']['lat']), float(res['geometry']['location']['lng'])]
				)
			)
	return possibilities

class Api:

	def getDefaultSupportString(self):
		return 'Katalogsuche und Konto'

	def prompt(self, data):
		return data

class Bibliotheca(Api):

	def getDefaultSupportString(self):
		return 'Katalogsuche und Konto'

	def prompt(self, data):
		datadata = data['data']
		global TYPES

		print("Muss eine bestimmte Datenbank geladen werden?")
		print("Enter drücken, wenn nicht benötigt.")
		inp = getInput(required=False)
		suffix = ''
		if inp is not None:
			datadata['db'] = inp

			print("Welchen Suffix hat diese Datenbank im System? (Bsp. _DB1)")
			inp = getInput(required=False, default='')
			if inp is not None:
				suffix = inp

		fetched = None

		try:
			fetched = self._fetchData(datadata['baseurl'], suffix)
			datadata['accounttable'] = fetched['accounttable']
			datadata['reservationtable'] = fetched['reservationtable']
			datadata['copiestable'] = fetched['copiestable']
		except:
			print("WARNUNG! Konfiguration konnte nicht ausgelesen werden. HANDARBEIT NÖTIG!")
			print("Mehr Informationen:")
			print("https://github.com/raphaelm/opacclient/wiki/Supported-library-types#bibliotheca")

		if fetched is not None:
			if len(fetched['mediatypes']) > 0:
				print("Bitte weise die Medientypen ihren Entsprechungen in der App zu.")
				print("Verfügbar sind:")
				print(" ".join(TYPES))
				print("")
				datadata['mediatypes'] = {}
				for k, v in fetched['mediatypes'].items():
					inp = ''
					while inp not in TYPES:
						print("'%s' ('%s')?" % (v, k))
						inp = getInput(required=False, default="UNKNOWN")
						datadata['mediatypes'][k] = inp

		data['data'] = datadata
		return data

	def _fetchData(self, url, suff = ''):
		config = configparser.RawConfigParser(allow_no_value=True)
		config.read_string(urllib.request.urlopen(url+'/w3oini.txt').read().decode('iso-8859-1'))
		data = {
			'accounttable': [-1]*8,
			'reservationtable': [-1]*6,
			'copiestable': [-1]*7
		}
		i_acc = 0
		i_res = 0
		for i in range(1,21):
			conf = config.get("ANZEIGEKONTOFELDER", "konto"+str(i))
			if conf == '': continue
			key = conf.split("#")[0]

			if key == 'buchungsnr':
				data['accounttable'][0] = i_acc
				i_acc += 1
			elif key == 'verf':
				data['accounttable'][1] = i_acc
				data['reservationtable'][0] = i_res
				i_acc += 1
				i_res += 1
			elif key == 'titel':
				data['accounttable'][2] = i_acc
				data['reservationtable'][1] = i_res
				i_acc += 1
				i_res += 1
			elif key == 'frist':
				data['accounttable'][3] = i_acc
				i_acc += 1
			elif key == 'bereit':
				data['reservationtable'][2] = i_res
				i_res += 1
			elif key == 'ausleihstatus':
				data['accounttable'][4] = i_acc
				i_acc += 1
			elif key == 'zwst':
				data['accounttable'][5] = i_acc
				data['reservationtable'][3] = i_res
				i_acc += 1
				i_res += 1
			elif key == 'ausleihstelle':
				data['accounttable'][6] = i_acc
				i_acc += 1
			elif key == 'mediengrp':
				i_acc += 1
			elif key == 'bereit bis':
				data['reservationtable'][5] = i_res
				i_res += 1
			else:
				print("WARNING! NOT COUNTING ", key, url)
				sys.exit(0)
		data['accounttable'][-1] = i_acc
		data['reservationtable'][-2] = i_res
		if data['accounttable'][6] == -1 and data['accounttable'][5] > 0:
			data['accounttable'][6] = data['accounttable'][5]

		i_copy = 0
		for i in range(1,11):
			conf = config.get("ANZEIGE_EXEMPLAR"+suff, "AE"+str(i))
			if conf == '': continue
			key = conf.split("#")[1]
			if key == 'buchungsnr':
				data['copiestable'][0] = i_copy
			elif key == 'zweigstelle':
				data['copiestable'][1] = i_copy
			elif key == 'standort2':
				data['copiestable'][2] = i_copy
			elif key == 'standort':
				data['copiestable'][3] = i_copy
			elif key == 'exemplarstatus':
				data['copiestable'][4] = i_copy
			elif key == 'rueckgabedatum':
				data['copiestable'][5] = i_copy
			elif key == 'Auslanzvorbestakt':
				data['copiestable'][6] = i_copy
			i_copy += 1

		data['mediatypes'] = {}
		for i in range(1,100):
			conf = config.get("ANZEIGE_MEDIGRPPIC", "MEDIGRPPIC"+str(i))
			if conf == '': continue
			split = conf.split("#")
			data['mediatypes'][split[1]] = split[2]
		return data

class Sisis(Api):

	def getDefaultSupportString(self):
		return 'Katalogsuche und Konto'

	def prompt(self, data):
		print("Sind zusätzliche Parameter nötig?")
		print("Ein häufiges Beispiel wäre sowas wie 'Login=opsb'")
		inp = getInput(required=False)
		if inp is not None:
			data['data']['startparams'] = inp
		return data

class Biber1992(Api):

	def getDefaultSupportString(self):
		return 'Katalogsuche'

	def prompt(self, data):
		print("WARNUNG! Konfiguration kann nicht ausgelesen werden. HANDARBEIT NÖTIG!")
		return data

class Zones22(Api):

	def getDefaultSupportString(self):
		return 'Katalogsuche'

APIS = {
		'bibliotheca' : Bibliotheca,
		'sisis'       : Sisis,
		'biber1992'   : Biber1992,
		'zones22'     : Zones22
	}

data = {}

if __name__ == '__main__':

	print("Hallo! Dieses Skript hilft dir, eine neue Bibliothek hinzuzufügen")

	if not os.path.isdir(LIBDIR):
		print("Bitte wechsle in das Verzeichnis, in dem die App liegt.")
		sys.exit(0)

	print("In welcher Stadt befindet sich die Bibliothek?")
	print("Suffixe wie 'Frankfurt (Main)' werden in Klammern gesetzt.")

	data['city'] = getInput(required=True)

	print("Lade Geodaten…")

	geo = loadGeoPossibilities(data['city'])
	for k, g in enumerate(geo):
		print("[%d]	%s" % (k+1, g[0]))

	print("Welche dieser Positionen trifft am besten zu? 0 für keine.")
	print("Nummer", end=" ")
	geokey = int(getInput(default="0"))
	if geokey > 0:
		data['geo'] = geo[geokey-1][1]

	print("Zu welcher Gruppe soll die Bibliothek gehören?")
	print("Aktuell benutzen wir für die Gruppennamen die deutschen Bundesländer sowie 'Österreich' und 'Schweiz'")

	data['group'] = getInput(required=True)

	print("Wie heißt die Bibliothek?")
	print("Dies sollte etwas in dieser Stadt eindeutiges sein wie 'Stadtbibliothek', 'Unibibliothek' oder 'Ruprecht-Karls-Universität'. Der Name der Stadt soll nicht erneut vorkommen!")

	data['title'] = getInput(default="Stadtbibliothek")

	print("Welche API-Implementierung wird genutzt?")
	print("Verfügbar sind: " + " ".join(APIS.keys()))

	data['api'] = ''
	while data['api'] not in APIS.keys():
		data['api'] = getInput(required=True)

	print("URL zum OPAC")
	print("Ohne abschließenden /")

	data['data'] = {}
	data['data']['baseurl'] = getInput(required=True)

	print("URL zu einer Informationsseite")
	print("Sollte Öffnungszeiten u.ä. enthalten")

	data['data']['information'] = getInput(required=True)

	api = APIS[data['api']]()
	data = api.prompt(data)

	print("Grad der Unterstützung")

	data['support'] = getInput(required=False, default=api.getDefaultSupportString())

	print("Dateiname")
	print("Sowas wie 'Mannheim' oder 'Heidelberg_Uni'. Möglichst keine Leerzeichen und Umlaute.")

	ident = getInput(required=True)

	print(json.dumps(data, indent=4, sort_keys=True), end="\n\n")
	json.dump(data, open(LIBDIR+ident+'.json', 'w'), sort_keys=True, indent=4)
	print("In Datei %s geschrieben." % (LIBDIR+ident+'.json'))
<|endoftext|>


<commit_before>#!/mesonet/python/bin/python
# Generate web output for precip data
# Daryl Herzmann 06 Mar 2003
#  7 Mar 2003:	Do some more cleanups

import pg, stationTable, mx.DateTime, cgi
mydb = pg.connect("snet", "10.10.10.20", 5432)
st = stationTable.stationTable("/mesonet/TABLES/snet.stns")

requireHrs = [0]*25
stData = {}
totp = {}

# Return the Date we will be looking for...
def doHeader():
  print 'Content-type: text/html \n\n'
  print """

  <title>IEM | Hourly Precip Grid</title>

Iowa Mesonet >
School Net >
Hourly Precipitation [SNET]

This archive starts on 12 Feb 2002 for KCCI sites and 12 Sept 2002 for
KELO sites.  Data from the previous day is the most current available.

"""
  print '
Hourly Precip Grid
'
  form = cgi.FormContent()
  try:
    postDate = form["date"][0]
    myTime = mx.DateTime.strptime(postDate, "%Y-%m-%d")
  except:
    myTime = mx.DateTime.now() + mx.DateTime.RelativeDateTime(days=-1)

  print '

'
  print 'Back:  \
    '+ (myTime - 1 ).strftime("%Y-%m-%d") +'	'

  print 'Shown: '+ myTime.strftime("%d %B %Y") +'	'

  print 'Forward:  \
    '+ (myTime + 1 ).strftime("%Y-%m-%d") +'	'

  print """
Pick: (yyyy-mm-dd)
<form method="GET" action="catSNET.py">
<input type="text" size="8" name="date">
<input type="submit" value="Submit Date">
</form>

"""
  return myTime

def setupTable():
  print """

  Station
  Morning (AM)

  Afternoon/Evening (PM)

   Station

  Mid	 1	 2
  3	 4	 5
  6	 7	 8
  9	 10	 11
  Noon	 1	 2
  3	 4	 5
  6	 7	 8
  9	 10	 11
  Tot:

"""
def loadstations():
  for station in st.ids:
    stData[station] = ["M"]*25
    totp[station] = 0

def Main():
  ts = doHeader()
  loadstations()
  setupTable()

  sqlDate = ts.strftime("%Y-%m-%d")
  td = ts.strftime("%Y-%m-%d")
  tm = (ts + 1).strftime("%Y-%m-%d")

   ##
   #  Hack, since postgres won't index date(valid)

  sqlStr = "SELECT extract('hour' from valid) as vhour, \
    station, valid, p01i from t"+ sqlDate[:4] +"_hp WHERE \
    valid = '"+td+" 01:00' or valid = '"+td+" 02:00' or \
    valid = '"+td+" 03:00' or valid = '"+td+" 04:00' or \
    valid = '"+td+" 05:00' or valid = '"+td+" 06:00' or \
    valid = '"+td+" 07:00' or valid = '"+td+" 08:00' or \
    valid = '"+td+" 09:00' or valid = '"+td+" 10:00' or \
    valid = '"+td+" 11:00' or valid = '"+td+" 12:00' or \
    valid = '"+td+" 13:00' or valid = '"+td+" 14:00' or \
    valid = '"+td+" 15:00' or valid = '"+td+" 16:00' or \
    valid = '"+td+" 17:00' or valid = '"+td+" 18:00' or \
    valid = '"+td+" 19:00' or valid = '"+td+" 20:00' or \
    valid = '"+td+" 21:00' or valid = '"+td+" 22:00' or \
    valid = '"+td+" 23:00' or valid = '"+td+" 00:00'  "

  rs = ""
  try:
    rs = mydb.query(sqlStr).dictresult()
  except:
    print "
Error: No data for that year"

  for i in range(len(rs)):
    p01i = float(rs[i]["p01i"])
    vhour = int(rs[i]["vhour"])
#    if (vhour == 0):
#      vhour = 24
    if (p01i >= 0):  # We should require this timestep
      requireHrs[ vhour ] = 1
      try:
        stData[ rs[i]["station"] ][ vhour ]  = round(p01i,2)
      except KeyError:
        continue
    else:
      try:
        stData[ rs[i]["station"] ][ vhour ]  = " "
      except KeyError:
        continue

  for j in range(len(st.ids)):
    station = st.ids[j]
    print "
",
    print "%s%s%s" % ("", st.sts[station]['name'], "	") ,
    for i in range(0,24):
      print "",
      print "%s%s " % ( stData[station][i], "	") ,
      try:
        totp[station] = totp[station] + stData[station][i]
      except:
        continue
    print "%s%s%s" % ("", totp[station], "	") ,
    print "%s%s%s" % ("", station, "	") ,
    print "
"

  print """

Precipitation values are shown for the hour in which they are valid.  For
example, the value in the 1AM column is precipitation accumulation from 1 AM
till 2 AM.
"""
Main()
<commit_msg>Fix bad code<commit_after>#!/mesonet/python/bin/python
# Generate web output for precip data
# Daryl Herzmann 06 Mar 2003
#  7 Mar 2003:	Do some more cleanups

import pg, mx.DateTime, cgi
from pyIEM import stationTable, iemdb
i = iemdb.iemdb()
mydb = i["snet"]
st = stationTable.stationTable("/mesonet/TABLES/snet.stns")

requireHrs = [0]*25
stData = {}
totp = {}

# Return the Date we will be looking for...
def doHeader():
  print 'Content-type: text/html \n\n'
  print """

  <title>IEM | Hourly Precip Grid</title>

Iowa Mesonet >
School Net >
Hourly Precipitation [SNET]

This archive starts on 12 Feb 2002 for KCCI sites and 12 Sept 2002 for
KELO sites.  Data from the previous day is the most current available.

"""
  print '
Hourly Precip Grid
'
  form = cgi.FormContent()
  try:
    postDate = form["date"][0]
    myTime = mx.DateTime.strptime(postDate, "%Y-%m-%d")
  except:
    myTime = mx.DateTime.now() + mx.DateTime.RelativeDateTime(days=-1)

  print '

'
  print 'Back:  \
    '+ (myTime - 1 ).strftime("%Y-%m-%d") +'	'

  print 'Shown: '+ myTime.strftime("%d %B %Y") +'	'

  print 'Forward:  \
    '+ (myTime + 1 ).strftime("%Y-%m-%d") +'	'

  print """
Pick: (yyyy-mm-dd)
<form method="GET" action="catSNET.py">
<input type="text" size="8" name="date">
<input type="submit" value="Submit Date">
</form>

"""
  return myTime

def setupTable():
  print """

  Station
  Morning (AM)

  Afternoon/Evening (PM)

   Station

  Mid	 1	 2
  3	 4	 5
  6	 7	 8
  9	 10	 11
  Noon	 1	 2
  3	 4	 5
  6	 7	 8
  9	 10	 11
  Tot:

"""
def loadstations():
  for station in st.ids:
    stData[station] = ["M"]*25
    totp[station] = 0

def Main():
  ts = doHeader()
  loadstations()
  setupTable()

  sqlDate = ts.strftime("%Y-%m-%d")
  td = ts.strftime("%Y-%m-%d")
  tm = (ts + 1).strftime("%Y-%m-%d")

   ##
   #  Hack, since postgres won't index date(valid)

  sqlStr = "SELECT extract('hour' from valid) as vhour, \
    station, valid, p01i from t"+ sqlDate[:4] +"_hp WHERE \
    valid = '"+td+" 01:00' or valid = '"+td+" 02:00' or \
    valid = '"+td+" 03:00' or valid = '"+td+" 04:00' or \
    valid = '"+td+" 05:00' or valid = '"+td+" 06:00' or \
    valid = '"+td+" 07:00' or valid = '"+td+" 08:00' or \
    valid = '"+td+" 09:00' or valid = '"+td+" 10:00' or \
    valid = '"+td+" 11:00' or valid = '"+td+" 12:00' or \
    valid = '"+td+" 13:00' or valid = '"+td+" 14:00' or \
    valid = '"+td+" 15:00' or valid = '"+td+" 16:00' or \
    valid = '"+td+" 17:00' or valid = '"+td+" 18:00' or \
    valid = '"+td+" 19:00' or valid = '"+td+" 20:00' or \
    valid = '"+td+" 21:00' or valid = '"+td+" 22:00' or \
    valid = '"+td+" 23:00' or valid = '"+td+" 00:00'  "

  rs = ""
  try:
    rs = mydb.query(sqlStr).dictresult()
  except:
    print "
Error: No data for that year"

  for i in range(len(rs)):
    p01i = float(rs[i]["p01i"])
    vhour = int(rs[i]["vhour"])
#    if (vhour == 0):
#      vhour = 24
    if (p01i >= 0):  # We should require this timestep
      requireHrs[ vhour ] = 1
      try:
        stData[ rs[i]["station"] ][ vhour ]  = round(p01i,2)
      except KeyError:
        continue
    else:
      try:
        stData[ rs[i]["station"] ][ vhour ]  = " "
      except KeyError:
        continue

  for j in range(len(st.ids)):
    station = st.ids[j]
    print "
",
    print "%s%s%s" % ("", st.sts[station]['name'], "	") ,
    for i in range(0,24):
      print "",
      print "%s%s " % ( stData[station][i], "	") ,
      try:
        totp[station] = totp[station] + stData[station][i]
      except:
        continue
    print "%s%s%s" % ("", totp[station], "	") ,
    print "%s%s%s" % ("", station, "	") ,
    print "
"

  print """

Precipitation values are shown for the hour in which they are valid.  For
example, the value in the 1AM column is precipitation accumulation from 1 AM
till 2 AM.
"""
Main()
<|endoftext|>


<commit_before>"""
Generate web output for precip data
"""
from io import StringIO
import datetime

import psycopg2.extras
from paste.request import parse_formvars
from pyiem.network import Table as NetworkTable
from pyiem.util import get_dbconn

nt = NetworkTable(("KCCI", "KIMIT", "KELO"), only_online=False)
IEM = get_dbconn("iem")
icursor = IEM.cursor(cursor_factory=psycopg2.extras.DictCursor)

requireHrs = [0] * 25
stData = {}
totp = {}

# Return the Date we will be looking for...
def doHeader(environ, start_response, sio):
    """header please"""
    start_response("200 OK", [("Content-type", "text/html")])
    sio.write(
        """

  <title>IEM | Hourly Precip Grid</title>

Iowa Mesonet >
School Net >
Hourly Precipitation [SNET]

This archive starts on 12 Feb 2002 for KCCI sites and 12 Sept 2002 for
KELO sites.  Data from the previous day is the most current available.

"""
    )
    sio.write('
Hourly Precip Grid
')
    form = parse_formvars(environ)
    try:
        postDate = form.get("date")
        myTime = datetime.datetime.strptime(postDate, "%Y-%m-%d")
    except Exception:
        myTime = datetime.datetime.now() - datetime.timedelta(days=1)

    sio.write("

")
    sio.write(
        'Back:  \
        '
        + (myTime - datetime.timedelta(days=1)).strftime("%Y-%m-%d")
        + "	"
    )

    sio.write("Shown: " + myTime.strftime("%d %B %Y") + "	")

    sio.write(
        'Forward:  \
        '
        + (myTime + datetime.timedelta(days=1)).strftime("%Y-%m-%d")
        + "	"
    )

    sio.write(
        """
Pick: (yyyy-mm-dd)
<form method="GET" action="catSNET.py">
<input type="text" size="8" name="date">
<input type="submit" value="Submit Date">
</form>

"""
    )
    return myTime

def setupTable(sio):
    """Do"""
    sio.write(
        """

  Station
  Morning (AM)

  Afternoon/Evening (PM)

   Station

  Mid	 1	 2
  3	 4	 5
  6	 7	 8
  9	 10	 11
  Noon	 1	 2
  3	 4	 5
  6	 7	 8
  9	 10	 11
  Tot:

"""
    )

def loadstations():
    """Bah"""
    for station in nt.sts:
        stData[station] = ["M"] * 25
        totp[station] = 0

def application(environ, start_response):
    """Go Main Go."""
    sio = StringIO()
    ts = doHeader(environ, start_response, sio)
    loadstations()
    setupTable(sio)

    td = ts.strftime("%Y-%m-%d")
    tm = (ts + datetime.timedelta(days=1)).strftime("%Y-%m-%d")

    sqlStr = """
        SELECT extract('hour' from valid) as vhour,
        station, valid, phour from hourly_%s WHERE
        valid > '%s 00:00' and valid <= '%s 00:00'
        and network in ('KCCI','KIMT','KELO')
    """ % (
        ts.year,
        td,
        tm,
    )

    icursor.execute(sqlStr)

    for row in icursor:
        p01i = float(row["phour"])
        vhour = int(row["vhour"])
        if p01i >= 0:  # We should require this timestep
            requireHrs[vhour] = 1
            try:
                stData[row["station"]][vhour] = round(p01i, 2)
            except KeyError:
                continue
        else:
            try:
                stData[row["station"]][vhour] = " "
            except KeyError:
                continue

    j = 0
    ids = list(nt.sts.keys())
    ids.sort()
    for station in ids:
        j += 1
        sio.write('
')
        sio.write("%s%s%s" % ("", nt.sts[station]["name"], "	"))
        for i in range(0, 24):
            sio.write('')
            sio.write("%s%s " % (stData[station][i], "	"))
            try:
                totp[station] = totp[station] + stData[station][i]
            except Exception:
                continue
        sio.write("%s%s%s" % ("", totp[station], "	"))
        sio.write("%s%s%s" % ("", station, "	"))
        sio.write("
")

    sio.write(
        """

Precipitation values are shown for the hour in which they are valid.  For
example, the value in the 1AM column is precipitation accumulation from 1 AM
till 2 AM.
"""
    )
    return [sio.getvalue().encode("ascii")]
<commit_msg>fix sql statement<commit_after>"""
Generate web output for precip data
"""
from io import StringIO
import datetime

import psycopg2.extras
from paste.request import parse_formvars
from pyiem.network import Table as NetworkTable
from pyiem.util import get_dbconn

nt = NetworkTable(("KCCI", "KIMIT", "KELO"), only_online=False)

requireHrs = [0] * 25
stData = {}
totp = {}

# Return the Date we will be looking for...
def doHeader(environ, start_response, sio):
    """header please"""
    start_response("200 OK", [("Content-type", "text/html")])
    sio.write(
        """

  <title>IEM | Hourly Precip Grid</title>

Iowa Mesonet >
School Net >
Hourly Precipitation [SNET]

This archive starts on 12 Feb 2002 for KCCI sites and 12 Sept 2002 for
KELO sites. SchoolNet data stops on 3 May 2019.

"""
    )
    sio.write('
Hourly Precip Grid
')
    form = parse_formvars(environ)
    try:
        postDate = form.get("date")
        myTime = datetime.datetime.strptime(postDate, "%Y-%m-%d")
    except Exception:
        myTime = datetime.datetime(2019, 5, 3)

    sio.write("

")
    sio.write(
        'Back:  \
        '
        + (myTime - datetime.timedelta(days=1)).strftime("%Y-%m-%d")
        + "	"
    )

    sio.write("Shown: " + myTime.strftime("%d %B %Y") + "	")

    sio.write(
        'Forward:  \
        '
        + (myTime + datetime.timedelta(days=1)).strftime("%Y-%m-%d")
        + "	"
    )

    sio.write(
        """
Pick: (yyyy-mm-dd)
<form method="GET" action="catSNET.py">
<input type="text" size="8" name="date">
<input type="submit" value="Submit Date">
</form>

"""
    )
    return myTime

def setupTable(sio):
    """Do"""
    sio.write(
        """

  Station
  Morning (AM)

  Afternoon/Evening (PM)

   Station

  Mid
  1	 2
  3
  4	 5
  6
  7	 8
  9
  10	 11
  Noon
  1	 2
  3
  4	 5
  6
  7	 8
  9
  10	 11
  Tot:

"""
    )

def loadstations():
    """Bah"""
    for station in nt.sts:
        stData[station] = ["M"] * 25
        totp[station] = 0

def application(environ, start_response):
    """Go Main Go."""
    sio = StringIO()
    ts = doHeader(environ, start_response, sio)
    loadstations()
    setupTable(sio)

    td = ts.strftime("%Y-%m-%d")
    tm = (ts + datetime.timedelta(days=1)).strftime("%Y-%m-%d")

    sqlStr = """
        SELECT extract('hour' from valid) as vhour,
        id as station, valid, phour from hourly_%s h JOIN stations s on
        (h.iemid = s.iemid) WHERE
        valid > '%s 00:00' and valid <= '%s 00:00'
        and network in ('KCCI','KIMT','KELO')
    """ % (
        ts.year,
        td,
        tm,
    )
    pgconn = get_dbconn("iem")
    icursor = pgconn.cursor(cursor_factory=psycopg2.extras.DictCursor)

    icursor.execute(sqlStr)

    for row in icursor:
        p01i = float(row["phour"])
        vhour = int(row["vhour"])
        if p01i >= 0:  # We should require this timestep
            requireHrs[vhour] = 1
            try:
                stData[row["station"]][vhour] = round(p01i, 2)
            except KeyError:
                continue
        else:
            try:
                stData[row["station"]][vhour] = " "
            except KeyError:
                continue

    j = 0
    ids = list(nt.sts.keys())
    ids.sort()
    for station in ids:
        j += 1
        sio.write('
')
        sio.write("%s%s%s" % ("", nt.sts[station]["name"], "	"))
        for i in range(0, 24):
            sio.write('')
            sio.write("%s%s " % (stData[station][i], "	"))
            try:
                totp[station] = totp[station] + stData[station][i]
            except Exception:
                continue
        sio.write("%s%s%s" % ("", totp[station], "	"))
        sio.write("%s%s%s" % ("", station, "	"))
        sio.write("
")

    sio.write(
        """

Precipitation values are shown for the hour in which they are valid.  For
example, the value in the 1AM column is precipitation accumulation from 1 AM
till 2 AM.
"""
    )
    return [sio.getvalue().encode("ascii")]
<|endoftext|>


<commit_before>#!/usr/bin/env python
"""
Return a simple CSV of recent observations from the database
"""

import psycopg2
import cgi
import sys
import memcache

def run(sid):
    """ run() """

    dbconn = psycopg2.connect(database='iem', host='iemdb', user='nobody')
    cursor = dbconn.cursor()
    cursor.execute("""SELECT valid at time zone 'UTC', tmpf, dwpf, raw
     from current_log c JOIN
     stations t on (t.iemid = c.iemid) WHERE t.id = %s
     ORDER by valid ASC""", (sid,))

    res = "station,valid,raw\n"
    for row in cursor:
        res += "%s,%s,%s\n" % (sid, row[0].strftime("%Y-%m-%d %H:%M"),
                                         row[3])

    return res

if __name__ == '__main__':
    sys.stdout.write("Content-type: text/plain\n\n")
    form = cgi.FieldStorage()
    sid = form.getfirst('station', 'AMW')[:5]

    mckey = "/cgi-bin/request/recent.py|%s" % (sid,)
    mc = memcache.Client(['iem-memcached:11211'], debug=0)
    res = mc.get(mckey)
    if not res:
        res = run(sid)
        sys.stdout.write(res)
        mc.set(mckey, res, 300)
    sys.stdout.write( res )

<commit_msg>Add more variables and prevent duplicated reports<commit_after>#!/usr/bin/env python
"""
Return a simple CSV of recent observations from the database
"""

import psycopg2
import cgi
import sys
import memcache

def run(sid):
    """ run() """

    dbconn = psycopg2.connect(database='iem', host='iemdb', user='nobody')
    cursor = dbconn.cursor()
    cursor.execute("""SELECT valid at time zone 'UTC', tmpf, dwpf, raw,
    x(geom), y(geom) , tmpf, dwpf, drct, sknt, phour, alti, mslp, vsby, gust
     from current_log c JOIN
     stations t on (t.iemid = c.iemid) WHERE t.id = %s and
     (t.network ~* 'ASOS' or t.network = 'AWOS')
     ORDER by valid ASC""", (sid,))

    res = "station,utcvalid,lon,lat,tmpf,dwpf,drct,sknt,p01i,alti,mslp,vsby,gust,raw\n"
    for row in cursor:
        res += "%s,%s,%.4f,%.4f,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n" % (sid, row[0].strftime("%Y-%m-%d %H:%M"),
                                         row[4], row[5], row[6], row[7], row[8],
                                         row[9], row[10], row[11], row[12],
                                         row[13], row[14], row[3])

    return res.replace("None", "M")

if __name__ == '__main__':
    sys.stdout.write("Content-type: text/plain\n\n")
    form = cgi.FieldStorage()
    sid = form.getfirst('station', 'AMW')[:5]

    mckey = "/cgi-bin/request/recent.py|%s" % (sid,)
    mc = memcache.Client(['iem-memcached:11211'], debug=0)
    res = mc.get(mckey)
    if not res:
        res = run(sid)
        sys.stdout.write(res)
        mc.set(mckey, res, 300)
    sys.stdout.write( res )

<|endoftext|>