<commit_before>"""Support for Fibaro lights."""
from __future__ import annotations

import asyncio
from contextlib import suppress
from functools import partial

from homeassistant.components.light import (
    ATTR_BRIGHTNESS,
    ATTR_HS_COLOR,
    ATTR_WHITE_VALUE,
    ENTITY_ID_FORMAT,
    SUPPORT_BRIGHTNESS,
    SUPPORT_COLOR,
    SUPPORT_WHITE_VALUE,
    LightEntity,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
import homeassistant.util.color as color_util

from . import FIBARO_DEVICES, FibaroDevice
from .const import DOMAIN

def scaleto255(value):
    """Scale the input value from 0-100 to 0-255."""
    # Fibaro has a funny way of storing brightness either 0-100 or 0-99
    # depending on device type (e.g. dimmer vs led)
    if value > 98:
        value = 100
    return max(0, min(255, ((value * 255.0) / 100.0)))

def scaleto100(value):
    """Scale the input value from 0-255 to 0-100."""
    # Make sure a low but non-zero value is not rounded down to zero
    if 0 < value < 3:
        return 1
    return max(0, min(100, ((value * 100.0) / 255.0)))

async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Perform the setup for Fibaro controller devices."""
    async_add_entities(
        [
            FibaroLight(device)
            for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES]["light"]
        ],
        True,
    )

class FibaroLight(FibaroDevice, LightEntity):
    """Representation of a Fibaro Light, including dimmable."""

    def __init__(self, fibaro_device):
        """Initialize the light."""
        self._brightness = None
        self._color = (0, 0)
        self._last_brightness = 0
        self._supported_flags = 0
        self._update_lock = asyncio.Lock()
        self._white = 0

        self._reset_color = False
        supports_color = (
            "color" in fibaro_device.properties
            or "colorComponents" in fibaro_device.properties
            or "RGB" in fibaro_device.type
            or "rgb" in fibaro_device.type
            or "color" in fibaro_device.baseType
        ) and (
            "setColor" in fibaro_device.actions
            or "setColorComponents" in fibaro_device.actions
        )
        supports_white_v = (
            "setW" in fibaro_device.actions
            or "RGBW" in fibaro_device.type
            or "rgbw" in fibaro_device.type
        )
        supports_dimming = (
            "levelChange" in fibaro_device.interfaces
            or supports_color
            or supports_white_v
        )

        # Configuration can override default capability detection
        if supports_dimming:
            self._supported_flags |= SUPPORT_BRIGHTNESS
        if supports_color:
            self._supported_flags |= SUPPORT_COLOR
        if supports_white_v:
            self._supported_flags |= SUPPORT_WHITE_VALUE

        super().__init__(fibaro_device)
        self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)

    @property
    def brightness(self):
        """Return the brightness of the light."""
        return scaleto255(self._brightness)

    @property
    def hs_color(self):
        """Return the color of the light."""
        return self._color

    @property
    def white_value(self):
        """Return the white value of this light between 0..255."""
        return self._white

    @property
    def supported_features(self):
        """Flag supported features."""
        return self._supported_flags

    async def async_turn_on(self, **kwargs):
        """Turn the light on."""
        async with self._update_lock:
            await self.hass.async_add_executor_job(partial(self._turn_on, **kwargs))

    def _turn_on(self, **kwargs):
        """Really turn the light on."""
        if self._supported_flags & SUPPORT_BRIGHTNESS:
            target_brightness = kwargs.get(ATTR_BRIGHTNESS)

            # No brightness specified, so we either restore it to
            # last brightness or switch it on at maximum level
            if target_brightness is None:
                if self._brightness == 0:
                    if self._last_brightness:
                        self._brightness = self._last_brightness
                    else:
                        self._brightness = 100
            else:
                # We set it to the target brightness and turn it on
                self._brightness = scaleto100(target_brightness)

        if self._supported_flags & SUPPORT_COLOR and (
            kwargs.get(ATTR_WHITE_VALUE) is not None
            or kwargs.get(ATTR_HS_COLOR) is not None
        ):
            if self._reset_color:
                self._color = (100, 0)

            # Update based on parameters
            self._white = kwargs.get(ATTR_WHITE_VALUE, self._white)
            self._color = kwargs.get(ATTR_HS_COLOR, self._color)
            rgb = color_util.color_hs_to_RGB(*self._color)
            self.call_set_color(
                round(rgb[0]),
                round(rgb[1]),
                round(rgb[2]),
                round(self._white),
            )

            if self.state == "off":
                self.set_level(min(int(self._brightness), 99))
            return

        if self._reset_color:
            bri255 = scaleto255(self._brightness)
            self.call_set_color(bri255, bri255, bri255, bri255)

        if self._supported_flags & SUPPORT_BRIGHTNESS:
            self.set_level(min(int(self._brightness), 99))
            return

        # The simplest case is left for last. No dimming, just switch on
        self.call_turn_on()

    async def async_turn_off(self, **kwargs):
        """Turn the light off."""
        async with self._update_lock:
            await self.hass.async_add_executor_job(partial(self._turn_off, **kwargs))

    def _turn_off(self, **kwargs):
        """Really turn the light off."""
        # Let's save the last brightness level before we switch it off
        if (
            (self._supported_flags & SUPPORT_BRIGHTNESS)
            and self._brightness
            and self._brightness > 0
        ):
            self._last_brightness = self._brightness
        self._brightness = 0
        self.call_turn_off()

    @property
    def is_on(self) -> bool | None:
        """Return true if device is on.

        Dimmable and RGB lights can be on based on different
        properties, so we need to check here several values.

        JSON for HC2 uses always string, HC3 uses int for integers.
        """
        props = self.fibaro_device.properties
        if self.current_binary_state:
            return True
        with suppress(ValueError, TypeError):
            if "brightness" in props and int(props.brightness) != 0:
                return True
        with suppress(ValueError, TypeError):
            if "currentProgram" in props and int(props.currentProgram) != 0:
                return True
        with suppress(ValueError, TypeError):
            if "currentProgramID" in props and int(props.currentProgramID) != 0:
                return True

        return False

    async def async_update(self):
        """Update the state."""
        async with self._update_lock:
            await self.hass.async_add_executor_job(self._update)

    def _update(self):
        """Really update the state."""
        # Brightness handling
        if self._supported_flags & SUPPORT_BRIGHTNESS:
            self._brightness = float(self.fibaro_device.properties.value)
            # Fibaro might report 0-99 or 0-100 for brightness,
            # based on device type, so we round up here
            if self._brightness > 99:
                self._brightness = 100
        # Color handling
        if (
            self._supported_flags & SUPPORT_COLOR
            and "color" in self.fibaro_device.properties
            and "," in self.fibaro_device.properties.color
        ):
            # Fibaro communicates the color as an 'R, G, B, W' string
            rgbw_s = self.fibaro_device.properties.color
            if rgbw_s == "0,0,0,0" and "lastColorSet" in self.fibaro_device.properties:
                rgbw_s = self.fibaro_device.properties.lastColorSet
            rgbw_list = [int(i) for i in rgbw_s.split(",")][:4]
            if rgbw_list[0] or rgbw_list[1] or rgbw_list[2]:
                self._color = color_util.color_RGB_to_hs(*rgbw_list[:3])
            if (self._supported_flags & SUPPORT_WHITE_VALUE) and self.brightness != 0:
                self._white = min(255, max(0, rgbw_list[3]))
<commit_msg>Color mode support for fibaro light (#69189)<commit_after>"""Support for Fibaro lights."""
from __future__ import annotations

import asyncio
from contextlib import suppress
from functools import partial

from homeassistant.components.light import (
    ATTR_BRIGHTNESS,
    ATTR_RGB_COLOR,
    ATTR_RGBW_COLOR,
    ENTITY_ID_FORMAT,
    ColorMode,
    LightEntity,
    brightness_supported,
    color_supported,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from . import FIBARO_DEVICES, FibaroDevice
from .const import DOMAIN

def scaleto255(value: int | None) -> int:
    """Scale the input value from 0-100 to 0-255."""
    if value is None:
        return 0
    # Fibaro has a funny way of storing brightness either 0-100 or 0-99
    # depending on device type (e.g. dimmer vs led)
    if value > 98:
        value = 100
    return round(value * 2.55)

def scaleto99(value: int | None) -> int:
    """Scale the input value from 0-255 to 0-99."""
    if value is None:
        return 0
    # Make sure a low but non-zero value is not rounded down to zero
    if 0 < value < 3:
        return 1
    return min(round(value / 2.55), 99)

async def async_setup_entry(
    hass: HomeAssistant,
    entry: ConfigEntry,
    async_add_entities: AddEntitiesCallback,
) -> None:
    """Perform the setup for Fibaro controller devices."""
    async_add_entities(
        [
            FibaroLight(device)
            for device in hass.data[DOMAIN][entry.entry_id][FIBARO_DEVICES]["light"]
        ],
        True,
    )

class FibaroLight(FibaroDevice, LightEntity):
    """Representation of a Fibaro Light, including dimmable."""

    def __init__(self, fibaro_device):
        """Initialize the light."""
        self._update_lock = asyncio.Lock()

        supports_color = (
            "color" in fibaro_device.properties
            or "colorComponents" in fibaro_device.properties
            or "RGB" in fibaro_device.type
            or "rgb" in fibaro_device.type
            or "color" in fibaro_device.baseType
        ) and (
            "setColor" in fibaro_device.actions
            or "setColorComponents" in fibaro_device.actions
        )
        supports_white_v = (
            "setW" in fibaro_device.actions
            or "RGBW" in fibaro_device.type
            or "rgbw" in fibaro_device.type
        )
        supports_dimming = "levelChange" in fibaro_device.interfaces

        if supports_color and supports_white_v:
            self._attr_supported_color_modes = {ColorMode.RGBW}
            self._attr_color_mode = ColorMode.RGBW
        elif supports_color:
            self._attr_supported_color_modes = {ColorMode.RGB}
            self._attr_color_mode = ColorMode.RGB
        elif supports_dimming:
            self._attr_supported_color_modes = {ColorMode.BRIGHTNESS}
            self._attr_color_mode = ColorMode.BRIGHTNESS
        else:
            self._attr_supported_color_modes = {ColorMode.ONOFF}
            self._attr_color_mode = ColorMode.ONOFF

        super().__init__(fibaro_device)
        self.entity_id = ENTITY_ID_FORMAT.format(self.ha_id)

    async def async_turn_on(self, **kwargs):
        """Turn the light on."""
        async with self._update_lock:
            await self.hass.async_add_executor_job(partial(self._turn_on, **kwargs))

    def _turn_on(self, **kwargs):
        """Really turn the light on."""
        if ATTR_BRIGHTNESS in kwargs:
            self._attr_brightness = kwargs[ATTR_BRIGHTNESS]
            self.set_level(scaleto99(self._attr_brightness))

        if ATTR_RGB_COLOR in kwargs:
            # Update based on parameters
            self._attr_rgb_color = kwargs[ATTR_RGB_COLOR]
            self.call_set_color(*self._attr_rgb_color, 0)
            return

        if ATTR_RGBW_COLOR in kwargs:
            # Update based on parameters
            self._attr_rgbw_color = kwargs[ATTR_RGBW_COLOR]
            self.call_set_color(*self._attr_rgbw_color)
            return

        # The simplest case is left for last. No dimming, just switch on
        self.call_turn_on()

    async def async_turn_off(self, **kwargs):
        """Turn the light off."""
        async with self._update_lock:
            await self.hass.async_add_executor_job(partial(self._turn_off, **kwargs))

    def _turn_off(self, **kwargs):
        """Really turn the light off."""
        self.call_turn_off()

    @property
    def is_on(self) -> bool | None:
        """Return true if device is on.

        Dimmable and RGB lights can be on based on different
        properties, so we need to check here several values.

        JSON for HC2 uses always string, HC3 uses int for integers.
        """
        props = self.fibaro_device.properties
        if self.current_binary_state:
            return True
        with suppress(ValueError, TypeError):
            if "brightness" in props and int(props.brightness) != 0:
                return True
        with suppress(ValueError, TypeError):
            if "currentProgram" in props and int(props.currentProgram) != 0:
                return True
        with suppress(ValueError, TypeError):
            if "currentProgramID" in props and int(props.currentProgramID) != 0:
                return True

        return False

    async def async_update(self):
        """Update the state."""
        async with self._update_lock:
            await self.hass.async_add_executor_job(self._update)

    def _update(self):
        """Really update the state."""
        # Brightness handling
        if brightness_supported(self.supported_color_modes):
            self._attr_brightness = scaleto255(int(self.fibaro_device.properties.value))

        # Color handling
        if (
            color_supported(self.supported_color_modes)
            and "color" in self.fibaro_device.properties
            and "," in self.fibaro_device.properties.color
        ):
            # Fibaro communicates the color as an 'R, G, B, W' string
            rgbw_s = self.fibaro_device.properties.color
            if rgbw_s == "0,0,0,0" and "lastColorSet" in self.fibaro_device.properties:
                rgbw_s = self.fibaro_device.properties.lastColorSet
            rgbw_list = [int(i) for i in rgbw_s.split(",")][:4]

            if self._attr_color_mode == ColorMode.RGB:
                self._attr_rgb_color = tuple(*rgbw_list[:3])
            else:
                self._attr_rgbw_color = tuple(rgbw_list)
<|endoftext|>


<commit_before>import logging
import requests
from tqdm import tqdm

from django.db import transaction
from django.core.management.base import BaseCommand

from reference_data.models import HumanPhenotypeOntology

logger = logging.getLogger(__name__)

def parse_obo_file(file_iterator):
    """
    Parse an .obo file which contains a record for each term in the Human Phenotype Ontology

    Args:
        file_iterator: Iterator over lines in the hp.obo file
    Returns:
        dictionary that maps HPO id strings to a record containing
    """

    hpo_id_to_record = {}
    for line in tqdm(file_iterator, unit=" lines"):
        line = line.strip("\n")
        if line.startswith("id: "):
            hpo_id = line.split("id: ")[1]
            hpo_id_to_record[hpo_id] = {
                'hpo_id': hpo_id,
                'is_category': False,
                #'definition': None,
                #'comment': None,
            }
        elif line.startswith("is_a: "):
            is_a = line.split(" ")[1]
            if is_a == "HP:0000118":
                hpo_id_to_record[hpo_id]['is_category'] = True

            hpo_id_to_record[hpo_id]['parent_id'] = is_a
        elif line.startswith("name: "):
            name = line.split(" ")[1]
            hpo_id_to_record[hpo_id]['name'] = name
        elif line.startswith("def: "):
            definition = line.split(" ")[1]
            hpo_id_to_record[hpo_id]['definition'] = definition
        elif line.startswith("comment: "):
            comment = line.split(" ")[1]
            hpo_id_to_record[hpo_id]['comment'] = comment

    return hpo_id_to_record

def get_category_id(hpo_id_to_record, hpo_id):
    """For a given hpo_id, get the hpo id of it's top-level category (eg. 'cardiovascular') and
    return it. If the hpo_id belongs to multiple top-level categories, return one of them.
    """

    if hpo_id == "HP:0000001":
        return None

    if 'parent_id' not in hpo_id_to_record[hpo_id]:
        return None

    while hpo_id_to_record[hpo_id]['parent_id'] != "HP:0000118":

        hpo_id = hpo_id_to_record[hpo_id]['parent_id']
        if hpo_id == "HP:0000001":
            return None
        if hpo_id not in hpo_id_to_record:
            raise ValueError("Strange id: %s" % hpo_id)

    return hpo_id

class Command(BaseCommand):
    """Command to download the latest hp.obo release and update the HumanPhenotypeOntology table."""

    def add_arguments(self, parser):
        parser.add_argument('args', nargs='*')

    def handle(self, *args, **options):

        url = 'http://purl.obolibrary.org/obo/hp.obo'
        print("Parsing %s" % url)
        response = requests.get(url)
        obo_file_iterator = response.content.split("\n")

        hpo_id_to_record = parse_obo_file(obo_file_iterator)

        # for each hpo id, find its top level category
        for hpo_id in hpo_id_to_record.keys():
            hpo_id_to_record[hpo_id]['category_id'] = get_category_id(hpo_id_to_record, hpo_id)

        # save to database
        records_in_category = [
            record for record in hpo_id_to_record.values() if record['category_id'] is not None
        ]

        logger.info("Deleting table with %s records and creating new table with %s records" % (
            HumanPhenotypeOntology.objects.all().count(),
            len(records_in_category))
        )

        with transaction.atomic():
            HumanPhenotypeOntology.objects.all().delete()

            HumanPhenotypeOntology.objects.bulk_create(
                HumanPhenotypeOntology(**record) for record in tqdm(records_in_category, unit=" records"),
            )

        logger.info("Done")<commit_msg>fixed parsing of names<commit_after>import logging
import requests
from tqdm import tqdm

from django.db import transaction
from django.core.management.base import BaseCommand

from reference_data.models import HumanPhenotypeOntology

logger = logging.getLogger(__name__)

def parse_obo_file(file_iterator):
    """
    Parse an .obo file which contains a record for each term in the Human Phenotype Ontology

    Args:
        file_iterator: Iterator over lines in the hp.obo file
    Returns:
        dictionary that maps HPO id strings to a record containing
    """

    hpo_id_to_record = {}
    for line in tqdm(file_iterator, unit=" lines"):
        line = line.rstrip("\n")
        value = " ".join(line.split(" ")[1:])
        if line.startswith("id: "):
            hpo_id = value
            hpo_id_to_record[hpo_id] = {
                'hpo_id': hpo_id,
                'is_category': False,
            }
        elif line.startswith("is_a: "):
            is_a = value.split(" ! ")[0]
            if is_a == "HP:0000118":
                hpo_id_to_record[hpo_id]['is_category'] = True
            hpo_id_to_record[hpo_id]['parent_id'] = is_a
        elif line.startswith("name: "):
            hpo_id_to_record[hpo_id]['name'] = value
        elif line.startswith("def: "):
            hpo_id_to_record[hpo_id]['definition'] = value
        elif line.startswith("comment: "):
            hpo_id_to_record[hpo_id]['comment'] = value

    return hpo_id_to_record

def get_category_id(hpo_id_to_record, hpo_id):
    """For a given hpo_id, get the hpo id of it's top-level category (eg. 'cardiovascular') and
    return it. If the hpo_id belongs to multiple top-level categories, return one of them.
    """

    if hpo_id == "HP:0000001":
        return None

    if 'parent_id' not in hpo_id_to_record[hpo_id]:
        return None

    while hpo_id_to_record[hpo_id]['parent_id'] != "HP:0000118":

        hpo_id = hpo_id_to_record[hpo_id]['parent_id']
        if hpo_id == "HP:0000001":
            return None
        if hpo_id not in hpo_id_to_record:
            raise ValueError("Strange id: %s" % hpo_id)

    return hpo_id

class Command(BaseCommand):
    """Command to download the latest hp.obo release and update the HumanPhenotypeOntology table."""

    def add_arguments(self, parser):
        parser.add_argument('args', nargs='*')

    def handle(self, *args, **options):

        url = 'http://purl.obolibrary.org/obo/hp.obo'
        print("Parsing %s" % url)
        response = requests.get(url)
        obo_file_iterator = response.content.split("\n")

        hpo_id_to_record = parse_obo_file(obo_file_iterator)

        # for each hpo id, find its top level category
        for hpo_id in hpo_id_to_record.keys():
            hpo_id_to_record[hpo_id]['category_id'] = get_category_id(hpo_id_to_record, hpo_id)

        # save to database
        records_in_category = [
            record for record in hpo_id_to_record.values() if record['category_id'] is not None
        ]

        logger.info("Deleting table with %s records and creating new table with %s records" % (
            HumanPhenotypeOntology.objects.all().count(),
            len(records_in_category))
        )

        with transaction.atomic():
            HumanPhenotypeOntology.objects.all().delete()

            HumanPhenotypeOntology.objects.bulk_create(
                HumanPhenotypeOntology(**record) for record in tqdm(records_in_category, unit=" records"),
            )

        logger.info("Done")
<|endoftext|>


<commit_before>
from pulse.vendor.Qt import QtCore, QtWidgets, QtGui
import pymel.core as pm
import pymetanode as meta

import pulse.names
from . import utils as viewutils

__all__ = [
    'ActionAttrForm',
    'BoolAttrForm',
    'DefaultAttrForm',
    'NodeAttrForm',
    'OptionAttrForm',
    'StringAttrForm',
]

class ActionAttrForm(QtWidgets.QWidget):
    """
    The base class for all forms used to edit action attributes.
    Provides input validation and basic signals for keeping
    track of value changes.
    """

    TYPEMAP = {}

    LABEL_WIDTH = 150
    LABEL_HEIGHT = 20

    # valueChanged(newValue, isValueValid)
    valueChanged = QtCore.Signal(object, bool)

    @staticmethod
    def createAttrForm(attr, attrValue, parent=None):
        """
        Create a new ActionAttrForm of the appropriate
        type based on a BuildAction attribute.

        Args:
            attr: A dict representing the config of a BuildAction attribute
            attrValue: The current value of the attribute
        """
        attrType = attr['type']
        if attrType in ActionAttrForm.TYPEMAP:
            return ActionAttrForm.TYPEMAP[attrType](attr, attrValue, parent=parent)
        # fallback to the default widget
        return DefaultAttrForm(attr, attrValue)

    def __init__(self, attr, attrValue, parent=None):
        super(ActionAttrForm, self).__init__(parent=parent)
        # the config data of the attribute being edited
        self.attr = attr
        # the current value of the attribute
        self.attrValue = attrValue
        # build the ui
        self.setupUi(self)
        # update the ui with the current attr value
        self._setFormValue(self.attrValue)
        # update current valid state after ui has been setup
        self.isValueValid = self._isValueValid(self.attrValue)
        self._setUiValidState(self.isValueValid)

    def setAttrValue(self, newValue):
        """
        Set the current value of the attribute in this form.
        Performs partial validation and prevents setting
        the value if it's type is invalid.
        """
        # value doesn't need to be valid as long
        # as it has the right type
        if self._isValueTypeValid(newValue):
            self.attrValue = newValue
            self._setFormValue(newValue)
            self.isValueValid = self._isValueValid(newValue)
            self._setUiValidState(self.isValueValid)
            return True
        else:
            return False

    def setupUi(self, parent):
        """
        Build the appropriate ui for the attribute
        """
        raise NotImplementedError

    def _setFormValue(self, attrValue):
        """
        Set the current value displayed in the UI form
        """
        raise NotImplementedError

    def _getFormValue(self):
        """
        Return the current attribute value from the UI form.
        The result must always be of a valid type for this attr,
        though the value itself can be invalid.
        """
        raise NotImplementedError

    def _isFormValid(self):
        """
        Return True if the current form contains valid data.
        """
        return True

    def _isValueTypeValid(self, attrValue):
        """
        Return True if a potential value for the attribute matches
        the type of attribute. Attributes of at least a valid
        type can be saved, even though they may cause issues if not
        fixed before building.
        """
        return True

    def _isValueValid(self, attrValue):
        """
        Return True if a potential value for the attribute is valid
        """
        return True

    def _valueChanged(self):
        """
        Update the current attrValue and isValueValid state.
        Should be called whenever relevant UI values change.
        The new value will be retrieved by using `_getFormValue`,
        and validated using `_isValueValid`
        """
        # only emit when form is valid
        if self._isFormValid():
            self.attrValue = self._getFormValue()
            self.isValueValid = self._isValueValid(self.attrValue)
            self._setUiValidState(self.isValueValid)
            self.valueChanged.emit(self.attrValue, self.isValueValid)
        else:
            self._setUiValidState(False)

    def _setUiValidState(self, isValid):
        if hasattr(self, 'frame'):
            if isValid:
                self.frame.setStyleSheet('')
            else:
                self.frame.setStyleSheet('.QFrame{ background-color: rgb(255, 0, 0, 35); }')

    def setupDefaultFormUi(self, parent):
        """
        Optional UI setup that builds a standardized layout.
        Includes a form layout and a label with the attributes name.
        Should be called at the start of setupUi if desired.
        """
        layout = QtWidgets.QVBoxLayout(parent)
        layout.setContentsMargins(0, 0, 0, 0)

        self.frame = QtWidgets.QFrame(parent)
        layout.addWidget(self.frame)

        self.formLayout = QtWidgets.QFormLayout(self.frame)
        # margin that will give us some visible area of
        # the frame that can change color based on valid state
        self.formLayout.setContentsMargins(2, 2, 2, 2)
        self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow)
        self.formLayout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTop|QtCore.Qt.AlignTrailing)
        self.formLayout.setHorizontalSpacing(10)

        # attribute name
        self.label = QtWidgets.QLabel(self.frame)
        self.label.setMinimumSize(QtCore.QSize(self.LABEL_WIDTH, self.LABEL_HEIGHT))
        self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignTop)
        # add some space above the label so it lines up
        self.label.setMargin(2)
        self.label.setText(pulse.names.toTitle(self.attr['name']))
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)

    def setDefaultFormWidget(self, widget):
        """
        Set the widget to be used as the field in the default form layout
        Requires `setupDefaultFormUi` to be used.
        """
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, widget)

    def setDefaultFormLayout(self, layout):
        """
        Set the layout to be used as the field in the default form layout.
        Requires `setupDefaultFormUi` to be used.
        """
        self.formLayout.setLayout(0, QtWidgets.QFormLayout.FieldRole, layout)

class DefaultAttrForm(ActionAttrForm):
    """
    A catchall attribute form that can handle any attribute type
    by leveraging pymetanode serialization. Provides a text field
    where values can typed representing serialized string data.
    """
    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        self._didFailDecode = False

        self.textEdit = QtWidgets.QLineEdit(parent)
        self.textEdit.setStyleSheet('font: 8pt "Consolas";')
        self.textEdit.textChanged.connect(self._valueChanged)

        self.setDefaultFormWidget(self.textEdit)

    def _setFormValue(self, attrValue):
        if isinstance(attrValue, basestring):
            self.textEdit.setText(repr(attrValue))
        else:
            self.textEdit.setText(meta.encodeMetaData(attrValue))

    def _getFormValue(self):
        return meta.decodeMetaData(self.textEdit.text())

    def _isFormValid(self):
        try:
            meta.decodeMetaData(self.textEdit.text())
            return True
        except Exception as e:
            return False

class OptionAttrForm(ActionAttrForm):
    """
    A options list form that uses a combo box
    to display options and keeps data stored as an int value
    """

    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        self.combo = QtWidgets.QComboBox(parent)
        for option in self.attr['options']:
            self.combo.addItem(option)
        self.combo.currentIndexChanged.connect(self._valueChanged)

        self.setDefaultFormWidget(self.combo)

    def _setFormValue(self, attrValue):
        self.combo.setCurrentIndex(attrValue)

    def _getFormValue(self):
        return self.combo.currentIndex()

    def _isValueTypeValid(self, attrValue):
        return isinstance(attrValue, (int, long))

    def _isValueValid(self, attrValue):
        return attrValue >= 0 and attrValue < len(self.attr['options'])

ActionAttrForm.TYPEMAP['option'] = OptionAttrForm

class BoolAttrForm(ActionAttrForm):
    """
    A simple checkbox attribute form
    """

    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        self.checkbox = QtWidgets.QCheckBox(parent)
        self.checkbox.setMinimumHeight(self.LABEL_HEIGHT)
        self.checkbox.stateChanged.connect(self._valueChanged)

        self.setDefaultFormWidget(self.checkbox)

    def _setFormValue(self, attrValue):
        self.checkbox.setChecked(attrValue)

    def _getFormValue(self):
        return self.checkbox.isChecked()

    def _isValueTypeValid(self, attrValue):
        return attrValue is True or attrValue is False

ActionAttrForm.TYPEMAP['bool'] = BoolAttrForm

class StringAttrForm(ActionAttrForm):
    """
    A simple checkbox attribute form
    """

    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        self.lineEdit = QtWidgets.QLineEdit(parent)
        self.lineEdit.setMinimumHeight(self.LABEL_HEIGHT)
        self.lineEdit.textChanged.connect(self._valueChanged)

        self.setDefaultFormWidget(self.lineEdit)

    def _setFormValue(self, attrValue):
        self.lineEdit.setText(attrValue)

    def _getFormValue(self):
        return self.lineEdit.text()

    def _isValueTypeValid(self, attrValue):
        return isinstance(attrValue, basestring)

ActionAttrForm.TYPEMAP['string'] = StringAttrForm

class NodeAttrForm(ActionAttrForm):
    """
    A special form that allows picking nodes from the scene.
    """
    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        hlayout = QtWidgets.QHBoxLayout(parent)
        hlayout.setSpacing(4)

        self.listWidget = QtWidgets.QListWidget(parent)
        self.listWidget.setFixedHeight(20)
        self.listWidget.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        self.listWidget.setSortingEnabled(True)
        self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection)
        hlayout.addWidget(self.listWidget)

        self.pickButton = QtWidgets.QPushButton(parent)
        self.pickButton.setIcon(viewutils.getIcon("select.png"))
        self.pickButton.setFixedSize(QtCore.QSize(20, 20))
        self.pickButton.clicked.connect(self.setFromSelection)
        hlayout.addWidget(self.pickButton)
        hlayout.setAlignment(self.pickButton, QtCore.Qt.AlignTop)

        self.setDefaultFormLayout(hlayout)

    def _setFormValue(self, attrValue):
        while self.listWidget.takeItem(0):
            pass
        if attrValue:
            self.listWidget.addItem(QtWidgets.QListWidgetItem(attrValue.nodeName()))

    def _getFormValue(self):
        return self.attrValue

    def _isValueTypeValid(self, attrValue):
        return attrValue is None or isinstance(attrValue, pm.nt.DependNode)

    def setFromSelection(self):
        sel = pm.selected()
        if sel:
            self.setAttrValue(sel[0])
            self.valueChanged.emit(self.attrValue, self.isValueValid)
        else:
            self.setAttrValue(None)

ActionAttrForm.TYPEMAP['node'] = NodeAttrForm

class NodeListAttrForm(ActionAttrForm):
    """
    A special form that allows picking nodes from the scene.
    """
    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        hlayout = QtWidgets.QHBoxLayout(parent)
        hlayout.setSpacing(4)

        self.listWidget = QtWidgets.QListWidget(parent)
        self.listWidget.setSortingEnabled(True)
        self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection)
        hlayout.addWidget(self.listWidget)

        self.pickButton = QtWidgets.QPushButton(parent)
        self.pickButton.setIcon(viewutils.getIcon("select.png"))
        self.pickButton.setFixedSize(QtCore.QSize(20, 20))
        self.pickButton.clicked.connect(self.setFromSelection)
        hlayout.addWidget(self.pickButton)
        hlayout.setAlignment(self.pickButton, QtCore.Qt.AlignTop)

        self.setDefaultFormLayout(hlayout)

    def _setFormValue(self, attrValue):
        while self.listWidget.takeItem(0):
            pass
        for node in attrValue:
            self.listWidget.addItem(QtWidgets.QListWidgetItem(node.nodeName()))

    def _getFormValue(self):
        return self.attrValue

    def _isValueTypeValid(self, attrValue):
        if not isinstance(attrValue, list):
            return False
        return all([isinstance(n, pm.nt.DependNode) for n in attrValue])

    def setFromSelection(self):
        self.setAttrValue(pm.selected())
        self.valueChanged.emit(self.attrValue, self.isValueValid)

ActionAttrForm.TYPEMAP['nodelist'] = NodeListAttrForm
<commit_msg>adds missing nodelist attr form to all list<commit_after>
from pulse.vendor.Qt import QtCore, QtWidgets, QtGui
import pymel.core as pm
import pymetanode as meta

import pulse.names
from . import utils as viewutils

__all__ = [
    'ActionAttrForm',
    'BoolAttrForm',
    'DefaultAttrForm',
    'NodeAttrForm',
    'NodeListAttrForm',
    'OptionAttrForm',
    'StringAttrForm',
]

class ActionAttrForm(QtWidgets.QWidget):
    """
    The base class for all forms used to edit action attributes.
    Provides input validation and basic signals for keeping
    track of value changes.
    """

    TYPEMAP = {}

    LABEL_WIDTH = 150
    LABEL_HEIGHT = 20

    # valueChanged(newValue, isValueValid)
    valueChanged = QtCore.Signal(object, bool)

    @staticmethod
    def createAttrForm(attr, attrValue, parent=None):
        """
        Create a new ActionAttrForm of the appropriate
        type based on a BuildAction attribute.

        Args:
            attr: A dict representing the config of a BuildAction attribute
            attrValue: The current value of the attribute
        """
        attrType = attr['type']
        if attrType in ActionAttrForm.TYPEMAP:
            return ActionAttrForm.TYPEMAP[attrType](attr, attrValue, parent=parent)
        # fallback to the default widget
        return DefaultAttrForm(attr, attrValue)

    def __init__(self, attr, attrValue, parent=None):
        super(ActionAttrForm, self).__init__(parent=parent)
        # the config data of the attribute being edited
        self.attr = attr
        # the current value of the attribute
        self.attrValue = attrValue
        # build the ui
        self.setupUi(self)
        # update the ui with the current attr value
        self._setFormValue(self.attrValue)
        # update current valid state after ui has been setup
        self.isValueValid = self._isValueValid(self.attrValue)
        self._setUiValidState(self.isValueValid)

    def setAttrValue(self, newValue):
        """
        Set the current value of the attribute in this form.
        Performs partial validation and prevents setting
        the value if it's type is invalid.
        """
        # value doesn't need to be valid as long
        # as it has the right type
        if self._isValueTypeValid(newValue):
            self.attrValue = newValue
            self._setFormValue(newValue)
            self.isValueValid = self._isValueValid(newValue)
            self._setUiValidState(self.isValueValid)
            return True
        else:
            return False

    def setupUi(self, parent):
        """
        Build the appropriate ui for the attribute
        """
        raise NotImplementedError

    def _setFormValue(self, attrValue):
        """
        Set the current value displayed in the UI form
        """
        raise NotImplementedError

    def _getFormValue(self):
        """
        Return the current attribute value from the UI form.
        The result must always be of a valid type for this attr,
        though the value itself can be invalid.
        """
        raise NotImplementedError

    def _isFormValid(self):
        """
        Return True if the current form contains valid data.
        """
        return True

    def _isValueTypeValid(self, attrValue):
        """
        Return True if a potential value for the attribute matches
        the type of attribute. Attributes of at least a valid
        type can be saved, even though they may cause issues if not
        fixed before building.
        """
        return True

    def _isValueValid(self, attrValue):
        """
        Return True if a potential value for the attribute is valid
        """
        return True

    def _valueChanged(self):
        """
        Update the current attrValue and isValueValid state.
        Should be called whenever relevant UI values change.
        The new value will be retrieved by using `_getFormValue`,
        and validated using `_isValueValid`
        """
        # only emit when form is valid
        if self._isFormValid():
            self.attrValue = self._getFormValue()
            self.isValueValid = self._isValueValid(self.attrValue)
            self._setUiValidState(self.isValueValid)
            self.valueChanged.emit(self.attrValue, self.isValueValid)
        else:
            self._setUiValidState(False)

    def _setUiValidState(self, isValid):
        if hasattr(self, 'frame'):
            if isValid:
                self.frame.setStyleSheet('')
            else:
                self.frame.setStyleSheet('.QFrame{ background-color: rgb(255, 0, 0, 35); }')

    def setupDefaultFormUi(self, parent):
        """
        Optional UI setup that builds a standardized layout.
        Includes a form layout and a label with the attributes name.
        Should be called at the start of setupUi if desired.
        """
        layout = QtWidgets.QVBoxLayout(parent)
        layout.setContentsMargins(0, 0, 0, 0)

        self.frame = QtWidgets.QFrame(parent)
        layout.addWidget(self.frame)

        self.formLayout = QtWidgets.QFormLayout(self.frame)
        # margin that will give us some visible area of
        # the frame that can change color based on valid state
        self.formLayout.setContentsMargins(2, 2, 2, 2)
        self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.ExpandingFieldsGrow)
        self.formLayout.setLabelAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTop|QtCore.Qt.AlignTrailing)
        self.formLayout.setHorizontalSpacing(10)

        # attribute name
        self.label = QtWidgets.QLabel(self.frame)
        self.label.setMinimumSize(QtCore.QSize(self.LABEL_WIDTH, self.LABEL_HEIGHT))
        self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignTop)
        # add some space above the label so it lines up
        self.label.setMargin(2)
        self.label.setText(pulse.names.toTitle(self.attr['name']))
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label)

    def setDefaultFormWidget(self, widget):
        """
        Set the widget to be used as the field in the default form layout
        Requires `setupDefaultFormUi` to be used.
        """
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, widget)

    def setDefaultFormLayout(self, layout):
        """
        Set the layout to be used as the field in the default form layout.
        Requires `setupDefaultFormUi` to be used.
        """
        self.formLayout.setLayout(0, QtWidgets.QFormLayout.FieldRole, layout)

class DefaultAttrForm(ActionAttrForm):
    """
    A catchall attribute form that can handle any attribute type
    by leveraging pymetanode serialization. Provides a text field
    where values can typed representing serialized string data.
    """
    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        self._didFailDecode = False

        self.textEdit = QtWidgets.QLineEdit(parent)
        self.textEdit.setStyleSheet('font: 8pt "Consolas";')
        self.textEdit.textChanged.connect(self._valueChanged)

        self.setDefaultFormWidget(self.textEdit)

    def _setFormValue(self, attrValue):
        if isinstance(attrValue, basestring):
            self.textEdit.setText(repr(attrValue))
        else:
            self.textEdit.setText(meta.encodeMetaData(attrValue))

    def _getFormValue(self):
        return meta.decodeMetaData(self.textEdit.text())

    def _isFormValid(self):
        try:
            meta.decodeMetaData(self.textEdit.text())
            return True
        except Exception as e:
            return False

class OptionAttrForm(ActionAttrForm):
    """
    A options list form that uses a combo box
    to display options and keeps data stored as an int value
    """

    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        self.combo = QtWidgets.QComboBox(parent)
        for option in self.attr['options']:
            self.combo.addItem(option)
        self.combo.currentIndexChanged.connect(self._valueChanged)

        self.setDefaultFormWidget(self.combo)

    def _setFormValue(self, attrValue):
        self.combo.setCurrentIndex(attrValue)

    def _getFormValue(self):
        return self.combo.currentIndex()

    def _isValueTypeValid(self, attrValue):
        return isinstance(attrValue, (int, long))

    def _isValueValid(self, attrValue):
        return attrValue >= 0 and attrValue < len(self.attr['options'])

ActionAttrForm.TYPEMAP['option'] = OptionAttrForm

class BoolAttrForm(ActionAttrForm):
    """
    A simple checkbox attribute form
    """

    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        self.checkbox = QtWidgets.QCheckBox(parent)
        self.checkbox.setMinimumHeight(self.LABEL_HEIGHT)
        self.checkbox.stateChanged.connect(self._valueChanged)

        self.setDefaultFormWidget(self.checkbox)

    def _setFormValue(self, attrValue):
        self.checkbox.setChecked(attrValue)

    def _getFormValue(self):
        return self.checkbox.isChecked()

    def _isValueTypeValid(self, attrValue):
        return attrValue is True or attrValue is False

ActionAttrForm.TYPEMAP['bool'] = BoolAttrForm

class StringAttrForm(ActionAttrForm):
    """
    A simple checkbox attribute form
    """

    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        self.lineEdit = QtWidgets.QLineEdit(parent)
        self.lineEdit.setMinimumHeight(self.LABEL_HEIGHT)
        self.lineEdit.textChanged.connect(self._valueChanged)

        self.setDefaultFormWidget(self.lineEdit)

    def _setFormValue(self, attrValue):
        self.lineEdit.setText(attrValue)

    def _getFormValue(self):
        return self.lineEdit.text()

    def _isValueTypeValid(self, attrValue):
        return isinstance(attrValue, basestring)

ActionAttrForm.TYPEMAP['string'] = StringAttrForm

class NodeAttrForm(ActionAttrForm):
    """
    A special form that allows picking nodes from the scene.
    """
    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        hlayout = QtWidgets.QHBoxLayout(parent)
        hlayout.setSpacing(4)

        self.listWidget = QtWidgets.QListWidget(parent)
        self.listWidget.setFixedHeight(20)
        self.listWidget.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        self.listWidget.setSortingEnabled(True)
        self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection)
        hlayout.addWidget(self.listWidget)

        self.pickButton = QtWidgets.QPushButton(parent)
        self.pickButton.setIcon(viewutils.getIcon("select.png"))
        self.pickButton.setFixedSize(QtCore.QSize(20, 20))
        self.pickButton.clicked.connect(self.setFromSelection)
        hlayout.addWidget(self.pickButton)
        hlayout.setAlignment(self.pickButton, QtCore.Qt.AlignTop)

        self.setDefaultFormLayout(hlayout)

    def _setFormValue(self, attrValue):
        while self.listWidget.takeItem(0):
            pass
        if attrValue:
            self.listWidget.addItem(QtWidgets.QListWidgetItem(attrValue.nodeName()))

    def _getFormValue(self):
        return self.attrValue

    def _isValueTypeValid(self, attrValue):
        return attrValue is None or isinstance(attrValue, pm.nt.DependNode)

    def setFromSelection(self):
        sel = pm.selected()
        if sel:
            self.setAttrValue(sel[0])
            self.valueChanged.emit(self.attrValue, self.isValueValid)
        else:
            self.setAttrValue(None)

ActionAttrForm.TYPEMAP['node'] = NodeAttrForm

class NodeListAttrForm(ActionAttrForm):
    """
    A special form that allows picking nodes from the scene.
    """
    def setupUi(self, parent):
        self.setupDefaultFormUi(parent)

        hlayout = QtWidgets.QHBoxLayout(parent)
        hlayout.setSpacing(4)

        self.listWidget = QtWidgets.QListWidget(parent)
        self.listWidget.setSortingEnabled(True)
        self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.SelectionMode.ExtendedSelection)
        hlayout.addWidget(self.listWidget)

        self.pickButton = QtWidgets.QPushButton(parent)
        self.pickButton.setIcon(viewutils.getIcon("select.png"))
        self.pickButton.setFixedSize(QtCore.QSize(20, 20))
        self.pickButton.clicked.connect(self.setFromSelection)
        hlayout.addWidget(self.pickButton)
        hlayout.setAlignment(self.pickButton, QtCore.Qt.AlignTop)

        self.setDefaultFormLayout(hlayout)

    def _setFormValue(self, attrValue):
        while self.listWidget.takeItem(0):
            pass
        for node in attrValue:
            self.listWidget.addItem(QtWidgets.QListWidgetItem(node.nodeName()))

    def _getFormValue(self):
        return self.attrValue

    def _isValueTypeValid(self, attrValue):
        if not isinstance(attrValue, list):
            return False
        return all([isinstance(n, pm.nt.DependNode) for n in attrValue])

    def setFromSelection(self):
        self.setAttrValue(pm.selected())
        self.valueChanged.emit(self.attrValue, self.isValueValid)

ActionAttrForm.TYPEMAP['nodelist'] = NodeListAttrForm
<|endoftext|>


<commit_before>#
#  Copyright 2.02-2017 Barcelona Supercomputing Center (www.bsc.es)
#
#  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.
#
"""
@author: fconejer

PyCOMPSs API - Implement (Versioning)
=====================================
    This file contains the class constraint, needed for the implement
    definition through the decorator.
"""
import inspect
import logging
import os
from functools import wraps
from pycompss.runtime.binding import register_ce
from pycompss.util.location import i_am_at_master

logger = logging.getLogger(__name__)

class implement(object):
    """
    This decorator also preserves the argspec, but includes the __init__ and
    __call__ methods, useful on mpi task creation.
    """
    def __init__(self, *args, **kwargs):
        # store arguments passed to the decorator
        self.args = args
        self.kwargs = kwargs
        logger.debug("Init @implement decorator...")
        # self = itself.
        # args = not used.
        # kwargs = dictionary with the given constraints.

    def __call__(self, func):

        if i_am_at_master():
            # master code
            mod = inspect.getmodule(func)
            self.module = mod.__name__    # not func.__module__

            if(self.module == '__main__' or
               self.module == 'pycompss.runtime.launch'):
                # The module where the function is defined was run as __main__,
                # we need to find out the real module name.

                # path=mod.__file__
                # dirs=mod.__file__.split(os.sep)
                # file_name=os.path.splitext(os.path.basename(mod.__file__))[0]

                # Get the real module name from our launch.py variable
                path = getattr(mod, "app_path")

                dirs = path.split(os.path.sep)
                file_name = os.path.splitext(os.path.basename(path))[0]
                mod_name = file_name

                i = len(dirs) - 1
                while i > 0:
                    new_l = len(path) - (len(dirs[i]) + 1)
                    path = path[0:new_l]
                    if "__init__.py" in os.listdir(path):
                        # directory is a package
                        i -= 1
                        mod_name = dirs[i] + '.' + mod_name
                    else:
                        break
                self.module = mod_name

            # Include the registering info related to @MPI

            # Retrieve the base coreElement established at @task decorator
            coreElement = func.__to_register__
            # Update the core element information with the mpi information
            anotherClass = self.kwargs['source_class']
            anotherMethod = self.kwargs['method']
            implSignature = anotherClass + '.' + anotherMethod
            coreElement.set_implSignature(implSignature)
            implArgs = [anotherClass, anotherMethod]
            coreElement.set_implTypeArgs(implArgs)
            coreElement.set_implType("METHOD")
            func.__to_register__ = coreElement
            # Do the task register if I am the top decorator
            if func.__who_registers__ == __name__:
                logger.debug("[@IMPLEMENT] I have to do the register of function %s in module %s" % (func.__name__, self.module))
                register_ce(coreElement)
        else:
            # worker code
            pass

        @wraps(func)
        def implement_f(*args, **kwargs):
            # This is executed only when called.
            logger.debug("Executing implement_f wrapper.")

            # The 'self' for a method function is passed as args[0]
            slf = args[0]

            # Replace and store the attributes
            saved = {}
            for k, v in self.kwargs.items():
                if hasattr(slf, k):
                    saved[k] = getattr(slf, k)
                    setattr(slf, k, v)

            # Call the method
            ret = func(*args, **kwargs)

            # Put things back
            for k, v in saved.items():
                setattr(slf, k, v)

            return ret
        implement_f.__doc__ = func.__doc__
        return implement_f
<commit_msg>Fixed versioning signature bug in PyCOMPSs.<commit_after>#
#  Copyright 2.02-2017 Barcelona Supercomputing Center (www.bsc.es)
#
#  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.
#
"""
@author: fconejer

PyCOMPSs API - Implement (Versioning)
=====================================
    This file contains the class constraint, needed for the implement
    definition through the decorator.
"""
import inspect
import logging
import os
from functools import wraps
from pycompss.runtime.binding import register_ce
from pycompss.util.location import i_am_at_master

logger = logging.getLogger(__name__)

class implement(object):
    """
    This decorator also preserves the argspec, but includes the __init__ and
    __call__ methods, useful on mpi task creation.
    """
    def __init__(self, *args, **kwargs):
        # store arguments passed to the decorator
        self.args = args
        self.kwargs = kwargs
        logger.debug("Init @implement decorator...")
        # self = itself.
        # args = not used.
        # kwargs = dictionary with the given constraints.

    def __call__(self, func):

        if i_am_at_master():
            # master code
            mod = inspect.getmodule(func)
            self.module = mod.__name__    # not func.__module__

            if(self.module == '__main__' or
               self.module == 'pycompss.runtime.launch'):
                # The module where the function is defined was run as __main__,
                # we need to find out the real module name.

                # path=mod.__file__
                # dirs=mod.__file__.split(os.sep)
                # file_name=os.path.splitext(os.path.basename(mod.__file__))[0]

                # Get the real module name from our launch.py variable
                path = getattr(mod, "app_path")

                dirs = path.split(os.path.sep)
                file_name = os.path.splitext(os.path.basename(path))[0]
                mod_name = file_name

                i = len(dirs) - 1
                while i > 0:
                    new_l = len(path) - (len(dirs[i]) + 1)
                    path = path[0:new_l]
                    if "__init__.py" in os.listdir(path):
                        # directory is a package
                        i -= 1
                        mod_name = dirs[i] + '.' + mod_name
                    else:
                        break
                self.module = mod_name

            # Include the registering info related to @MPI

            # Retrieve the base coreElement established at @task decorator
            coreElement = func.__to_register__
            # Update the core element information with the mpi information
            ce_signature = coreElement.get_ce_signature()
            implSignature = ce_signature
            coreElement.set_implSignature(implSignature)

            anotherClass = self.kwargs['source_class']
            anotherMethod = self.kwargs['method']
            ce_signature = anotherClass + '.' + anotherMethod
            coreElement.set_ce_signature(ce_signature)

            # This is not needed since the arguments are already set by the task decorator.
            #implArgs = [anotherClass, anotherMethod]
            #coreElement.set_implTypeArgs(implArgs)

            coreElement.set_implType("METHOD")
            func.__to_register__ = coreElement
            # Do the task register if I am the top decorator
            if func.__who_registers__ == __name__:
                logger.debug("[@IMPLEMENT] I have to do the register of function %s in module %s" % (func.__name__, self.module))
                register_ce(coreElement)
        else:
            # worker code
            pass

        @wraps(func)
        def implement_f(*args, **kwargs):
            # This is executed only when called.
            logger.debug("Executing implement_f wrapper.")

            # The 'self' for a method function is passed as args[0]
            slf = args[0]

            # Replace and store the attributes
            saved = {}
            for k, v in self.kwargs.items():
                if hasattr(slf, k):
                    saved[k] = getattr(slf, k)
                    setattr(slf, k, v)

            # Call the method
            ret = func(*args, **kwargs)

            # Put things back
            for k, v in saved.items():
                setattr(slf, k, v)

            return ret
        implement_f.__doc__ = func.__doc__
        return implement_f
<|endoftext|>


<commit_before><commit_msg>ZI HDAWG updates to make dio calibration work<commit_after><|endoftext|>


<commit_before><commit_msg>Create 7kyu_turn_any_word_into_a_beef_taco.py<commit_after><|endoftext|>


<commit_before># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    This program 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.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import time
from osv import fields,osv
from tools.translate import _

WARNING_MESSAGE = [
                   ('no-message','No Message'),
                   ('warning','Warning'),
                   ('block','Blocking Message')
                   ]

WARNING_HELP = _('Selecting the "Warning" option will notify user with the message, Selecting "Blocking Message" will throw an exception with the message and block the flow. The Message has to be written in the next field.')

class res_partner(osv.osv):
    _inherit = 'res.partner'
    _columns = {
        'sale_warn' : fields.selection(WARNING_MESSAGE, 'Sale Order', help=WARNING_HELP),
        'sale_warn_msg' : fields.text('Message for Sale Order'),
        'purchase_warn' : fields.selection(WARNING_MESSAGE, 'Purchase Order', help=WARNING_HELP),
        'purchase_warn_msg' : fields.text('Message for Purchase Order'),
        'picking_warn' : fields.selection(WARNING_MESSAGE, 'Stock Picking', help=WARNING_HELP),
        'picking_warn_msg' : fields.text('Message for Stock Picking'),
        'invoice_warn' : fields.selection(WARNING_MESSAGE, 'Invoice', help=WARNING_HELP),
        'invoice_warn_msg' : fields.text('Message for Invoice'),
    }
    _defaults = {
         'sale_warn' : lambda *a: 'no-message',
         'purchase_warn' : lambda *a: 'no-message',
         'picking_warn' : lambda *a: 'no-message',
         'invoice_warn' : lambda *a: 'no-message',
    }

res_partner()

class sale_order(osv.osv):
    _inherit = 'sale.order'
    def onchange_partner_id(self, cr, uid, ids, part):
        if not part:
            return {'value':{'partner_invoice_id': False, 'partner_shipping_id':False, 'partner_order_id':False, 'payment_term' : False}}
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, part)
        if partner.sale_warn != 'no-message':
            if partner.sale_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (partner.name), partner.sale_warn_msg)
            title =  _("Warning for %s") % partner.name
            message = partner.sale_warn_msg
            warning = {
                    'title': title,
                    'message': message,
            }

        result =  super(sale_order, self).onchange_partner_id(cr, uid, ids, part)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}
sale_order()

class purchase_order(osv.osv):
    _inherit = 'purchase.order'
    def onchange_partner_id(self, cr, uid, ids, part):
        if not part:
            return {'value':{'partner_address_id': False}}
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, part)
        if partner.purchase_warn != 'no-message':
            if partner.purchase_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (partner.name), partner.purchase_warn_msg)
            title = _("Warning for %s") % partner.name
            message = partner.purchase_warn_msg
            warning = {
                'title': title,
                'message': message
                }
        result =  super(purchase_order, self).onchange_partner_id(cr, uid, ids, part)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

purchase_order()

class account_invoice(osv.osv):
    _inherit = 'account.invoice'
    def onchange_partner_id(self, cr, uid, ids, type, partner_id,
            date_invoice=False, payment_term=False, partner_bank_id=False, company_id=False):
        if not partner_id:
            return {'value': {
            'address_contact_id': False ,
            'address_invoice_id': False,
            'account_id': False,
            'payment_term': False,
            }
        }
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id)
        if partner.invoice_warn != 'no-message':
            if partner.invoice_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (partner.name), partner.invoice_warn_msg)

            title = _("Warning for %s") % partner.name
            message = partner.invoice_warn_msg
            warning = {
                'title': title,
                'message': message
                }
        result =  super(account_invoice, self).onchange_partner_id(cr, uid, ids, type, partner_id,
            date_invoice=False, payment_term=False, partner_bank_id=False)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

account_invoice()

class stock_picking(osv.osv):
    _inherit = 'stock.picking'

    def onchange_partner_in(self, cr, uid, context, partner_id=None):
        if not partner_id:
            return {}
        partner = self.pool.get('res.partner.address').browse(cr, uid, [partner_id])[0].partner_id
        warning = {}
        title = False
        message = False
        if partner.picking_warn != 'no-message':
            if partner.picking_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (partner.name), partner.picking_warn_msg)
            title = _("Warning for %s") % partner.name
            message = partner.picking_warn_msg
            warning = {
                'title': title,
                'message': message
            }
        result =  super(stock_picking, self).onchange_partner_in(cr, uid, context, partner_id)
        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

stock_picking()

class product_product(osv.osv):
    _inherit = 'product.product'
    _columns = {
         'sale_line_warn' : fields.selection(WARNING_MESSAGE,'Sale Order Line', help=WARNING_HELP),
         'sale_line_warn_msg' : fields.text('Message for Sale Order Line'),
         'purchase_line_warn' : fields.selection(WARNING_MESSAGE,'Purchase Order Line', help=WARNING_HELP),
         'purchase_line_warn_msg' : fields.text('Message for Purchase Order Line'),
     }

    _defaults = {
         'sale_line_warn' : lambda *a: 'no-message',
         'purchase_line_warn' : lambda *a: 'no-message',
    }

product_product()

class sale_order_line(osv.osv):
    _inherit = 'sale.order.line'
    def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
            uom=False, qty_uos=0, uos=False, name='', partner_id=False,
            lang=False, update_tax=True, date_order=False, packaging=False,
            fiscal_position=False, flag=False):
        warning = {}
        if not product:
            return {'value': {'th_weight' : 0, 'product_packaging': False,
                'product_uos_qty': qty}, 'domain': {'product_uom': [],
                   'product_uos': []}}
        product_obj = self.pool.get('product.product')
        product_info = product_obj.browse(cr, uid, product)
        title = False
        message = False

        if product_info.sale_line_warn != 'no-message':
            if product_info.sale_line_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (product_info.name), product_info.sale_line_warn_msg)
            title = _("Warning for %s") % product_info.name
            message = product_info.sale_line_warn_msg
            warning['title'] = title
            warning['message'] = message

        result =  super(sale_order_line, self).product_id_change( cr, uid, ids, pricelist, product, qty,
            uom, qty_uos, uos, name, partner_id,
            lang, update_tax, date_order, packaging, fiscal_position, flag)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message +'\n\n'+result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

sale_order_line()

class purchase_order_line(osv.osv):
    _inherit = 'purchase.order.line'
    def product_id_change(self,cr, uid, ids, pricelist, product, qty, uom,
            partner_id, date_order=False, fiscal_position=False, date_planned=False,
            name=False, price_unit=False, notes=False):
        warning = {}
        if not product:
            return {'value': {'price_unit': 0.0, 'name':'','notes':'', 'product_uom' : False}, 'domain':{'product_uom':[]}}
        product_obj = self.pool.get('product.product')
        product_info = product_obj.browse(cr, uid, product)
        title = False
        message = False

        if product_info.purchase_line_warn != 'no-message':
            if product_info.purchase_line_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (product_info.name), product_info.purchase_line_warn_msg)
            title = _("Warning for %s") % product_info.name
            message = product_info.purchase_line_warn_msg
            warning['title'] = title
            warning['message'] = message

        result =  super(purchase_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty, uom,
            partner_id, date_order, fiscal_position)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message +'\n\n'+result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

purchase_order_line()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<commit_msg>warning: set message-selectors as required, to force 'no-message' [Bug 720247]<commit_after># -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
#    This program 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.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

import time
from osv import fields,osv
from tools.translate import _

WARNING_MESSAGE = [
                   ('no-message','No Message'),
                   ('warning','Warning'),
                   ('block','Blocking Message')
                   ]

WARNING_HELP = _('Selecting the "Warning" option will notify user with the message, Selecting "Blocking Message" will throw an exception with the message and block the flow. The Message has to be written in the next field.')

class res_partner(osv.osv):
    _inherit = 'res.partner'
    _columns = {
        'sale_warn' : fields.selection(WARNING_MESSAGE, 'Sale Order', help=WARNING_HELP, required=True),
        'sale_warn_msg' : fields.text('Message for Sale Order'),
        'purchase_warn' : fields.selection(WARNING_MESSAGE, 'Purchase Order', help=WARNING_HELP, required=True),
        'purchase_warn_msg' : fields.text('Message for Purchase Order'),
        'picking_warn' : fields.selection(WARNING_MESSAGE, 'Stock Picking', help=WARNING_HELP, required=True),
        'picking_warn_msg' : fields.text('Message for Stock Picking'),
        'invoice_warn' : fields.selection(WARNING_MESSAGE, 'Invoice', help=WARNING_HELP, required=True),
        'invoice_warn_msg' : fields.text('Message for Invoice'),
    }
    _defaults = {
         'sale_warn' : 'no-message',
         'purchase_warn' : 'no-message',
         'picking_warn' : 'no-message',
         'invoice_warn' : 'no-message',
    }

res_partner()

class sale_order(osv.osv):
    _inherit = 'sale.order'
    def onchange_partner_id(self, cr, uid, ids, part):
        if not part:
            return {'value':{'partner_invoice_id': False, 'partner_shipping_id':False, 'partner_order_id':False, 'payment_term' : False}}
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, part)
        if partner.sale_warn != 'no-message':
            if partner.sale_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (partner.name), partner.sale_warn_msg)
            title =  _("Warning for %s") % partner.name
            message = partner.sale_warn_msg
            warning = {
                    'title': title,
                    'message': message,
            }

        result =  super(sale_order, self).onchange_partner_id(cr, uid, ids, part)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}
sale_order()

class purchase_order(osv.osv):
    _inherit = 'purchase.order'
    def onchange_partner_id(self, cr, uid, ids, part):
        if not part:
            return {'value':{'partner_address_id': False}}
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, part)
        if partner.purchase_warn != 'no-message':
            if partner.purchase_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (partner.name), partner.purchase_warn_msg)
            title = _("Warning for %s") % partner.name
            message = partner.purchase_warn_msg
            warning = {
                'title': title,
                'message': message
                }
        result =  super(purchase_order, self).onchange_partner_id(cr, uid, ids, part)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

purchase_order()

class account_invoice(osv.osv):
    _inherit = 'account.invoice'
    def onchange_partner_id(self, cr, uid, ids, type, partner_id,
            date_invoice=False, payment_term=False, partner_bank_id=False, company_id=False):
        if not partner_id:
            return {'value': {
            'address_contact_id': False ,
            'address_invoice_id': False,
            'account_id': False,
            'payment_term': False,
            }
        }
        warning = {}
        title = False
        message = False
        partner = self.pool.get('res.partner').browse(cr, uid, partner_id)
        if partner.invoice_warn != 'no-message':
            if partner.invoice_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (partner.name), partner.invoice_warn_msg)

            title = _("Warning for %s") % partner.name
            message = partner.invoice_warn_msg
            warning = {
                'title': title,
                'message': message
                }
        result =  super(account_invoice, self).onchange_partner_id(cr, uid, ids, type, partner_id,
            date_invoice=False, payment_term=False, partner_bank_id=False)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

account_invoice()

class stock_picking(osv.osv):
    _inherit = 'stock.picking'

    def onchange_partner_in(self, cr, uid, context, partner_id=None):
        if not partner_id:
            return {}
        partner = self.pool.get('res.partner.address').browse(cr, uid, [partner_id])[0].partner_id
        warning = {}
        title = False
        message = False
        if partner.picking_warn != 'no-message':
            if partner.picking_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (partner.name), partner.picking_warn_msg)
            title = _("Warning for %s") % partner.name
            message = partner.picking_warn_msg
            warning = {
                'title': title,
                'message': message
            }
        result =  super(stock_picking, self).onchange_partner_in(cr, uid, context, partner_id)
        if result.get('warning',False):
            warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

stock_picking()

class product_product(osv.osv):
    _inherit = 'product.product'
    _columns = {
         'sale_line_warn' : fields.selection(WARNING_MESSAGE,'Sale Order Line', help=WARNING_HELP, required=True),
         'sale_line_warn_msg' : fields.text('Message for Sale Order Line'),
         'purchase_line_warn' : fields.selection(WARNING_MESSAGE,'Purchase Order Line', help=WARNING_HELP, required=True),
         'purchase_line_warn_msg' : fields.text('Message for Purchase Order Line'),
     }

    _defaults = {
         'sale_line_warn' : 'no-message',
         'purchase_line_warn' : 'no-message',
    }

product_product()

class sale_order_line(osv.osv):
    _inherit = 'sale.order.line'
    def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
            uom=False, qty_uos=0, uos=False, name='', partner_id=False,
            lang=False, update_tax=True, date_order=False, packaging=False,
            fiscal_position=False, flag=False):
        warning = {}
        if not product:
            return {'value': {'th_weight' : 0, 'product_packaging': False,
                'product_uos_qty': qty}, 'domain': {'product_uom': [],
                   'product_uos': []}}
        product_obj = self.pool.get('product.product')
        product_info = product_obj.browse(cr, uid, product)
        title = False
        message = False

        if product_info.sale_line_warn != 'no-message':
            if product_info.sale_line_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (product_info.name), product_info.sale_line_warn_msg)
            title = _("Warning for %s") % product_info.name
            message = product_info.sale_line_warn_msg
            warning['title'] = title
            warning['message'] = message

        result =  super(sale_order_line, self).product_id_change( cr, uid, ids, pricelist, product, qty,
            uom, qty_uos, uos, name, partner_id,
            lang, update_tax, date_order, packaging, fiscal_position, flag)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message +'\n\n'+result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

sale_order_line()

class purchase_order_line(osv.osv):
    _inherit = 'purchase.order.line'
    def product_id_change(self,cr, uid, ids, pricelist, product, qty, uom,
            partner_id, date_order=False, fiscal_position=False, date_planned=False,
            name=False, price_unit=False, notes=False):
        warning = {}
        if not product:
            return {'value': {'price_unit': 0.0, 'name':'','notes':'', 'product_uom' : False}, 'domain':{'product_uom':[]}}
        product_obj = self.pool.get('product.product')
        product_info = product_obj.browse(cr, uid, product)
        title = False
        message = False

        if product_info.purchase_line_warn != 'no-message':
            if product_info.purchase_line_warn == 'block':
                raise osv.except_osv(_('Alert for %s !') % (product_info.name), product_info.purchase_line_warn_msg)
            title = _("Warning for %s") % product_info.name
            message = product_info.purchase_line_warn_msg
            warning['title'] = title
            warning['message'] = message

        result =  super(purchase_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty, uom,
            partner_id, date_order, fiscal_position)

        if result.get('warning',False):
            warning['title'] = title and title +' & '+result['warning']['title'] or result['warning']['title']
            warning['message'] = message and message +'\n\n'+result['warning']['message'] or result['warning']['message']

        return {'value': result.get('value',{}), 'warning':warning}

purchase_order_line()

# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
<|endoftext|>


<commit_before># -*- coding: utf-8; -*-
#
# This file is part of Superdesk.
#
# Copyright 2013, 2014 Sourcefabric z.u. and contributors.
#
# For the full copyright and license information, please see the
# AUTHORS and LICENSE files distributed with this source code, or
# at https://www.sourcefabric.org/superdesk/license
import logging
import re
from superdesk.errors import SuperdeskApiError
from superdesk import get_resource_service
from superdesk.services import BaseService

logger = logging.getLogger(__name__)

class FilterConditionService(BaseService):
    def on_create(self, docs):
        self._check_equals(docs)
        self._check_parameters(docs)

    def on_update(self, updates, original):
        doc = dict(original)
        doc.update(updates)
        self._check_equals([doc])
        self._check_parameters([doc])

    def delete(self, lookup):
        referenced_filters = self._get_referenced_filter_conditions(lookup.get('_id'))
        if referenced_filters.count() > 0:
            references = ','.join([pf['name'] for pf in referenced_filters])
            raise SuperdeskApiError.badRequestError('Filter condition has been referenced in pf:{}'.format(references))
        return super().delete(lookup)

    def _get_referenced_filter_conditions(self, id):
        lookup = {'content_filter.expression.fc': [id]}
        content_filters = get_resource_service('content_filters').get(req=None, lookup=lookup)
        return content_filters

    def _check_parameters(self, docs):
        parameters = get_resource_service('filter_condition_parameters').get(req=None, lookup=None)
        for doc in docs:
            parameter = [p for p in parameters if p['field'] == doc['field']]
            if not parameter or len(parameter) == 0:
                raise SuperdeskApiError.badRequestError(
                    'Filter condition:{} has unidentified field: {}'
                    .format(doc['name'], doc['field']))
            if doc['operator'] not in parameter[0]['operators']:
                raise SuperdeskApiError.badRequestError(
                    'Filter condition:{} has unidentified operator: {}'
                    .format(doc['name'], doc['operator']))

    def _check_equals(self, docs):
        """
        Checks if any of the filter conditions in the docs
        already exists
        :param docs: List of filter conditions to be tested
        :raises SuperdeskApiError: if any of the filter conditions in the docs
        already exists
        """
        for doc in docs:
            existing_docs = self.get(None, {'field': doc['field'], 'operator': doc['operator']})
            for existing_doc in existing_docs:
                if '_id' in doc and doc['_id'] == existing_doc['_id']:
                    continue
                if self._are_equal(doc, existing_doc):
                    raise SuperdeskApiError.badRequestError(
                        'Filter condition:{} has identical settings'.format(existing_doc['name']))

    def check_similar(self, filter_condition):
        """
        Checks if the given filter condition already exists (for text fields like headline) or
        if there's any other filter condition that contains the given filter
        condition (for controlled vocabulary fields like urgency).
        For example: if filter_condition ['urgency' in 3,4] exists and if
        filter condition ['urgency' in 3] is searched we'll have a match
        :param filter_condition: Filter conditions to be tested
        :return: Returns the list of matching filter conditions
        """
        parameters = get_resource_service('filter_condition_parameters').get(req=None, lookup=None)
        parameter = [p for p in parameters if p['field'] == filter_condition['field']]
        if parameter[0]['operators'] == ['in', 'nin']:
            # this is a controlled vocabulary field so find the overlapping values
            existing_docs = list(self.get(None,
                                          {'field': filter_condition['field'],
                                           'operator': filter_condition['operator'],
                                           'value': {'$regex': re.compile('.*{}.*'.format(filter_condition['value']),
                                                                          re.IGNORECASE)}}))
            parameter[0]['operators'].remove(filter_condition['operator'])
            existing_docs.extend(list(self.get(None,
                                               {'field': filter_condition['field'],
                                                'operator': parameter[0]['operators'][0],
                                                'value': {'$not': re.compile('.*{}.*'.format(filter_condition['value']),
                                                                             re.IGNORECASE)}})))
        else:
            # find the exact matches
            existing_docs = list(self.get(None, {'field': filter_condition['field'],
                                                 'operator': filter_condition['operator'],
                                                 'value': filter_condition['value']}))
        return existing_docs

    def _are_equal(self, fc1, fc2):
        def get_comparer(fc):
            return ''.join(sorted(fc['value'].upper()))

        return all([fc1['field'] == fc2['field'],
                    fc1['operator'] == fc2['operator'],
                    get_comparer(fc1) == get_comparer(fc2)])
<commit_msg>fix(content-filters) - Filter search is made case insensitive<commit_after># -*- coding: utf-8; -*-
#
# This file is part of Superdesk.
#
# Copyright 2013, 2014 Sourcefabric z.u. and contributors.
#
# For the full copyright and license information, please see the
# AUTHORS and LICENSE files distributed with this source code, or
# at https://www.sourcefabric.org/superdesk/license
import logging
import re
from superdesk.errors import SuperdeskApiError
from superdesk import get_resource_service
from superdesk.services import BaseService

logger = logging.getLogger(__name__)

class FilterConditionService(BaseService):
    def on_create(self, docs):
        self._check_equals(docs)
        self._check_parameters(docs)

    def on_update(self, updates, original):
        doc = dict(original)
        doc.update(updates)
        self._check_equals([doc])
        self._check_parameters([doc])

    def delete(self, lookup):
        referenced_filters = self._get_referenced_filter_conditions(lookup.get('_id'))
        if referenced_filters.count() > 0:
            references = ','.join([pf['name'] for pf in referenced_filters])
            raise SuperdeskApiError.badRequestError('Filter condition has been referenced in pf:{}'.format(references))
        return super().delete(lookup)

    def _get_referenced_filter_conditions(self, id):
        lookup = {'content_filter.expression.fc': [id]}
        content_filters = get_resource_service('content_filters').get(req=None, lookup=lookup)
        return content_filters

    def _check_parameters(self, docs):
        parameters = get_resource_service('filter_condition_parameters').get(req=None, lookup=None)
        for doc in docs:
            parameter = [p for p in parameters if p['field'] == doc['field']]
            if not parameter or len(parameter) == 0:
                raise SuperdeskApiError.badRequestError(
                    'Filter condition:{} has unidentified field: {}'
                    .format(doc['name'], doc['field']))
            if doc['operator'] not in parameter[0]['operators']:
                raise SuperdeskApiError.badRequestError(
                    'Filter condition:{} has unidentified operator: {}'
                    .format(doc['name'], doc['operator']))

    def _check_equals(self, docs):
        """
        Checks if any of the filter conditions in the docs
        already exists
        :param docs: List of filter conditions to be tested
        :raises SuperdeskApiError: if any of the filter conditions in the docs
        already exists
        """
        for doc in docs:
            existing_docs = self.get(None, {'field': doc['field'], 'operator': doc['operator']})
            for existing_doc in existing_docs:
                if '_id' in doc and doc['_id'] == existing_doc['_id']:
                    continue
                if self._are_equal(doc, existing_doc):
                    raise SuperdeskApiError.badRequestError(
                        'Filter condition:{} has identical settings'.format(existing_doc['name']))

    def check_similar(self, filter_condition):
        """
        Checks if the given filter condition already exists (for text fields like headline) or
        if there's any other filter condition that contains the given filter
        condition (for controlled vocabulary fields like urgency).
        For example: if filter_condition ['urgency' in 3,4] exists and if
        filter condition ['urgency' in 3] is searched we'll have a match
        :param filter_condition: Filter conditions to be tested
        :return: Returns the list of matching filter conditions
        """
        parameters = get_resource_service('filter_condition_parameters').get(req=None, lookup=None)
        parameter = [p for p in parameters if p['field'] == filter_condition['field']]
        if parameter[0]['operators'] == ['in', 'nin']:
            # this is a controlled vocabulary field so find the overlapping values
            existing_docs = list(self.get(None,
                                          {'field': filter_condition['field'],
                                           'operator': filter_condition['operator'],
                                           'value': {'$regex': re.compile('.*{}.*'.format(filter_condition['value']),
                                                                          re.IGNORECASE)}}))
            parameter[0]['operators'].remove(filter_condition['operator'])
            existing_docs.extend(list(self.get(None,
                                               {'field': filter_condition['field'],
                                                'operator': parameter[0]['operators'][0],
                                                'value': {'$not': re.compile('.*{}.*'.format(filter_condition['value']),
                                                                             re.IGNORECASE)}})))
        else:
            # find the exact matches
            existing_docs = list(self.get(None, {'field': filter_condition['field'],
                                                 'operator': filter_condition['operator'],
                                                 'value': {'$regex': re.compile('{}'.format(filter_condition['value']),
                                                                                re.IGNORECASE)}}))
        return existing_docs

    def _are_equal(self, fc1, fc2):
        def get_comparer(fc):
            return ''.join(sorted(fc['value'].upper()))

        return all([fc1['field'] == fc2['field'],
                    fc1['operator'] == fc2['operator'],
                    get_comparer(fc1) == get_comparer(fc2)])
<|endoftext|>