<commit_before>import datetime
import json
import jsonschema
import logging
import os.path
import yaml

from django.conf import settings
from django.contrib.auth.models import Group
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.urls import reverse
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.db import models
from django.db.models.signals import pre_delete
from django.dispatch.dispatcher import receiver
from django.forms.models import model_to_dict
from django.utils.safestring import mark_safe

from rdrf.helpers.utils import check_calculation
from rdrf.helpers.utils import format_date, parse_iso_datetime
from rdrf.events.events import EventType

from rdrf.forms.fields.jsonb import DataField

logger = logging.getLogger(__name__)


class InvalidStructureError(Exception):
    pass


class InvalidQuestionnaireError(Exception):
    pass


def new_style_questionnaire(registry):
    for form_model in registry.forms:
        if form_model.questionnaire_questions:
            if len(form_model.questionnaire_list) > 0:
                return True
    return False


class SectionManager(models.Manager):

    def get_by_natural_key(self, code):
        return self.get(code=code)


class Section(models.Model):
    objects = SectionManager()

    """
    A group of fields that appear on a form as a unit
    """
    code = models.CharField(max_length=100, unique=True)
    display_name = models.CharField(max_length=200)
    questionnaire_display_name = models.CharField(max_length=200, blank=True)
    elements = models.TextField()
    allow_multiple = models.BooleanField(
        default=False, help_text="Allow extra items to be added")
    extra = models.IntegerField(
        blank=True, null=True, help_text="Extra rows to show if allow_multiple checked")
    questionnaire_help = models.TextField(blank=True)

    def natural_key(self):
        return (self.code, )

    def __str__(self):
        return self.code

    def get_elements(self):
        return [code.strip() for code in self.elements.split(",")]

    @property
    def cde_models(self):
        codes = self.get_elements()
        qs = CommonDataElement.objects.filter(code__in=codes)
        cdes = {cde.code: cde for cde in qs}
        return [cdes[code] for code in codes]

    def clean(self):
        errors = {}
        codes = set(self.get_elements())
        qs = CommonDataElement.objects.filter(code__in=codes)
        missing = sorted(codes - set(qs.values_list("code", flat=True)))

        if missing:
            errors["elements"] = [
                ValidationError(
                    "section %s refers to CDE with code %s which doesn't exist" %
                    (self.display_name, code)) for code in missing]

        if " " in self.code:
            errors["code"] = ValidationError(
                "Section %s code '%s' contains spaces" %
                (self.display_name, self.code))

        if errors:
            raise ValidationError(errors)
<commit_msg>#812 fix: do not accept '&' in section code<commit_after>import datetime
import json
import jsonschema
import logging
import os.path
import yaml

from django.conf import settings
from django.contrib.auth.models import Group
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.urls import reverse
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.db import models
from django.db.models.signals import pre_delete
from django.dispatch.dispatcher import receiver
from django.forms.models import model_to_dict
from django.utils.safestring import mark_safe

from rdrf.helpers.utils import check_calculation
from rdrf.helpers.utils import format_date, parse_iso_datetime
from rdrf.events.events import EventType

from rdrf.forms.fields.jsonb import DataField

logger = logging.getLogger(__name__)


class InvalidStructureError(Exception):
    pass


class InvalidQuestionnaireError(Exception):
    pass


def new_style_questionnaire(registry):
    for form_model in registry.forms:
        if form_model.questionnaire_questions:
            if len(form_model.questionnaire_list) > 0:
                return True
    return False


class SectionManager(models.Manager):

    def get_by_natural_key(self, code):
        return self.get(code=code)


class Section(models.Model):
    objects = SectionManager()

    """
    A group of fields that appear on a form as a unit
    """
    code = models.CharField(max_length=100, unique=True)
    display_name = models.CharField(max_length=200)
    questionnaire_display_name = models.CharField(max_length=200, blank=True)
    elements = models.TextField()
    allow_multiple = models.BooleanField(
        default=False, help_text="Allow extra items to be added")
    extra = models.IntegerField(
        blank=True, null=True, help_text="Extra rows to show if allow_multiple checked")
    questionnaire_help = models.TextField(blank=True)

    def natural_key(self):
        return (self.code, )

    def __str__(self):
        return self.code

    def get_elements(self):
        return [code.strip() for code in self.elements.split(",")]

    @property
    def cde_models(self):
        codes = self.get_elements()
        qs = CommonDataElement.objects.filter(code__in=codes)
        cdes = {cde.code: cde for cde in qs}
        return [cdes[code] for code in codes]

    def clean(self):
        errors = {}
        codes = set(self.get_elements())
        qs = CommonDataElement.objects.filter(code__in=codes)
        missing = sorted(codes - set(qs.values_list("code", flat=True)))

        if missing:
            errors["elements"] = [
                ValidationError(
                    "section %s refers to CDE with code %s which doesn't exist" %
                    (self.display_name, code)) for code in missing]

        if any(x in self.code for x in (" ", "&")):
            errors["code"] = ValidationError(
                "Section %s code '%s' should not contain spaces or &" %
                (self.display_name, self.code))

        if errors:
            raise ValidationError(errors)

{'fraction_non_alphanumeric': 0.08501118568232663, 'fraction_numerical': 0.0036752956216043464, 'mean_word_length': 3.1838235294117645, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, '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': '1543094', 'n_tokens_mistral': 1794, 'n_tokens_neox': 1725, 'n_words': 488}