<commit_before># -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (C) 2015-2017 GEM Foundation
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake. If not, see <http://www.gnu.org/licenses/>.

"""
Disaggregation calculator core functionality
"""
from __future__ import division
import math
import logging
import numpy

from openquake.baselib import hdf5
from openquake.baselib.general import split_in_blocks
from openquake.hazardlib.calc import disagg
from openquake.hazardlib.calc.filters import SourceFilter
from openquake.hazardlib.gsim.base import ContextMaker
from openquake.baselib import parallel
from openquake.hazardlib import sourceconverter
from openquake.commonlib import calc
from openquake.calculators import base, classical

DISAGG_RES_FMT = 'disagg/poe-%(poe)s-rlz-%(rlz)s-%(imt)s-%(lon)s-%(lat)s'

def compute_disagg(src_filter, sources, rlzs_by_gsim,
                   trt_names, curves_dict, bin_edges, oqparam, monitor):
    # see https://bugs.launchpad.net/oq-engine/+bug/1279247 for an explanation
    # of the algorithm used
    """
    :param src_filter:
        a :class:`openquake.hazardlib.calc.filter.SourceFilter` instance
    :param sources:
        list of hazardlib source objects
    :param gsims:
        list of GSIMs for the current source group
    :param rlzs_by_gsim:
        a dictionary GSIM -> realizations
    :param dict trt_names:
        a tuple of names for the given tectonic region type
    :param curves_dict:
        a dictionary with the hazard curves for sites, realizations and IMTs
    :param bin_egdes:
        a dictionary site_id -> edges
    :param oqparam:
        the parameters in the job.ini file
    :param monitor:
        monitor of the currently running job
    :returns:
        a dictionary of probability arrays, with composite key
        (sid, rlz.id, poe, imt, iml, trt_names).
    """
    sitecol = src_filter.sitecol
    trt_num = dict((trt, i) for i, trt in enumerate(trt_names))
    result = {}  # sid, rlz.id, poe, imt, iml, trt_names -> array

    collecting_mon = monitor('collecting bins')
    arranging_mon = monitor('arranging bins')

    for site, sid in zip(sitecol, sitecol.sids):
        # edges as wanted by disagg._arrange_data_in_bins
        try:
            edges = bin_edges[sid]
        except KeyError:
            # bin_edges for a given site are missing if the site is far away
            continue

        # generate source, rupture, sites once per site
        with collecting_mon:
            cmaker = ContextMaker(
                rlzs_by_gsim, src_filter.integration_distance)
            bdata = disagg._collect_bins_data(
                trt_num, sources, site, curves_dict[sid],
                rlzs_by_gsim, cmaker, oqparam.imtls,
                oqparam.poes_disagg, oqparam.truncation_level,
                oqparam.num_epsilon_bins, oqparam.iml_disagg,
                monitor)

        for (rlzi, poe, imt), iml_pne_pairs in bdata.pnes.items():
            # extract the probabilities of non-exceedance for the
            # given realization, disaggregation PoE, and IMT
            iml = iml_pne_pairs[0][0]
            probs = numpy.array([p for (i, p) in iml_pne_pairs], float)

            # bins in a format handy for hazardlib
            bins = [bdata.mags, bdata.dists,
                    bdata.lons, bdata.lats,
                    bdata.trts, None, probs]

            # call disagg._arrange_data_in_bins
            with arranging_mon:
                key = (sid, rlzi, poe, imt, iml, trt_names)
                matrix = disagg._arrange_data_in_bins(
                    bins, edges + (trt_names,))
                result[key] = numpy.array(
                    [fn(matrix) for fn in disagg.pmf_map.values()])
    return result

@base.calculators.add('disaggregation')
class DisaggregationCalculator(classical.ClassicalCalculator):
    """
    Classical PSHA disaggregation calculator
    """
    def post_execute(self, nbytes_by_kind):
        """Performs the disaggregation"""
        self.full_disaggregation()

    def agg_result(self, acc, result):
        """
        Collect the results coming from compute_disagg into self.results,
        a dictionary with key (sid, rlz.id, poe, imt, iml, trt_names)
        and values which are probability arrays.

        :param acc: dictionary accumulating the results
        :param result: dictionary with the result coming from a task
        """
        for key, val in result.items():
            acc[key] = 1. - (1. - acc.get(key, 0)) * (1. - val)
        return acc

    def get_curves(self, sid):
        """
        Get all the relevant hazard curves for the given site ordinal.
        Returns a dictionary {(rlz_id, imt) -> curve}.
        """
        dic = {}
        imtls = self.oqparam.imtls
        pgetter = calc.PmapGetter(self.datastore)
        for rlz in self.rlzs_assoc.realizations:
            try:
                pmap = pgetter.get(numpy.array([sid]), rlz.ordinal)
            except ValueError:  # empty pmaps
                logging.info(
                    'hazard curve contains all zero probabilities; '
                    'skipping site %d, rlz=%d', sid, rlz.ordinal)
                continue
            if sid not in pmap:
                continue
            poes = pmap[sid].convert(imtls)
            for imt_str in imtls:
                if all(x == 0.0 for x in poes[imt_str]):
                    logging.info(
                        'hazard curve contains all zero probabilities; '
                        'skipping site %d, rlz=%d, IMT=%s',
                        sid, rlz.ordinal, imt_str)
                    continue
                dic[rlz.ordinal, imt_str] = poes[imt_str]
        return dic

    def full_disaggregation(self):
        """
        Run the disaggregation phase after hazard curve finalization.
        """
        oq = self.oqparam
        tl = self.oqparam.truncation_level
        bb_dict = self.datastore['bb_dict']
        sitecol = self.sitecol
        mag_bin_width = self.oqparam.mag_bin_width
        eps_edges = numpy.linspace(-tl, tl, self.oqparam.num_epsilon_bins + 1)
        logging.info('%d epsilon bins from %s to %s', len(eps_edges) - 1,
                     min(eps_edges), max(eps_edges))

        self.bin_edges = {}
        curves_dict = {sid: self.get_curves(sid) for sid in sitecol.sids}
        # determine the number of effective source groups
        sg_data = self.datastore['csm_info/sg_data']
        num_grps = sum(1 for effrup in sg_data['effrup'] if effrup > 0)
        nblocks = math.ceil(oq.concurrent_tasks / num_grps)
        all_args = []
        src_filter = SourceFilter(sitecol, oq.maximum_distance)
        for smodel in self.csm.source_models:
            sm_id = smodel.ordinal
            trt_names = tuple(mod.trt for mod in smodel.src_groups)
            max_mag = max(mod.max_mag for mod in smodel.src_groups)
            min_mag = min(mod.min_mag for mod in smodel.src_groups)
            mag_edges = mag_bin_width * numpy.arange(
                int(numpy.floor(min_mag / mag_bin_width)),
                int(numpy.ceil(max_mag / mag_bin_width) + 1))
            logging.info('%d mag bins from %s to %s', len(mag_edges) - 1,
                         min_mag, max_mag)
            for sid, site in zip(sitecol.sids, sitecol):
                curves = curves_dict[sid]
                if not curves:
                    continue  # skip zero-valued hazard curves
                bb = bb_dict[sid]
                if not bb:
                    logging.info(
                        'location %s was too far, skipping disaggregation',
                        site.location)
                    continue

                dist_edges, lon_edges, lat_edges = bb.bins_edges(
                    oq.distance_bin_width, oq.coordinate_bin_width)
                logging.info(
                    '[sid=%d] %d dist bins from %s to %s', sid,
                    len(dist_edges) - 1, min(dist_edges), max(dist_edges))
                logging.info(
                    '[sid=%d] %d lon bins from %s to %s', sid,
                    len(lon_edges) - 1, bb.west, bb.east)
                logging.info(
                    '[sid=%d] %d lat bins from %s to %s', sid,
                    len(lon_edges) - 1, bb.south, bb.north)

                self.bin_edges[sm_id, sid] = (
                    mag_edges, dist_edges, lon_edges, lat_edges, eps_edges)

            bin_edges = {sid: self.bin_edges[sm_id, sid]
                         for sid in sitecol.sids
                         if (sm_id, sid) in self.bin_edges}
            for src_group in smodel.src_groups:
                split_sources = []
                for src in src_group:
                    for split, _sites in src_filter(
                            sourceconverter.split_source(src), sitecol):
                        split_sources.append(split)
                mon = self.monitor('disaggregation')
                rlzs_by_gsim = self.rlzs_assoc.get_rlzs_by_gsim(
                    src_group.trt, sm_id)
                for srcs in split_in_blocks(split_sources, nblocks):
                    all_args.append(
                        (src_filter, srcs, rlzs_by_gsim, trt_names,
                         curves_dict, bin_edges, oq, mon))

        results = parallel.Starmap(compute_disagg, all_args).reduce(
            self.agg_result)
        self.save_disagg_results(results)

    def save_disagg_results(self, results):
        """
        Save all the results of the disaggregation. NB: the number of results
        to save is #sites * #rlzs * #disagg_poes * #IMTs.

        :param results:
            a dictionary of probability arrays
        """
        # build a dictionary rlz.ordinal -> source_model.ordinal
        sm_id = {}
        for i, rlzs in self.rlzs_assoc.rlzs_by_smodel.items():
            for rlz in rlzs:
                sm_id[rlz.ordinal] = i

        # since an extremely small subset of the full disaggregation matrix
        # is saved this method can be run sequentially on the controller node
        for key, probs in sorted(results.items()):
            sid, rlz_id, poe, imt, iml, trt_names = key
            edges = self.bin_edges[sm_id[rlz_id], sid]
            self.save_disagg_result(
                sid, edges, trt_names, probs, rlz_id,
                self.oqparam.investigation_time, imt, iml, poe)

    def save_disagg_result(self, site_id, bin_edges, trt_names, matrix,
                           rlz_id, investigation_time, imt_str, iml, poe):
        """
        Save a computed disaggregation matrix to `hzrdr.disagg_result` (see
        :class:`~openquake.engine.db.models.DisaggResult`).

        :param site_id:
            id of the current site
        :param bin_edges:
            The 5-uple mag, dist, lon, lat, eps
        :param trt_names:
            The list of Tectonic Region Types
        :param matrix:
            A probability array
        :param rlz_id:
            ordinal of the realization to which the results belong.
        :param float investigation_time:
            Investigation time (years) for the calculation.
        :param imt_str:
            Intensity measure type string (PGA, SA, etc.)
        :param float iml:
            Intensity measure level interpolated (using `poe`) from the hazard
            curve at the `site`.
        :param float poe:
            Disaggregation probability of exceedance value for this result.
        """
        lon = self.sitecol.lons[site_id]
        lat = self.sitecol.lats[site_id]
        mag, dist, lons, lats, eps = bin_edges
        disp_name = DISAGG_RES_FMT % dict(
            poe=poe, rlz=rlz_id, imt=imt_str, lon=lon, lat=lat)
        self.datastore[disp_name] = {
            '_'.join(key): mat for key, mat in zip(disagg.pmf_map, matrix)}
        attrs = self.datastore.hdf5[disp_name].attrs
        attrs['rlzi'] = rlz_id
        attrs['imt'] = imt_str
        attrs['iml'] = iml
        attrs['poe'] = poe
        attrs['trts'] = hdf5.array_of_vstr(trt_names)
        attrs['mag_bin_edges'] = mag
        attrs['dist_bin_edges'] = dist
        attrs['lon_bin_edges'] = lons
        attrs['lat_bin_edges'] = lats
        attrs['eps_bin_edges'] = eps
        attrs['location'] = (lon, lat)
<commit_msg>Fixed docstring [skip CI]<commit_after># -*- coding: utf-8 -*-
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (C) 2015-2017 GEM Foundation
#
# OpenQuake is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# OpenQuake 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with OpenQuake. If not, see <http://www.gnu.org/licenses/>.

"""
Disaggregation calculator core functionality
"""
from __future__ import division
import math
import logging
import numpy

from openquake.baselib import hdf5
from openquake.baselib.general import split_in_blocks
from openquake.hazardlib.calc import disagg
from openquake.hazardlib.calc.filters import SourceFilter
from openquake.hazardlib.gsim.base import ContextMaker
from openquake.baselib import parallel
from openquake.hazardlib import sourceconverter
from openquake.commonlib import calc
from openquake.calculators import base, classical

DISAGG_RES_FMT = 'disagg/poe-%(poe)s-rlz-%(rlz)s-%(imt)s-%(lon)s-%(lat)s'

def compute_disagg(src_filter, sources, rlzs_by_gsim,
                   trt_names, curves_dict, bin_edges, oqparam, monitor):
    # see https://bugs.launchpad.net/oq-engine/+bug/1279247 for an explanation
    # of the algorithm used
    """
    :param src_filter:
        a :class:`openquake.hazardlib.calc.filter.SourceFilter` instance
    :param sources:
        list of hazardlib source objects
    :param rlzs_by_gsim:
        a dictionary GSIM -> realizations
    :param dict trt_names:
        a tuple of names for the given tectonic region type
    :param curves_dict:
        a dictionary with the hazard curves for sites, realizations and IMTs
    :param bin_egdes:
        a dictionary site_id -> edges
    :param oqparam:
        the parameters in the job.ini file
    :param monitor:
        monitor of the currently running job
    :returns:
        a dictionary of probability arrays, with composite key
        (sid, rlz.id, poe, imt, iml, trt_names).
    """
    sitecol = src_filter.sitecol
    trt_num = dict((trt, i) for i, trt in enumerate(trt_names))
    result = {}  # sid, rlz.id, poe, imt, iml, trt_names -> array

    collecting_mon = monitor('collecting bins')
    arranging_mon = monitor('arranging bins')

    for site, sid in zip(sitecol, sitecol.sids):
        # edges as wanted by disagg._arrange_data_in_bins
        try:
            edges = bin_edges[sid]
        except KeyError:
            # bin_edges for a given site are missing if the site is far away
            continue

        # generate source, rupture, sites once per site
        with collecting_mon:
            cmaker = ContextMaker(
                rlzs_by_gsim, src_filter.integration_distance)
            bdata = disagg._collect_bins_data(
                trt_num, sources, site, curves_dict[sid],
                rlzs_by_gsim, cmaker, oqparam.imtls,
                oqparam.poes_disagg, oqparam.truncation_level,
                oqparam.num_epsilon_bins, oqparam.iml_disagg,
                monitor)

        for (rlzi, poe, imt), iml_pne_pairs in bdata.pnes.items():
            # extract the probabilities of non-exceedance for the
            # given realization, disaggregation PoE, and IMT
            iml = iml_pne_pairs[0][0]
            probs = numpy.array([p for (i, p) in iml_pne_pairs], float)

            # bins in a format handy for hazardlib
            bins = [bdata.mags, bdata.dists,
                    bdata.lons, bdata.lats,
                    bdata.trts, None, probs]

            # call disagg._arrange_data_in_bins
            with arranging_mon:
                key = (sid, rlzi, poe, imt, iml, trt_names)
                matrix = disagg._arrange_data_in_bins(
                    bins, edges + (trt_names,))
                result[key] = numpy.array(
                    [fn(matrix) for fn in disagg.pmf_map.values()])
    return result

@base.calculators.add('disaggregation')
class DisaggregationCalculator(classical.ClassicalCalculator):
    """
    Classical PSHA disaggregation calculator
    """
    def post_execute(self, nbytes_by_kind):
        """Performs the disaggregation"""
        self.full_disaggregation()

    def agg_result(self, acc, result):
        """
        Collect the results coming from compute_disagg into self.results,
        a dictionary with key (sid, rlz.id, poe, imt, iml, trt_names)
        and values which are probability arrays.

        :param acc: dictionary accumulating the results
        :param result: dictionary with the result coming from a task
        """
        for key, val in result.items():
            acc[key] = 1. - (1. - acc.get(key, 0)) * (1. - val)
        return acc

    def get_curves(self, sid):
        """
        Get all the relevant hazard curves for the given site ordinal.
        Returns a dictionary {(rlz_id, imt) -> curve}.
        """
        dic = {}
        imtls = self.oqparam.imtls
        pgetter = calc.PmapGetter(self.datastore)
        for rlz in self.rlzs_assoc.realizations:
            try:
                pmap = pgetter.get(numpy.array([sid]), rlz.ordinal)
            except ValueError:  # empty pmaps
                logging.info(
                    'hazard curve contains all zero probabilities; '
                    'skipping site %d, rlz=%d', sid, rlz.ordinal)
                continue
            if sid not in pmap:
                continue
            poes = pmap[sid].convert(imtls)
            for imt_str in imtls:
                if all(x == 0.0 for x in poes[imt_str]):
                    logging.info(
                        'hazard curve contains all zero probabilities; '
                        'skipping site %d, rlz=%d, IMT=%s',
                        sid, rlz.ordinal, imt_str)
                    continue
                dic[rlz.ordinal, imt_str] = poes[imt_str]
        return dic

    def full_disaggregation(self):
        """
        Run the disaggregation phase after hazard curve finalization.
        """
        oq = self.oqparam
        tl = self.oqparam.truncation_level
        bb_dict = self.datastore['bb_dict']
        sitecol = self.sitecol
        mag_bin_width = self.oqparam.mag_bin_width
        eps_edges = numpy.linspace(-tl, tl, self.oqparam.num_epsilon_bins + 1)
        logging.info('%d epsilon bins from %s to %s', len(eps_edges) - 1,
                     min(eps_edges), max(eps_edges))

        self.bin_edges = {}
        curves_dict = {sid: self.get_curves(sid) for sid in sitecol.sids}
        # determine the number of effective source groups
        sg_data = self.datastore['csm_info/sg_data']
        num_grps = sum(1 for effrup in sg_data['effrup'] if effrup > 0)
        nblocks = math.ceil(oq.concurrent_tasks / num_grps)
        all_args = []
        src_filter = SourceFilter(sitecol, oq.maximum_distance)
        for smodel in self.csm.source_models:
            sm_id = smodel.ordinal
            trt_names = tuple(mod.trt for mod in smodel.src_groups)
            max_mag = max(mod.max_mag for mod in smodel.src_groups)
            min_mag = min(mod.min_mag for mod in smodel.src_groups)
            mag_edges = mag_bin_width * numpy.arange(
                int(numpy.floor(min_mag / mag_bin_width)),
                int(numpy.ceil(max_mag / mag_bin_width) + 1))
            logging.info('%d mag bins from %s to %s', len(mag_edges) - 1,
                         min_mag, max_mag)
            for sid, site in zip(sitecol.sids, sitecol):
                curves = curves_dict[sid]
                if not curves:
                    continue  # skip zero-valued hazard curves
                bb = bb_dict[sid]
                if not bb:
                    logging.info(
                        'location %s was too far, skipping disaggregation',
                        site.location)
                    continue

                dist_edges, lon_edges, lat_edges = bb.bins_edges(
                    oq.distance_bin_width, oq.coordinate_bin_width)
                logging.info(
                    '[sid=%d] %d dist bins from %s to %s', sid,
                    len(dist_edges) - 1, min(dist_edges), max(dist_edges))
                logging.info(
                    '[sid=%d] %d lon bins from %s to %s', sid,
                    len(lon_edges) - 1, bb.west, bb.east)
                logging.info(
                    '[sid=%d] %d lat bins from %s to %s', sid,
                    len(lon_edges) - 1, bb.south, bb.north)

                self.bin_edges[sm_id, sid] = (
                    mag_edges, dist_edges, lon_edges, lat_edges, eps_edges)

            bin_edges = {sid: self.bin_edges[sm_id, sid]
                         for sid in sitecol.sids
                         if (sm_id, sid) in self.bin_edges}
            for src_group in smodel.src_groups:
                split_sources = []
                for src in src_group:
                    for split, _sites in src_filter(
                            sourceconverter.split_source(src), sitecol):
                        split_sources.append(split)
                mon = self.monitor('disaggregation')
                rlzs_by_gsim = self.rlzs_assoc.get_rlzs_by_gsim(
                    src_group.trt, sm_id)
                for srcs in split_in_blocks(split_sources, nblocks):
                    all_args.append(
                        (src_filter, srcs, rlzs_by_gsim, trt_names,
                         curves_dict, bin_edges, oq, mon))

        results = parallel.Starmap(compute_disagg, all_args).reduce(
            self.agg_result)
        self.save_disagg_results(results)

    def save_disagg_results(self, results):
        """
        Save all the results of the disaggregation. NB: the number of results
        to save is #sites * #rlzs * #disagg_poes * #IMTs.

        :param results:
            a dictionary of probability arrays
        """
        # build a dictionary rlz.ordinal -> source_model.ordinal
        sm_id = {}
        for i, rlzs in self.rlzs_assoc.rlzs_by_smodel.items():
            for rlz in rlzs:
                sm_id[rlz.ordinal] = i

        # since an extremely small subset of the full disaggregation matrix
        # is saved this method can be run sequentially on the controller node
        for key, probs in sorted(results.items()):
            sid, rlz_id, poe, imt, iml, trt_names = key
            edges = self.bin_edges[sm_id[rlz_id], sid]
            self.save_disagg_result(
                sid, edges, trt_names, probs, rlz_id,
                self.oqparam.investigation_time, imt, iml, poe)

    def save_disagg_result(self, site_id, bin_edges, trt_names, matrix,
                           rlz_id, investigation_time, imt_str, iml, poe):
        """
        Save a computed disaggregation matrix to `hzrdr.disagg_result` (see
        :class:`~openquake.engine.db.models.DisaggResult`).

        :param site_id:
            id of the current site
        :param bin_edges:
            The 5-uple mag, dist, lon, lat, eps
        :param trt_names:
            The list of Tectonic Region Types
        :param matrix:
            A probability array
        :param rlz_id:
            ordinal of the realization to which the results belong.
        :param float investigation_time:
            Investigation time (years) for the calculation.
        :param imt_str:
            Intensity measure type string (PGA, SA, etc.)
        :param float iml:
            Intensity measure level interpolated (using `poe`) from the hazard
            curve at the `site`.
        :param float poe:
            Disaggregation probability of exceedance value for this result.
        """
        lon = self.sitecol.lons[site_id]
        lat = self.sitecol.lats[site_id]
        mag, dist, lons, lats, eps = bin_edges
        disp_name = DISAGG_RES_FMT % dict(
            poe=poe, rlz=rlz_id, imt=imt_str, lon=lon, lat=lat)
        self.datastore[disp_name] = {
            '_'.join(key): mat for key, mat in zip(disagg.pmf_map, matrix)}
        attrs = self.datastore.hdf5[disp_name].attrs
        attrs['rlzi'] = rlz_id
        attrs['imt'] = imt_str
        attrs['iml'] = iml
        attrs['poe'] = poe
        attrs['trts'] = hdf5.array_of_vstr(trt_names)
        attrs['mag_bin_edges'] = mag
        attrs['dist_bin_edges'] = dist
        attrs['lon_bin_edges'] = lons
        attrs['lat_bin_edges'] = lats
        attrs['eps_bin_edges'] = eps
        attrs['location'] = (lon, lat)
<|endoftext|>


<commit_before># Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.

import configparser #To read config files.
import io #To output config files to string.

import UM.VersionUpgrade #To indicate that a file is of the wrong format.

##  Creates a new preferences instance by parsing a serialised preferences file
#   in version 1 of the file format.
#
#   \param serialised The serialised form of a preferences file in version 1.
#   \return A representation of those preferences, or None if the file format is
#   incorrect.
def importFrom(serialised):
    try:
        return Preferences(serialised)
    except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
        return None

##  A representation of preferences files as intermediary form for conversion
#   from one format to the other.
class Preferences:
    ##  Reads version 2 of the preferences file format, storing it in memory.
    #
    #   \param serialised A serialised version 2 preferences file.
    def __init__(self, serialised):
        self._config = configparser.ConfigParser(interpolation = None)
        self._config.read_string(serialised)

        #Checking file correctness.
        if not self._config.has_section("general"):
            raise UM.VersionUpgrade.FormatException("No \"general\" section.")
        if not self._config.has_option("general", "version"):
            raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.")
        if int(self._config.get("general", "version")) != 2: # Explicitly hard-code version 2, since if this number changes the programmer MUST change this entire function.
            raise UM.VersionUpgrade.InvalidVersionException("The version of this preferences file is wrong. It must be 2.")

    ##  Serialises these preferences as a preferences file of version 3.
    #
    #   This is where the actual translation happens.
    #
    #   \return A serialised version of a preferences file in version 3.
    def export(self):
        #Reset the cura/categories_expanded property since it works differently now.
        if self._config.has_section("cura") and self._config.has_option("cura", "categories_expanded"):
            self._config.remove_option("cura", "categories_expanded")

        #Translate the setting names in the visible settings.
        if self._config.has_section("machines") and self._config.has_option("machines", "setting_visibility"):
            visible_settings = self._config.get("machines", "setting_visibility")
            visible_settings = visible_settings.split(",")
            import VersionUpgrade21to22 #Import here to prevent a circular dependency.
            visible_settings = VersionUpgrade21to22.translateSettingNames(visible_settings)
            visible_settings = visible_settings.join(",")
            self._config.set("machines", "setting_visibility", value = visible_settings)

        #Output the result as a string.
        output = io.StringIO()
        self._config.write(output)
        return output.getvalue()<commit_msg>Also increment the version number<commit_after># Copyright (c) 2016 Ultimaker B.V.
# Cura is released under the terms of the AGPLv3 or higher.

import configparser #To read config files.
import io #To output config files to string.

import UM.VersionUpgrade #To indicate that a file is of the wrong format.

##  Creates a new preferences instance by parsing a serialised preferences file
#   in version 1 of the file format.
#
#   \param serialised The serialised form of a preferences file in version 1.
#   \return A representation of those preferences, or None if the file format is
#   incorrect.
def importFrom(serialised):
    try:
        return Preferences(serialised)
    except (configparser.Error, UM.VersionUpgrade.FormatException, UM.VersionUpgrade.InvalidVersionException):
        return None

##  A representation of preferences files as intermediary form for conversion
#   from one format to the other.
class Preferences:
    ##  Reads version 2 of the preferences file format, storing it in memory.
    #
    #   \param serialised A serialised version 2 preferences file.
    def __init__(self, serialised):
        self._config = configparser.ConfigParser(interpolation = None)
        self._config.read_string(serialised)

        #Checking file correctness.
        if not self._config.has_section("general"):
            raise UM.VersionUpgrade.FormatException("No \"general\" section.")
        if not self._config.has_option("general", "version"):
            raise UM.VersionUpgrade.FormatException("No \"version\" in \"general\" section.")
        if int(self._config.get("general", "version")) != 2: # Explicitly hard-code version 2, since if this number changes the programmer MUST change this entire function.
            raise UM.VersionUpgrade.InvalidVersionException("The version of this preferences file is wrong. It must be 2.")

    ##  Serialises these preferences as a preferences file of version 3.
    #
    #   This is where the actual translation happens.
    #
    #   \return A serialised version of a preferences file in version 3.
    def export(self):
        #Reset the cura/categories_expanded property since it works differently now.
        if self._config.has_section("cura") and self._config.has_option("cura", "categories_expanded"):
            self._config.remove_option("cura", "categories_expanded")

        #Translate the setting names in the visible settings.
        if self._config.has_section("machines") and self._config.has_option("machines", "setting_visibility"):
            visible_settings = self._config.get("machines", "setting_visibility")
            visible_settings = visible_settings.split(",")
            import VersionUpgrade21to22 #Import here to prevent a circular dependency.
            visible_settings = VersionUpgrade21to22.translateSettingNames(visible_settings)
            visible_settings = visible_settings.join(",")
            self._config.set("machines", "setting_visibility", value = visible_settings)

        #Update the version number itself.
        self._config.set("general", "version", value = "3")

        #Output the result as a string.
        output = io.StringIO()
        self._config.write(output)
        return output.getvalue()<|endoftext|>


<commit_before>import numpy as np
from adf15 import Adf15

class PhotonEmissivity(object):
    def __init__(self, element, nuclear_charge, charge, datablocks):
        self.element = element
        self.nuclear_charge = nuclear_charge
        self.charge = charge
        self.datablocks = datablocks

    @classmethod
    def fromfile(cls, filename):
        f = Adf15(filename).read()
        element = f['element']
        nuclear_charge = f['nuclear_charge']
        charge = f['charge']
        datablocks = f['datablocks']

        return cls(element, charge, nuclear_charge, datablocks)

    def select_transition(self, type_):
        name = self._figure_out_type(type_)

        new_datablocks = []
        for d in self.datablocks:
            if d['transition'] != name: continue
            new_datablocks.append(d)

        return self.__class__(self.element, self.charge, self.nuclear_charge,
                new_datablocks)

    def _figure_out_type(self, type_):
        if type_ in ['excitation', 'excit', 'ex']:
            name = 'excit'
        elif type_ in ['recombination', 'recom', 'rec']:
            name = 'recom'
        elif type_ in ['charge_exchange', 'chexc', 'cx']:
            name = 'chexc'
        else:
            raise ValueError('invalid type: %s.' % type_)
        return name

    @property
    def wavelengths(self):
        return np.array([i['wavelength'] for i in self.datablocks])

    @property
    def density(self):
        return np.array([i['density'] for i in self.datablocks])

    @property
    def temperature(self):
        return np.array([i['temperature'] for i in self.datablocks])

    @property
    def coeffs(self):
        return np.array([i['pec'] for i in self.datablocks])

def wavelength_to_joule(lambda_):
    from scipy.constants import h, c
    return h * c / lambda_

def P_bremsstrahlung(k, Te, ne):
    """
    W m^3
    """
    return 1.53e-38 * Te**0.5 * k**2

if __name__ == '__main__':
    pec = PhotonEmissivity.fromfile('pec96#c_pju#c3.dat')

    import matplotlib.pyplot as plt

    pec = pec.select_transition('ex')
    te_ = pec.temperature
    ne_ = pec.density * 1e6
    coeffs_ = pec.coeffs * 1e-6
    w = pec.wavelengths * 1e-10
    te, ne = te_[0], ne_[0]

    energy = wavelength_to_joule(w)
    power = energy[:, np.newaxis, np.newaxis] * coeffs_
    power = power.sum(0)

    #power_brems =  P_bremsstrahlung(3, te, ne_)
    #power += power_brems[:, None]

    plt.figure(1); plt.clf()
    ax = plt.gca()
    ax.set_xlim(1e0, 1e3)

    import atomic
    ad = atomic.element('carbon')
    cont_power = ad.coeffs['continuum_power']
    line_power = ad.coeffs['line_power']
    #line_power = atomic.atomic_data.ZeroCoefficient()
    cont_power = atomic.atomic_data.ZeroCoefficient()

    for density_index in [10, 16]:
        c1 = line_power(3,te, ne[density_index])
        c2 = cont_power(3,te, ne[density_index])
        c = c1 + c2

        line, = plt.loglog(te, c, lw=2, ls='--', label='adas')
        plt.loglog(te, power[:,density_index], lw=2, color=line.get_color())
    plt.legend(loc='best')

    plt.draw()
    plt.show()
<commit_msg>Rework Transition and Photon Emissivity objects.<commit_after>import numpy as np
from adf15 import Adf15

class Transition(object):
    def __init__(self, element, nuclear_charge, charge, datablock):
        self.element = element
        self.nuclear_charge = nuclear_charge
        self.charge = charge

        self.wavelength = datablock['wavelength'] * 1e-10
        self.type_ = datablock['type']

        self.electron_density = datablock['density'] * 1e6
        self.electron_temperature = datablock['temperature']
        self.photon_emissivity = datablock['pec'] * 1e-6

class PhotonEmissivity(object):
    def __init__(self, transitions=None):
        if transitions == None: transitions = []

        self.transitions = transitions

    def append_file(self, filename):
        f = Adf15(filename).read()
        element = f['element']
        nuclear_charge = f['nuclear_charge']
        charge = f['charge']
        datablocks = f['datablocks']

        for d in datablocks:
            t = Transition(element, nuclear_charge, charge, d)
            self.transitions.append(t)

    def filter_type(self, *type_names):
        names = self._interpret_type(*type_names)
        new_transitions = filter(lambda t: t.type_ in names, self.transitions)
        return self.__class__(new_transitions)

    def _interpret_type(self, *type_names):
        return map(self._figure_out_type, type_names)

    def _figure_out_type(self, type_):
        if type_ in ['excitation', 'excit', 'ex']:
            name = 'excit'
        elif type_ in ['recombination', 'recom', 'rec']:
            name = 'recom'
        elif type_ in ['charge_exchange', 'chexc', 'cx']:
            name = 'chexc'
        else:
            raise ValueError('invalid type: %s.' % type_)
        return name

    def sum_transitions(self):
        energies = wavelength_to_joule(self.wavelengths)
        energies = energies[:, np.newaxis, np.newaxis]
        coeffs = self.coeffs

        power = energies * coeffs
        power = power.sum(0)

        return power

    @property
    def wavelengths(self):
        return np.array([t.wavelength for t in self.transitions])

    @property
    def electron_densities(self):
        return np.array([t.electron_density for t in self.transitions])

    @property
    def electron_temperatures(self):
        return np.array([t.electron_temperature for t in self.transitions])

    @property
    def coeffs(self):
        return np.array([t.photon_emissivity for t in self.transitions])

def wavelength_to_joule(lambda_):
    from scipy.constants import h, c
    return h * c / lambda_

def P_bremsstrahlung(k, Te, ne):
    """
    W m^3
    """
    return 1.53e-38 * Te**0.5 * k**2

if __name__ == '__main__':
    pec = PhotonEmissivity()
    pec.append_file('pec96#c_pju#c3.dat')

    power = pec.filter_type('rec', 'ex').sum_transitions()
    te = pec.transitions[0].electron_temperature
    ne = pec.transitions[0].electron_density

    import atomic
    ad = atomic.element('carbon')
    cont_power = ad.coeffs['continuum_power']
    line_power = ad.coeffs['line_power']
    #line_power = atomic.atomic_data.ZeroCoefficient()
    #cont_power = atomic.atomic_data.ZeroCoefficient()

    import matplotlib.pyplot as plt
    plt.figure(1); plt.clf()
    ax = plt.gca()
    ax.set_xlim(1e0, 1e3)

    densities = [1e18, 1e19, 1e20]
    for density_index in np.searchsorted(ne, densities):
        c1 = line_power(3,te, ne[density_index])
        c2 = cont_power(3,te, ne[density_index])
        c = c1 + c2
        label = r'$n_\mathrm{e} = 10^{%d}\ \mathrm{m^{-3}}$' %\
            np.log10(ne[density_index])
        line, = plt.loglog(te, c, 'o')
        plt.loglog(te, power[:,density_index], color=line.get_color(),
                label=label)
    plt.legend(loc='best')

    plt.draw()
    plt.show()
<|endoftext|>


<commit_before>#  -*- coding: utf-8 -*-
#  vim: tabstop=4 shiftwidth=4 softtabstop=4

#  Copyright (c) 2014, GEM Foundation

#  OpenQuake is free software: you can redistribute it and/or modify it
#  under the terms of the GNU Affero General Public License as published
#  by the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.

#  OpenQuake 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 General Public License for more details.

#  You should have received a copy of the GNU Affero General Public License
#  along with OpenQuake.  If not, see <http://www.gnu.org/licenses/>.

import os
import sys
import time
import signal
import threading

import celery.task.control
from amqplib.client_0_8.exceptions import AMQPChannelException

from openquake.engine import logs
from openquake.engine.utils import config
from openquake.commonlib.general import str2bool

class MasterKilled(KeyboardInterrupt):
    """
    Exception raised when a job is killed manually or aborted
    by the `openquake.engine.engine.CeleryNodeMonitor`.
    """
    registered_handlers = False  # set when the signal handlers are registered

    @classmethod
    def handle_signal(cls, signum, _stack):
        """
        When a SIGTERM or a SIGABRT is received, raise the MasterKilled
        exception with an appropriate error message.

        :param int signum: the number of the received signal
        :param _stack: the current frame object, ignored
        """
        if signum == signal.SIGTERM:
            msg = 'The openquake master process was killed manually'
        elif signum == signal.SIGABRT:
            msg = ('The openquake master process was killed by the '
                   'CeleryNodeMonitor because some node failed')
        else:
            msg = 'This should never happen'
        raise cls(msg)

    @classmethod
    def register_handlers(cls):
        """
        Register the signal handlers for SIGTERM and SIGABRT
        if they were not registered before.
        """
        if not cls.registered_handlers:  # called only once
            signal.signal(signal.SIGTERM, cls.handle_signal)
            signal.signal(signal.SIGABRT, cls.handle_signal)
            cls.registered_handlers = True

class CeleryNodeMonitor(object):
    """
    Context manager wrapping a block of code with a monitor thread
    checking that the celery nodes are accessible. The check is
    performed periodically by pinging the nodes. If some node fail,
    for instance due to an out of memory error, a SIGABRT signal
    is sent to the master process.

    :param float interval:
        polling interval in seconds
    :param bool no_distribute:
        if True, the CeleryNodeMonitor will do nothing at all
    """
    def __init__(self, no_distribute, interval):
        self.interval = interval
        self.no_distribute = no_distribute
        self.job_running = True
        self.live_nodes = None  # set of live worker nodes
        self.th = None
        MasterKilled.register_handlers()

    def __enter__(self):
        if self.no_distribute:
            return self  # do nothing
        self.live_nodes = self.ping(timeout=1)
        if not self.live_nodes:
            print >> sys.stderr, "No live compute nodes, aborting calculation"
            sys.exit(2)
        self.th = threading.Thread(None, self.check_nodes)
        self.th.start()
        return self

    def __exit__(self, etype, exc, tb):
        self.job_running = False
        if self.th:
            self.th.join()

    def ping(self, timeout):
        """
        Ping the celery nodes by using .interval as timeout parameter
        """
        celery_inspect = celery.task.control.inspect(timeout=timeout)
        try:
            response_dict = celery_inspect.ping() or {}
        except AMQPChannelException as e:
            logs.LOG.warn(str(e))
            response_dict = {}
        return set(response_dict)

    def check_nodes(self):
        """
        Check that the expected celery nodes are all up. The loop
        continues until the main thread keeps running. In case of
        heavy computations it is best to set the environment variable
        OQ_TERMINATE_JOB_WHEN_CELERY_IS_DOWN=false, otherwise if any
        node does not respond to the ping the calculation is killed.
        """
        while self.job_is_running(sleep=self.interval):
            live_nodes = self.ping(timeout=self.interval)
            if live_nodes < self.live_nodes:
                dead_nodes = list(self.live_nodes - live_nodes)
                logs.LOG.critical(
                    'Cluster nodes not accessible: %s', dead_nodes)
                terminate = os.environ.get(
                    'OQ_TERMINATE_JOB_WHEN_CELERY_IS_DOWN', True)
                if terminate:
                    os.kill(os.getpid(), signal.SIGABRT)  # commit suicide

    def job_is_running(self, sleep):
        """
        Check for 10 times during the sleep interval if the flag
        self.job_running becomes false and then exit.
        """
        for _ in range(10):
            if not self.job_running:
                break
            time.sleep(sleep / 10.)
        return self.job_running
<commit_msg>Fixed a stupid bug with OQ_TERMINATE_JOB_WHEN_CELERY_IS_DOWN<commit_after>#  -*- coding: utf-8 -*-
#  vim: tabstop=4 shiftwidth=4 softtabstop=4

#  Copyright (c) 2014, GEM Foundation

#  OpenQuake is free software: you can redistribute it and/or modify it
#  under the terms of the GNU Affero General Public License as published
#  by the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.

#  OpenQuake 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 General Public License for more details.

#  You should have received a copy of the GNU Affero General Public License
#  along with OpenQuake.  If not, see <http://www.gnu.org/licenses/>.

import os
import sys
import time
import signal
import threading

import celery.task.control
from amqplib.client_0_8.exceptions import AMQPChannelException

from openquake.engine import logs
from openquake.commonlib.general import str2bool

class MasterKilled(KeyboardInterrupt):
    """
    Exception raised when a job is killed manually or aborted
    by the `openquake.engine.engine.CeleryNodeMonitor`.
    """
    registered_handlers = False  # set when the signal handlers are registered

    @classmethod
    def handle_signal(cls, signum, _stack):
        """
        When a SIGTERM or a SIGABRT is received, raise the MasterKilled
        exception with an appropriate error message.

        :param int signum: the number of the received signal
        :param _stack: the current frame object, ignored
        """
        if signum == signal.SIGTERM:
            msg = 'The openquake master process was killed manually'
        elif signum == signal.SIGABRT:
            msg = ('The openquake master process was killed by the '
                   'CeleryNodeMonitor because some node failed')
        else:
            msg = 'This should never happen'
        raise cls(msg)

    @classmethod
    def register_handlers(cls):
        """
        Register the signal handlers for SIGTERM and SIGABRT
        if they were not registered before.
        """
        if not cls.registered_handlers:  # called only once
            signal.signal(signal.SIGTERM, cls.handle_signal)
            signal.signal(signal.SIGABRT, cls.handle_signal)
            cls.registered_handlers = True

class CeleryNodeMonitor(object):
    """
    Context manager wrapping a block of code with a monitor thread
    checking that the celery nodes are accessible. The check is
    performed periodically by pinging the nodes. If some node fail,
    for instance due to an out of memory error, a SIGABRT signal
    is sent to the master process.

    :param float interval:
        polling interval in seconds
    :param bool no_distribute:
        if True, the CeleryNodeMonitor will do nothing at all
    """
    def __init__(self, no_distribute, interval):
        self.interval = interval
        self.no_distribute = no_distribute
        self.job_running = True
        self.live_nodes = None  # set of live worker nodes
        self.th = None
        MasterKilled.register_handlers()

    def __enter__(self):
        if self.no_distribute:
            return self  # do nothing
        self.live_nodes = self.ping(timeout=1)
        if not self.live_nodes:
            print >> sys.stderr, "No live compute nodes, aborting calculation"
            sys.exit(2)
        self.th = threading.Thread(None, self.check_nodes)
        self.th.start()
        return self

    def __exit__(self, etype, exc, tb):
        self.job_running = False
        if self.th:
            self.th.join()

    def ping(self, timeout):
        """
        Ping the celery nodes by using .interval as timeout parameter
        """
        celery_inspect = celery.task.control.inspect(timeout=timeout)
        try:
            response_dict = celery_inspect.ping() or {}
        except AMQPChannelException as e:
            logs.LOG.warn(str(e))
            response_dict = {}
        return set(response_dict)

    def check_nodes(self):
        """
        Check that the expected celery nodes are all up. The loop
        continues until the main thread keeps running. In case of
        heavy computations it is best to set the environment variable
        OQ_TERMINATE_JOB_WHEN_CELERY_IS_DOWN=false, otherwise if any
        node does not respond to the ping the calculation is killed.
        """
        while self.job_is_running(sleep=self.interval):
            live_nodes = self.ping(timeout=self.interval)
            if live_nodes < self.live_nodes:
                dead_nodes = list(self.live_nodes - live_nodes)
                logs.LOG.critical(
                    'Cluster nodes not accessible: %s', dead_nodes)
                terminate = str2bool(os.environ.get(
                    'OQ_TERMINATE_JOB_WHEN_CELERY_IS_DOWN', 'true'))
                if terminate:
                    os.kill(os.getpid(), signal.SIGABRT)  # commit suicide

    def job_is_running(self, sleep):
        """
        Check for 10 times during the sleep interval if the flag
        self.job_running becomes false and then exit.
        """
        for _ in range(10):
            if not self.job_running:
                break
            time.sleep(sleep / 10.)
        return self.job_running
<|endoftext|>


<commit_before>import subprocess
import time
import random
import sys
import os
import datetime

import psutil
import discord

from discord.ext import commands

from .common import Cog

FEEDBACK_CHANNEL_ID = 290244095820038144

OAUTH_URL = 'https://discordapp.com/oauth2/authorize?permissions=379968&scope=bot&client_id=202586824013643777'
SUPPORT_SERVER = 'https://discord.gg/5ASwg4C'

class Basic(Cog):
    """Basic commands."""
    def __init__(self, bot):
        super().__init__(bot)
        self.process = psutil.Process(os.getpid())

    @commands.command(aliases=['p'])
    async def ping(self, ctx):
        """Ping."""
        t1 = time.monotonic()
        m = await ctx.send('pinging...')
        t2 = time.monotonic()
        delta = round((t2 - t1) * 1000, 2)
        await m.edit(content=f'`{delta}ms`')

    @commands.command(aliases=['rand'])
    async def random(self, ctx, n_min: int, n_max: int):
        """Get a random number."""
        if n_min > n_max:
            await ctx.send("`min > max` u wot")
            return

        n_rand = random.randint(n_min, n_max)
        await ctx.send(f"from {n_min} to {n_max} I go {n_rand}")

    @commands.command(aliases=['choose', 'choice'])
    async def pick(self, ctx, *choices: commands.clean_content):
        """Pick a random element."""
        if len(choices) < 1:
            await ctx.send("dude what")
            return

        await ctx.send(random.choice(choices))

    @commands.command()
    async def version(self, ctx):
        """Show current josé version"""
        pyver = '%d.%d.%d' % (sys.version_info[:3])
        do = lambda cmd: subprocess.check_output(cmd, shell=True).decode('utf-8').strip()
        head_id = do('git rev-parse --short HEAD')
        branch = do('git rev-parse --abbrev-ref HEAD')

        await ctx.send(f'`José v{self.JOSE_VERSION} git:{branch}-{head_id} py:{pyver} d.py:{discord.__version__}`')

    @commands.command()
    async def uptime(self, ctx):
        """Show uptime"""
        sec = round(time.time() - self.bot.init_time)

        m, s = divmod(sec, 60)
        h, m = divmod(m, 60)
        d, h = divmod(h, 24)

        await ctx.send(f'Uptime: **`{d} days, {h} hours, {m} minutes, {s} seconds`**')

    @commands.command()
    async def stats(self, ctx):
        """Statistics."""
        em = discord.Embed(title='Statistics')

        # get memory usage
        mem_bytes = self.process.memory_full_info().rss
        mem_mb = round(mem_bytes / 1024 / 1024, 2)

        cpu_usage = round(self.process.cpu_percent() / psutil.cpu_count(), 2)

        em.add_field(name='Memory / CPU usage', value=f'`{mem_mb}MB / {cpu_usage}% CPU`')

        channels = sum([len(g.channels) for g in self.bot.guilds])

        em.add_field(name='Guilds', value=f'{len(self.bot.guilds)}')
        em.add_field(name='Channels', value=f'{channels}')

        gay_chans = len(c for c in self.bot.get_all_channels() if 'gay' in c.name.lower())
        em.add_field(name='Gaynnels', value=gay_chans)

        em.add_field(name='Texters', value=f'{len(self.bot.cogs["Speak"].text_generators)}/{len(self.bot.guilds)}')
        em.add_field(name='Members', value=len(list(self.bot.get_all_members())))

        humans = [m for m in self.bot.get_all_members() if not m.bot]
        em.add_field(name='human/unique humans', value=f'`{len(humans)}, {len(set(humans))}`')

        await ctx.send(embed=em)

    @commands.command()
    async def feedback(self, ctx, *, feedback: str):
        """Sends feedback to a special channel."""

        author = ctx.author
        channel = ctx.channel
        guild = ctx.guild

        em = discord.Embed(title='', colour=discord.Colour.magenta())
        em.timestamp = datetime.datetime.now()
        em.set_footer(text='Feedback Report')
        em.set_author(name=str(author), icon_url=author.avatar_url or \
            author.default_avatar_url)

        em.add_field(name="Feedback", value=feedback)
        em.add_field(name="Guild", value=f'{guild.name} [{guild.id}]')
        em.add_field(name="Channel", value=f'{channel.name} [{channel.id}]')

        feedback_channel = self.bot.get_channel(FEEDBACK_CHANNEL_ID)
        if feedback_channel is None:
            await ctx.send('feedback channel not found we are fuckd')
            return

        await feedback_channel.send(embed=em)
        await ctx.ok()

    @commands.command()
    async def clist(self, ctx, cog_name: str):
        """Search for all commands that were declared by a cog.

        This is case sensitive.
        """
        matched = [cmd.name for cmd in self.bot.commands if cmd.cog_name == cog_name]

        if len(matched) < 1:
            await ctx.send('No commands found')
            return

        _res = ' '.join(matched)
        await ctx.send(f'```\n{_res}\n```')

    @commands.command()
    async def invite(self, ctx):
        """Get invite URL"""
        em = discord.Embed(title='Invite stuff')
        em.add_field(name='OAuth URL', value=OAUTH_URL)
        em.add_field(name='Support Server', value=SUPPORT_SERVER)
        await ctx.send(embed=em)

def setup(bot):
    bot.add_cog(Basic(bot))
<commit_msg>basic: thanks dog<commit_after>import subprocess
import time
import random
import sys
import os
import datetime

import psutil
import discord

from discord.ext import commands

from .common import Cog

FEEDBACK_CHANNEL_ID = 290244095820038144

OAUTH_URL = 'https://discordapp.com/oauth2/authorize?permissions=379968&scope=bot&client_id=202586824013643777'
SUPPORT_SERVER = 'https://discord.gg/5ASwg4C'

class Basic(Cog):
    """Basic commands."""
    def __init__(self, bot):
        super().__init__(bot)
        self.process = psutil.Process(os.getpid())

    @commands.command(aliases=['p'])
    async def ping(self, ctx):
        """Ping."""
        t1 = time.monotonic()
        m = await ctx.send('pinging...')
        t2 = time.monotonic()
        delta = round((t2 - t1) * 1000, 2)
        await m.edit(content=f'`{delta}ms`')

    @commands.command(aliases=['rand'])
    async def random(self, ctx, n_min: int, n_max: int):
        """Get a random number."""
        if n_min > n_max:
            await ctx.send("`min > max` u wot")
            return

        n_rand = random.randint(n_min, n_max)
        await ctx.send(f"from {n_min} to {n_max} I go {n_rand}")

    @commands.command(aliases=['choose', 'choice'])
    async def pick(self, ctx, *choices: commands.clean_content):
        """Pick a random element."""
        if len(choices) < 1:
            await ctx.send("dude what")
            return

        await ctx.send(random.choice(choices))

    @commands.command()
    async def version(self, ctx):
        """Show current josé version"""
        pyver = '%d.%d.%d' % (sys.version_info[:3])
        do = lambda cmd: subprocess.check_output(cmd, shell=True).decode('utf-8').strip()
        head_id = do('git rev-parse --short HEAD')
        branch = do('git rev-parse --abbrev-ref HEAD')

        await ctx.send(f'`José v{self.JOSE_VERSION} git:{branch}-{head_id} py:{pyver} d.py:{discord.__version__}`')

    @commands.command()
    async def uptime(self, ctx):
        """Show uptime"""
        sec = round(time.time() - self.bot.init_time)

        m, s = divmod(sec, 60)
        h, m = divmod(m, 60)
        d, h = divmod(h, 24)

        await ctx.send(f'Uptime: **`{d} days, {h} hours, {m} minutes, {s} seconds`**')

    @commands.command()
    async def stats(self, ctx):
        """Statistics."""
        em = discord.Embed(title='Statistics')

        # get memory usage
        mem_bytes = self.process.memory_full_info().rss
        mem_mb = round(mem_bytes / 1024 / 1024, 2)

        cpu_usage = round(self.process.cpu_percent() / psutil.cpu_count(), 2)

        em.add_field(name='Memory / CPU usage', value=f'`{mem_mb}MB / {cpu_usage}% CPU`')

        channels = sum([len(g.channels) for g in self.bot.guilds])

        em.add_field(name='Guilds', value=f'{len(self.bot.guilds)}')
        em.add_field(name='Channels', value=f'{channels}')

        gay_chans = sum(1 for c in self.bot.get_all_channels() if 'gay' in c.name.lower())
        em.add_field(name='Gaynnels', value=gay_chans)

        em.add_field(name='Texters', value=f'{len(self.bot.cogs["Speak"].text_generators)}/{len(self.bot.guilds)}')
        em.add_field(name='Members', value=len(list(self.bot.get_all_members())))

        humans = [m for m in self.bot.get_all_members() if not m.bot]
        em.add_field(name='human/unique humans', value=f'`{len(humans)}, {len(set(humans))}`')

        await ctx.send(embed=em)

    @commands.command()
    async def feedback(self, ctx, *, feedback: str):
        """Sends feedback to a special channel."""

        author = ctx.author
        channel = ctx.channel
        guild = ctx.guild

        em = discord.Embed(title='', colour=discord.Colour.magenta())
        em.timestamp = datetime.datetime.now()
        em.set_footer(text='Feedback Report')
        em.set_author(name=str(author), icon_url=author.avatar_url or \
            author.default_avatar_url)

        em.add_field(name="Feedback", value=feedback)
        em.add_field(name="Guild", value=f'{guild.name} [{guild.id}]')
        em.add_field(name="Channel", value=f'{channel.name} [{channel.id}]')

        feedback_channel = self.bot.get_channel(FEEDBACK_CHANNEL_ID)
        if feedback_channel is None:
            await ctx.send('feedback channel not found we are fuckd')
            return

        await feedback_channel.send(embed=em)
        await ctx.ok()

    @commands.command()
    async def clist(self, ctx, cog_name: str):
        """Search for all commands that were declared by a cog.

        This is case sensitive.
        """
        matched = [cmd.name for cmd in self.bot.commands if cmd.cog_name == cog_name]

        if len(matched) < 1:
            await ctx.send('No commands found')
            return

        _res = ' '.join(matched)
        await ctx.send(f'```\n{_res}\n```')

    @commands.command()
    async def invite(self, ctx):
        """Get invite URL"""
        em = discord.Embed(title='Invite stuff')
        em.add_field(name='OAuth URL', value=OAUTH_URL)
        em.add_field(name='Support Server', value=SUPPORT_SERVER)
        await ctx.send(embed=em)

def setup(bot):
    bot.add_cog(Basic(bot))
<|endoftext|>


<commit_before># Copyright (C) 2015 Brent Baude <bbaude@redhat.com>
# Copyright (C) 2015 Red Hat Inc., Durham, North Carolina.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser 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.

'''Reporter Class'''

import collections
import os

class Reporter(object):
    ''' Does stdout reporting '''
    def __init__(self, appc):
        self.output = collections.namedtuple('Summary', 'iid, cid, os, sevs,'
                                             'log, msg',)
        self.list_of_outputs = []
        self.appc = appc
        self.report_dir = os.path.join(self.appc.reportdir, "reports")
        self.appc.docker_state = os.path.join(self.report_dir,
                                              "docker_state.json")

        if not os.path.exists(self.report_dir):
            os.mkdir(self.report_dir)
        self.content = ""

    def report_summary(self):
        '''
        This function is the primary function to output results
        to stdout when running the image-scanner
        '''
        for image in self.list_of_outputs:
            short_cid_list = []
            image_json = {image.iid: {}}
            image_json[image.iid]['xml_path'] = os.path.join(
                self.report_dir, image.iid + ".xml")
            if image.msg is None:
                for cid in image.cid:
                    short_cid_list.append(cid[:12])
                image_json[image.iid]['cids'] = short_cid_list
                image_json[image.iid]['critical'] = image.sevs['Critical']
                image_json[image.iid]['important'] = \
                    image.sevs['Important']
                image_json[image.iid]['moderate'] = image.sevs['Moderate']
                image_json[image.iid]['low'] = image.sevs['Low']
                image_json[image.iid]['os'] = self.appc.os_release
            else:
                image_json[image.iid]['msg'] = image.msg
            self.appc.return_json[image.iid] = image_json[image.iid]
        report_files = []
        for image in self.list_of_outputs:
            if image.msg is None:
                short_image = image.iid[:12] + ".scap"
                out = open(os.path.join(self.report_dir, short_image), 'wb')
                report_files.append(short_image)
                out.write(image.log)
                out.close()
        for report in report_files:
            os.path.join(self.report_dir, report)

    def _get_dtype(self, iid):
        ''' Returns whether the given id is an image or container '''
        # Images
        for image in self.appc.allimages:
            if image['Id'].startswith(iid):
                return "Image"
        # Containers
        for con in self.appc.cons:
            if con['Id'].startswith(iid):
                return "Container"
        return None
<commit_msg>Reporter fix python3<commit_after># Copyright (C) 2015 Brent Baude <bbaude@redhat.com>
# Copyright (C) 2015 Red Hat Inc., Durham, North Carolina.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser 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.

'''Reporter Class'''

import collections
import os

class Reporter(object):
    ''' Does stdout reporting '''
    def __init__(self, appc):
        self.output = collections.namedtuple('Summary', 'iid, cid, os, sevs,'
                                             'log, msg',)
        self.list_of_outputs = []
        self.appc = appc
        self.report_dir = os.path.join(self.appc.reportdir, "reports")
        self.appc.docker_state = os.path.join(self.report_dir,
                                              "docker_state.json")

        if not os.path.exists(self.report_dir):
            os.mkdir(self.report_dir)
        self.content = ""

    def report_summary(self):
        '''
        This function is the primary function to output results
        to stdout when running the image-scanner
        '''
        for image in self.list_of_outputs:
            short_cid_list = []
            image_json = {image.iid: {}}
            image_json[image.iid]['xml_path'] = os.path.join(
                self.report_dir, image.iid + ".xml")
            if image.msg is None:
                for cid in image.cid:
                    short_cid_list.append(cid[:12])
                image_json[image.iid]['cids'] = short_cid_list
                image_json[image.iid]['critical'] = image.sevs['Critical']
                image_json[image.iid]['important'] = \
                    image.sevs['Important']
                image_json[image.iid]['moderate'] = image.sevs['Moderate']
                image_json[image.iid]['low'] = image.sevs['Low']
                image_json[image.iid]['os'] = self.appc.os_release
            else:
                image_json[image.iid]['msg'] = image.msg
            self.appc.return_json[image.iid] = image_json[image.iid]
        report_files = []
        for image in self.list_of_outputs:
            if image.msg is None:
                short_image = image.iid[:12] + ".scap"
                out = open(os.path.join(self.report_dir, short_image), 'w')
                report_files.append(short_image)
                out.write(image.log)
                out.close()
        for report in report_files:
            os.path.join(self.report_dir, report)

    def _get_dtype(self, iid):
        ''' Returns whether the given id is an image or container '''
        # Images
        for image in self.appc.allimages:
            if image['Id'].startswith(iid):
                return "Image"
        # Containers
        for con in self.appc.cons:
            if con['Id'].startswith(iid):
                return "Container"
        return None
<|endoftext|>


<commit_before>"""Tracking for bluetooth low energy devices."""
import asyncio
from datetime import datetime, timedelta
import logging
from uuid import UUID

import pygatt  # pylint: disable=import-error

from homeassistant.components.device_tracker.const import (
    CONF_SCAN_INTERVAL,
    CONF_TRACK_NEW,
    SCAN_INTERVAL,
    SOURCE_TYPE_BLUETOOTH_LE,
)
from homeassistant.components.device_tracker.legacy import (
    YAML_DEVICES,
    async_load_config,
)
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.helpers.event import track_point_in_utc_time
import homeassistant.util.dt as dt_util

_LOGGER = logging.getLogger(__name__)

# Base UUID: 00000000-0000-1000-8000-00805F9B34FB
# Battery characteristic: 0x2a19 (https://www.bluetooth.com/specifications/gatt/characteristics/)
BATTERY_CHARACTERISTIC_UUID = UUID("00002a19-0000-1000-8000-00805f9b34fb")
CONF_TRACK_BATTERY = "track_battery"
DEFAULT_TRACK_BATTERY_INTERVAL = timedelta(days=1)
DATA_BLE = "BLE"
DATA_BLE_ADAPTER = "ADAPTER"
BLE_PREFIX = "BLE_"
MIN_SEEN_NEW = 5

def setup_scanner(hass, config, see, discovery_info=None):
    """Set up the Bluetooth LE Scanner."""

    new_devices = {}
    hass.data.setdefault(DATA_BLE, {DATA_BLE_ADAPTER: None})

    def handle_stop(event):
        """Try to shut down the bluetooth child process nicely."""
        # These should never be unset at the point this runs, but just for
        # safety's sake, use `get`.
        adapter = hass.data.get(DATA_BLE, {}).get(DATA_BLE_ADAPTER)
        if adapter is not None:
            adapter.kill()

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, handle_stop)

    if config.get(CONF_TRACK_BATTERY):
        battery_track_interval = DEFAULT_TRACK_BATTERY_INTERVAL
    else:
        battery_track_interval = timedelta(0)

    def see_device(address, name, new_device=False, battery=None):
        """Mark a device as seen."""
        if name is not None:
            name = name.strip("\x00")

        if new_device:
            if address in new_devices:
                new_devices[address]["seen"] += 1
                if name:
                    new_devices[address]["name"] = name
                else:
                    name = new_devices[address]["name"]
                _LOGGER.debug("Seen %s %s times", address, new_devices[address]["seen"])
                if new_devices[address]["seen"] < MIN_SEEN_NEW:
                    return
                _LOGGER.debug("Adding %s to tracked devices", address)
                devs_to_track.append(address)
                if battery_track_interval > timedelta(0):
                    devs_track_battery[address] = dt_util.as_utc(
                        datetime.fromtimestamp(0)
                    )
            else:
                _LOGGER.debug("Seen %s for the first time", address)
                new_devices[address] = {"seen": 1, "name": name}
                return

        see(
            mac=BLE_PREFIX + address,
            host_name=name,
            source_type=SOURCE_TYPE_BLUETOOTH_LE,
            battery=battery,
        )

    def discover_ble_devices():
        """Discover Bluetooth LE devices."""
        _LOGGER.debug("Discovering Bluetooth LE devices")
        try:
            adapter = pygatt.GATTToolBackend()
            hass.data[DATA_BLE][DATA_BLE_ADAPTER] = adapter
            devs = adapter.scan()

            devices = {x["address"]: x["name"] for x in devs}
            _LOGGER.debug("Bluetooth LE devices discovered = %s", devices)
        except (RuntimeError, pygatt.exceptions.BLEError) as error:
            _LOGGER.error("Error during Bluetooth LE scan: %s", error)
            return {}
        return devices

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []
    devs_track_battery = {}

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in asyncio.run_coroutine_threadsafe(
        async_load_config(yaml_path, hass, 0), hass.loop
    ).result():
        # check if device is a valid bluetooth device
        if device.mac and device.mac[:4].upper() == BLE_PREFIX:
            address = device.mac[4:]
            if device.track:
                _LOGGER.debug("Adding %s to BLE tracker", device.mac)
                devs_to_track.append(address)
                if battery_track_interval > timedelta(0):
                    devs_track_battery[address] = dt_util.as_utc(
                        datetime.fromtimestamp(0)
                    )
            else:
                _LOGGER.debug("Adding %s to BLE do not track", device.mac)
                devs_donot_track.append(address)

    # if track new devices is true discover new devices
    # on every scan.
    track_new = config.get(CONF_TRACK_NEW)

    if not devs_to_track and not track_new:
        _LOGGER.warning("No Bluetooth LE devices to track!")
        return False

    interval = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL)

    def update_ble(now):
        """Lookup Bluetooth LE devices and update status."""
        devs = discover_ble_devices()
        if devs_track_battery:
            adapter = hass.data[DATA_BLE][DATA_BLE_ADAPTER]
        for mac in devs_to_track:
            if mac not in devs:
                continue

            if devs[mac] is None:
                devs[mac] = mac

            battery = None
            if (
                mac in devs_track_battery
                and now > devs_track_battery[mac] + battery_track_interval
            ):
                handle = None
                try:
                    adapter.start(reset_on_start=True)
                    _LOGGER.debug("Reading battery for Bluetooth LE device %s", mac)
                    bt_device = adapter.connect(mac)
                    # Try to get the handle; it will raise a BLEError exception if not available
                    handle = bt_device.get_handle(BATTERY_CHARACTERISTIC_UUID)
                    battery = ord(bt_device.char_read(BATTERY_CHARACTERISTIC_UUID))
                    devs_track_battery[mac] = now
                except pygatt.exceptions.NotificationTimeout:
                    _LOGGER.warning("Timeout when trying to get battery status")
                except pygatt.exceptions.BLEError as err:
                    _LOGGER.warning("Could not read battery status: %s", err)
                    if handle is not None:
                        # If the device does not offer battery information, there is no point in asking again later on.
                        # Remove the device from the battery-tracked devices, so that their battery is not wasted
                        # trying to get an unavailable information.
                        del devs_track_battery[mac]
                finally:
                    adapter.stop()
            see_device(mac, devs[mac], battery=battery)

        if track_new:
            for address in devs:
                if address not in devs_to_track and address not in devs_donot_track:
                    _LOGGER.info("Discovered Bluetooth LE device %s", address)
                    see_device(address, devs[address], new_device=True)

        track_point_in_utc_time(hass, update_ble, dt_util.utcnow() + interval)

    update_ble(dt_util.utcnow())
    return True
<commit_msg>Fix permissions on bluetooth_le_tracker (#35432)<commit_after>"""Tracking for bluetooth low energy devices."""
import asyncio
from datetime import datetime, timedelta
import logging
from uuid import UUID

import pygatt  # pylint: disable=import-error

from homeassistant.components.device_tracker.const import (
    CONF_SCAN_INTERVAL,
    CONF_TRACK_NEW,
    SCAN_INTERVAL,
    SOURCE_TYPE_BLUETOOTH_LE,
)
from homeassistant.components.device_tracker.legacy import (
    YAML_DEVICES,
    async_load_config,
)
from homeassistant.const import EVENT_HOMEASSISTANT_STOP
from homeassistant.helpers.event import track_point_in_utc_time
import homeassistant.util.dt as dt_util

_LOGGER = logging.getLogger(__name__)

# Base UUID: 00000000-0000-1000-8000-00805F9B34FB
# Battery characteristic: 0x2a19 (https://www.bluetooth.com/specifications/gatt/characteristics/)
BATTERY_CHARACTERISTIC_UUID = UUID("00002a19-0000-1000-8000-00805f9b34fb")
CONF_TRACK_BATTERY = "track_battery"
DEFAULT_TRACK_BATTERY_INTERVAL = timedelta(days=1)
DATA_BLE = "BLE"
DATA_BLE_ADAPTER = "ADAPTER"
BLE_PREFIX = "BLE_"
MIN_SEEN_NEW = 5

def setup_scanner(hass, config, see, discovery_info=None):
    """Set up the Bluetooth LE Scanner."""

    new_devices = {}
    hass.data.setdefault(DATA_BLE, {DATA_BLE_ADAPTER: None})

    def handle_stop(event):
        """Try to shut down the bluetooth child process nicely."""
        # These should never be unset at the point this runs, but just for
        # safety's sake, use `get`.
        adapter = hass.data.get(DATA_BLE, {}).get(DATA_BLE_ADAPTER)
        if adapter is not None:
            adapter.kill()

    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, handle_stop)

    if config.get(CONF_TRACK_BATTERY):
        battery_track_interval = DEFAULT_TRACK_BATTERY_INTERVAL
    else:
        battery_track_interval = timedelta(0)

    def see_device(address, name, new_device=False, battery=None):
        """Mark a device as seen."""
        if name is not None:
            name = name.strip("\x00")

        if new_device:
            if address in new_devices:
                new_devices[address]["seen"] += 1
                if name:
                    new_devices[address]["name"] = name
                else:
                    name = new_devices[address]["name"]
                _LOGGER.debug("Seen %s %s times", address, new_devices[address]["seen"])
                if new_devices[address]["seen"] < MIN_SEEN_NEW:
                    return
                _LOGGER.debug("Adding %s to tracked devices", address)
                devs_to_track.append(address)
                if battery_track_interval > timedelta(0):
                    devs_track_battery[address] = dt_util.as_utc(
                        datetime.fromtimestamp(0)
                    )
            else:
                _LOGGER.debug("Seen %s for the first time", address)
                new_devices[address] = {"seen": 1, "name": name}
                return

        see(
            mac=BLE_PREFIX + address,
            host_name=name,
            source_type=SOURCE_TYPE_BLUETOOTH_LE,
            battery=battery,
        )

    def discover_ble_devices():
        """Discover Bluetooth LE devices."""
        _LOGGER.debug("Discovering Bluetooth LE devices")
        try:
            adapter = pygatt.GATTToolBackend()
            hass.data[DATA_BLE][DATA_BLE_ADAPTER] = adapter
            devs = adapter.scan()

            devices = {x["address"]: x["name"] for x in devs}
            _LOGGER.debug("Bluetooth LE devices discovered = %s", devices)
        except (RuntimeError, pygatt.exceptions.BLEError) as error:
            _LOGGER.error("Error during Bluetooth LE scan: %s", error)
            return {}
        return devices

    yaml_path = hass.config.path(YAML_DEVICES)
    devs_to_track = []
    devs_donot_track = []
    devs_track_battery = {}

    # Load all known devices.
    # We just need the devices so set consider_home and home range
    # to 0
    for device in asyncio.run_coroutine_threadsafe(
        async_load_config(yaml_path, hass, 0), hass.loop
    ).result():
        # check if device is a valid bluetooth device
        if device.mac and device.mac[:4].upper() == BLE_PREFIX:
            address = device.mac[4:]
            if device.track:
                _LOGGER.debug("Adding %s to BLE tracker", device.mac)
                devs_to_track.append(address)
                if battery_track_interval > timedelta(0):
                    devs_track_battery[address] = dt_util.as_utc(
                        datetime.fromtimestamp(0)
                    )
            else:
                _LOGGER.debug("Adding %s to BLE do not track", device.mac)
                devs_donot_track.append(address)

    # if track new devices is true discover new devices
    # on every scan.
    track_new = config.get(CONF_TRACK_NEW)

    if not devs_to_track and not track_new:
        _LOGGER.warning("No Bluetooth LE devices to track!")
        return False

    interval = config.get(CONF_SCAN_INTERVAL, SCAN_INTERVAL)

    def update_ble(now):
        """Lookup Bluetooth LE devices and update status."""
        devs = discover_ble_devices()
        if devs_track_battery:
            adapter = hass.data[DATA_BLE][DATA_BLE_ADAPTER]
        for mac in devs_to_track:
            if mac not in devs:
                continue

            if devs[mac] is None:
                devs[mac] = mac

            battery = None
            if (
                mac in devs_track_battery
                and now > devs_track_battery[mac] + battery_track_interval
            ):
                handle = None
                try:
                    adapter.start(reset_on_start=True)
                    _LOGGER.debug("Reading battery for Bluetooth LE device %s", mac)
                    bt_device = adapter.connect(mac)
                    # Try to get the handle; it will raise a BLEError exception if not available
                    handle = bt_device.get_handle(BATTERY_CHARACTERISTIC_UUID)
                    battery = ord(bt_device.char_read(BATTERY_CHARACTERISTIC_UUID))
                    devs_track_battery[mac] = now
                except pygatt.exceptions.NotificationTimeout:
                    _LOGGER.warning("Timeout when trying to get battery status")
                except pygatt.exceptions.BLEError as err:
                    _LOGGER.warning("Could not read battery status: %s", err)
                    if handle is not None:
                        # If the device does not offer battery information, there is no point in asking again later on.
                        # Remove the device from the battery-tracked devices, so that their battery is not wasted
                        # trying to get an unavailable information.
                        del devs_track_battery[mac]
                finally:
                    adapter.stop()
            see_device(mac, devs[mac], battery=battery)

        if track_new:
            for address in devs:
                if address not in devs_to_track and address not in devs_donot_track:
                    _LOGGER.info("Discovered Bluetooth LE device %s", address)
                    see_device(address, devs[address], new_device=True)

        track_point_in_utc_time(hass, update_ble, dt_util.utcnow() + interval)

    update_ble(dt_util.utcnow())
    return True
<|endoftext|>


<commit_before>#!/usr/bin/env python

##########################################################################
# Copyright (c) 2009, ETH Zurich.
# All rights reserved.
#
# This file is distributed under the terms in the attached LICENSE file.
# If you do not find this file, copies can be found by writing to:
# ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
##########################################################################

import os, optparse, re
import harness, debug, tests

def parse_args():
    p = optparse.OptionParser(usage='Usage: %prog [options] RESULTDIR...',
            description='Reprocess raw results from scalebench/harness runs')
    debug.addopts(p, 'debuglevel')
    options, dirs = p.parse_args()

    if len(dirs) == 0:
        p.error('no result directories specified')

    # check validity of result dirs
    for d in dirs:
        if not (os.path.isdir(d) and os.access(d, os.W_OK)
                and os.access(os.path.join(d, 'description.txt'), os.R_OK)):
            p.error('invalid results directory %s' % d)

    debug.current_level = options.debuglevel
    return dirs

def main(dirs):
    for dirname in dirs:
        debug.log('reprocessing %s' % dirname)
        debug.verbose('parse %s/description.txt for test' % dirname)
        testname = test = None
        f = open(os.path.join(dirname, 'description.txt'), 'r')
        for line in f:
            m = re.match(r'test:\s+(.*)', line)
            if m:
                testname = m.group(1)
                break
        f.close()

        if not testname:
            debug.error('unable to parse description for %s, skipped' % dirname)
            continue

        debug.verbose('locate test "%s"' % testname)
        for t in tests.all_tests:
            if t.name.lower() == testname.lower():
                test = t(None) # XXX: dummy options
        if not test:
            debug.error('unknown test "%s" in %s, skipped' % (testname, dirname))
            continue

        debug.verbose('reprocess results')
        h = harness.Harness()
        h.process_results(test, dirname)

if __name__ == "__main__":
    main(parse_args())
<commit_msg>harness: make reprocess.py a bit smarter, add command line args for sourcedir, builddir and buildarch<commit_after>#!/usr/bin/env python

##########################################################################
# Copyright (c) 2009, ETH Zurich.
# All rights reserved.
#
# This file is distributed under the terms in the attached LICENSE file.
# If you do not find this file, copies can be found by writing to:
# ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
##########################################################################

import os, optparse, re
import harness, debug, tests

def parse_args():
    p = optparse.OptionParser(usage='Usage: %prog [options] RESULTDIR...',
            description='Reprocess raw results from scalebench/harness runs')
    debug.addopts(p, 'debuglevel')
    p.add_option('-e', '--existingbuild', dest='existingbuild', metavar='DIR',
                 help='existing build directory (may not be used with -b)')
    p.add_option('-S', '--sourcedir', dest='sourcedir', metavar='DIR',
                 help='source directory')
    p.add_option('-A', '--arch', dest='arch', metavar='ARCH',
            help='architecture to use')
    options, dirs = p.parse_args()

    print "options.existingbuild:",options.existingbuild
    options.buildbase = options.existingbuild
    print "options.buildbase:",options.buildbase
    options.machines = None

    if len(dirs) == 0:
        p.error('no result directories specified')

    # check validity of result dirs
    for d in dirs:
        if not (os.path.isdir(d) and os.access(d, os.W_OK)
                and os.access(os.path.join(d, 'description.txt'), os.R_OK)):
            p.error('invalid results directory %s' % d)

    debug.current_level = options.debuglevel
    return dirs,options

def main(dirs, options=None):
    for dirname in dirs:
        debug.log('reprocessing %s' % dirname)
        debug.verbose('parse %s/description.txt for test' % dirname)
        testname = test = None
        f = open(os.path.join(dirname, 'description.txt'), 'r')
        for line in f:
            m = re.match(r'test:\s+(.*)', line)
            if m:
                testname = m.group(1)
                break
        f.close()

        if not testname:
            debug.error('unable to parse description for %s, skipped' % dirname)
            continue

        debug.verbose('locate test "%s"' % testname)
        for t in tests.all_tests:
            if t.name.lower() == testname.lower():
                test = t(options)
        if not test:
            debug.error('unknown test "%s" in %s, skipped' % (testname, dirname))
            continue

        debug.verbose('reprocess results')
        h = harness.Harness()
        h.process_results(test, dirname)

if __name__ == "__main__":
    dirs, options = parse_args()
    main(dirs, options=options)
<|endoftext|>


<commit_before>"""Switchboard class.

This class is used to coordinate updates among all Viewers.  Every Viewer must
conform to the following interface:

    - it must include a method called update_yourself() which takes three
      arguments; the red, green, and blue values of the selected color.

    - When a Viewer selects a color and wishes to update all other Views, it
      should call update_views() on the Switchboard object.  Not that the
      Viewer typically does *not* update itself before calling update_views(),
      since this would cause it to get updated twice.
"""

class Switchboard:
    def __init__(self, colordb):
        self.__views = []
        self.__colordb = colordb
        self.__red = 0
        self.__green = 0
        self.__blue = 0

    def add_view(self, view):
        self.__views.append(view)

    def update_views(self, red, green, blue):
        self.__red = red
        self.__green = green
        self.__blue = blue
        for v in self.__views:
            v.update_yourself(red, green, blue)

    def update_views_current(self):
        self.update_views(self.__red, self.__green, self.__blue)

    def colordb(self):
        return self.__colordb
<commit_msg>current_rgb(): New method<commit_after>"""Switchboard class.

This class is used to coordinate updates among all Viewers.  Every Viewer must
conform to the following interface:

    - it must include a method called update_yourself() which takes three
      arguments; the red, green, and blue values of the selected color.

    - When a Viewer selects a color and wishes to update all other Views, it
      should call update_views() on the Switchboard object.  Not that the
      Viewer typically does *not* update itself before calling update_views(),
      since this would cause it to get updated twice.
"""

class Switchboard:
    def __init__(self, colordb):
        self.__views = []
        self.__colordb = colordb
        self.__red = 0
        self.__green = 0
        self.__blue = 0

    def add_view(self, view):
        self.__views.append(view)

    def update_views(self, red, green, blue):
        self.__red = red
        self.__green = green
        self.__blue = blue
        for v in self.__views:
            v.update_yourself(red, green, blue)

    def update_views_current(self):
        self.update_views(self.__red, self.__green, self.__blue)

    def current_rgb(self):
        return self.__red, self.__green, self.__blue

    def colordb(self):
        return self.__colordb
<|endoftext|>