<commit_before>from sortedcontainers import SortedSet

from .cursor import CursorField, FieldCursor


class MultiSelectCursor(FieldCursor):
    """Cursor allowing setting and unsetting values on a MultiSelectField
    
    Respects parent field's validation
    """

    def __init__(self, *args, **kwargs):
        super(MultiSelectCursor, self).__init__(*args, **kwargs)

        self._elements = SortedSet(self._elements)

    def select(self, element):
        """Add an element to the set of selected elements
        
        Proxy to internal set.add and sync field
        """
        self._field.validate_value(element)
        self._elements.add(element)
        self._sync_field()

    def deselect(self, element):
        """Remove an element from the set of selected elements
        
        Proxy to internal set.remove and sync field
        """
        self._elements.remove(element)
        self._sync_field()


class MultiSelectField(CursorField):
    """Base class for fields that can be multi-selection or single-selection field"""

    cursor_class = MultiSelectCursor

    def __init__(self, *args, **kwargs):
        super(MultiSelectField, self).__init__(*args, **kwargs)

        selection_type = self.field_definition.get('selectionType', 'single')
        self.is_multiselect = selection_type == 'multi'

    def get_python(self):
        """Only return cursor instance if configured for multiselect"""
        if self.is_multiselect:
            return super(MultiSelectField, self).get_python()

        return self._get()

    def get_swimlane(self):
        if self.is_multiselect:
            value = self._get()
            children = []

            for child in value:
                children.append(self.cast_to_swimlane(child))

            return children

        return super(MultiSelectField, self).get_swimlane()

    def _set(self, value):
        """Expect single instance of supported_types or iterable of instances of supported_types when multi-select"""
        if self.is_multiselect:
            value = value or []
            elements = []

            for element in value:
                self.validate_value(element)
                elements.append(element)

            value = elements
        else:
            self.validate_value(value)

        self._value = value
        self._cursor = None

    def set_swimlane(self, value):
        if self.is_multiselect:
            value = value or []
            children = []

            for child in value:
                children.append(self.cast_to_python(child))

            return self._set(children)

        return super(MultiSelectField, self).set_swimlane(value)
<commit_msg>Fix clearing multiselect values list and usergroup fields<commit_after>from sortedcontainers import SortedSet

from .cursor import CursorField, FieldCursor


class MultiSelectCursor(FieldCursor):
    """Cursor allowing setting and unsetting values on a MultiSelectField
    
    Respects parent field's validation
    """

    def __init__(self, *args, **kwargs):
        super(MultiSelectCursor, self).__init__(*args, **kwargs)

        self._elements = SortedSet(self._elements)

    def select(self, element):
        """Add an element to the set of selected elements
        
        Proxy to internal set.add and sync field
        """
        self._field.validate_value(element)
        self._elements.add(element)
        self._sync_field()

    def deselect(self, element):
        """Remove an element from the set of selected elements
        
        Proxy to internal set.remove and sync field
        """
        self._elements.remove(element)
        self._sync_field()


class MultiSelectField(CursorField):
    """Base class for fields that can be multi-selection or single-selection field"""

    cursor_class = MultiSelectCursor

    def __init__(self, *args, **kwargs):
        super(MultiSelectField, self).__init__(*args, **kwargs)

        selection_type = self.field_definition.get('selectionType', 'single')
        self.is_multiselect = selection_type == 'multi'

    def get_python(self):
        """Only return cursor instance if configured for multiselect"""
        if self.is_multiselect:
            return super(MultiSelectField, self).get_python()

        return self._get()

    def get_swimlane(self):
        if self.is_multiselect:
            value = self._get()
            children = []

            for child in value:
                children.append(self.cast_to_swimlane(child))

            return children

        return super(MultiSelectField, self).get_swimlane()

    def _set(self, value):
        """Expect single instance of supported_types or iterable of instances of supported_types when multi-select"""
        if self.is_multiselect:
            value = value or []
            elements = []

            for element in value:
                self.validate_value(element)
                elements.append(element)

            value = elements
        else:
            self.validate_value(value)

        self._value = value
        self._cursor = None

    def set_python(self, value):
        """Override to remove key from raw data when empty to work with server 2.16+ validation"""
        super(MultiSelectField, self).set_python(value)

        if self.is_multiselect and not self._value:
            self.record._raw['values'].pop(self.id, None)

    def set_swimlane(self, value):
        if self.is_multiselect:
            value = value or []
            children = []

            for child in value:
                children.append(self.cast_to_python(child))

            return self._set(children)

        return super(MultiSelectField, self).set_swimlane(value)

{'fraction_non_alphanumeric': 0.08662242403073699, 'fraction_numerical': 0.0005239259517988125, 'mean_word_length': 2.3510825043885313, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1873580', 'n_tokens_mistral': 1528, 'n_tokens_neox': 1463, 'n_words': 420}