<commit_before>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#  IkaLog
#  ======
#  Copyright (C) 2015 Takeshi HASEGAWA
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#

from ikalog.utils import *

class IkaScene_GameStart:

    # 720p サイズでの値
    mapname_width = 430
    mapname_left = 1280 - mapname_width
    mapname_top = 580
    mapname_height = 640 - mapname_top

    rulename_left = 640 - 120
    rulename_right = 640 + 120
    rulename_width = rulename_right - rulename_left
    rulename_top = 250
    rulename_bottom = 310
    rulename_height = rulename_bottom - rulename_top

    def guess_map(self, frame):
        # マップ名を判断
        for map in self.map_list:
            r = map['mask'].match(frame)
            if r:
                return map
        return None

    def guess_rule(self, frame):
        # モード名を判断
        for rule in self.rule_list:
            r = rule['mask'].match(frame)
            if r:
                return rule
        return None

    def match(self, context):
        map = self.guess_map(context['engine']['frame'])
        rule = self.guess_rule(context['engine']['frame'])

        if not map is None:
            context['game']['map'] = map
        if not rule is None:
            context['game']['rule'] = rule

        return (map or rule)

    def __init__(self, debug=False):
        self.map_list = [
            {'name': 'タチウオパーキング', 'file': 'masks/gachi_tachiuo.png'},
            {'name': 'モズク農園',         'file': 'masks/nawabari_mozuku.png'},
            {'name': 'ネギトロ炭鉱',       'file': 'masks/gachi_negitoro.png'},
            {'name': 'アロワナモール',     'file': 'masks/nawabari_arowana.png'},
            {'name': 'デカライン高架下',   'file': 'masks/yagura_decaline.png'},
            {'name': 'Bバスパーク',        'file': 'masks/gachi_buspark.png'},
            {'name': 'ハコフグ倉庫',       'file': 'masks/gachi_hakofugu.png'},
            {'name': 'シオノメ油田',       'file': 'masks/gachi_shionome.png'},
            {'name': 'モンガラキャンプ場', 'file': 'masks/hoko_mongara.png'},
            {'name': 'ホッケふ頭',         'file': 'masks/nawabari_hokke.png'},
            {'name': 'ヒラメが丘団地',     'file': 'masks/nawabari_hirame.png'},
            {'name': 'マサバ海峡大橋',     'file': 'masks/nawabari_masaba.png'},
        ]

        self.rule_list = [
            {'name': 'ガチエリア',     'file': 'masks/gachi_tachiuo.png'},
            {'name': 'ガチヤグラ',     'file': 'masks/yagura_decaline.png'},
            {'name': 'ガチホコバトル', 'file': 'masks/hoko_mongara.png'},
            {'name': 'ナワバリバトル', 'file': 'masks/nawabari_mozuku.png'},
        ]

        for map in self.map_list:
            map['mask'] = IkaMatcher(
                self.mapname_left, self.mapname_top, self.mapname_width, self.mapname_height,
                img_file=map['file'],
                threshold=0.95,
                orig_threshold=0.20,
                false_positive_method=IkaMatcher.FP_FRONT_IS_WHITE,
                pre_threshold_value=230,
                label='map:%s' % map['name'],
                debug=False,
            )

        for rule in self.rule_list:
            rule['mask'] = IkaMatcher(
                self.rulename_left, self.rulename_top, self.rulename_width, self.rulename_height,
                img_file=rule['file'],
                threshold=0.95,
                orig_threshold=0.20,
                false_positive_method=IkaMatcher.FP_FRONT_IS_WHITE,
                pre_threshold_value=230,
                label='rule:%s' % rule['name'],
                debug=False,
            )

if __name__ == "__main__":
    target = cv2.imread(sys.argv[1])

    context = {
        'engine': {'frame': target},
        'game': {},
    }

    obj = IkaScene_GameStart(debug=True)

    r = obj.match(context)

    print(r, context['game'])

    cv2.imshow('Scene', target)
    cv2.waitKey()
<commit_msg>ikalog/scenes/game_start: Introduced election for stage/rule identification.<commit_after>#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#  IkaLog
#  ======
#  Copyright (C) 2015 Takeshi HASEGAWA
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#

from ikalog.utils import *

class IkaScene_GameStart:

    # 720p サイズでの値
    mapname_width = 430
    mapname_left = 1280 - mapname_width
    mapname_top = 580
    mapname_height = 640 - mapname_top

    rulename_left = 640 - 120
    rulename_right = 640 + 120
    rulename_width = rulename_right - rulename_left
    rulename_top = 250
    rulename_bottom = 310
    rulename_height = rulename_bottom - rulename_top

    def guess_map(self, frame):
        # マップ名を判断
        for map in self.map_list:
            r = map['mask'].match(frame)
            if r:
                return map
        return None

    def guess_rule(self, frame):
        # モード名を判断
        for rule in self.rule_list:
            r = rule['mask'].match(frame)
            if r:
                return rule
        return None

    def elect(self, context):
        # 古すぎる投票は捨てる
        election_start = context['engine']['msec'] - self.election_period
        while (len(self.votes) and self.votes[0][0] < election_start):
            del self.votes[0]

        # 考えづらいがゼロ票なら開票しない
        if len(self.votes) == 0:
            return None, None

        # 3秒以上たっていたら開票開始
        #election_start2 = context['engine']['msec'] - (3 * 1000)
        #if election_start2 < self.votes[0][0]:
        #    return None, None

        # 開票作業
        maps = {}
        rules = {}

        count = 0
        map_top = (0, None)  # 最高票数の tuple   (17[票], <IkaMatcher>)
        rule_top = (0, None)

        for vote in self.votes:
            if vote[1] is not None:
                map = vote[1]['name']
                maps[map] = maps[map] + 1 if map in maps else 1
                if  map_top[0] < maps[map]:
                    map_top = (maps[map], vote[1])

            if vote[2] is not None:
                rule = vote[2]['name']
                rules[rule] = rules[rule] + 1 if rule in rules else 1
                if  rule_top[0] < rules[rule]:
                    rule_top = (rules[rule], vote[2])

        # 必要票数
        quorum = 1 # max(3, len(self.votes) / 2)
        print(maps)
        print(rules)
        print('quorum = %s' % quorum)

        # 必要票数が達しなかった場合
        if map_top[0] < quorum:
            map_top = (0, None)

        if rule_top[0] < quorum:
            rule_top = (0, None)

        # 必要票数に達したものだけ更新
        if map_top[1] is not None:
            context['game']['map'] = map_top[1]
        if rule_top[1] is not None:
            context['game']['rule'] = rule_top[1]
        print(map_top, rule_top)

        return map_top[1], rule_top[1]

    def match(self, context):
        map = self.guess_map(context['engine']['frame'])
        rule = self.guess_rule(context['engine']['frame'])

        if not map is None:
            context['game']['map'] = map
        if not rule is None:
            context['game']['rule'] = rule

        if len(self.votes) or map or rule:
            self.votes.append((context['engine']['msec'], map, rule))

        self.elect(context)

        return (map or rule)

    def __init__(self, debug=False):
        self.election_period = 5 * 1000 # msec
        self.votes = []

        self.map_list = [
            {'name': 'タチウオパーキング', 'file': 'masks/gachi_tachiuo.png'},
            {'name': 'モズク農園',         'file': 'masks/nawabari_mozuku.png'},
            {'name': 'ネギトロ炭鉱',       'file': 'masks/gachi_negitoro.png'},
            {'name': 'アロワナモール',     'file': 'masks/nawabari_arowana.png'},
            {'name': 'デカライン高架下',   'file': 'masks/yagura_decaline.png'},
            {'name': 'Bバスパーク',        'file': 'masks/gachi_buspark.png'},
            {'name': 'ハコフグ倉庫',       'file': 'masks/gachi_hakofugu.png'},
            {'name': 'シオノメ油田',       'file': 'masks/gachi_shionome.png'},
            {'name': 'モンガラキャンプ場', 'file': 'masks/hoko_mongara.png'},
            {'name': 'ホッケふ頭',         'file': 'masks/nawabari_hokke.png'},
            {'name': 'ヒラメが丘団地',     'file': 'masks/nawabari_hirame.png'},
            {'name': 'マサバ海峡大橋',     'file': 'masks/nawabari_masaba.png'},
        ]

        self.rule_list = [
            {'name': 'ガチエリア',     'file': 'masks/gachi_tachiuo.png'},
            {'name': 'ガチヤグラ',     'file': 'masks/yagura_decaline.png'},
            {'name': 'ガチホコバトル', 'file': 'masks/hoko_mongara.png'},
            {'name': 'ナワバリバトル', 'file': 'masks/nawabari_mozuku.png'},
        ]

        for map in self.map_list:
            map['mask'] = IkaMatcher(
                self.mapname_left, self.mapname_top, self.mapname_width, self.mapname_height,
                img_file=map['file'],
                threshold=0.95,
                orig_threshold=0.20,
                false_positive_method=IkaMatcher.FP_FRONT_IS_WHITE,
                pre_threshold_value=230,
                label='map:%s' % map['name'],
                debug=False,
            )

        for rule in self.rule_list:
            rule['mask'] = IkaMatcher(
                self.rulename_left, self.rulename_top, self.rulename_width, self.rulename_height,
                img_file=rule['file'],
                threshold=0.95,
                orig_threshold=0.20,
                false_positive_method=IkaMatcher.FP_FRONT_IS_WHITE,
                pre_threshold_value=230,
                label='rule:%s' % rule['name'],
                debug=False,
            )

if __name__ == "__main__":
    target = cv2.imread(sys.argv[1])

    context = {
        'engine': {'frame': target, 'msec': 0, },
        'game': {},
    }

    obj = IkaScene_GameStart(debug=True)

    r = obj.match(context)

    print(r, context['game'])

    cv2.imshow('Scene', target)
    cv2.waitKey()
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Copyright (c) 2010-2013, 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/>.

"""
The Classical Probabilistic Seismic Hazard Analysis (cPSHA) approach
allows calculation of hazard curves and hazard maps following the
classical integration procedure (**Cornell [1968]**, **McGuire [1976]**)
as formulated by **Field et al. [2003]**.

Sources:

* | Cornell, C. A. (1968).
  | Engineering seismic risk analysis.
  | Bulletin of the Seismological Society of America, 58:1583–1606.
* | Field, E. H., Jordan, T. H., and Cornell, C. A. (2003).
  | OpenSHA - A developing Community-Modeling
  | Environment for Seismic Hazard Analysis. Seism. Res. Lett., 74:406–419.
* | McGuire, K. K. (1976).
  | Fortran computer program for seismic risk analysis. Open-File report 76-67,
  | United States Department of the Interior, Geological Survey. 102 pages.

*******
Outputs
*******

* :ref:`Hazard Curves <hazard-curves>`
* :ref:`Hazard Maps <hazard-maps>`
* :ref:`Uniform Hazard Spectra <uniform-hazard-spectra>`

.. _hazard-curves:

Hazard Curves
=============

Hazard Curves are discrete functions which describe probability of ground
motion exceedance in a given time frame. Hazard curves are composed of several
key elements:

* **Intensity Measure Levels (IMLs)** - IMLs define the x-axis values (or
  "ordinates") of the curve. IMLs are defined with an Intensity Measure Type
  (IMT) unit. IMLs are a `strictly monotonically increasing sequence`.
* **Probabilitites of Exceedance (PoEs)** - PoEs define the y-axis values, (or
  "abscissae") of the curve. For each node in the curve, the PoE denotes the
  probability that ground motion will exceedence a given level in a given time
  span.
* **Intensity Measure Type (IMT)** - The unit of measurement for the defined
  IMLs.
* **Investigation time** - The period of time (in years) for an earthquake
  hazard study. It is important to consider the investigation time when
  analyzing hazard curve results, because one can logically conclude that, the
  longer the time span, there is greater probability of ground motion exceeding
  the given values.
* **Spectral Acceleration (SA) Period** - Optional; used only if the IMT is SA.
* **Spectral Acceleration (SA) Damping** - Optional; used only if the IMT is
  SA.
* **Source Model Logic Tree Path (SMLT Path)** - The path taken through the
  calculation's source model logic tree. Does not apply to statistical curves,
  since these aggregate are computed over multiple logic tree realizations.
* **GSIM (Ground Shaking Intensity Model) Logic Tree Path (GSIMLT Path)** - The
  path take through the calculation's GSIM logic tree. As with the SMLT Path,
  this does not apply to statistical curves.

For a given calculation, hazard curves are computed for each logic tree
realization, each IMT/IML definition, and each geographical point of interest.
(In other words: If a calculation specifies 4 logic tree samples, a geometry
with 10 points of interest, and 3 IMT/IML definitions, 120 curves will be
computed.)

Another way to put it is:

``T = R * P * I``

where

* ``T`` is the total number of curves
* ``R`` is the total number of logic tree realizations
* ``P`` is the number of geographical points of interest
* ``I`` is the number of IMT/IML definitions

Hazard curves are grouped by IMT and realization (1 group per IMT per
realization). Each group includes 1 curve for each point of interest.

Additionally, for each realization a hazard curve
container (with ``output_type`` equal to ``hazard_curve_multi``) is
created. This container output could be used in contexts where
you need to identify a whole group of hazard curves sharing the
same realization as when you run a risk calculation supporting
structure dependent intensity measure types.

Statistical Curves
------------------

The classical hazard calculator is also capable of producing `mean` and
`quantile` curves. These aggregates are computed from the curves for a given
point and IMT over all logic tree realizations.

Similar to hazard curves for individual realizations, statistical hazard curves
are grouped by IMT and statistic type. (For quantiles, groups are separated by
quantile level.) Each group includes 1 curve for each point of interest.

Mean Curves
^^^^^^^^^^^

Mean hazard curves can be computed by specifying `mean_hazard_curves = true` in
the job configuration.

When computing a mean hazard curve for a given point/IMT, there are two
possible approaches:

1. mean, unweighted
2. mean, weighted

Technically, both approaches are "weighted". In the first approach, however,
the weights are `implicit` and are taken into account in the process of logic
tree sampling. This approach is used in the case of random Monte-Carlo logic
tree sampling. The total of number of logic tree samples is defined by the user
with the `number_of_logic_tree_samples` configuration parameter.

In the second approach, the weights are explicit in the
caluclation of the mean. This approach is used in the case of end-branch logic
tree enumeration, whereby each possible logic tree path is traversed. (Each
logic tree path in this case defines a weight.) The total number of logic tree
samples in this case is determined by the total number of possible tree paths.
(To perform end-branch enumeration, the user must specify
`number_of_logic_tree_samples = 0` in the job configuration.

The total number of mean curves calculated is

``T = P * I``

where

* ``T`` is the total number of curves
* ``P`` is the number of geographical points of interest
* ``I`` is the number of IMT/IML definitions

Furthermore, also in that case a hazard curve set grouping all the mean curves
is produced (of type ``hazard_curve_multi``).

Quantile Curves
^^^^^^^^^^^^^^^

Quantile hazard curves can be computed by specifying one or more
`quantile_hazard_curves` values (for example,
`quantile_hazard_curves = 0.15, 0.85`) in the job configuration.

Similar to mean curves, quantiles curves can be produced for a given
point/IMT/quantile level (in the range [0.0, 1.0]), there are two possible
approaches:

1. quantile, unweighted
2. quantile, weighted

As with mean curves, `unweighted quantiles` are calculated when Monte-Carlo
logic tree sampling is used and `weighted quantiles` are calculated when logic
tree end-branch enumeration is used.

The total number of quantile curves calculated is

``T = Q * P * I``

where

* ``T`` is the total number of curves
* ``Q`` is the number of quantile levels
* ``P`` is the number of geographical points of interest
* ``I`` is the number of IMT/IML definitions

Moreover, also in that case curves sharing the same quantile are grouped into
a virtual output container of type ``hazard_curve_multi``.

.. _hazard-maps:

Hazard Maps
===========

Hazard maps are geographical meshes of intensity values. Intensity values are
extracted from hazard curve functions by interpolating at a given probability
exceedance. To put it another way, hazard maps seek to answer the following
question: "At the given level probability, what intensity level is likely to be
exceeded at a given geographical points in the next X years?"

The resulting geographical mesh is often depicted graphically, with a color key
defining which color to plot at the given location for a given value or range
values.

Hazard maps bear the same metadata as
:ref:`hazard curves <hazard-curves>`, with the addition of the
probability at which the hazard maps were computed.

For a given calulcation, hazard maps are computed for each hazard curve. Maps
can be computed for one or more probabilities of exceedance, so the total
number of hazard maps is

``T = C * E``

where

* ``T`` is the total number of maps
* ``C`` is the total number of hazard curves (see the method for calculating
  the :ref:`number of hazard curves <hazard-curves>`)
* ``E`` is the total number of probabilities of exceedance

Note: This includes mean and quantile maps.

Statistical Maps
----------------

Hazard maps can be produced from any set of hazard curves, including mean and
quantile aggregates. There are no special methods required for computing these
maps; the process is the same for all hazard map computation.

.. _uniform-hazard-spectra:

Uniform Hazard Spectra
======================

Uniform Hazard Spectra (UHS) are discrete functions which are essentially
derived from hazard maps. Thus, hazard map computation is a prerequisite step
in producing UHS. UHS derivation isn't so much a computation, but rather a
special arrangement or "view" of hazard map data.

UHS "curves" are composed of a few key elements:

* **Spectra Acceleration Periods** - These values make up the x-axis values
  ("ordinates") of the curve.
* **Intensity Measure Levels** - These values make up the y-axis values
  ("abscissae") of the curve.
* **Probability of Exceedance** - The hazard map probability value from which
  the UHS is derived. The "Uniform" in UHS indicates a uniform PoE.
* **Location** - A 2D geographical point, consisting of longitude and latitude.

To construct UHS from a set of hazard maps, one can conceptualize this process
as simply extracting from multiple hazard maps all of the intensity measure
levels for a given location and arranging values in order of SA period,
beginning with the lowest period value. This is done for all locations.

Note: All maps with IMT = SA are considered, in addition to PGA. PGA is
equivalent to SA(0.0). Hazard maps with other IMTs (such as PGV or PGD) are
ignored.

The example below illustrates extracting the IML values for a given location
(indicated by `x`) from three hazard maps::

    Hazard maps PoE: 0.1

        /--------------/
       /              /<-- PGA [equivalent to SA(0.0)]
      /     x        /-/
     /--------------/ /<-- SA(0.025)
      /     x        /-/
     /--------------/ /<-- SA(0.1)
      /     x        /-/
     /--------------/ /<-- SA(0.2)
      /     x        /
     /--------------/

Assuming that the IMLs from the PGA, SA(0.025), SA(0.1), and SA(0.2) maps are
0.3, 0.5, 0.2, and 0.1, respectively, the resulting UHS curve would look like
this::

    [IML]
     ^
     |
    0.5           *
     |
    0.4
     |
    0.3   *
     |
    0.2                   *
     |
    0.1                           *
     |
     +----|-------|-------|-------|----> [SA Period]
         0.0    0.025    0.1     0.2

Uniform Hazard Spectra are grouped into result sets where each result set
corresponds to a probability of exceedance and either a logic tree realization
or statistical aggregate of realizations. Each result set contains a curve for
each geographical point of interest in the calculation.

The number of UHS results (each containing curves for all sites) is

``Tr = E * (Q + M + R)``

where

* ``Tr`` is the total number of result sets (and also the number of files, if
  the results are exported)
* ``E`` is the total number of probabilities of exceedance
* ``Q`` is the number of quantile levels
* ``M`` is 1 if the calculation computes mean results, else 0
* ``R`` is the total number of logic tree realizations

The total of UHS curves is

``T = Tr * P``

where

* ``Tr`` is the total number of result sets (see abov)
* ``P`` is the number of geographical points of interest
"""
<commit_msg>calcs/hazard/classical:<commit_after># -*- coding: utf-8 -*-
# Copyright (c) 2010-2013, 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/>.

"""
The Classical Probabilistic Seismic Hazard Analysis (cPSHA) approach
allows calculation of hazard curves and hazard maps following the
classical integration procedure (**Cornell [1968]**, **McGuire [1976]**)
as formulated by **Field et al. [2003]**.

Sources:

* | Cornell, C. A. (1968).
  | Engineering seismic risk analysis.
  | Bulletin of the Seismological Society of America, 58:1583–1606.
* | Field, E. H., Jordan, T. H., and Cornell, C. A. (2003).
  | OpenSHA - A developing Community-Modeling
  | Environment for Seismic Hazard Analysis. Seism. Res. Lett., 74:406–419.
* | McGuire, K. K. (1976).
  | Fortran computer program for seismic risk analysis. Open-File report 76-67,
  | United States Department of the Interior, Geological Survey. 102 pages.

*******
Outputs
*******

* :ref:`Hazard Curves <hazard-curves>`
* :ref:`Hazard Maps <hazard-maps>`
* :ref:`Uniform Hazard Spectra <uniform-hazard-spectra>`

.. _hazard-curves:

Hazard Curves
=============

Hazard Curves are discrete functions which describe probability of ground
motion exceedance in a given time frame. Hazard curves are composed of several
key elements:

* **Intensity Measure Levels (IMLs)** - IMLs define the x-axis values (or
  "ordinates") of the curve. IMLs are defined with an Intensity Measure Type
  (IMT) unit. IMLs are a `strictly monotonically increasing sequence`.
* **Probabilitites of Exceedance (PoEs)** - PoEs define the y-axis values, (or
  "abscissae") of the curve. For each node in the curve, the PoE denotes the
  probability that ground motion will exceedence a given level in a given time
  span.
* **Intensity Measure Type (IMT)** - The unit of measurement for the defined
  IMLs.
* **Investigation time** - The period of time (in years) for an earthquake
  hazard study. It is important to consider the investigation time when
  analyzing hazard curve results, because one can logically conclude that, the
  longer the time span, there is greater probability of ground motion exceeding
  the given values.
* **Spectral Acceleration (SA) Period** - Optional; used only if the IMT is SA.
* **Spectral Acceleration (SA) Damping** - Optional; used only if the IMT is
  SA.
* **Source Model Logic Tree Path (SMLT Path)** - The path taken through the
  calculation's source model logic tree. Does not apply to statistical curves,
  since these aggregates are computed over multiple logic tree realizations.
* **GSIM (Ground Shaking Intensity Model) Logic Tree Path (GSIMLT Path)** - The
  path taken through the calculation's GSIM logic tree. As with the SMLT Path,
  this does not apply to statistical curves.

For a given calculation, hazard curves are computed for each logic tree
realization, each IMT/IML definition, and each geographical point of interest.
(In other words: If a calculation specifies 4 logic tree samples, a geometry
with 10 points of interest, and 3 IMT/IML definitions, 120 curves will be
computed.)

Another way to put it is:

``T = R * P * I``

where

* ``T`` is the total number of curves
* ``R`` is the total number of logic tree realizations
* ``P`` is the number of geographical points of interest
* ``I`` is the number of IMT/IML definitions

Hazard curves are grouped by IMT and realization (1 group per IMT per
realization). Each group includes 1 curve for each point of interest.

Additionally, for each realization a hazard curve
container (with ``output_type`` equal to ``hazard_curve_multi``) is
created. This container output could be used in contexts where
you need to identify a whole group of hazard curves sharing the
same realization as when you run a risk calculation supporting
structure dependent intensity measure types.

Statistical Curves
------------------

The classical hazard calculator is also capable of producing `mean` and
`quantile` curves. These aggregates are computed from the curves for a given
point and IMT over all logic tree realizations.

Similar to hazard curves for individual realizations, statistical hazard curves
are grouped by IMT and statistic type. (For quantiles, groups are separated by
quantile level.) Each group includes 1 curve for each point of interest.

Mean Curves
^^^^^^^^^^^

Mean hazard curves can be computed by specifying `mean_hazard_curves = true` in
the job configuration.

When computing a mean hazard curve for a given point/IMT, there are two
possible approaches:

1. mean, unweighted
2. mean, weighted

Technically, both approaches are "weighted". In the first approach, however,
the weights are `implicit` and are taken into account in the process of logic
tree sampling. This approach is used in the case of random Monte-Carlo logic
tree sampling. The total of number of logic tree samples is defined by the user
with the `number_of_logic_tree_samples` configuration parameter.

In the second approach, the weights are explicit in the
caluclation of the mean. This approach is used in the case of end-branch logic
tree enumeration, whereby each possible logic tree path is traversed. (Each
logic tree path in this case defines a weight.) The total number of logic tree
samples in this case is determined by the total number of possible tree paths.
(To perform end-branch enumeration, the user must specify
`number_of_logic_tree_samples = 0` in the job configuration.

The total number of mean curves calculated is

``T = P * I``

where

* ``T`` is the total number of curves
* ``P`` is the number of geographical points of interest
* ``I`` is the number of IMT/IML definitions

Furthermore, also in that case a hazard curve set grouping all the mean curves
is produced (of type ``hazard_curve_multi``).

Quantile Curves
^^^^^^^^^^^^^^^

Quantile hazard curves can be computed by specifying one or more
`quantile_hazard_curves` values (for example,
`quantile_hazard_curves = 0.15, 0.85`) in the job configuration.

Similar to mean curves, quantiles curves can be produced for a given
point/IMT/quantile level (in the range [0.0, 1.0]), there are two possible
approaches:

1. quantile, unweighted
2. quantile, weighted

As with mean curves, `unweighted quantiles` are calculated when Monte-Carlo
logic tree sampling is used and `weighted quantiles` are calculated when logic
tree end-branch enumeration is used.

The total number of quantile curves calculated is

``T = Q * P * I``

where

* ``T`` is the total number of curves
* ``Q`` is the number of quantile levels
* ``P`` is the number of geographical points of interest
* ``I`` is the number of IMT/IML definitions

Moreover, also in that case curves sharing the same quantile are grouped into
a virtual output container of type ``hazard_curve_multi``.

.. _hazard-maps:

Hazard Maps
===========

Hazard maps are geographical meshes of intensity values. Intensity values are
extracted from hazard curve functions by interpolating at a given probability
exceedance. To put it another way, hazard maps seek to answer the following
question: "At the given level probability, what intensity level is likely to be
exceeded at a given geographical points in the next X years?"

The resulting geographical mesh is often depicted graphically, with a color key
defining which color to plot at the given location for a given value or range
values.

Hazard maps bear the same metadata as
:ref:`hazard curves <hazard-curves>`, with the addition of the
probability at which the hazard maps were computed.

For a given calulcation, hazard maps are computed for each hazard curve. Maps
can be computed for one or more probabilities of exceedance, so the total
number of hazard maps is

``T = C * E``

where

* ``T`` is the total number of maps
* ``C`` is the total number of hazard curves (see the method for calculating
  the :ref:`number of hazard curves <hazard-curves>`)
* ``E`` is the total number of probabilities of exceedance

Note: This includes mean and quantile maps.

Statistical Maps
----------------

Hazard maps can be produced from any set of hazard curves, including mean and
quantile aggregates. There are no special methods required for computing these
maps; the process is the same for all hazard map computation.

.. _uniform-hazard-spectra:

Uniform Hazard Spectra
======================

Uniform Hazard Spectra (UHS) are discrete functions which are essentially
derived from hazard maps. Thus, hazard map computation is a prerequisite step
in producing UHS. UHS derivation isn't so much a computation, but rather a
special arrangement or "view" of hazard map data.

UHS "curves" are composed of a few key elements:

* **Spectra Acceleration Periods** - These values make up the x-axis values
  ("ordinates") of the curve.
* **Intensity Measure Levels** - These values make up the y-axis values
  ("abscissae") of the curve.
* **Probability of Exceedance** - The hazard map probability value from which
  the UHS is derived. The "Uniform" in UHS indicates a uniform PoE.
* **Location** - A 2D geographical point, consisting of longitude and latitude.

To construct UHS from a set of hazard maps, one can conceptualize this process
as simply extracting from multiple hazard maps all of the intensity measure
levels for a given location and arranging values in order of SA period,
beginning with the lowest period value. This is done for all locations.

Note: All maps with IMT = SA are considered, in addition to PGA. PGA is
equivalent to SA(0.0). Hazard maps with other IMTs (such as PGV or PGD) are
ignored.

The example below illustrates extracting the IML values for a given location
(indicated by `x`) from three hazard maps::

    Hazard maps PoE: 0.1

        /--------------/
       /              /<-- PGA [equivalent to SA(0.0)]
      /     x        /-/
     /--------------/ /<-- SA(0.025)
      /     x        /-/
     /--------------/ /<-- SA(0.1)
      /     x        /-/
     /--------------/ /<-- SA(0.2)
      /     x        /
     /--------------/

Assuming that the IMLs from the PGA, SA(0.025), SA(0.1), and SA(0.2) maps are
0.3, 0.5, 0.2, and 0.1, respectively, the resulting UHS curve would look like
this::

    [IML]
     ^
     |
    0.5           *
     |
    0.4
     |
    0.3   *
     |
    0.2                   *
     |
    0.1                           *
     |
     +----|-------|-------|-------|----> [SA Period]
         0.0    0.025    0.1     0.2

Uniform Hazard Spectra are grouped into result sets where each result set
corresponds to a probability of exceedance and either a logic tree realization
or statistical aggregate of realizations. Each result set contains a curve for
each geographical point of interest in the calculation.

The number of UHS results (each containing curves for all sites) is

``Tr = E * (Q + M + R)``

where

* ``Tr`` is the total number of result sets (and also the number of files, if
  the results are exported)
* ``E`` is the total number of probabilities of exceedance
* ``Q`` is the number of quantile levels
* ``M`` is 1 if the calculation computes mean results, else 0
* ``R`` is the total number of logic tree realizations

The total of UHS curves is

``T = Tr * P``

where

* ``Tr`` is the total number of result sets (see abov)
* ``P`` is the number of geographical points of interest
"""
<|endoftext|>


<commit_before>from bs4 import BeautifulSoup
import csv
import os
import re
import json
import pprint

from django.core.management.base import BaseCommand

from programs.models import Program, ProgramModules, LearningPlan
from disciplines.models import Discipline
from modules.models import Module

pp = pprint.PrettyPrinter(indent=4)

class Command(BaseCommand):
    help = "Create Django objects from raw&ugly UrFU data."
    requires_system_checks = True
    requires_migrations_checks = True

    def add_arguments(self, parser):
        parser.add_argument('html_path', nargs=1)
        parser.add_argument('uni_modules_path', nargs=1)
        parser.add_argument('programs_path', nargs=1)
        parser.add_argument('program_title', nargs=1)

    def handle(self, *args, **options):
        html_path = options["html_path"][0]
        uni_modules_path = options["uni_modules_path"][0]
        program_title = options["program_title"][0]
        programs_path = options["programs_path"][0]

        try:
            with open(html_path, encoding='utf-8') as html_file:
                raw_html = '\n'.join(html_file.readlines())
        except:
            raise FileNotFoundError

        try:
            with open(uni_modules_path, encoding='utf-8') as modules_file:
                modules_json = json.load(modules_file)
        except:
            raise FileNotFoundError

        try:
            with open(programs_path, encoding='utf-8') as programs_file:
                raw_programs = '\n'.join(programs_file.readlines())
        except:
            raise FileNotFoundError

        if raw_programs:
            programs_soup = BeautifulSoup(raw_programs, 'html.parser')
            rows = []
            for row in programs_soup.find_all('tr', {"class": "main-info"}):
                rows.append([val.text.strip() for val in row.find_all('td')])

            for row in rows:
                try:
                    program = Program.objects.get(title=row[1])
                except:
                    def level(x):
                        return {
                            'Магистр'.lower() in str(x).lower(): "m",
                            'Специалист'.lower() in str(x).lower(): "s",
                            'Бакалавр'.lower() in str(x).lower(): "b",
                        }[True]
                    program = Program(title=row[1],
                                      training_direction=row[2],
                                      level=level(row[4]),
                                      )
                    program.save()

        try:
            program = Program.objects.filter(title=program_title).first()
            program.status = "p"
            program.save()
        except:
            raise NotImplementedError

        if raw_html:
            soup = BeautifulSoup(raw_html, 'html.parser')
            [s.extract() for s in soup('script')]
            [s.extract() for s in soup('style')]
            self.decompose(soup, "table", "menu_table")
            self.decompose(soup, "td", "navpath")
            self.decompose(soup, "div", "buttons")
            soup.find('td', id="nav_td").decompose()

            stage = soup.find('td', id="EduVersionPlanTab.EduVersionPlan.stage").text.strip().lower() == "утверждено"
            displayableTitle = soup.find('td', id="EduVersionPlanTab.EduVersionPlan.displayableTitle").text.strip()
            number = soup.find('td', id="EduVersionPlanTab.EduVersionPlan.number").text.strip()
            active = soup.find('td', id="EduVersionPlanTab.EduVersionPlan.active").text.strip()
            title = soup.find('td', id="EduVersionPlanTab.EduVersionPlan.title").text.strip()
            loadTimeType = soup.find("td", id="EduVersionPlanTab.EduVersionPlan.loadTimeType").text.strip()
            html = soup.find("table", {"class": "basic"}).prettify()

            lps = LearningPlan.objects.filter(uni_number=number, status="p")
            if len(lps) > 0:
                for lp in lps:
                    lp.uni_displayableTitle = displayableTitle
                    lp.uni_number = number
                    lp.uni_active = active
                    lp.uni_title = title
                    lp.uni_stage = stage
                    lp.uni_loadTimeType = loadTimeType
                    lp.uni_html = html
                    lp.save()
                    if lp not in program.learning_plans.all():
                        program.learning_plans.add(lp)
                        program.save()
            else:
                lp = LearningPlan(uni_displayableTitle=displayableTitle,
                                  uni_number=number,
                                  uni_active=active,
                                  uni_title=title,
                                  uni_stage=stage,
                                  uni_loadTimeType=loadTimeType,
                                  uni_html=html,
                                  status="p"
                                  )
                lp.save()
                program.learning_plans.add(lp)
                program.save()

            if 'зао' not in number:
                fulltime = True

            table = soup.find('table', id="EduVersionPlanTab.EduDisciplineList")
            headers = [header.text.strip() for header in table.find_all('th')]
            rows = []
            for row in table.find_all('tr'):
                rows.append([val.text.strip() for val in row.find_all('td')])

            # Ищем модули
            modules = []
            for header in headers:
                if "Номер модуля, дисциплины".lower() == header.lower():
                    module_numbers_col = headers.index(header)

            for row in rows:
                if row:
                    m = re.search('\d\d+', row[module_numbers_col])
                    if m:
                        for module in modules_json:
                            if str(module["number"]) == str(m.group(0)):
                                module["row"] = row
                                modules.append(module)

            for module in [m for m in modules if m["disciplines"]]:
                try:
                    module_obj = Module.objects.filter(title=module["title"]).first()
                    module_obj.uni_uuid = module["uuid"]
                    module_obj.uni_number = module["number"]
                    module_obj.uni_coordinator = module["coordinator"]
                    module_obj.uni_type = module["type"]
                    module_obj.uni_title = module["title"]
                    module_obj.uni_competence = module["competence"]
                    module_obj.uni_testUnits = module["testUnits"]
                    module_obj.uni_priority = module["priority"]
                    module_obj.uni_state = module["state"]
                    module_obj.uni_approvedDate = module["approvedDate"]
                    module_obj.uni_comment = module["comment"]
                    module_obj.uni_file = module["file"]
                    module_obj.uni_specialities = module["specialities"]
                    module_obj.save()
                except:
                    module_obj = Module(title=module["title"],
                                        uni_uuid=module["uuid"],
                                        uni_number=module["number"],
                                        uni_coordinator=module["coordinator"],
                                        uni_type=module["type"],
                                        uni_title=module["title"],
                                        uni_competence=module["competence"],
                                        uni_testUnits=module["testUnits"],
                                        uni_priority=module["priority"],
                                        uni_state=module["state"],
                                        uni_approvedDate=module["approvedDate"],
                                        uni_comment=module["comment"],
                                        uni_file=module["file"],
                                        uni_specialities=module["specialities"]
                                        )
                    module_obj.save()  # Создали модуль

                # Ищем дисциплины
                for d in module["disciplines"]:
                    if int(d["testUnits"]) > 0:
                        for row in rows:
                            if d["title"] in row:
                                print(row)
                                break
                        try:
                            discipline = Discipline.objects.get(title=d["title"])
                        except:
                            discipline = Discipline(title=d["title"])
                            discipline.module = module_obj
                            discipline.labor = d["testUnits"]

                            discipline.uni_uid = d["uid"]
                            discipline.uni_discipline = d["discipline"]
                            discipline.uni_number = d["number"]
                            discipline.uni_section = d["section"]
                            discipline.uni_file = d["file"]

                            discipline.status = "p"
                            discipline.save()

            # Собираем группу выбора

            # pm = ProgramModules(program=program,
            #                     module=module_obj)  # TODO: ChoiceGroup
            # pm.save()

    def decompose(self, soup, tag, classname):
        [el.decompose() for el in soup.find_all(tag, {'class': classname})]

<commit_msg>discipline#11<commit_after>from bs4 import BeautifulSoup
import csv
import os
import re
import json
import pprint

from django.core.management.base import BaseCommand

from programs.models import Program, ProgramModules, LearningPlan
from disciplines.models import Discipline
from modules.models import Module

pp = pprint.PrettyPrinter(indent=4)

class Command(BaseCommand):
    help = "Create Django objects from raw&ugly UrFU data."
    requires_system_checks = True
    requires_migrations_checks = True

    def add_arguments(self, parser):
        parser.add_argument('html_path', nargs=1)
        parser.add_argument('uni_modules_path', nargs=1)
        parser.add_argument('programs_path', nargs=1)
        parser.add_argument('program_title', nargs=1)

    def handle(self, *args, **options):
        html_path = options["html_path"][0]
        uni_modules_path = options["uni_modules_path"][0]
        program_title = options["program_title"][0]
        programs_path = options["programs_path"][0]

        try:
            with open(html_path, encoding='utf-8') as html_file:
                raw_html = '\n'.join(html_file.readlines())
        except:
            raise FileNotFoundError

        try:
            with open(uni_modules_path, encoding='utf-8') as modules_file:
                modules_json = json.load(modules_file)
        except:
            raise FileNotFoundError

        try:
            with open(programs_path, encoding='utf-8') as programs_file:
                raw_programs = '\n'.join(programs_file.readlines())
        except:
            raise FileNotFoundError

        if raw_programs:
            programs_soup = BeautifulSoup(raw_programs, 'html.parser')
            rows = []
            for row in programs_soup.find_all('tr', {"class": "main-info"}):
                rows.append([val.text.strip() for val in row.find_all('td')])

            for row in rows:
                try:
                    program = Program.objects.get(title=row[1])
                except:
                    def level(x):
                        return {
                            'Магистр'.lower() in str(x).lower(): "m",
                            'Специалист'.lower() in str(x).lower(): "s",
                            'Бакалавр'.lower() in str(x).lower(): "b",
                        }[True]
                    program = Program(title=row[1],
                                      training_direction=row[2],
                                      level=level(row[4]),
                                      )
                    program.save()

        try:
            program = Program.objects.filter(title=program_title).first()
            program.status = "p"
            program.save()
        except:
            raise NotImplementedError

        if raw_html:
            soup = BeautifulSoup(raw_html, 'html.parser')
            [s.extract() for s in soup('script')]
            [s.extract() for s in soup('style')]
            self.decompose(soup, "table", "menu_table")
            self.decompose(soup, "td", "navpath")
            self.decompose(soup, "div", "buttons")
            soup.find('td', id="nav_td").decompose()

            stage = soup.find('td', id="EduVersionPlanTab.EduVersionPlan.stage").text.strip().lower() == "утверждено"
            displayableTitle = soup.find('td', id="EduVersionPlanTab.EduVersionPlan.displayableTitle").text.strip()
            number = soup.find('td', id="EduVersionPlanTab.EduVersionPlan.number").text.strip()
            active = soup.find('td', id="EduVersionPlanTab.EduVersionPlan.active").text.strip()
            title = soup.find('td', id="EduVersionPlanTab.EduVersionPlan.title").text.strip()
            loadTimeType = soup.find("td", id="EduVersionPlanTab.EduVersionPlan.loadTimeType").text.strip()
            html = soup.find("table", {"class": "basic"}).prettify()

            lps = LearningPlan.objects.filter(uni_number=number, status="p")
            if len(lps) > 0:
                for lp in lps:
                    lp.uni_displayableTitle = displayableTitle
                    lp.uni_number = number
                    lp.uni_active = active
                    lp.uni_title = title
                    lp.uni_stage = stage
                    lp.uni_loadTimeType = loadTimeType
                    lp.uni_html = html
                    lp.save()
                    if lp not in program.learning_plans.all():
                        program.learning_plans.add(lp)
                        program.save()
            else:
                lp = LearningPlan(uni_displayableTitle=displayableTitle,
                                  uni_number=number,
                                  uni_active=active,
                                  uni_title=title,
                                  uni_stage=stage,
                                  uni_loadTimeType=loadTimeType,
                                  uni_html=html,
                                  status="p"
                                  )
                lp.save()
                program.learning_plans.add(lp)
                program.save()

            if 'зао' not in number:
                fulltime = True

            table = soup.find('table', id="EduVersionPlanTab.EduDisciplineList")
            headers = [header.text.strip() for header in table.find_all('th')]
            rows = []
            for row in table.find_all('tr'):
                rows.append([val.text.strip() for val in row.find_all('td')])

            # Ищем модули
            modules = []
            for header in headers:
                if "Номер модуля, дисциплины".lower() == header.lower():
                    module_numbers_col = headers.index(header)

            for row in rows:
                if row:
                    m = re.search('\d\d+', row[module_numbers_col])
                    if m:
                        for module in modules_json:
                            if str(module["number"]) == str(m.group(0)):
                                module["row"] = row
                                modules.append(module)

            for module in [m for m in modules if m["disciplines"]]:
                try:
                    module_obj = Module.objects.filter(title=module["title"]).first()
                    module_obj.uni_uuid = module["uuid"]
                    module_obj.uni_number = module["number"]
                    module_obj.uni_coordinator = module["coordinator"]
                    module_obj.uni_type = module["type"]
                    module_obj.uni_title = module["title"]
                    module_obj.uni_competence = module["competence"]
                    module_obj.uni_testUnits = module["testUnits"]
                    module_obj.uni_priority = module["priority"]
                    module_obj.uni_state = module["state"]
                    module_obj.uni_approvedDate = module["approvedDate"]
                    module_obj.uni_comment = module["comment"]
                    module_obj.uni_file = module["file"]
                    module_obj.uni_specialities = module["specialities"]
                    module_obj.save()
                except:
                    module_obj = Module(title=module["title"],
                                        uni_uuid=module["uuid"],
                                        uni_number=module["number"],
                                        uni_coordinator=module["coordinator"],
                                        uni_type=module["type"],
                                        uni_title=module["title"],
                                        uni_competence=module["competence"],
                                        uni_testUnits=module["testUnits"],
                                        uni_priority=module["priority"],
                                        uni_state=module["state"],
                                        uni_approvedDate=module["approvedDate"],
                                        uni_comment=module["comment"],
                                        uni_file=module["file"],
                                        uni_specialities=module["specialities"]
                                        )
                    module_obj.save()  # Создали модуль

                # Ищем дисциплины
                for d in module["disciplines"]:
                    if int(d["testUnits"]) > 0:
                        for row in rows:
                            if d["title"] in row:
                                break
                        try:
                            discipline = Discipline.objects.get(title=d["title"])
                        except:
                            discipline = Discipline(title=d["title"])
                        discipline.module = module_obj
                        discipline.labor = d["testUnits"]

                        discipline.uni_uid = d["uid"]
                        discipline.uni_discipline = d["discipline"]
                        discipline.uni_number = d["number"]
                        discipline.uni_section = d["section"]
                        discipline.uni_file = d["file"]

                        if isinstance(row[5], int):
                            discipline.form = "e"
                        if isinstance(row[5], int):
                            discipline.form = "z"

                        discipline.status = "p"
                        discipline.save()

            # Собираем группу выбора

            # pm = ProgramModules(program=program,
            #                     module=module_obj)  # TODO: ChoiceGroup
            # pm.save()

    def decompose(self, soup, tag, classname):
        [el.decompose() for el in soup.find_all(tag, {'class': classname})]

<|endoftext|>


<commit_before>#
#   Copyright 2011 Red Hat, Inc.
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http:/www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by icable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

import logging
from imgfac.rest.bottle import *
from imgfac.rest.RESTtools import *
from imgfac.rest.OAuthTools import oauth_protect
from traceback import *
from imgfac.BuildDispatcher import BuildDispatcher
from imgfac.JobRegistry import JobRegistry

log = logging.getLogger(__name__)

rest_api = Bottle(catchall=True)

@rest_api.get('/imagefactory')
def api_info():
    return {'name':'imagefactory', 'version':'1.0'}

@rest_api.post('/imagefactory/images')
@rest_api.put('/imagefactory/images/:image_id')
@oauth_protect
def build_image(image_id=None):
    help_txt = """To build a new target image, supply a template and list of targets to build for.
To import an image, supply target_name, provider_name, target_identifier, and image_descriptor."""

    _request_data = _form_data_for_content_type(request.headers.get('Content-Type'))
    # build image arguments
    template = _request_data.get('template')
    targets = _request_data.get('targets')
    build_id = _request_data.get('build_id') #optional
    # import image arguments
    target_name = _request_data.get('target_name')
    provider_name = _request_data.get('provider_name')
    target_identifier = _request_data.get('target_identifier')
    image_descriptor = _request_data.get('image_descriptor')

    if(template and targets):
        try:
            if build_id and not image_id:
                raise Exception("The parameter build_id must be used with a specific image_id...")
            jobs = BuildDispatcher().build_image_for_targets(image_id, build_id, template, targets.split(','))
            if(image_id):
                base_url = request.url
            else:
                image_id = jobs[0].image_id
                base_url = '%s/%s' % (request.url, image_id)

            image = {'_type':'image','id':image_id,'href':base_url}
            if not build_id:
                build_id = jobs[0].build_id
            build = {'_type':'build',
                        'id':build_id,
                        'href':'%s/builds/%s' % (base_url, build_id)}
            target_images = []
            for job in jobs:
                target_image_id = job.new_image_id
                target_images.append({'_type':'target_image',
                                        'id':target_image_id,
                                        'href':'%s/builds/%s/target_images/%s' % (base_url, build_id, target_image_id)})
            build['target_images'] = target_images
            image['build'] = build

            response.status = 202
            return image
        except Exception as e:
            log.exception(e)
            raise HTTPResponse(status=500, output=e)

    elif(target_name and provider_name and target_identifier and image_descriptor):
        image_id = _request_data.get('image_id')
        build_id = _request_data.get('build_id')
        try:
            import_result = BuildDispatcher().import_image(image_id,
                                                            build_id,
                                                            target_identifier,
                                                            image_descriptor,
                                                            target_name,
                                                            provider_name)
            image_id = import_result[0]
            base_url = '%s/%s' % (request.url, image_id)
            image = {'_type':'image','id':image_id,'href':base_url}
            build_id = import_result[1]
            build = {'_type':'build',
                        'id':build_id,
                        'href':'%s/builds/%s' % (base_url, build_id)}
            target_image_id = import_result[2]
            target_image = {'_type':'target_image',
                            'id':target_image_id,
                            'href':'%s/builds/%s/target_images/%s' % (base_url, build_id, target_image_id)}
            provider_image_id = import_result[3]
            provider_image = {'_type':'provider_image',
                                'id':provider_image_id,
                                'href':'%s/builds/%s/target_images/%s/provider_images/%s' % (base_url, build_id, target_image_id, provider_image_id)}

            target_image['provider_images'] = (provider_image,)
            build['target_images'] = (target_image,)
            image['build'] = build

            response.status = 200
            return image
        except Exception as e:
            log.exception(e)
            raise HTTPResponse(status=500, output=e)
    else:
        raise HTTPResponse(status=400, output=help_txt)

@rest_api.post('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images')
@oauth_protect
def push_image(image_id, build_id, target_image_id):
    try:
        _request_data = _form_data_for_content_type(request.headers.get('Content-Type'))
        provider = _request_data['provider']
        credentials = _request_data['credentials']
        job = BuildDispatcher().push_image_to_providers(image_id, build_id, (provider, ), credentials)[0]
        provider_image_id = job.new_image_id
        response.status = 202
        return {'_type':'provider_image',
                'id':provider_image_id,
                'href':'%s/%s' % (request.url, provider_image_id)}

    except KeyError as e:
        raise HTTPResponse(status=400, output="Missing either 'provider' or 'credentials' in request: %s" % e)
    except Exception as e:
        log.exception(e)
        raise HTTPResponse(status=500, output=e)

@rest_api.post('/imagefactory/provider_images')
@oauth_protect
def create_provider_image():
    try:
        _request_data = _form_data_for_content_type(request.headers.get('Content-Type'))
        image_id = _request_data.get('image_id')
        build_id = _request_data.get('build_id')
        target_image_id = _request_data.get('target_image_id')
        return push_image(image_id, build_id, target_image_id)
    except KeyError as e:
        raise HTTPResponse(status=400, output="Missing one or more of 'image_id', 'build_id', or 'target_image_id': %s" % e)
    except Exception as e:
        log.exception(e)
        raise HTTPResponse(status=500, output=e)

@rest_api.get('/imagefactory/builders')
def list_builders():
    collection = {'_type':'builders','href':request.url}
    jobs = JobRegistry().jobs
    builders = []
    for builder_id in jobs.keys():
        job = jobs[builder_id]
        builders.append({'completed':job.percent_complete,
                         'status':job.status,
                         'operation':job.operation,
                         'target':job.target,
                         '_type':'builder',
                         'id':builder_id,
                         'image_id':job.image_id,
                         'build_id':job.build_id,
                         'target_image_id':job.target_image_id,
                         'provider':job.provider,
                         'provider_account_identifier':job.provider_account_identifier,
                         'href':'%s/%s' % (request.url, builder_id)})

    collection['builders'] = builders
    return collection

@rest_api.get('/imagefactory/builders/:builder_id')
@rest_api.get('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id')
@rest_api.get('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images/:provider_image_id')
def builder_detail(builder_id=None, image_id=None, build_id=None, target_image_id=None, provider_image_id=None):
    if(builder_id):
        _id = builder_id
        _type = 'builder'
    elif(target_image_id and provider_image_id):
        _id = provider_image_id
        _type = 'provider_image_status'
    elif(target_image_id):
        _id = target_image_id
        _type = 'target_image_status'
    else:
        log.warn('No uuid provided, unable to fetch builder...')
        raise HTTPResponse(status=400, output='No uuid provided, unable to fetch builder...')

    try:
        job = JobRegistry().jobs[_id]
        return {'completed':job.percent_complete,
                'status':job.status,
                'operation':job.operation,
                'target':job.target,
                'href':request.url,
                'id':_id,
                'image_id':job.image_id,
                'build_id':job.build_id,
                'target_image_id':job.target_image_id,
                'provider':job.provider,
                'provider_account_identifier':job.provider_account_identifier,
                '_type':_type}
    except KeyError as e:
        raise HTTPResponse(status=404, output="No builder found with uuid %s" % _id)
    except Exception as e:
        log.exception(e)
        raise HTTPResponse(status=500, output=e)

@rest_api.get('/imagefactory/builders/:builder_id/status')
@rest_api.get('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/status')
@rest_api.get('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images/:provider_image_id/status')
def builder_status(builder_id=None, image_id=None, build_id=None, target_image_id=None, provider_image_id=None):
    if(builder_id):
        _id = builder_id
        _type = 'builder_status'
    elif(target_image_id and provider_image_id):
        _id = provider_image_id
        _type = 'provider_image_status'
    elif(target_image_id):
        _id = target_image_id
        _type = 'target_image_status'
    else:
        log.warn('No uuid provided, unable to fetch builder...')
        raise HTTPResponse(status=400, output='No uuid provided, unable to fetch builder...')

    try:
        job = JobRegistry().jobs[_id]
        return {'_type':_type,
                'id':_id,
                'href':request.url,
                'status':job.status}
    except KeyError as e:
        raise HTTPResponse(status=404, output="No builder found with uuid %s" % _id)
    except Exception as e:
        log.exception(e)
        raise HTTPResponse(status=500, output=e)

# Things we have not yet implemented
@rest_api.route('/imagefactory/images', method=('GET','DELETE'))
@rest_api.route('/imagefactory/images/:image_id', method=('GET','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds', method=('GET','POST','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id', method=('GET','PUT','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images', method=('GET','POST','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id', method=('PUT','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images', method=('GET','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images/:provider_image_id', method=('PUT','DELETE'))
@rest_api.route('/imagefactory/targets', method=('GET'))
@rest_api.route('/imagefactory/targets/:target_name', method=('GET'))
@rest_api.route('/imagefactory/targets/:target_name/providers', method=('GET','POST','DELETE'))
@rest_api.route('/imagefactory/targets/:target_name/providers/:provider_name', method=('GET','PUT','DELETE'))
@rest_api.delete('/imagefactory/builders/:builder_id')
def method_not_implemented(**kw):
    """
    @return 501 Not Implemented
    """
    raise HTTPResponse(status=501)

# Things we don't plan to implement
@rest_api.route('/imagefactory/images', method=('PUT'))
@rest_api.route('/imagefactory/images/:image_id', method=('POST'))
@rest_api.route('/imagefactory/images/:image_id/builds', method=('PUT'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id', method=('POST'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images', method=('PUT'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id', method=('POST'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images', method=('PUT'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images/:provider_image_id', method=('POST'))
@rest_api.route('/imagefactory/targets', method=('PUT','POST','DELETE'))
@rest_api.route('/imagefactory/targets/:target_name', method=('PUT','POST','DELETE'))
@rest_api.route('/imagefactory/targets/:target_name/providers', method=('PUT'))
@rest_api.route('/imagefactory/targets/:target_name/providers/:provider_name', method=('POST'))
@rest_api.route('/imagefactory/builders', method=('PUT','POST','DELETE'))
@rest_api.route('/imagefactory/builders/:builder_id', method=('PUT','POST'))
@rest_api.route('/imagefactory/builders/:builder_id/status', method=('PUT','POST','DELETE'))
def method_not_allowed(**kw):
    """
    @return 405 Method Not Allowed
    """
    raise HTTPResponse(status=405)
<commit_msg>fix reference to RESTtools<commit_after>#
#   Copyright 2011 Red Hat, Inc.
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http:/www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by icable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.

import logging
from imgfac.rest.bottle import *
import imgfac.rest.RESTtools as RESTtools
from imgfac.rest.OAuthTools import oauth_protect
from imgfac.BuildDispatcher import BuildDispatcher
from imgfac.JobRegistry import JobRegistry

log = logging.getLogger(__name__)

rest_api = Bottle(catchall=True)

@rest_api.get('/imagefactory')
def api_info():
    return {'name':'imagefactory', 'version':'1.0'}

@rest_api.post('/imagefactory/images')
@rest_api.put('/imagefactory/images/:image_id')
@oauth_protect
def build_image(image_id=None):
    help_txt = """To build a new target image, supply a template and list of targets to build for.
To import an image, supply target_name, provider_name, target_identifier, and image_descriptor."""

    _request_data = RESTtools.form_data_for_content_type(request.headers.get('Content-Type'))
    # build image arguments
    template = _request_data.get('template')
    targets = _request_data.get('targets')
    build_id = _request_data.get('build_id') #optional
    # import image arguments
    target_name = _request_data.get('target_name')
    provider_name = _request_data.get('provider_name')
    target_identifier = _request_data.get('target_identifier')
    image_descriptor = _request_data.get('image_descriptor')

    if(template and targets):
        try:
            if build_id and not image_id:
                raise Exception("The parameter build_id must be used with a specific image_id...")
            jobs = BuildDispatcher().build_image_for_targets(image_id, build_id, template, targets.split(','))
            if(image_id):
                base_url = request.url
            else:
                image_id = jobs[0].image_id
                base_url = '%s/%s' % (request.url, image_id)

            image = {'_type':'image','id':image_id,'href':base_url}
            if not build_id:
                build_id = jobs[0].build_id
            build = {'_type':'build',
                        'id':build_id,
                        'href':'%s/builds/%s' % (base_url, build_id)}
            target_images = []
            for job in jobs:
                target_image_id = job.new_image_id
                target_images.append({'_type':'target_image',
                                        'id':target_image_id,
                                        'href':'%s/builds/%s/target_images/%s' % (base_url, build_id, target_image_id)})
            build['target_images'] = target_images
            image['build'] = build

            response.status = 202
            return image
        except Exception as e:
            log.exception(e)
            raise HTTPResponse(status=500, output=e)

    elif(target_name and provider_name and target_identifier and image_descriptor):
        image_id = _request_data.get('image_id')
        build_id = _request_data.get('build_id')
        try:
            import_result = BuildDispatcher().import_image(image_id,
                                                            build_id,
                                                            target_identifier,
                                                            image_descriptor,
                                                            target_name,
                                                            provider_name)
            image_id = import_result[0]
            base_url = '%s/%s' % (request.url, image_id)
            image = {'_type':'image','id':image_id,'href':base_url}
            build_id = import_result[1]
            build = {'_type':'build',
                        'id':build_id,
                        'href':'%s/builds/%s' % (base_url, build_id)}
            target_image_id = import_result[2]
            target_image = {'_type':'target_image',
                            'id':target_image_id,
                            'href':'%s/builds/%s/target_images/%s' % (base_url, build_id, target_image_id)}
            provider_image_id = import_result[3]
            provider_image = {'_type':'provider_image',
                                'id':provider_image_id,
                                'href':'%s/builds/%s/target_images/%s/provider_images/%s' % (base_url, build_id, target_image_id, provider_image_id)}

            target_image['provider_images'] = (provider_image,)
            build['target_images'] = (target_image,)
            image['build'] = build

            response.status = 200
            return image
        except Exception as e:
            log.exception(e)
            raise HTTPResponse(status=500, output=e)
    else:
        raise HTTPResponse(status=400, output=help_txt)

@rest_api.post('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images')
@oauth_protect
def push_image(image_id, build_id, target_image_id):
    try:
        _request_data = RESTtools.form_data_for_content_type(request.headers.get('Content-Type'))
        provider = _request_data['provider']
        credentials = _request_data['credentials']
        job = BuildDispatcher().push_image_to_providers(image_id, build_id, (provider, ), credentials)[0]
        provider_image_id = job.new_image_id
        response.status = 202
        return {'_type':'provider_image',
                'id':provider_image_id,
                'href':'%s/%s' % (request.url, provider_image_id)}

    except KeyError as e:
        raise HTTPResponse(status=400, output="Missing either 'provider' or 'credentials' in request: %s" % e)
    except Exception as e:
        log.exception(e)
        raise HTTPResponse(status=500, output=e)

@rest_api.post('/imagefactory/provider_images')
@oauth_protect
def create_provider_image():
    try:
        _request_data = RESTtools.form_data_for_content_type(request.headers.get('Content-Type'))
        image_id = _request_data.get('image_id')
        build_id = _request_data.get('build_id')
        target_image_id = _request_data.get('target_image_id')
        return push_image(image_id, build_id, target_image_id)
    except KeyError as e:
        raise HTTPResponse(status=400, output="Missing one or more of 'image_id', 'build_id', or 'target_image_id': %s" % e)
    except Exception as e:
        log.exception(e)
        raise HTTPResponse(status=500, output=e)

@rest_api.get('/imagefactory/builders')
def list_builders():
    collection = {'_type':'builders','href':request.url}
    jobs = JobRegistry().jobs
    builders = []
    for builder_id in jobs.keys():
        job = jobs[builder_id]
        builders.append({'completed':job.percent_complete,
                         'status':job.status,
                         'operation':job.operation,
                         'target':job.target,
                         '_type':'builder',
                         'id':builder_id,
                         'image_id':job.image_id,
                         'build_id':job.build_id,
                         'target_image_id':job.target_image_id,
                         'provider':job.provider,
                         'provider_account_identifier':job.provider_account_identifier,
                         'href':'%s/%s' % (request.url, builder_id)})

    collection['builders'] = builders
    return collection

@rest_api.get('/imagefactory/builders/:builder_id')
@rest_api.get('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id')
@rest_api.get('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images/:provider_image_id')
def builder_detail(builder_id=None, image_id=None, build_id=None, target_image_id=None, provider_image_id=None):
    if(builder_id):
        _id = builder_id
        _type = 'builder'
    elif(target_image_id and provider_image_id):
        _id = provider_image_id
        _type = 'provider_image_status'
    elif(target_image_id):
        _id = target_image_id
        _type = 'target_image_status'
    else:
        log.warn('No uuid provided, unable to fetch builder...')
        raise HTTPResponse(status=400, output='No uuid provided, unable to fetch builder...')

    try:
        job = JobRegistry().jobs[_id]
        return {'completed':job.percent_complete,
                'status':job.status,
                'operation':job.operation,
                'target':job.target,
                'href':request.url,
                'id':_id,
                'image_id':job.image_id,
                'build_id':job.build_id,
                'target_image_id':job.target_image_id,
                'provider':job.provider,
                'provider_account_identifier':job.provider_account_identifier,
                '_type':_type}
    except KeyError as e:
        raise HTTPResponse(status=404, output="No builder found with uuid %s" % _id)
    except Exception as e:
        log.exception(e)
        raise HTTPResponse(status=500, output=e)

@rest_api.get('/imagefactory/builders/:builder_id/status')
@rest_api.get('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/status')
@rest_api.get('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images/:provider_image_id/status')
def builder_status(builder_id=None, image_id=None, build_id=None, target_image_id=None, provider_image_id=None):
    if(builder_id):
        _id = builder_id
        _type = 'builder_status'
    elif(target_image_id and provider_image_id):
        _id = provider_image_id
        _type = 'provider_image_status'
    elif(target_image_id):
        _id = target_image_id
        _type = 'target_image_status'
    else:
        log.warn('No uuid provided, unable to fetch builder...')
        raise HTTPResponse(status=400, output='No uuid provided, unable to fetch builder...')

    try:
        job = JobRegistry().jobs[_id]
        return {'_type':_type,
                'id':_id,
                'href':request.url,
                'status':job.status}
    except KeyError as e:
        raise HTTPResponse(status=404, output="No builder found with uuid %s" % _id)
    except Exception as e:
        log.exception(e)
        raise HTTPResponse(status=500, output=e)

# Things we have not yet implemented
@rest_api.route('/imagefactory/images', method=('GET','DELETE'))
@rest_api.route('/imagefactory/images/:image_id', method=('GET','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds', method=('GET','POST','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id', method=('GET','PUT','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images', method=('GET','POST','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id', method=('PUT','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images', method=('GET','DELETE'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images/:provider_image_id', method=('PUT','DELETE'))
@rest_api.route('/imagefactory/targets', method=('GET'))
@rest_api.route('/imagefactory/targets/:target_name', method=('GET'))
@rest_api.route('/imagefactory/targets/:target_name/providers', method=('GET','POST','DELETE'))
@rest_api.route('/imagefactory/targets/:target_name/providers/:provider_name', method=('GET','PUT','DELETE'))
@rest_api.delete('/imagefactory/builders/:builder_id')
def method_not_implemented(**kw):
    """
    @return 501 Not Implemented
    """
    raise HTTPResponse(status=501)

# Things we don't plan to implement
@rest_api.route('/imagefactory/images', method=('PUT'))
@rest_api.route('/imagefactory/images/:image_id', method=('POST'))
@rest_api.route('/imagefactory/images/:image_id/builds', method=('PUT'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id', method=('POST'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images', method=('PUT'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id', method=('POST'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images', method=('PUT'))
@rest_api.route('/imagefactory/images/:image_id/builds/:build_id/target_images/:target_image_id/provider_images/:provider_image_id', method=('POST'))
@rest_api.route('/imagefactory/targets', method=('PUT','POST','DELETE'))
@rest_api.route('/imagefactory/targets/:target_name', method=('PUT','POST','DELETE'))
@rest_api.route('/imagefactory/targets/:target_name/providers', method=('PUT'))
@rest_api.route('/imagefactory/targets/:target_name/providers/:provider_name', method=('POST'))
@rest_api.route('/imagefactory/builders', method=('PUT','POST','DELETE'))
@rest_api.route('/imagefactory/builders/:builder_id', method=('PUT','POST'))
@rest_api.route('/imagefactory/builders/:builder_id/status', method=('PUT','POST','DELETE'))
def method_not_allowed(**kw):
    """
    @return 405 Method Not Allowed
    """
    raise HTTPResponse(status=405)
<|endoftext|>