Exemplo n.º 1
0
"""Tests for views in the userprofile Django app"""

from django.test import RequestFactory
from django.urls import reverse

from test_plus.test import TestCase

from projectroles.app_settings import AppSettingAPI
from projectroles.tests.test_models import EXAMPLE_APP_NAME, AppSettingMixin

# App settings API
app_settings = AppSettingAPI()


class TestViewsBase(TestCase):
    """Base class for view testing"""
    def setUp(self):
        self.req_factory = RequestFactory()

        # Init superuser
        self.user = self.make_user('superuser')
        self.user.is_staff = True
        self.user.is_superuser = True
        self.user.save()


# View tests -------------------------------------------------------------------


class TestUserDetailView(TestViewsBase):
    """Tests for the user profile detail view"""
Exemplo n.º 2
0
def get_user_setting(user, app_name, setting_name):
    """Return user setting."""
    if settings.KIOSK_MODE:
        return
    setting_api = AppSettingAPI()
    return setting_api.get_app_setting(app_name, setting_name, user=user)
Exemplo n.º 3
0
    def _init_app_settings(self):
        # Set up setting query kwargs
        self.p_kwargs = ({
            'user_modifiable': True
        } if not self.current_user.is_superuser else {})
        self.app_settings = AppSettingAPI()
        self.app_plugins = sorted(get_active_plugins(), key=lambda x: x.name)

        for plugin in self.app_plugins:
            # Show non-modifiable settings to superusers
            p_settings = self.app_settings.get_setting_defs(
                APP_SETTING_SCOPE_PROJECT, plugin=plugin, **self.p_kwargs)

            for s_key, s_val in p_settings.items():
                s_field = 'settings.{}.{}'.format(plugin.name, s_key)
                s_widget_attrs = s_val.get('widget_attrs') or {}
                setting_kwargs = {
                    'required':
                    False,
                    'label':
                    s_val.get('label') or '{}.{}'.format(plugin.name, s_key),
                    'help_text':
                    s_val['description'],
                }

                if s_val['type'] == 'JSON':
                    # NOTE: Attrs MUST be supplied here (#404)
                    if 'class' in s_widget_attrs:
                        s_widget_attrs['class'] += ' sodar-json-input'

                    else:
                        s_widget_attrs['class'] = 'sodar-json-input'

                    self.fields[s_field] = forms.CharField(
                        widget=forms.Textarea(attrs=s_widget_attrs),
                        **setting_kwargs)
                    if self.instance.pk:
                        self.initial[s_field] = json.dumps(
                            self.app_settings.get_app_setting(
                                app_name=plugin.name,
                                setting_name=s_key,
                                project=self.instance,
                            ))

                    else:
                        self.initial[s_field] = json.dumps(
                            self.app_settings.get_default_setting(
                                app_name=plugin.name, setting_name=s_key))
                else:
                    if s_val['type'] == 'STRING':
                        self.fields[s_field] = forms.CharField(
                            max_length=APP_SETTING_VAL_MAXLENGTH,
                            **setting_kwargs)

                    elif s_val['type'] == 'INTEGER':
                        self.fields[s_field] = forms.IntegerField(
                            **setting_kwargs)

                    elif s_val['type'] == 'BOOLEAN':
                        self.fields[s_field] = forms.BooleanField(
                            **setting_kwargs)

                    # Add optional attributes from plugin (#404)
                    # NOTE: Experimental! Use at your own risk!
                    self.fields[s_field].widget.attrs.update(s_widget_attrs)

                    # Set initial value
                    if self.instance.pk:
                        self.initial[
                            s_field] = self.app_settings.get_app_setting(
                                app_name=plugin.name,
                                setting_name=s_key,
                                project=self.instance,
                            )

                    else:
                        self.initial[
                            s_field] = self.app_settings.get_default_setting(
                                app_name=plugin.name, setting_name=s_key)

                # Add hidden note
                if s_val.get('user_modifiable') is False:
                    self.fields[s_field].label += ' [HIDDEN]'
                    self.fields[s_field].help_text += ' [HIDDEN FROM USERS]'
    def test_submit_case_filter_umd(self, mock):
        from projectroles.app_settings import AppSettingAPI

        app_settings = AppSettingAPI()
        app_settings.set_app_setting("variants",
                                     "umd_predictor_api_token",
                                     "FAKETOKEN",
                                     user=self.user)

        return_text = "This page was created in 0.001 seconds\n\n"
        return_text += "chr{}\t{}\tXXX\t{}\t{}\t1234\t{}\t{}\tW\tY\t{}\t{}\n".format(
            self.small_vars[0].chromosome,
            str(self.small_vars[0].start),
            self.small_vars[0].ensembl_transcript_id,
            self.small_vars[0].ensembl_gene_id,
            self.small_vars[0].reference,
            self.small_vars[0].alternative,
            "98",
            "Likely Pathogenic",
        )
        return_text += "chr{}\t{}\tXXX\t{}\t{}\t1234\t{}\t{}\tW\tY\t{}\t{}\n".format(
            self.small_vars[1].chromosome,
            str(self.small_vars[1].start),
            self.small_vars[1].ensembl_transcript_id,
            self.small_vars[1].ensembl_gene_id,
            self.small_vars[1].reference,
            self.small_vars[1].alternative,
            "100",
            "Pathogenic",
        )
        return_text += "chr{}\t{}\tXXX\t{}\t{}\t1234\t{}\t{}\tW\tY\t{}\t{}\n".format(
            self.small_vars[2].chromosome,
            str(self.small_vars[2].start),
            self.small_vars[2].ensembl_transcript_id,
            self.small_vars[2].ensembl_gene_id,
            self.small_vars[2].reference,
            self.small_vars[2].alternative,
            "99",
            "Pathogenic",
        )
        mock.get(settings.VARFISH_UMD_REST_API_URL,
                 status_code=200,
                 text=return_text)

        # Enable UMD pathogenicity scoring
        self.bgjob.smallvariantquery.query_settings["patho_enabled"] = True
        self.bgjob.smallvariantquery.query_settings["patho_score"] = "umd"
        self.bgjob.smallvariantquery.save()

        # Run query
        CaseFilter(self.bgjob, self.bgjob.smallvariantquery).run()

        # Watch out. Django does not necessarily keep the order of the list when inserting into many-to-many-relationships.
        self.assertEqual(SmallVariantQueryVariantScores.objects.count(), 3)
        variant_scores = SmallVariantQueryVariantScores.objects.all()
        self.assertEqual(variant_scores[0].score, 98)
        self.assertEqual(variant_scores[1].score, 100)
        self.assertEqual(variant_scores[2].score, 99)

        self.assertEqual(SmallVariantQuery.objects.count(), 1)
        self.assertEqual(
            SmallVariantQuery.objects.first().query_results.count(), 3)
        self.assertEqual(UmdPathogenicityScoreCache.objects.count(), 3)
Exemplo n.º 5
0
class ProjectForm(SODARModelForm):
    """Form for Project creation/updating"""

    # Set up owner field
    owner = SODARUserChoiceField(label='Owner', help_text='Owner')

    class Meta:
        model = Project
        fields = ['title', 'type', 'parent', 'owner', 'description', 'readme']

    def _get_parent_choices(self, instance, user):
        """
        Return valid choices of parent categories for moving a project.

        :param instance: Project instance being updated
        :param user: Request user
        :return: List of tuples
        """
        ret = []

        # Add empty choice (root) in cases where valid
        if (user.is_superuser or not instance.parent or
            (instance.type == PROJECT_TYPE_PROJECT and DISABLE_CATEGORIES)):
            ret.append((None, '--------'))

        categories = Project.objects.filter(
            type=PROJECT_TYPE_CATEGORY).exclude(pk=instance.pk)

        if not user.is_superuser:
            categories = categories.filter(
                roles__in=RoleAssignment.objects.filter(
                    user=user,
                    role__name__in=[
                        'project owner',
                        'project delegate',
                        'project contributor',
                    ],
                ))

            # Add categories with inherited ownership
            other_categories = Project.objects.filter(
                type=PROJECT_TYPE_CATEGORY).exclude(pk__in=categories)
            categories = list(categories)

            for c in other_categories:
                if c.is_owner(user):
                    categories.append(c)

        # If instance is category, exclude children
        if instance.type == PROJECT_TYPE_CATEGORY:
            categories = [
                c for c in categories
                if c not in instance.get_children(flat=True)
            ]

        ret += [(c.sodar_uuid, c.get_full_title()) for c in categories]
        return sorted(ret, key=lambda x: x[1])

    def _init_app_settings(self):
        # Set up setting query kwargs
        self.p_kwargs = ({
            'user_modifiable': True
        } if not self.current_user.is_superuser else {})
        self.app_settings = AppSettingAPI()
        self.app_plugins = sorted(get_active_plugins(), key=lambda x: x.name)

        for plugin in self.app_plugins:
            # Show non-modifiable settings to superusers
            p_settings = self.app_settings.get_setting_defs(
                APP_SETTING_SCOPE_PROJECT, plugin=plugin, **self.p_kwargs)

            for s_key, s_val in p_settings.items():
                s_field = 'settings.{}.{}'.format(plugin.name, s_key)
                s_widget_attrs = s_val.get('widget_attrs') or {}
                setting_kwargs = {
                    'required':
                    False,
                    'label':
                    s_val.get('label') or '{}.{}'.format(plugin.name, s_key),
                    'help_text':
                    s_val['description'],
                }

                if s_val['type'] == 'JSON':
                    # NOTE: Attrs MUST be supplied here (#404)
                    if 'class' in s_widget_attrs:
                        s_widget_attrs['class'] += ' sodar-json-input'

                    else:
                        s_widget_attrs['class'] = 'sodar-json-input'

                    self.fields[s_field] = forms.CharField(
                        widget=forms.Textarea(attrs=s_widget_attrs),
                        **setting_kwargs)
                    if self.instance.pk:
                        self.initial[s_field] = json.dumps(
                            self.app_settings.get_app_setting(
                                app_name=plugin.name,
                                setting_name=s_key,
                                project=self.instance,
                            ))

                    else:
                        self.initial[s_field] = json.dumps(
                            self.app_settings.get_default_setting(
                                app_name=plugin.name, setting_name=s_key))
                else:
                    if s_val['type'] == 'STRING':
                        self.fields[s_field] = forms.CharField(
                            max_length=APP_SETTING_VAL_MAXLENGTH,
                            **setting_kwargs)

                    elif s_val['type'] == 'INTEGER':
                        self.fields[s_field] = forms.IntegerField(
                            **setting_kwargs)

                    elif s_val['type'] == 'BOOLEAN':
                        self.fields[s_field] = forms.BooleanField(
                            **setting_kwargs)

                    # Add optional attributes from plugin (#404)
                    # NOTE: Experimental! Use at your own risk!
                    self.fields[s_field].widget.attrs.update(s_widget_attrs)

                    # Set initial value
                    if self.instance.pk:
                        self.initial[
                            s_field] = self.app_settings.get_app_setting(
                                app_name=plugin.name,
                                setting_name=s_key,
                                project=self.instance,
                            )

                    else:
                        self.initial[
                            s_field] = self.app_settings.get_default_setting(
                                app_name=plugin.name, setting_name=s_key)

                # Add hidden note
                if s_val.get('user_modifiable') is False:
                    self.fields[s_field].label += ' [HIDDEN]'
                    self.fields[s_field].help_text += ' [HIDDEN FROM USERS]'

    def __init__(self, project=None, current_user=None, *args, **kwargs):
        """Override for form initialization"""
        super().__init__(*args, **kwargs)

        # Get current user for checking permissions for form items
        if current_user:
            self.current_user = current_user

        # Add settings fields
        self._init_app_settings()

        # Access parent project if present
        parent_project = None

        if project:
            parent_project = Project.objects.filter(sodar_uuid=project).first()

        # Update help texts to match DISPLAY_NAMES
        self.fields['title'].help_text = 'Title'
        self.fields['type'].help_text = 'Type of container ({} or {})'.format(
            get_display_name(PROJECT_TYPE_CATEGORY),
            get_display_name(PROJECT_TYPE_PROJECT),
        )
        self.fields['type'].choices = PROJECT_TYPE_CHOICES
        self.fields['parent'].help_text = 'Parent {} if nested'.format(
            get_display_name(PROJECT_TYPE_CATEGORY))
        self.fields['description'].help_text = 'Short description'
        self.fields[
            'readme'].help_text = 'README (optional, supports markdown)'

        ####################
        # Form modifications
        ####################

        # Modify ModelChoiceFields to use sodar_uuid
        self.fields['parent'].to_field_name = 'sodar_uuid'

        # Set readme widget with preview
        self.fields['readme'].widget = PagedownWidget(show_preview=True)

        # Updating an existing project
        if self.instance.pk:
            # Set readme value as raw markdown
            self.initial['readme'] = self.instance.readme.raw

            # Hide project type selection
            self.fields['type'].widget = forms.HiddenInput()

            # Set hidden project field for autocomplete
            self.initial['project'] = self.instance

            # Set owner value
            self.initial['owner'] = self.instance.get_owner().user.sodar_uuid

            # Hide owner widget if updating (changed in member modification UI)
            self.fields['owner'].widget = forms.HiddenInput()

            # Set valid choices for parent
            if not DISABLE_CATEGORIES:
                self.fields['parent'].choices = self._get_parent_choices(
                    self.instance, self.current_user)

                # Hide widget if no valid choices are available
                if len(self.fields['parent'].choices) == 0:
                    self.fields['parent'].widget = forms.HiddenInput()

                # Set initial value for parent
                if self.instance.parent:
                    self.initial['parent'] = self.instance.parent.sodar_uuid

            else:  # Categories disabled
                # Hide parent selection
                self.fields['parent'].widget = forms.HiddenInput()

        # Project creation
        else:
            # Set hidden project field for autocomplete
            self.initial['project'] = None

            # Hide parent selection
            self.fields['parent'].widget = forms.HiddenInput()

            # Creating a subproject
            if parent_project:
                # Parent must be current parent
                self.initial['parent'] = parent_project.sodar_uuid

                # Set current user as initial value
                self.initial['owner'] = self.current_user.sodar_uuid

                # If current user is not parent owner, disable owner select
                if (not self.current_user.is_superuser and
                        self.current_user != parent_project.get_owner().user):
                    self.fields['owner'].widget = forms.HiddenInput()

            # Creating a top level project
            else:
                # Force project type
                if getattr(settings, 'PROJECTROLES_DISABLE_CATEGORIES', False):
                    self.initial['type'] = PROJECT_TYPE_PROJECT

                else:
                    self.initial['type'] = PROJECT_TYPE_CATEGORY

                # Hide project type selection
                self.fields['type'].widget = forms.HiddenInput()

                # Set up parent field
                self.initial['parent'] = None

    def clean(self):
        """Function for custom form validation and cleanup"""
        instance_owner_as = self.instance.get_owner(
        ) if self.instance else None

        parent = self.cleaned_data.get('parent')

        # Check for category/project being placed in root
        if not parent and (not self.instance or self.instance.parent):
            if (self.cleaned_data.get('type') == PROJECT_TYPE_CATEGORY
                    and not self.current_user.is_superuser):
                self.add_error(
                    'parent',
                    'You do not have permission to place a category under root',
                )

            elif (self.cleaned_data.get('type') == PROJECT_TYPE_PROJECT
                  and not DISABLE_CATEGORIES):
                self.add_error('parent',
                               'Projects can not be placed under root')

        # Ensure title does not match parent
        if parent and parent.title == self.cleaned_data.get('title'):
            self.add_error(
                'title',
                '{} and parent titles can not be equal'.format(
                    get_display_name(self.cleaned_data.get('type'),
                                     title=True)),
            )

        # Ensure title is unique within parent
        existing_project = Project.objects.filter(
            parent=self.cleaned_data.get('parent'),
            title=self.cleaned_data.get('title'),
        ).first()

        if existing_project and (not self.instance
                                 or existing_project.pk != self.instance.pk):
            self.add_error('title', 'Title must be unique within parent')

        # Ensure owner has been set
        if not self.cleaned_data.get('owner'):
            self.add_error(
                'owner',
                'Owner must be set for {}'.format(
                    get_display_name(self.cleaned_data.get('type'))),
            )

        # Ensure owner is not changed on update (must use ownership transfer)
        if (instance_owner_as
                and self.cleaned_data.get('owner') != instance_owner_as.user):
            self.add_error(
                'owner',
                'Owner update is not allowed in this form, use Ownership '
                'Transfer instead',
            )

        # Verify settings fields
        for plugin in self.app_plugins:
            p_settings = self.app_settings.get_setting_defs(
                APP_SETTING_SCOPE_PROJECT, plugin=plugin, **self.p_kwargs)

            for s_key, s_val in p_settings.items():
                s_field = 'settings.{}.{}'.format(plugin.name, s_key)

                if s_val['type'] == 'JSON':
                    # for some reason, there is a distinct possiblity, that the
                    # initial value has been discarded and we get '' as value.
                    # Seems to only happen in automated tests. Will catch that
                    # here.
                    if not self.cleaned_data.get(s_field):
                        self.cleaned_data[s_field] = '{}'

                    try:
                        self.cleaned_data[s_field] = json.loads(
                            self.cleaned_data.get(s_field))

                    except json.JSONDecodeError as err:
                        # TODO: Shouldn't we use add_error() instead?
                        raise forms.ValidationError('Couldn\'t encode JSON\n' +
                                                    str(err))

                if not self.app_settings.validate_setting(
                        setting_type=s_val['type'],
                        setting_value=self.cleaned_data.get(s_field),
                ):
                    self.add_error(s_field, 'Invalid value')

        return self.cleaned_data