<commit_before>from platform import machine

from cloudshell.cp.azure.common.operations_helper import OperationsHelper
from cloudshell.cp.azure.domain.services.tags import TagNames
from cloudshell.cp.azure.models.prepare_connectivity_action_result import PrepareConnectivityActionResult

INVALID_REQUEST_ERROR = 'Invalid request: {0}'

class PrepareConnectivityOperation(object):
    def __init__(self,
                 logger,
                 vm_service,
                 network_service,
                 storage_service,
                 tags_service,
                 key_pair_service):
        """

        :param logger:
        :param cloudshell.cp.azure.domain.services.virtual_machine_service.VirtualMachineService vm_service:
        :param cloudshell.cp.azure.domain.services.network_service.NetworkService network_service:
        :param cloudshell.cp.azure.domain.services.storage_service.StorageService storage_service:
        :param cloudshell.cp.azure.domain.services.tags.TagService tags_service:
        :param cloudshell.cp.azure.domain.services.key_pair.KeyPairService key_pair_service:
        :return:
        """

        self.logger = logger
        self.vm_service = vm_service
        self.network_service = network_service
        self.storage_service = storage_service
        self.tags_service = tags_service
        self.key_pair_service = key_pair_service

    def prepare_connectivity(self,
                             reservation,
                             cloud_provider_model,
                             storage_client,
                             resource_client,
                             network_client,
                             logger,
                             request):
        """

        :param logging.Logger logger:
        :param request:
        :param network_client:
        :param storage_client:
        :param resource_client:
        :param cloudshell.cp.azure.models.reservation_model.ReservationModel reservation:
        :param cloudshell.cp.azure.models.azure_cloud_provider_resource_model.AzureCloudProviderResourceModel cloud_provider_model:cloud provider
        :return:
        """

        resource_name = OperationsHelper.generate_name("base")
        reservation_id = reservation.reservation_id
        group_name = str(reservation_id)
        vnet = cloud_provider_model.azure_mgmt_vnet
        tags = self.tags_service.get_tags(reservation=reservation)
        result = []
        action_result = PrepareConnectivityActionResult()

        # 1. Create a resource group
        self.vm_service.create_resource_group(resource_management_client=resource_client, group_name=group_name,
                                              region=cloud_provider_model.region, tags=tags)

        storage_account_name = OperationsHelper.generate_name(reservation_id[0:8])
        # 2. Create a storage account
        action_result.storage_name = self.storage_service.create_storage_account(storage_client=storage_client,
                                                                                group_name=group_name,
                                                                                region=cloud_provider_model.region,
                                                                                storage_account_name=storage_account_name,
                                                                                tags=tags,
                                                                                wait_until_created=True)
        # 3 Create a Key pair for the sandbox
        key_pair = self.key_pair_service.generate_key_pair()

        account_key = self.storage_service.get_storage_account_key(storage_client=storage_client,
                                                                   group_name=group_name,
                                                                   storage_name=storage_account_name)
        self.key_pair_service.save_key_pair(account_key=account_key,
                                            key_pair=key_pair,
                                            group_name=group_name,
                                            storage_name=storage_account_name)

        for action in request.actions:
            cidr = self._extract_cidr(action)
            logger.info("Received CIDR {0} from server".format(cidr))

            # 4. Create the network interface
            # todo: change that to create a subnet
            action_result.subnet_name = self.network_service.create_virtual_network(management_group_name=group_name,
                                                                                network_client=network_client,
                                                                                network_name=resource_name,
                                                                                region=cloud_provider_model.region,
                                                                                subnet_name=resource_name,
                                                                                tags=tags,
                                                                                subnet_cidr=cidr,
                                                                                vnet_cidr=vnet).name

            # 5.Create the NSG object
            # todo: crete the subnet

            result.append(action_result)
        return result

    @staticmethod
    def _extract_cidr(action):
        cidrs = [custom_attribute.attributeValue
                 for custom_attribute in action.customActionAttributes
                 if custom_attribute.attributeName == 'Network']
        if not cidrs:
            raise ValueError(INVALID_REQUEST_ERROR.format('CIDR is missing'))
        if len(cidrs) > 1:
            raise ValueError(INVALID_REQUEST_ERROR.format('Too many CIDRs parameters were found'))
        return cidrs[0]
<commit_msg>remove group_name key from the call to save_key_pair method in prepare_connectivity<commit_after>from platform import machine

from cloudshell.cp.azure.common.operations_helper import OperationsHelper
from cloudshell.cp.azure.domain.services.tags import TagNames
from cloudshell.cp.azure.models.prepare_connectivity_action_result import PrepareConnectivityActionResult

INVALID_REQUEST_ERROR = 'Invalid request: {0}'

class PrepareConnectivityOperation(object):
    def __init__(self,
                 logger,
                 vm_service,
                 network_service,
                 storage_service,
                 tags_service,
                 key_pair_service):
        """

        :param logger:
        :param cloudshell.cp.azure.domain.services.virtual_machine_service.VirtualMachineService vm_service:
        :param cloudshell.cp.azure.domain.services.network_service.NetworkService network_service:
        :param cloudshell.cp.azure.domain.services.storage_service.StorageService storage_service:
        :param cloudshell.cp.azure.domain.services.tags.TagService tags_service:
        :param cloudshell.cp.azure.domain.services.key_pair.KeyPairService key_pair_service:
        :return:
        """

        self.logger = logger
        self.vm_service = vm_service
        self.network_service = network_service
        self.storage_service = storage_service
        self.tags_service = tags_service
        self.key_pair_service = key_pair_service

    def prepare_connectivity(self,
                             reservation,
                             cloud_provider_model,
                             storage_client,
                             resource_client,
                             network_client,
                             logger,
                             request):
        """

        :param logging.Logger logger:
        :param request:
        :param network_client:
        :param storage_client:
        :param resource_client:
        :param cloudshell.cp.azure.models.reservation_model.ReservationModel reservation:
        :param cloudshell.cp.azure.models.azure_cloud_provider_resource_model.AzureCloudProviderResourceModel cloud_provider_model:cloud provider
        :return:
        """

        resource_name = OperationsHelper.generate_name("base")
        reservation_id = reservation.reservation_id
        group_name = str(reservation_id)
        vnet = cloud_provider_model.azure_mgmt_vnet
        tags = self.tags_service.get_tags(reservation=reservation)
        result = []
        action_result = PrepareConnectivityActionResult()

        # 1. Create a resource group
        self.vm_service.create_resource_group(resource_management_client=resource_client, group_name=group_name,
                                              region=cloud_provider_model.region, tags=tags)

        storage_account_name = OperationsHelper.generate_name(reservation_id[0:8])
        # 2. Create a storage account
        action_result.storage_name = self.storage_service.create_storage_account(storage_client=storage_client,
                                                                                group_name=group_name,
                                                                                region=cloud_provider_model.region,
                                                                                storage_account_name=storage_account_name,
                                                                                tags=tags,
                                                                                wait_until_created=True)
        # 3 Create a Key pair for the sandbox
        key_pair = self.key_pair_service.generate_key_pair()

        account_key = self.storage_service.get_storage_account_key(storage_client=storage_client,
                                                                   group_name=group_name,
                                                                   storage_name=storage_account_name)
        self.key_pair_service.save_key_pair(account_key=account_key,
                                            key_pair=key_pair,
                                            storage_name=storage_account_name)

        for action in request.actions:
            cidr = self._extract_cidr(action)
            logger.info("Received CIDR {0} from server".format(cidr))

            # 4. Create the network interface
            # todo: change that to create a subnet
            action_result.subnet_name = self.network_service.create_virtual_network(management_group_name=group_name,
                                                                                network_client=network_client,
                                                                                network_name=resource_name,
                                                                                region=cloud_provider_model.region,
                                                                                subnet_name=resource_name,
                                                                                tags=tags,
                                                                                subnet_cidr=cidr,
                                                                                vnet_cidr=vnet).name

            # 5.Create the NSG object
            # todo: crete the subnet

            result.append(action_result)
        return result

    @staticmethod
    def _extract_cidr(action):
        cidrs = [custom_attribute.attributeValue
                 for custom_attribute in action.customActionAttributes
                 if custom_attribute.attributeName == 'Network']
        if not cidrs:
            raise ValueError(INVALID_REQUEST_ERROR.format('CIDR is missing'))
        if len(cidrs) > 1:
            raise ValueError(INVALID_REQUEST_ERROR.format('Too many CIDRs parameters were found'))
        return cidrs[0]
<|endoftext|>


<commit_before>from pathlib import Path
import pytest
import shutil
import yaml
import datetime
from tests.common import run_prepare_cli
from eodatasets3.prepare import sentinel_sinergise_L1_prepare

path = (
    "data/sinergise_s2_l1c/S2B_MSIL1C_20201011T000249_N0209_R030_T55HFA_20201011T011446"
)

METADATA_XML_PATH: Path = Path(__file__).parent.parent / path / "metadata.xml"

PRODUCT_INFO_PATH: Path = Path(__file__).parent.parent / path / "productInfo.json"

FORMAT_CORRECTNESS_PATH: Path = (
    Path(__file__).parent.parent / path / "qi/FORMAT_CORRECTNESS.xml"
)

DATASET_DIR: Path = Path(__file__).parent.parent / path

@pytest.fixture()
def expected_dataset_document():
    return {
        "$schema": "https://schemas.opendatacube.org/dataset",
        "accessories": {},
        "crs": "epsg:32755",
        "geometry": {
            "coordinates": [
                [
                    [600332.7272727273, 6100000.0],
                    [709800.0, 6100000.0],
                    [709800.0, 5990200.0],
                    [600000.0, 5990200.0],
                    [600000.0, 6099500.909090909],
                    [600000.0, 6100000.0],
                    [600332.7272727273, 6100000.0],
                ]
            ],
            "type": "Polygon",
        },
        "grids": {
            "default": {
                "shape": [55, 55],
                "transform": [
                    1996.3636363636363,
                    0.0,
                    600000.0,
                    0.0,
                    -1996.3636363636363,
                    6100000.0,
                    0.0,
                    0.0,
                    1.0,
                ],
            },
            "green_nir_1_blue_red": {
                "shape": [110, 110],
                "transform": [
                    998.1818181818181,
                    0.0,
                    600000.0,
                    0.0,
                    -998.1818181818181,
                    6100000.0,
                    0.0,
                    0.0,
                    1.0,
                ],
            },
            "water_vapour_swir_1_cirrus_coastal_aerosol": {
                "shape": [19, 19],
                "transform": [
                    5778.9473684210525,
                    0.0,
                    600000.0,
                    0.0,
                    -5778.9473684210525,
                    6100000.0,
                    0.0,
                    0.0,
                    1.0,
                ],
            },
        },
        "label": "sinergise_s2am_level1_1-0-0_55HFA_2020-10-11",
        "lineage": {},
        "measurements": {
            "blue": {"grid": "green_nir_1_blue_red", "path": "B02.jp2"},
            "coastal_aerosol": {
                "grid": "water_vapour_swir_1_cirrus_coastal_aerosol",
                "path": "B01.jp2",
            },
            "green": {"grid": "green_nir_1_blue_red", "path": "B03.jp2"},
            "nir_1": {"grid": "green_nir_1_blue_red", "path": "B08.jp2"},
            "nir_2": {"path": "B8A.jp2"},
            "red": {"grid": "green_nir_1_blue_red", "path": "B04.jp2"},
            "red_edge_1": {"path": "B05.jp2"},
            "red_edge_2": {"path": "B06.jp2"},
            "red_edge_3": {"path": "B07.jp2"},
            "swir_1_cirrus": {
                "grid": "water_vapour_swir_1_cirrus_coastal_aerosol",
                "path": "B10.jp2",
            },
            "swir_2": {"path": "B11.jp2"},
            "swir_3": {"path": "B12.jp2"},
            "water_vapour": {
                "grid": "water_vapour_swir_1_cirrus_coastal_aerosol",
                "path": "B09.jp2",
            },
        },
        "product": {"name": "sinergise_s2am_level1_1"},
        "properties": {
            "datetime": datetime.datetime(
                2020, 10, 11, 0, 6, 49, 882000, tzinfo=datetime.timezone.utc
            ),
            "eo:cloud_cover": 24.9912,
            "eo:gsd": 10,
            "eo:instrument": "MSI",
            "eo:platform": "sentinel-2a",
            "eo:sun_azimuth": 37.3713908882192,
            "eo:sun_elevation": 46.3307328858312,
            "odc:dataset_version": "1.0.0",
            "odc:file_format": "JPEG2000",
            "odc:processing_datetime": datetime.datetime(
                2020, 10, 11, 1, 47, 21, tzinfo=datetime.timezone.utc
            ),
            "odc:producer": "sinergise.com",
            "odc:product_family": "level1",
            "odc:region_code": "55HFA",
            "sentinel:data_type": "Level-1C",
            "sentinel:datastrip_id": "S2B_OPER_MSI_L1C_DS_EPAE_20201011T011446_S20201011T000244_N02.09",
            "sentinel:datastrip_metadata": "GRANULE/L1C_T55HFA_A018789_20201011T000244/QI_DATA/FORMAT_CORRECTNESS.xml",
            "sentinel:downlink_priority": "NOMINAL",
            "sentinel:product_type": "S2MSI1C",
            "sentinel:sinergise_product_id": "73e1a409-595d-4fbf-8fe0-01e0ee26bf00",
            "sentinel:sinergise_product_name": "S2B_MSIL1C_20201011T000249_N0209_R030_T55HFA_20201011T011446",
            "sentinel:software_version": "01.06.04",
            "sentinel:source_system": "OLQC-SC",
        },
    }

def test_run(tmp_path, expected_dataset_document):

    # GIVEN:
    #     A folder of imagery
    outdir = tmp_path / DATASET_DIR.name
    indir = DATASET_DIR

    if indir.is_file():
        shutil.copy(indir, outdir)
    else:
        shutil.copytree(indir, outdir)

    # WHEN:
    #    Run prepare on that folder
    output_yaml_path = outdir / "test.yaml"

    run_prepare_cli(
        sentinel_sinergise_L1_prepare.main,
        PRODUCT_INFO_PATH,
        METADATA_XML_PATH,
        FORMAT_CORRECTNESS_PATH,
        output_yaml_path,
        outdir,
    )

    # THEN
    #     A metadata file is added to it, with valid properties
    #     Assert doc is expected doc
    with output_yaml_path.open("r") as f:
        generated_doc = yaml.safe_load(f)
        del generated_doc["id"]
    assert expected_dataset_document == generated_doc
<commit_msg>	modified:   test_prepare_sinergise_sentinel_l1.py<commit_after>from pathlib import Path
import pytest
import shutil
import yaml
import datetime
from tests.common import run_prepare_cli
from eodatasets3.prepare import sentinel_sinergise_L1_prepare

path = (
    "data/sinergise_s2_l1c/S2B_MSIL1C_20201011T000249_N0209_R030_T55HFA_20201011T011446"
)

METADATA_XML_PATH: Path = Path(__file__).parent.parent / path / "metadata.xml"

PRODUCT_INFO_PATH: Path = Path(__file__).parent.parent / path / "productInfo.json"

FORMAT_CORRECTNESS_PATH: Path = (
    Path(__file__).parent.parent / path / "qi/FORMAT_CORRECTNESS.xml"
)

DATASET_DIR: Path = Path(__file__).parent.parent / path

@pytest.fixture()
def expected_dataset_document():
    return {
        "$schema": "https://schemas.opendatacube.org/dataset",
        "accessories": {},
        "crs": "epsg:32755",
        "geometry": {
            "coordinates": [
                [
                    [600332.7272727273, 6100000.0],
                    [709800.0, 6100000.0],
                    [709800.0, 5990200.0],
                    [600000.0, 5990200.0],
                    [600000.0, 6099500.909090909],
                    [600000.0, 6100000.0],
                    [600332.7272727273, 6100000.0],
                ]
            ],
            "type": "Polygon",
        },
        "grids": {
            "default": {
                "shape": [55, 55],
                "transform": [
                    1996.3636363636363,
                    0.0,
                    600000.0,
                    0.0,
                    -1996.3636363636363,
                    6100000.0,
                    0.0,
                    0.0,
                    1.0,
                ],
            },
            "green_nir_1_blue_red": {
                "shape": [110, 110],
                "transform": [
                    998.1818181818181,
                    0.0,
                    600000.0,
                    0.0,
                    -998.1818181818181,
                    6100000.0,
                    0.0,
                    0.0,
                    1.0,
                ],
            },
            "water_vapour_swir_1_cirrus_coastal_aerosol": {
                "shape": [19, 19],
                "transform": [
                    5778.9473684210525,
                    0.0,
                    600000.0,
                    0.0,
                    -5778.9473684210525,
                    6100000.0,
                    0.0,
                    0.0,
                    1.0,
                ],
            },
        },
        "label": "sinergise_s2am_level1_1-0-0_55HFA_2020-10-11",
        "lineage": {},
        "measurements": {
            "blue": {"grid": "green_nir_1_blue_red", "path": "B02.jp2"},
            "coastal_aerosol": {
                "grid": "water_vapour_swir_1_cirrus_coastal_aerosol",
                "path": "B01.jp2",
            },
            "green": {"grid": "green_nir_1_blue_red", "path": "B03.jp2"},
            "nir_1": {"grid": "green_nir_1_blue_red", "path": "B08.jp2"},
            "nir_2": {"path": "B8A.jp2"},
            "red": {"grid": "green_nir_1_blue_red", "path": "B04.jp2"},
            "red_edge_1": {"path": "B05.jp2"},
            "red_edge_2": {"path": "B06.jp2"},
            "red_edge_3": {"path": "B07.jp2"},
            "swir_1_cirrus": {
                "grid": "water_vapour_swir_1_cirrus_coastal_aerosol",
                "path": "B10.jp2",
            },
            "swir_2": {"path": "B11.jp2"},
            "swir_3": {"path": "B12.jp2"},
            "water_vapour": {
                "grid": "water_vapour_swir_1_cirrus_coastal_aerosol",
                "path": "B09.jp2",
            },
        },
        "product": {"name": "sinergise_s2am_level1_1"},
        "properties": {
            "datetime": datetime.datetime(
                2020, 10, 11, 0, 6, 49, 882000, tzinfo=datetime.timezone.utc
            ),
            "eo:cloud_cover": 24.9912,
            "eo:gsd": 10,
            "eo:instrument": "MSI",
            "eo:platform": "sentinel-2a",
            "eo:sun_azimuth": 37.3713908882192,
            "eo:sun_elevation": 46.3307328858312,
            "odc:dataset_version": "1.0.0",
            "odc:file_format": "JPEG2000",
            "odc:processing_datetime": datetime.datetime(
                2020, 10, 11, 1, 47, 21, tzinfo=datetime.timezone.utc
            ),
            "odc:producer": "sinergise.com",
            "odc:product_family": "level1",
            "odc:region_code": "55HFA",
            "sentinel:data_type": "Level-1C",
            "sentinel:datastrip_id": "S2B_OPER_MSI_L1C_DS_EPAE_20201011T011446_S20201011T000244_N02.09",
            "sentinel:datastrip_metadata": "GRANULE/L1C_T55HFA_A018789_20201011T000244/QI_DATA/FORMAT_CORRECTNESS.xml",
            "sentinel:downlink_priority": "NOMINAL",
            "sentinel:product_type": "S2MSI1C",
            "sentinel:sinergise_product_id": "73e1a409-595d-4fbf-8fe0-01e0ee26bf00",
            "sentinel:sinergise_product_name": "S2B_MSIL1C_20201011T000249_N0209_R030_T55HFA_20201011T011446",
            "sentinel:software_version": "01.06.04",
            "sentinel:source_system": "OLQC-SC",
        },
    }

def test_run(tmp_path, expected_dataset_document):

    # GIVEN:
    #     A folder of imagery
    outdir = tmp_path / DATASET_DIR.name
    indir = DATASET_DIR

    if indir.is_file():
        shutil.copy(indir, outdir)
    else:
        shutil.copytree(indir, outdir)

    # WHEN:
    #    Run prepare on that folder
    output_yaml_path = outdir / "test.yaml"

    run_prepare_cli(
        sentinel_sinergise_L1_prepare.main,
        "--product",
        PRODUCT_INFO_PATH,
        "--metadata-xml",
        METADATA_XML_PATH,
        "--format-correctness",
        FORMAT_CORRECTNESS_PATH,
        "--dataset-document",
        output_yaml_path,
        "--dataset",
        outdir,
    )

    # THEN
    #     A metadata file is added to it, with valid properties
    #     Assert doc is expected doc
    with output_yaml_path.open("r") as f:
        generated_doc = yaml.safe_load(f)
        del generated_doc["id"]
    assert expected_dataset_document == generated_doc
<|endoftext|>


<commit_before>import copy

from django.shortcuts import render
from django.db import connections
from django.views import View

from lotes.forms import OpForm
import lotes.models as models

class Op(View):
    Form_class = OpForm
    template_name = 'lotes/op.html'
    title_name = 'OP'

    def mount_context(self, cursor, op):
        context = {'op': op}

        # Lotes ordenados por OS + referência + estágio
        data = models.op_lotes(cursor, op)
        if len(data) == 0:
            context.update({
                'msg_erro': 'Lotes não encontrados',
            })
        else:
            link = ('LOTE')
            for row in data:
                row['LOTE'] = '{}{:05}'.format(row['PERIODO'], row['OC'])
                row['LINK'] = '/lotes/posicao/{}'.format(row['LOTE'])
            context.update({
                'headers': ('Estágio', 'OS', 'Referência', 'Cor', 'Tamanho',
                            'Período', 'OC', 'Quant.', 'Lote'),
                'fields': ('EST', 'OS', 'REF', 'COR', 'TAM',
                           'PERIODO', 'OC', 'QTD', 'LOTE'),
                'data': data,
                'link': link,
            })

            # informações gerais
            i_data = models.op_inform(cursor, op)
            i2_data = copy.deepcopy(i_data)
            i3_data = copy.deepcopy(i_data)

            row = i_data[0]
            if row['OP_REL'] == 0:
                row['OP_REL'] = ''
            else:
                row['OP_REL|LINK'] = '/lotes/op/{}'.format(row['OP_REL'])
            if row['PEDIDO'] == 0:
                row['PEDIDO'] = ''
            else:
                row['PEDIDO|LINK'] = '/lotes/pedido/{}'.format(row['PEDIDO'])
            context.update({
                'i_headers': ('Situação', 'Cancelamento',
                              'Tipo de OP', 'OP relacionada', 'Pedido',
                              'Pedido do cliente'),
                'i_fields': ('SITUACAO', 'CANCELAMENTO',
                             'TIPO_OP', 'OP_REL', 'PEDIDO',
                             'PED_CLIENTE'),
                'i_data': i_data,
            })

            row = i2_data[0]
            row['REF|LINK'] = '/produto/ref/{}'.format(row['REF'])
            row['MODELO|LINK'] = '/produto/modelo/{}'.format(row['MODELO'])
            context.update({
                'i2_headers': ('Modelo', 'Tipo de referência', 'Referência',
                               'Alternativa', 'Roteiro',
                               'Qtd. Lotes', 'Quant. Itens'),
                'i2_fields': ('MODELO', 'TIPO_REF', 'REF',
                              'ALTERNATIVA', 'ROTEIRO',
                              'LOTES', 'QTD'),
                'i2_data': i2_data,
            })

            row = i3_data[0]
            row['DT_DIGITACAO'] = row['DT_DIGITACAO'].date()
            row['DT_CORTE'] = row['DT_CORTE'].date()
            row['PERIODO_INI'] = row['PERIODO_INI'].date()
            row['PERIODO_FIM'] = row['PERIODO_FIM'].date()
            context.update({
                'i3_headers': ('Depósito', 'Período', 'Período Início',
                               'Período Fim', 'Data Digitação', 'Data Corte'),
                'i3_fields': ('DEPOSITO', 'PERIODO', 'PERIODO_INI',
                              'PERIODO_FIM', 'DT_DIGITACAO', 'DT_CORTE'),
                'i3_data': i3_data,
            })

            # Grade
            g_header, g_fields, g_data = models.op_sortimento(cursor, op)
            if len(g_data) != 0:
                context.update({
                    'g_headers': g_header,
                    'g_fields': g_fields,
                    'g_data': g_data,
                })

            # Estágios
            e_data = models.op_estagios(cursor, op)
            context.update({
                'e_headers': ('Estágio', '% Produzido',
                              'Itens 1ª Qualidade', 'Itens 2ª Qualidade',
                              'Itens Perda', 'Lotes no estágio'),
                'e_fields': ('EST', 'PERC', 'PROD', 'Q2', 'PERDA', 'LOTES'),
                'e_data': e_data,
            })

            # Totais por referência + estágio
            t_data = models.op_ref_estagio(cursor, op)
            context.update({
                't_headers': ('Referência', 'Tamanho', 'Cor', 'Estágio',
                              'Qtd. Lotes', 'Quant. Itens'),
                't_fields': ('REF', 'TAM', 'COR', 'EST', 'LOTES', 'QTD'),
                't_data': t_data,
            })

            # OSs da OP
            os_data = models.op_get_os(cursor, op)
            if len(os_data) != 0:
                os_link = ('OS')
                for row in os_data:
                    row['LINK'] = '/lotes/os/{}'.format(row['OS'])
                    cnpj = '{:08d}/{:04d}-{:02d}'.format(
                        row['CNPJ9'],
                        row['CNPJ4'],
                        row['CNPJ2'])
                    row['TERC'] = '{} - {}'.format(cnpj, row['NOME'])
                context.update({
                    'os_headers': ('OS', 'Serviço', 'Terceiro',
                                   'Emissão', 'Entrega',
                                   'Situação', 'Cancelamento',
                                   'Lotes', 'Quant.'),
                    'os_fields': ('OS', 'SERV', 'TERC',
                                  'DATA_EMISSAO', 'DATA_ENTREGA',
                                  'SITUACAO', 'CANC',
                                  'LOTES', 'QTD'),
                    'os_data': os_data,
                    'os_link': os_link,
                })

            # Totais por OS + referência
            o_data = models.op_os_ref(cursor, op)
            o_link = ('OS')
            for row in o_data:
                if row['OS']:
                    row['LINK'] = '/lotes/os/{}'.format(row['OS'])
                else:
                    row['LINK'] = None
            context.update({
                'o_headers': ('OS', 'Referência', 'Tamanho', 'Cor',
                              'Qtd. Lotes', 'Quant. Itens'),
                'o_fields': ('OS', 'REF', 'TAM', 'COR', 'LOTES', 'QTD'),
                'o_data': o_data,
                'o_link': o_link,
            })
        return context

    def get(self, request, *args, **kwargs):
        if 'op' in kwargs:
            return self.post(request, *args, **kwargs)
        else:
            context = {'titulo': self.title_name}
            form = self.Form_class()
            context['form'] = form
            return render(request, self.template_name, context)

    def post(self, request, *args, **kwargs):
        context = {'titulo': self.title_name}
        form = self.Form_class(request.POST)
        if 'op' in kwargs:
            form.data['op'] = kwargs['op']
        if form.is_valid():
            op = form.cleaned_data['op']
            cursor = connections['so'].cursor()
            context.update(self.mount_context(cursor, op))
        context['form'] = form
        return render(request, self.template_name, context)
<commit_msg>OP with more than 1 child<commit_after>import copy

from django.shortcuts import render
from django.db import connections
from django.views import View

from fo2.template import group_rowspan

from lotes.forms import OpForm
import lotes.models as models

class Op(View):
    Form_class = OpForm
    template_name = 'lotes/op.html'
    title_name = 'OP'

    def mount_context(self, cursor, op):
        context = {'op': op}

        # Lotes ordenados por OS + referência + estágio
        data = models.op_lotes(cursor, op)
        if len(data) == 0:
            context.update({
                'msg_erro': 'Lotes não encontrados',
            })
        else:
            link = ('LOTE')
            for row in data:
                row['LOTE'] = '{}{:05}'.format(row['PERIODO'], row['OC'])
                row['LINK'] = '/lotes/posicao/{}'.format(row['LOTE'])
            context.update({
                'headers': ('Estágio', 'OS', 'Referência', 'Cor', 'Tamanho',
                            'Período', 'OC', 'Quant.', 'Lote'),
                'fields': ('EST', 'OS', 'REF', 'COR', 'TAM',
                           'PERIODO', 'OC', 'QTD', 'LOTE'),
                'data': data,
                'link': link,
            })

            # informações gerais
            i_data = models.op_inform(cursor, op)
            i2_data = [i_data[0]]
            i3_data = [i_data[0]]

            for row in i_data:
                if row['OP_REL'] == 0:
                    row['OP_REL'] = ''
                else:
                    row['OP_REL|LINK'] = '/lotes/op/{}'.format(row['OP_REL'])
                if row['PEDIDO'] == 0:
                    row['PEDIDO'] = ''
                else:
                    row['PEDIDO|LINK'] = '/lotes/pedido/{}'.format(
                        row['PEDIDO'])
            i_group = ['SITUACAO', 'CANCELAMENTO', 'PEDIDO', 'PED_CLIENTE']
            group_rowspan(i_data, i_group)
            context.update({
                'i_headers': ('Situação', 'Cancelamento', 'Pedido',
                              'Pedido do cliente', 'Tipo de OP',
                              'OP relacionada'),
                'i_fields': ('SITUACAO', 'CANCELAMENTO', 'PEDIDO',
                             'PED_CLIENTE', 'TIPO_OP', 'OP_REL'),
                'i_group': i_group,
                'i_data': i_data,
            })

            row = i2_data[0]
            row['REF|LINK'] = '/produto/ref/{}'.format(row['REF'])
            row['MODELO|LINK'] = '/produto/modelo/{}'.format(row['MODELO'])
            context.update({
                'i2_headers': ('Modelo', 'Tipo de referência', 'Referência',
                               'Alternativa', 'Roteiro',
                               'Qtd. Lotes', 'Quant. Itens'),
                'i2_fields': ('MODELO', 'TIPO_REF', 'REF',
                              'ALTERNATIVA', 'ROTEIRO',
                              'LOTES', 'QTD'),
                'i2_data': i2_data,
            })

            row = i3_data[0]
            row['DT_DIGITACAO'] = row['DT_DIGITACAO'].date()
            row['DT_CORTE'] = row['DT_CORTE'].date()
            row['PERIODO_INI'] = row['PERIODO_INI'].date()
            row['PERIODO_FIM'] = row['PERIODO_FIM'].date()
            context.update({
                'i3_headers': ('Depósito', 'Período', 'Período Início',
                               'Período Fim', 'Data Digitação', 'Data Corte'),
                'i3_fields': ('DEPOSITO', 'PERIODO', 'PERIODO_INI',
                              'PERIODO_FIM', 'DT_DIGITACAO', 'DT_CORTE'),
                'i3_data': i3_data,
            })

            # Grade
            g_header, g_fields, g_data = models.op_sortimento(cursor, op)
            if len(g_data) != 0:
                context.update({
                    'g_headers': g_header,
                    'g_fields': g_fields,
                    'g_data': g_data,
                })

            # Estágios
            e_data = models.op_estagios(cursor, op)
            context.update({
                'e_headers': ('Estágio', '% Produzido',
                              'Itens 1ª Qualidade', 'Itens 2ª Qualidade',
                              'Itens Perda', 'Lotes no estágio'),
                'e_fields': ('EST', 'PERC', 'PROD', 'Q2', 'PERDA', 'LOTES'),
                'e_data': e_data,
            })

            # Totais por referência + estágio
            t_data = models.op_ref_estagio(cursor, op)
            context.update({
                't_headers': ('Referência', 'Tamanho', 'Cor', 'Estágio',
                              'Qtd. Lotes', 'Quant. Itens'),
                't_fields': ('REF', 'TAM', 'COR', 'EST', 'LOTES', 'QTD'),
                't_data': t_data,
            })

            # OSs da OP
            os_data = models.op_get_os(cursor, op)
            if len(os_data) != 0:
                os_link = ('OS')
                for row in os_data:
                    row['LINK'] = '/lotes/os/{}'.format(row['OS'])
                    cnpj = '{:08d}/{:04d}-{:02d}'.format(
                        row['CNPJ9'],
                        row['CNPJ4'],
                        row['CNPJ2'])
                    row['TERC'] = '{} - {}'.format(cnpj, row['NOME'])
                context.update({
                    'os_headers': ('OS', 'Serviço', 'Terceiro',
                                   'Emissão', 'Entrega',
                                   'Situação', 'Cancelamento',
                                   'Lotes', 'Quant.'),
                    'os_fields': ('OS', 'SERV', 'TERC',
                                  'DATA_EMISSAO', 'DATA_ENTREGA',
                                  'SITUACAO', 'CANC',
                                  'LOTES', 'QTD'),
                    'os_data': os_data,
                    'os_link': os_link,
                })

            # Totais por OS + referência
            o_data = models.op_os_ref(cursor, op)
            o_link = ('OS')
            for row in o_data:
                if row['OS']:
                    row['LINK'] = '/lotes/os/{}'.format(row['OS'])
                else:
                    row['LINK'] = None
            context.update({
                'o_headers': ('OS', 'Referência', 'Tamanho', 'Cor',
                              'Qtd. Lotes', 'Quant. Itens'),
                'o_fields': ('OS', 'REF', 'TAM', 'COR', 'LOTES', 'QTD'),
                'o_data': o_data,
                'o_link': o_link,
            })
        return context

    def get(self, request, *args, **kwargs):
        if 'op' in kwargs:
            return self.post(request, *args, **kwargs)
        else:
            context = {'titulo': self.title_name}
            form = self.Form_class()
            context['form'] = form
            return render(request, self.template_name, context)

    def post(self, request, *args, **kwargs):
        context = {'titulo': self.title_name}
        form = self.Form_class(request.POST)
        if 'op' in kwargs:
            form.data['op'] = kwargs['op']
        if form.is_valid():
            op = form.cleaned_data['op']
            cursor = connections['so'].cursor()
            context.update(self.mount_context(cursor, op))
        context['form'] = form
        return render(request, self.template_name, context)
<|endoftext|>


<commit_before>'''
Created on Nov 8, 2012

@author: dstrauss

This ought to be identical to manageTasks, except that it tosses in a flag to make the mpiexec stuff work on the new nodes.
'''

import xml.etree.ElementTree as xml
import subprocess
import time
import sys
import doFolders
import math

def waitForExit(jobName):
    ''' Routine to check and see if a particular job is still running, if not, return.
    This program takes full control and does not return until the job has completed. '''
    doExit = False
    while doExit == False:
        pipeOut = subprocess.Popen(['qstat', '-x', jobName], stdout=subprocess.PIPE)
        f = pipeOut.stdout.read()
        try:
            P = xml.fromstring(f)
            for z in P.getiterator():
                if z.tag == 'job_state':
                    if z.text == 'R':
                        pass
                        # print 'Still Running ' + jobName
                    elif z.text == 'C':
                        doExit = True
                        print 'Finished ' + jobName
        except:
            print 'wrong string or something?'

        time.sleep(5)

def checkForExit(jobName):
    ''' Routine to check and see if a particular job is still running, if not, return.
    Returns True if the job is still running with status R, returns false if job has completed
    or simply does not have status R. '''
    doExit = False
    pipeOut = subprocess.Popen(['qstat', '-x', jobName], stdout=subprocess.PIPE)
    f = pipeOut.stdout.read()
    try:
        P = xml.fromstring(f)
        for z in P.getiterator():
            if z.tag == 'job_state':
                if z.text == 'R':
                    pass
                        # print 'Still Running ' + jobName
                elif z.text == 'C':
                    doExit = True
                    print 'Finished ' + jobName
    except:
        print 'wrong string or something?'
    return doExit

def submitJob(cmd):
    ''' Basic routine to submit/execute a line of code given in cmd and return whatever return
    values are spit out by the system.'''
    pipeOut = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    f = pipeOut.stdout.read()
    f = f[:-1]
    print f + ' ' + time.asctime(time.localtime())
    time.sleep(1)
    return f

def main():
    ''' main routine to run one of my python submission routines. It takes up to three arguments
    in the command line. It wants an iteration control script, a start index, and a number of submissions
    to maintain. From there, it just goes'''
    # problem specific issues
    if len(sys.argv) >= 3:
        startIx = int(sys.argv[2])
    else:
        startIx = 0

    if len(sys.argv) == 4:
        numWorkers = int(sys.argv[3])
    else:
        numWorkers = 1
    # import my runtime iteration control script
    prSpec = __import__(sys.argv[1])
    finalIx = prSpec.D['numRuns']

    # queue of indexes still to run
    runList = range(startIx,finalIx)
    # list of open/running jobs
    # jobList = list(range(numWorkers))
    jobList = list()

    # main loop: wait for all potential jobs to be executed
    while len(runList) > 0:
        # populate the job list
        if len(jobList) < numWorkers:
            # launch worker
            ix = runList.pop(0)
            doFolders.ensureFolders(prSpec.D, ix)

            lclD = prSpec.getMyVars(ix, prSpec.D)
            nProcs = lclD['numProcs']
            # nNodes = int(math.ceil(nProcs/8.0))

            if nProcs > 0:
                jobTitle = 'run' + sys.argv[1] + repr(ix)
                fileName = 'sub' + sys.argv[1] + repr(ix) + '.pbs'

                fid = open(fileName, 'w')
                fid.write('mpiexec -n ' + repr(nProcs) + '--mca btl_tcp_if_include "p3p2" -wdir /shared/users/dstrauss/subsurface/src python coordinate.py ' + sys.argv[1] + ' ' + repr(ix))
                fid.close()
                cmd = ['qsub', '-N', jobTitle, '-l' , 'walltime=20:00:00', '-l','nodes=' + repr(nProcs), '-l', 'nice=0', '-q','batchnew', fileName]
                print cmd

                jobList.append(submitJob(cmd))
            else:
                print 'skipping ' + repr(ix)

        else:
            time.sleep(5)

         # check for completion
        for jbs in jobList:
            if checkForExit(jbs):
                jobList.remove(jbs)

if __name__=='__main__':
    main()
<commit_msg>typo adjustments , switch to dev folder.<commit_after>'''
Created on Nov 8, 2012

@author: dstrauss

This ought to be identical to manageTasks, except that it tosses in a flag to make the mpiexec stuff work on the new nodes.
'''

import xml.etree.ElementTree as xml
import subprocess
import time
import sys
import doFolders
import math

def waitForExit(jobName):
    ''' Routine to check and see if a particular job is still running, if not, return.
    This program takes full control and does not return until the job has completed. '''
    doExit = False
    while doExit == False:
        pipeOut = subprocess.Popen(['qstat', '-x', jobName], stdout=subprocess.PIPE)
        f = pipeOut.stdout.read()
        try:
            P = xml.fromstring(f)
            for z in P.getiterator():
                if z.tag == 'job_state':
                    if z.text == 'R':
                        pass
                        # print 'Still Running ' + jobName
                    elif z.text == 'C':
                        doExit = True
                        print 'Finished ' + jobName
        except:
            print 'wrong string or something?'

        time.sleep(5)

def checkForExit(jobName):
    ''' Routine to check and see if a particular job is still running, if not, return.
    Returns True if the job is still running with status R, returns false if job has completed
    or simply does not have status R. '''
    doExit = False
    pipeOut = subprocess.Popen(['qstat', '-x', jobName], stdout=subprocess.PIPE)
    f = pipeOut.stdout.read()
    try:
        P = xml.fromstring(f)
        for z in P.getiterator():
            if z.tag == 'job_state':
                if z.text == 'R':
                    pass
                        # print 'Still Running ' + jobName
                elif z.text == 'C':
                    doExit = True
                    print 'Finished ' + jobName
    except:
        print 'wrong string or something?'
    return doExit

def submitJob(cmd):
    ''' Basic routine to submit/execute a line of code given in cmd and return whatever return
    values are spit out by the system.'''
    pipeOut = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    f = pipeOut.stdout.read()
    f = f[:-1]
    print f + ' ' + time.asctime(time.localtime())
    time.sleep(1)
    return f

def main():
    ''' main routine to run one of my python submission routines. It takes up to three arguments
    in the command line. It wants an iteration control script, a start index, and a number of submissions
    to maintain. From there, it just goes'''
    # problem specific issues
    if len(sys.argv) >= 3:
        startIx = int(sys.argv[2])
    else:
        startIx = 0

    if len(sys.argv) == 4:
        numWorkers = int(sys.argv[3])
    else:
        numWorkers = 1
    # import my runtime iteration control script
    prSpec = __import__(sys.argv[1])
    finalIx = prSpec.D['numRuns']

    # queue of indexes still to run
    runList = range(startIx,finalIx)
    # list of open/running jobs
    # jobList = list(range(numWorkers))
    jobList = list()

    # main loop: wait for all potential jobs to be executed
    while len(runList) > 0:
        # populate the job list
        if len(jobList) < numWorkers:
            # launch worker
            ix = runList.pop(0)
            doFolders.ensureFolders(prSpec.D, ix)

            lclD = prSpec.getMyVars(ix, prSpec.D)
            nProcs = lclD['numProcs']
            # nNodes = int(math.ceil(nProcs/8.0))

            if nProcs > 0:
                jobTitle = 'run' + sys.argv[1] + repr(ix)
                fileName = 'sub' + sys.argv[1] + repr(ix) + '.pbs'

                fid = open(fileName, 'w')
                fid.write('mpiexec -n ' + repr(nProcs) + ' --mca btl_tcp_if_include "p3p2" -wdir /shared/users/dstrauss/dev/subsurface/src python coordinate.py ' + sys.argv[1] + ' ' + repr(ix))
                fid.close()
                cmd = ['qsub', '-N', jobTitle, '-l' , 'walltime=20:00:00', '-l','nodes=' + repr(nProcs), '-l', 'nice=0', '-q','batchnew', fileName]
                print cmd

                jobList.append(submitJob(cmd))
            else:
                print 'skipping ' + repr(ix)

        else:
            time.sleep(5)

         # check for completion
        for jbs in jobList:
            if checkForExit(jbs):
                jobList.remove(jbs)

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


<commit_before>import falcon
import json

class SlackMessageRouter:
    def on_post(self, req, resp):
        resp.body = json.dumps({'message': 'hello, world!'})
        resp.status = falcon.HTTP_200
<commit_msg>send slack-consumable response<commit_after>import falcon
import json

class SlackMessageRouter:
    def on_post(self, req, resp):
        resp.body = json.dumps({'text': 'hello, world!'})
        resp.status = falcon.HTTP_200
<|endoftext|>


<commit_before>from __future__ import unicode_literals

from datetime import datetime
import functools
import json

from django.conf import settings
from django.template import Context, Template
from django.utils import six
from jira import JIRA, JIRAError, Comment
from jira.utils import json_loads

from nodeconductor_assembly_waldur.support import models
from nodeconductor_assembly_waldur.support.backend import SupportBackendError, SupportBackend

class JiraBackendError(SupportBackendError):
    pass

def reraise_exceptions(func):
    @functools.wraps(func)
    def wrapped(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except JIRAError as e:
            six.reraise(JiraBackendError, e)

    return wrapped

class JiraBackend(SupportBackend):
    credentials = settings.WALDUR_SUPPORT.get('CREDENTIALS', {})
    project_settings = settings.WALDUR_SUPPORT.get('PROJECT', {})
    issue_settings = settings.WALDUR_SUPPORT.get('ISSUE', {})

    @property
    @reraise_exceptions
    def manager(self):
        # manager will be the same for all issues - we can cache it on the class level.
        if not hasattr(self.__class__, '_manager'):
            self.__class__._manager = JIRA(
                server=self.credentials['server'],
                options={'verify': self.credentials['verify_ssl']},
                basic_auth=(self.credentials['username'], self.credentials['password']),
                validate=False)
        return self.__class__._manager

    @reraise_exceptions
    def _get_field_id_by_name(self, field_name):
        if not hasattr(self.__class__, '_fields'):
            self.__class__._fields = self.manager.fields()
        try:
            return next(f['id'] for f in self.__class__._fields if field_name in f['clauseNames'])
        except StopIteration:
            return JiraBackendError('Field "{0}" does not exist in JIRA.'.format(field_name))

    def _issue_to_dict(self, issue):
        """ Convert issue to dict that can be accepted by JIRA as input parameters """
        caller = issue.caller.full_name or issue.caller.username
        args = {
            'project': self.project_settings['key'],
            'summary': self._render_template('summary', issue),
            'description': issue.description,
            'issuetype': {'name': issue.type},
            self._get_field_id_by_name(self.issue_settings['caller_field']): caller,
        }

        if issue.reporter:
            args[self._get_field_id_by_name(self.issue_settings['reporter_field'])] = issue.reporter.name
        if issue.impact:
            args[self._get_field_id_by_name(self.issue_settings['impact_field'])] = issue.impact
        if issue.priority:
            args['priority'] = {'name': issue.priority}
        return args

    def _render_template(self, config_name, issue):
        raw = self.issue_settings[config_name]
        template = Template(raw)
        return template.render(Context({'issue': issue}))

    @reraise_exceptions
    def create_issue(self, issue):
        issue.description = self._render_template('description', issue)
        backend_issue = self.manager.create_issue(**self._issue_to_dict(issue))
        if issue.assignee:
            self.manager.assign_issue(backend_issue.key, issue.assignee.backend_id)
        issue.key = backend_issue.key
        issue.backend_id = backend_issue.key
        issue.resolution = backend_issue.fields.resolution or ''
        issue.status = backend_issue.fields.status.name or ''
        issue.link = backend_issue.permalink()
        issue.priority = backend_issue.fields.priority.name
        issue.first_response_sla = self._get_first_sla_field(backend_issue)
        issue.save()
        return backend_issue

    def _get_first_sla_field(self, backend_issue):
        field_name = self._get_field_id_by_name(self.issue_settings['sla_field'])
        value = getattr(backend_issue.fields, field_name, None)
        if value:
            epoch_milliseconds = value.ongoingCycle.breachTime.epochMillis
            if epoch_milliseconds:
                return datetime.fromtimestamp(epoch_milliseconds / 1000.0)

    @reraise_exceptions
    def update_issue(self, issue):
        backend_issue = self.manager.issue(issue.backend_id)
        backend_issue.update(summary=issue.summary, description=issue.description)

    @reraise_exceptions
    def delete_issue(self, issue):
        backend_issue = self.manager.issue(issue.backend_id)
        backend_issue.delete()

    def _prepare_comment_message(self, comment):
        return comment.description

    @reraise_exceptions
    def create_comment(self, comment):
        backend_comment = self.manager.add_comment(comment.issue.backend_id, self._prepare_comment_message(comment))
        comment.backend_id = backend_comment.id
        comment.save(update_fields=['backend_id'])

    @reraise_exceptions
    def update_comment(self, comment):
        backend_comment = self.manager.comment(comment.issue.backend_id, comment.backend_id)
        backend_comment.update(body=self._prepare_comment_message(comment))

    @reraise_exceptions
    def delete_comment(self, comment):
        backend_comment = self.manager.comment(comment.issue.backend_id, comment.backend_id)
        backend_comment.delete()

    @reraise_exceptions
    def get_users(self):
        users = self.manager.search_assignable_users_for_projects('', self.project_settings['key'], maxResults=False)
        return [models.SupportUser(name=user.displayName, backend_id=user.key) for user in users]

class ServiceDeskBackend(JiraBackend):
    servicedeskapi_path = 'servicedeskapi'

    @reraise_exceptions
    def create_comment(self, comment):
        backend_comment = self._add_comment(
            comment.issue.backend_id,
            self._prepare_comment_message(comment),
            is_internal=comment.is_public,
        )
        comment.backend_id = backend_comment.id
        comment.save(update_fields=['backend_id'])

    def _add_comment(self, issue, body, is_internal):
        data = {
            'body': body,
            'properties': [{'key': 'sd.public.comment', 'value': {'internal': is_internal}}, ]
        }

        url = self.manager._get_url('issue/{0}/comment'.format(issue))
        response = self.manager._session.post(url, data=json.dumps(data))

        comment = Comment(self._options, self._session, raw=json_loads(response))
        return comment

    @reraise_exceptions
    def create_issue(self, issue):
        if not issue.caller.email:
            return

        # customer will be associated with the issue by updating issue arguments in _issue_to_dict.
        self._create_customer(issue.caller.email, issue.caller.full_name)
        super(ServiceDeskBackend, self).create_issue(issue)

    def _issue_to_dict(self, issue):
        args = super(ServiceDeskBackend, self)._issue_to_dict(issue)
        args[self._get_field_id_by_name(self.issue_settings['caller_field'])] = [{
            "name": issue.caller.email,
            "key": issue.caller.email
        }]
        return args

    def _create_customer(self, email, full_name):
        """
        Creates customer in Jira Service Desk without assigning it to any particular service desk.
        :param email: customer email
        :param full_name: customer full name
        :return: True if customer is created. False if user exists already.
        """
        data = {
            "fullName": full_name,
            "email": email
        }

        headers = {
            'X-ExperimentalApi': 'true',
        }

        url = "{host}rest/{path}/customer".format(host=self.credentials['server'], path=self.servicedeskapi_path)
        try:
            self.manager._session.post(url, data=json.dumps(data), headers=headers)
        except JIRAError as e:
            # TODO [TM:1/11/17] replace it with api call when such an ability is provided
            if e.status_code == 400 and "already exists" in e.text:
                return False
            else:
                raise e
        else:
            return True
<commit_msg>Save issue summary before creating JIRA ticket [WAL-455]<commit_after>from __future__ import unicode_literals

from datetime import datetime
import functools
import json

from django.conf import settings
from django.template import Context, Template
from django.utils import six
from jira import JIRA, JIRAError, Comment
from jira.utils import json_loads

from nodeconductor_assembly_waldur.support import models
from nodeconductor_assembly_waldur.support.backend import SupportBackendError, SupportBackend

class JiraBackendError(SupportBackendError):
    pass

def reraise_exceptions(func):
    @functools.wraps(func)
    def wrapped(self, *args, **kwargs):
        try:
            return func(self, *args, **kwargs)
        except JIRAError as e:
            six.reraise(JiraBackendError, e)

    return wrapped

class JiraBackend(SupportBackend):
    credentials = settings.WALDUR_SUPPORT.get('CREDENTIALS', {})
    project_settings = settings.WALDUR_SUPPORT.get('PROJECT', {})
    issue_settings = settings.WALDUR_SUPPORT.get('ISSUE', {})

    @property
    @reraise_exceptions
    def manager(self):
        # manager will be the same for all issues - we can cache it on the class level.
        if not hasattr(self.__class__, '_manager'):
            self.__class__._manager = JIRA(
                server=self.credentials['server'],
                options={'verify': self.credentials['verify_ssl']},
                basic_auth=(self.credentials['username'], self.credentials['password']),
                validate=False)
        return self.__class__._manager

    @reraise_exceptions
    def _get_field_id_by_name(self, field_name):
        if not hasattr(self.__class__, '_fields'):
            self.__class__._fields = self.manager.fields()
        try:
            return next(f['id'] for f in self.__class__._fields if field_name in f['clauseNames'])
        except StopIteration:
            return JiraBackendError('Field "{0}" does not exist in JIRA.'.format(field_name))

    def _issue_to_dict(self, issue):
        """ Convert issue to dict that can be accepted by JIRA as input parameters """
        caller = issue.caller.full_name or issue.caller.username
        args = {
            'project': self.project_settings['key'],
            'summary': issue.summary,
            'description': issue.description,
            'issuetype': {'name': issue.type},
            self._get_field_id_by_name(self.issue_settings['caller_field']): caller,
        }

        if issue.reporter:
            args[self._get_field_id_by_name(self.issue_settings['reporter_field'])] = issue.reporter.name
        if issue.impact:
            args[self._get_field_id_by_name(self.issue_settings['impact_field'])] = issue.impact
        if issue.priority:
            args['priority'] = {'name': issue.priority}
        return args

    def _render_template(self, config_name, issue):
        raw = self.issue_settings[config_name]
        template = Template(raw)
        return template.render(Context({'issue': issue}))

    @reraise_exceptions
    def create_issue(self, issue):
        issue.description = self._render_template('description', issue)
        issue.summary = self._render_template('summary', issue)
        backend_issue = self.manager.create_issue(**self._issue_to_dict(issue))
        if issue.assignee:
            self.manager.assign_issue(backend_issue.key, issue.assignee.backend_id)
        issue.key = backend_issue.key
        issue.backend_id = backend_issue.key
        issue.resolution = backend_issue.fields.resolution or ''
        issue.status = backend_issue.fields.status.name or ''
        issue.link = backend_issue.permalink()
        issue.priority = backend_issue.fields.priority.name
        issue.first_response_sla = self._get_first_sla_field(backend_issue)
        issue.save()
        return backend_issue

    def _get_first_sla_field(self, backend_issue):
        field_name = self._get_field_id_by_name(self.issue_settings['sla_field'])
        value = getattr(backend_issue.fields, field_name, None)
        if value:
            epoch_milliseconds = value.ongoingCycle.breachTime.epochMillis
            if epoch_milliseconds:
                return datetime.fromtimestamp(epoch_milliseconds / 1000.0)

    @reraise_exceptions
    def update_issue(self, issue):
        backend_issue = self.manager.issue(issue.backend_id)
        backend_issue.update(summary=issue.summary, description=issue.description)

    @reraise_exceptions
    def delete_issue(self, issue):
        backend_issue = self.manager.issue(issue.backend_id)
        backend_issue.delete()

    def _prepare_comment_message(self, comment):
        return comment.description

    @reraise_exceptions
    def create_comment(self, comment):
        backend_comment = self.manager.add_comment(comment.issue.backend_id, self._prepare_comment_message(comment))
        comment.backend_id = backend_comment.id
        comment.save(update_fields=['backend_id'])

    @reraise_exceptions
    def update_comment(self, comment):
        backend_comment = self.manager.comment(comment.issue.backend_id, comment.backend_id)
        backend_comment.update(body=self._prepare_comment_message(comment))

    @reraise_exceptions
    def delete_comment(self, comment):
        backend_comment = self.manager.comment(comment.issue.backend_id, comment.backend_id)
        backend_comment.delete()

    @reraise_exceptions
    def get_users(self):
        users = self.manager.search_assignable_users_for_projects('', self.project_settings['key'], maxResults=False)
        return [models.SupportUser(name=user.displayName, backend_id=user.key) for user in users]

class ServiceDeskBackend(JiraBackend):
    servicedeskapi_path = 'servicedeskapi'

    @reraise_exceptions
    def create_comment(self, comment):
        backend_comment = self._add_comment(
            comment.issue.backend_id,
            self._prepare_comment_message(comment),
            is_internal=comment.is_public,
        )
        comment.backend_id = backend_comment.id
        comment.save(update_fields=['backend_id'])

    def _add_comment(self, issue, body, is_internal):
        data = {
            'body': body,
            'properties': [{'key': 'sd.public.comment', 'value': {'internal': is_internal}}, ]
        }

        url = self.manager._get_url('issue/{0}/comment'.format(issue))
        response = self.manager._session.post(url, data=json.dumps(data))

        comment = Comment(self._options, self._session, raw=json_loads(response))
        return comment

    @reraise_exceptions
    def create_issue(self, issue):
        if not issue.caller.email:
            return

        # customer will be associated with the issue by updating issue arguments in _issue_to_dict.
        self._create_customer(issue.caller.email, issue.caller.full_name)
        super(ServiceDeskBackend, self).create_issue(issue)

    def _issue_to_dict(self, issue):
        args = super(ServiceDeskBackend, self)._issue_to_dict(issue)
        args[self._get_field_id_by_name(self.issue_settings['caller_field'])] = [{
            "name": issue.caller.email,
            "key": issue.caller.email
        }]
        return args

    def _create_customer(self, email, full_name):
        """
        Creates customer in Jira Service Desk without assigning it to any particular service desk.
        :param email: customer email
        :param full_name: customer full name
        :return: True if customer is created. False if user exists already.
        """
        data = {
            "fullName": full_name,
            "email": email
        }

        headers = {
            'X-ExperimentalApi': 'true',
        }

        url = "{host}rest/{path}/customer".format(host=self.credentials['server'], path=self.servicedeskapi_path)
        try:
            self.manager._session.post(url, data=json.dumps(data), headers=headers)
        except JIRAError as e:
            # TODO [TM:1/11/17] replace it with api call when such an ability is provided
            if e.status_code == 400 and "already exists" in e.text:
                return False
            else:
                raise e
        else:
            return True
<|endoftext|>


<commit_before>"""fight URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.9/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^students/', include('hopes.student_urls')),
    url(r'^schools/', include('hopes.school_urls')),
    url(r'^one_time/', include('hopes.onetime_urls')),
]
<commit_msg>shrink space bar from 2 to 1 in school line. Add spec_date_time line<commit_after>"""fight URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.9/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^students/', include('hopes.student_urls')),
    url(r'^schools/',  include('hopes.school_urls')),
    url(r'^one_time/', include('hopes.onetime_urls')),
    url(r'^spec_date_time/', include('hopes.spec_date_time_urls')),
]
<|endoftext|>


<commit_before>import time

import requests
from requests.adapters import HTTPAdapter
import six
from six.moves.urllib.parse import urlencode

from .exceptions import StatusCakeError, StatusCakeAuthError, StatusCakeNotLinkedError, StatusCakeFieldMissingError, StatusCakeFieldError, StatusCakeResponseError

def to_comma_list(value):
    if isinstance(value, (list, tuple, set, frozenset)):
        value = ','.join(value)
    return value

class StatusCake(object):
    URL_LOCATIONS = "https://app.statuscake.com/API/Locations/json"
    URL_ALERT = "https://app.statuscake.com/API/Alerts/?TestID=%s"
    URL_ALL_GROUPS = "https://app.statuscake.com/API/ContactGroups/"
    URL_UPDATE_GROUP = "https://app.statuscake.com/API/ContactGroups/Update/"
    URL_DELETE_GROUP = URL_UPDATE_GROUP + "?ContactID=%s"
    URL_ALL_TESTS = "https://app.statuscake.com/API/Tests/"
    URL_DETAILS_TEST = "https://app.statuscake.com/API/Tests/Details/?TestID=%s"
    URL_UPDATE_TEST = "https://app.statuscake.com/API/Tests/Update"
    URL_PERIODS = "https://app.statuscake.com/API/Tests/Periods/?TestID=%s"
    URL_CHECKS = "https://app.statuscake.com/API/Tests/Checks/?TestID=%s"
    URL_SSL = "https://app.statuscake.com/API/SSL/"
    URL_UPDATE_SSL = "https://app.statuscake.com/API/SSL/Update"
    URL_PAGE_SPEED = "https://app.statuscake.com/API/Pagespeed"

    CONTACT_GROUP_FIELDS = {
        'GroupName': (six.string_types, None, None),
        'DesktopAlert': (int, (0, 1), None),
        'Email': (six.string_types, None, to_comma_list),
        'Boxcat': (six.string_types, None, None),
        'Pushover': (six.string_types, None, None),
        'PingURL': (six.string_types, None, None),
        'Mobile': (six.string_types, None, to_comma_list),
        'ContactID': (int, None, None),
    }

    SSL_FIELDS = {
        'domain': (six.string_types, None, None),
        'checkrate': (int, (300, 600, 2800, 3600, 86400, 2073600), None),
        'contact_groups': (six.string_types, None, to_comma_list),
        'alert_at': (six.string_types, None, None),
        'alert_expiray': (bool, None, None),
        'alert_reminder': (bool, None, None),
        'alert_broken': (bool, None, None)
    }

    TESTS_FIELDS = {
        'TestID': (int, None, None),
        'Paused': (int, (0, 1), None),
        'WebsiteName': (six.string_types, None, None),
        'WebsiteURL': (six.string_types, None, None),
        'Port': (int, None, None),
        'NodeLocations': (six.string_types, None, to_comma_list),
        'Timeout': (int, range(5, 101), None),
        'PingURL': (six.string_types, None, None),
        'Confirmation': (int, range(0, 11), None),
        'CheckRate': (int, range(0, 24001), None),
        'BasicUser': (six.string_types, None, None),
        'BasicPass': (six.string_types, None, None),
        'Public': (int, (0, 1), None),
        'LogoImage': (six.string_types, None, None),
        'Branding': (int, (0, 1), None),
        'WebsiteHost': (six.string_types, None, None),
        'Virus': (int, (0, 1), None),
        'FindString': (six.string_types, None, None),
        'DoNotFind': (int, (0, 1), None),
        'TestType': (six.string_types, ("HTTP", "TCP", "PING"), None),
        'ContactGroup': (int, None, None),
        'RealBrowser': (int, (0, 1), None),
        'TriggerRate': (int, range(0, 61), None),
        'TestTags': (six.string_types, None, to_comma_list),
        'FinalEndpoint': (six.string_types, None, None),
        'PostRaw': (six.string_types, None, None),
        'EnableSSLAlert': (int, (0, 1), None)
    }

    def __init__(self, api_key, api_user, timeout=10):
        self._api_key = api_key
        self._api_user = api_user
        self.timeout = 10

        self.session = requests.Session()
        self.session.mount('https://www.statuscake.com', HTTPAdapter(max_retries=5))

    def _request(self, method, url, data=None, auth_headers=True, check_errors=True, **kwargs):
        headers = {}
        if auth_headers:
            headers.update({
                'API': self._api_key,
                'Username': self._api_user,
            })

        if isinstance(data, dict):
            data = urlencode(data)

        kwargs.setdefault('timeout', self.timeout)
        print_json = kwargs.pop('print_json', False)
        print_raw = kwargs.pop('print_raw', False)
        response = getattr(self.session, method)(url, headers=headers, data=data, **kwargs)
        if print_raw:
            print(response.text)
        if print_json:
            print(response.json())
        if check_errors:
            json_resp = response.json()
            if isinstance(json_resp, dict) and (json_resp.get('Success', True) is False or json_resp.get('Error', None) is not None):
                errno = json_resp.get('ErrNo', -1)
                error_message = json_resp.get('Error')
                if not error_message:
                    error_message = json_resp.get('Message')
                if errno == 0:
                    raise StatusCakeAuthError(error_message or 'Authentication Failed')
                elif errno == 1:
                    raise StatusCakeNotLinkedError(error_message or 'Authentication Failed')
                raise StatusCakeResponseError(error_message or 'API Call Failed')
        return response

    def _check_fields(self, data, check_map):
        for field_name, (field_type, field_values, field_conv) in six.iteritems(check_map):
            if field_name not in data:
                continue
            if field_conv:
                try:
                    data[field_name] = field_conv(data[field_name])
                except TypeError as exc:
                    raise StatusCakeFieldError("Field %s: %s" % (field_name, str(exc)))
            if not isinstance(data[field_name], field_type):
                raise StatusCakeFieldError("Field %s must be of type %s" % (field_name, field_type))
            if field_values is not None and data[field_name] not in field_values:
                raise StatusCakeFieldError("Field %s value %s does not match one of: %s" % (field_name, field_type, field_values))

    def get_node_locations(self, **kwargs):
        if hasattr(self, '_location_cache_timeout') and hasattr(self, '_location_cache'):
            if self._location_cache_timeout > time.time():
                return self._location_cache
        locations = self._request('get', self.URL_LOCATIONS, auth_headers=False, check_errors=False, **kwargs).json()
        self._location_cache_timeout = time.time() + 900
        self._location_cache = locations
        return locations

    def get_contact_groups(self, **kwargs):
        return self._request('get', self.URL_ALL_GROUPS, **kwargs).json()

    def insert_contact_group(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'GroupName' not in data:
            raise StatusCakeFieldMissingError("GroupName missing")
        self._check_fields(data, self.CONTACT_GROUP_FIELDS)
        return self._request('put', self.URL_UPDATE_GROUP, data=data, **kwargs).json()

    def update_contact_group(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'ContactID' not in data:
            raise StatusCakeFieldMissingError("ContactID missing")
        self._check_fields(data, self.CONTACT_GROUP_FIELDS)
        return self._request('put', self.URL_UPDATE_GROUP, data=data, **kwargs).json()

    def delete_contact_group(self, contact_id, **kwargs):
        return self._request('delete', self.URL_DELETE_GROUP % contact_id, **kwargs).json()

    def get_alert(self, test_id, **kwargs):
        return self._request('get', self.URL_ALERT % test_id, **kwargs).json()

    def get_all_tests(self, **kwargs):
        return self._request('get', self.URL_ALL_TESTS, **kwargs).json()

    def get_all_ssl(self, **kwargs):
        return self._request('get', self.URL_SSL, **kwargs).json()

    def get_details_test(self, test_id, **kwargs):
        return self._request('get', self.URL_DETAILS_TEST % test_id, **kwargs).json()

    def get_periods(self, test_id, **kwargs):
        return self._request('get', self.URL_PERIODS % test_id, **kwargs).json()

    def get_checks(self, test_id, **kwargs):
        # pass optional parameters like params = {'Fields': 'location,performance'}
        return self._request('get', self.URL_CHECKS % test_id, **kwargs).json()

    def delete_test(self, test_id, **kwargs):
        return self._request('delete', self.URL_DETAILS_TEST % test_id, **kwargs).json()

    def insert_ssl(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'domain' not in data:
            raise StatusCakeFieldMissingError("domain missing")
        if 'checkrate' not in data:
            data['checkrate'] = 3600
        if 'contact_groups' not in data:
            raise StatusCakeFieldMissingError("contact_groups missing")
        if 'alert_at' not in data:
            data['alert_at'] = '1,7,30'
        if 'alert_expiry' not in data:
            data['alert_expiry'] = True
        if 'alert_reminder' not in data:
            data['alert_reminder'] = True
        if 'alert_broken' not in data:
            data['alert_broken'] = True
        self._check_fields(data, self.SSL_FIELDS)
        return self._request('put', self.URL_UPDATE_SSL, data=data, **kwargs).json()

    def update_ssl(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'id' not in data:
            raise StatusCakeFieldMissingError("id missing")
        self._check_fields(data, self.SSL_FIELDS)
        return self._request('put', self.URL_UPDATE_SSL, data=data, **kwargs).json()

    def delete_ssl(self, id, **kwargs):
        return self._request('delete', self.URL_UPDATE_SSL, data={'id': id}, **kwargs).json()

    def insert_test(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'WebsiteName' not in data:
            raise StatusCakeFieldMissingError("WebsiteName missing")
        if 'WebsiteURL' not in data:
            raise StatusCakeFieldMissingError("WebsiteURL missing")
        if 'TestType' not in data:
            raise StatusCakeFieldMissingError("TestType missing")
        if 'CheckRate' not in data:
            # Use default
            data['CheckRate'] = 300
        self._check_fields(data, self.TESTS_FIELDS)
        return self._request('put', self.URL_UPDATE_TEST, data=data, **kwargs).json()

    def update_test(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'TestID' not in data:
            raise StatusCakeFieldMissingError("TestID missing")
        self._check_fields(data, self.TESTS_FIELDS)
        return self._request('put', self.URL_UPDATE_TEST, data=data, **kwargs).json()

    def get_page_speed(self, **kwargs):
        """ https://www.statuscake.com/api/Page%20Speed/List%20Pagespeed%20Test.md
        """
        return self._request('get', self.URL_PAGE_SPEED, **kwargs).json()

    node_locations = property(get_node_locations)
<commit_msg>Fix: ContactGroup expects csv string of ints. Refs https://github.com/trbs/statuscake/issues/20<commit_after>import time

import requests
from requests.adapters import HTTPAdapter
import six
from six.moves.urllib.parse import urlencode

from .exceptions import StatusCakeError, StatusCakeAuthError, StatusCakeNotLinkedError, StatusCakeFieldMissingError, StatusCakeFieldError, StatusCakeResponseError

def to_comma_list(value):
    if isinstance(value, (list, tuple, set, frozenset)):
        value = ','.join(value)
    return value

class StatusCake(object):
    URL_LOCATIONS = "https://app.statuscake.com/API/Locations/json"
    URL_ALERT = "https://app.statuscake.com/API/Alerts/?TestID=%s"
    URL_ALL_GROUPS = "https://app.statuscake.com/API/ContactGroups/"
    URL_UPDATE_GROUP = "https://app.statuscake.com/API/ContactGroups/Update/"
    URL_DELETE_GROUP = URL_UPDATE_GROUP + "?ContactID=%s"
    URL_ALL_TESTS = "https://app.statuscake.com/API/Tests/"
    URL_DETAILS_TEST = "https://app.statuscake.com/API/Tests/Details/?TestID=%s"
    URL_UPDATE_TEST = "https://app.statuscake.com/API/Tests/Update"
    URL_PERIODS = "https://app.statuscake.com/API/Tests/Periods/?TestID=%s"
    URL_CHECKS = "https://app.statuscake.com/API/Tests/Checks/?TestID=%s"
    URL_SSL = "https://app.statuscake.com/API/SSL/"
    URL_UPDATE_SSL = "https://app.statuscake.com/API/SSL/Update"
    URL_PAGE_SPEED = "https://app.statuscake.com/API/Pagespeed"

    CONTACT_GROUP_FIELDS = {
        'GroupName': (six.string_types, None, None),
        'DesktopAlert': (int, (0, 1), None),
        'Email': (six.string_types, None, to_comma_list),
        'Boxcat': (six.string_types, None, None),
        'Pushover': (six.string_types, None, None),
        'PingURL': (six.string_types, None, None),
        'Mobile': (six.string_types, None, to_comma_list),
        'ContactID': (int, None, None),
    }

    SSL_FIELDS = {
        'domain': (six.string_types, None, None),
        'checkrate': (int, (300, 600, 2800, 3600, 86400, 2073600), None),
        'contact_groups': (six.string_types, None, to_comma_list),
        'alert_at': (six.string_types, None, None),
        'alert_expiray': (bool, None, None),
        'alert_reminder': (bool, None, None),
        'alert_broken': (bool, None, None)
    }

    TESTS_FIELDS = {
        'TestID': (int, None, None),
        'Paused': (int, (0, 1), None),
        'WebsiteName': (six.string_types, None, None),
        'WebsiteURL': (six.string_types, None, None),
        'Port': (int, None, None),
        'NodeLocations': (six.string_types, None, to_comma_list),
        'Timeout': (int, range(5, 101), None),
        'PingURL': (six.string_types, None, None),
        'Confirmation': (int, range(0, 11), None),
        'CheckRate': (int, range(0, 24001), None),
        'BasicUser': (six.string_types, None, None),
        'BasicPass': (six.string_types, None, None),
        'Public': (int, (0, 1), None),
        'LogoImage': (six.string_types, None, None),
        'Branding': (int, (0, 1), None),
        'WebsiteHost': (six.string_types, None, None),
        'Virus': (int, (0, 1), None),
        'FindString': (six.string_types, None, None),
        'DoNotFind': (int, (0, 1), None),
        'TestType': (six.string_types, ("HTTP", "TCP", "PING"), None),
        'ContactGroup': (six.string_types, None, None),
        'RealBrowser': (int, (0, 1), None),
        'TriggerRate': (int, range(0, 61), None),
        'TestTags': (six.string_types, None, to_comma_list),
        'FinalEndpoint': (six.string_types, None, None),
        'PostRaw': (six.string_types, None, None),
        'EnableSSLAlert': (int, (0, 1), None)
    }

    def __init__(self, api_key, api_user, timeout=10):
        self._api_key = api_key
        self._api_user = api_user
        self.timeout = 10

        self.session = requests.Session()
        self.session.mount('https://www.statuscake.com', HTTPAdapter(max_retries=5))

    def _request(self, method, url, data=None, auth_headers=True, check_errors=True, **kwargs):
        headers = {}
        if auth_headers:
            headers.update({
                'API': self._api_key,
                'Username': self._api_user,
            })

        if isinstance(data, dict):
            data = urlencode(data)

        kwargs.setdefault('timeout', self.timeout)
        print_json = kwargs.pop('print_json', False)
        print_raw = kwargs.pop('print_raw', False)
        response = getattr(self.session, method)(url, headers=headers, data=data, **kwargs)
        if print_raw:
            print(response.text)
        if print_json:
            print(response.json())
        if check_errors:
            json_resp = response.json()
            if isinstance(json_resp, dict) and (json_resp.get('Success', True) is False or json_resp.get('Error', None) is not None):
                errno = json_resp.get('ErrNo', -1)
                error_message = json_resp.get('Error')
                if not error_message:
                    error_message = json_resp.get('Message')
                if errno == 0:
                    raise StatusCakeAuthError(error_message or 'Authentication Failed')
                elif errno == 1:
                    raise StatusCakeNotLinkedError(error_message or 'Authentication Failed')
                raise StatusCakeResponseError(error_message or 'API Call Failed')
        return response

    def _check_fields(self, data, check_map):
        for field_name, (field_type, field_values, field_conv) in six.iteritems(check_map):
            if field_name not in data:
                continue
            if field_conv:
                try:
                    data[field_name] = field_conv(data[field_name])
                except TypeError as exc:
                    raise StatusCakeFieldError("Field %s: %s" % (field_name, str(exc)))
            if not isinstance(data[field_name], field_type):
                raise StatusCakeFieldError("Field %s must be of type %s" % (field_name, field_type))
            if field_values is not None and data[field_name] not in field_values:
                raise StatusCakeFieldError("Field %s value %s does not match one of: %s" % (field_name, field_type, field_values))

    def get_node_locations(self, **kwargs):
        if hasattr(self, '_location_cache_timeout') and hasattr(self, '_location_cache'):
            if self._location_cache_timeout > time.time():
                return self._location_cache
        locations = self._request('get', self.URL_LOCATIONS, auth_headers=False, check_errors=False, **kwargs).json()
        self._location_cache_timeout = time.time() + 900
        self._location_cache = locations
        return locations

    def get_contact_groups(self, **kwargs):
        return self._request('get', self.URL_ALL_GROUPS, **kwargs).json()

    def insert_contact_group(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'GroupName' not in data:
            raise StatusCakeFieldMissingError("GroupName missing")
        self._check_fields(data, self.CONTACT_GROUP_FIELDS)
        return self._request('put', self.URL_UPDATE_GROUP, data=data, **kwargs).json()

    def update_contact_group(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'ContactID' not in data:
            raise StatusCakeFieldMissingError("ContactID missing")
        self._check_fields(data, self.CONTACT_GROUP_FIELDS)
        return self._request('put', self.URL_UPDATE_GROUP, data=data, **kwargs).json()

    def delete_contact_group(self, contact_id, **kwargs):
        return self._request('delete', self.URL_DELETE_GROUP % contact_id, **kwargs).json()

    def get_alert(self, test_id, **kwargs):
        return self._request('get', self.URL_ALERT % test_id, **kwargs).json()

    def get_all_tests(self, **kwargs):
        return self._request('get', self.URL_ALL_TESTS, **kwargs).json()

    def get_all_ssl(self, **kwargs):
        return self._request('get', self.URL_SSL, **kwargs).json()

    def get_details_test(self, test_id, **kwargs):
        return self._request('get', self.URL_DETAILS_TEST % test_id, **kwargs).json()

    def get_periods(self, test_id, **kwargs):
        return self._request('get', self.URL_PERIODS % test_id, **kwargs).json()

    def get_checks(self, test_id, **kwargs):
        # pass optional parameters like params = {'Fields': 'location,performance'}
        return self._request('get', self.URL_CHECKS % test_id, **kwargs).json()

    def delete_test(self, test_id, **kwargs):
        return self._request('delete', self.URL_DETAILS_TEST % test_id, **kwargs).json()

    def insert_ssl(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'domain' not in data:
            raise StatusCakeFieldMissingError("domain missing")
        if 'checkrate' not in data:
            data['checkrate'] = 3600
        if 'contact_groups' not in data:
            raise StatusCakeFieldMissingError("contact_groups missing")
        if 'alert_at' not in data:
            data['alert_at'] = '1,7,30'
        if 'alert_expiry' not in data:
            data['alert_expiry'] = True
        if 'alert_reminder' not in data:
            data['alert_reminder'] = True
        if 'alert_broken' not in data:
            data['alert_broken'] = True
        self._check_fields(data, self.SSL_FIELDS)
        return self._request('put', self.URL_UPDATE_SSL, data=data, **kwargs).json()

    def update_ssl(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'id' not in data:
            raise StatusCakeFieldMissingError("id missing")
        self._check_fields(data, self.SSL_FIELDS)
        return self._request('put', self.URL_UPDATE_SSL, data=data, **kwargs).json()

    def delete_ssl(self, id, **kwargs):
        return self._request('delete', self.URL_UPDATE_SSL, data={'id': id}, **kwargs).json()

    def insert_test(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'WebsiteName' not in data:
            raise StatusCakeFieldMissingError("WebsiteName missing")
        if 'WebsiteURL' not in data:
            raise StatusCakeFieldMissingError("WebsiteURL missing")
        if 'TestType' not in data:
            raise StatusCakeFieldMissingError("TestType missing")
        if 'CheckRate' not in data:
            # Use default
            data['CheckRate'] = 300
        self._check_fields(data, self.TESTS_FIELDS)
        return self._request('put', self.URL_UPDATE_TEST, data=data, **kwargs).json()

    def update_test(self, data, **kwargs):
        if not isinstance(data, dict):
            raise StatusCakeError("data argument must be a dict")
        if 'TestID' not in data:
            raise StatusCakeFieldMissingError("TestID missing")
        self._check_fields(data, self.TESTS_FIELDS)
        return self._request('put', self.URL_UPDATE_TEST, data=data, **kwargs).json()

    def get_page_speed(self, **kwargs):
        """ https://www.statuscake.com/api/Page%20Speed/List%20Pagespeed%20Test.md
        """
        return self._request('get', self.URL_PAGE_SPEED, **kwargs).json()

    node_locations = property(get_node_locations)
<|endoftext|>