"""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"""
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)
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)
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