def test_experiment_firefox_channel_sort_does_sorting(self): ExperimentFactory.create(firefox_channel=Experiment.CHANNEL_NIGHTLY) ExperimentFactory.create(firefox_channel=Experiment.CHANNEL_RELEASE) ExperimentFactory.create(firefox_channel=Experiment.CHANNEL_BETA) ExperimentFactory.create(firefox_channel="") sorted_experiments = (Experiment.objects.annotate( firefox_channel_sort=Experiment.firefox_channel_sort()).order_by( "firefox_channel_sort").values("firefox_channel_sort")) self.assertEqual( [r["firefox_channel_sort"] for r in sorted_experiments], [ Experiment.CHANNEL_UNSET_ORDER, Experiment.CHANNEL_NIGHTLY_ORDER, Experiment.CHANNEL_BETA_ORDER, Experiment.CHANNEL_RELEASE_ORDER, ], )
def test_changelog_values(self): experiment = Experiment() experiment.save() ds_url = "{base}DS-123".format(base=settings.DS_ISSUE_HOST) bug_url = "{base}show_bug.cgi?id=123".format( base=settings.BUGZILLA_HOST) related_exp = ExperimentFactory.create() project = ProjectFactory.create() data = { "type": Experiment.TYPE_PREF, "name": "A new experiment!", "short_description": "Let us learn new things", "data_science_issue_url": ds_url, "owner": self.user.id, "analysis_owner": self.user.id, "engineering_owner": "Lisa the Engineer", "public_description": "Let us learn new public things", "related_to": [related_exp], "feature_bugzilla_url": bug_url, "related_work": "Designs: https://www.example.com/myproject/", "projects": [project], } form = ExperimentOverviewForm(request=self.request, data=data, instance=experiment) self.assertTrue(form.is_valid()) experiment = form.save() latest_changes = experiment.changes.latest() expected_data = { "analysis_owner": { "display_name": "Data Science Owner", "new_value": experiment.analysis_owner.id, "old_value": None, }, "data_science_issue_url": { "display_name": "Data Science Issue URL", "new_value": "https://jira.example.com/browse/DS-123", "old_value": None, }, "engineering_owner": { "display_name": "Engineering Owner", "new_value": "Lisa the Engineer", "old_value": None, }, "feature_bugzilla_url": { "display_name": "Feature Bugzilla URL", "new_value": "https://bugzilla.allizom.org/show_bug.cgi?id=123", "old_value": None, }, "name": { "display_name": "Public Name", "new_value": "A new experiment!", "old_value": None, }, "owner": { "display_name": "Delivery Owner", "new_value": experiment.owner.id, "old_value": None, }, "projects": { "display_name": "Related Projects", "new_value": [{ "slug": project.slug }], "old_value": None, }, "public_description": { "display_name": "Public Description", "new_value": "Let us learn new public things", "old_value": None, }, "related_to": { "display_name": "Related Deliveries", "new_value": [related_exp.id], "old_value": None, }, "related_work": { "display_name": "Related Work URLs", "new_value": "Designs: https://www.example.com/myproject/", "old_value": None, }, "short_description": { "display_name": "Internal Description", "new_value": "Let us learn new things", "old_value": None, }, } self.maxDiff = None self.assertEqual(expected_data, latest_changes.changed_values)
def get_queryset(self): qs = super().get_queryset() qs = qs.annotate( firefox_channel_sort=Experiment.firefox_channel_sort()) return qs
class ExperimentOverviewForm(ChangeLogMixin, forms.ModelForm): type = forms.ChoiceField( label="Type", choices=Experiment.FEATURE_TYPE_CHOICES(), help_text=Experiment.TYPE_HELP_TEXT, ) owner = forms.ModelChoiceField( required=True, label="Delivery Owner", help_text=Experiment.OWNER_HELP_TEXT, queryset=get_user_model().objects.all().order_by("email"), # This one forces the <select> widget to not include a blank # option which would otherwise be included because the model field # is nullable. empty_label=None, ) name = forms.CharField( required=True, label="Public Name", help_text=Experiment.PUBLIC_NAME_HELP_TEXT ) slug = forms.CharField(required=False, widget=forms.HiddenInput()) public_description = forms.CharField( required=True, label="Public Description", help_text=Experiment.PUBLIC_DESCRIPTION_HELP_TEXT, max_length=1024, widget=forms.Textarea(attrs={"rows": 3}), ) short_description = forms.CharField( required=True, label="Internal Description", help_text=Experiment.SHORT_DESCRIPTION_HELP_TEXT, widget=forms.Textarea(attrs={"rows": 3}), ) data_science_issue_url = DSIssueURLField( required=False, label="Data Science Issue URL", help_text=Experiment.DATA_SCIENCE_ISSUE_HELP_TEXT, ) engineering_owner = forms.CharField( required=False, label="Engineering Owner", help_text=Experiment.ENGINEERING_OWNER_HELP_TEXT, ) analysis_owner = forms.ModelChoiceField( required=False, label="Data Science Owner", help_text=Experiment.ANALYSIS_OWNER_HELP_TEXT, queryset=get_user_model().objects.all().order_by("email"), # This one forces the <select> widget to not include a blank # option which would otherwise be included because the model field # is nullable. empty_label="Data Science Owner", ) feature_bugzilla_url = BugzillaURLField( required=False, label="Feature Bugzilla URL", help_text=Experiment.FEATURE_BUGZILLA_HELP_TEXT, ) related_to = forms.ModelMultipleChoiceField( label="Related Deliveries", required=False, help_text="Is this related to a previously run delivery?", queryset=Experiment.objects.all(), ) projects = forms.ModelMultipleChoiceField( required=False, label="Projects", help_text=( """Is this delivery related to a specific project? Ask in #ask-experimenter on Slack if you need to add a new project""" ), queryset=Project.objects.all(), ) class Meta: model = Experiment fields = [ "type", "owner", "name", "slug", "public_description", "short_description", "data_science_issue_url", "analysis_owner", "engineering_owner", "feature_bugzilla_url", "related_to", "projects", ] related_to.widget.attrs.update({"data-live-search": "true"}) def clean_name(self): name = self.cleaned_data["name"] slug = slugify(name) if not slug: raise forms.ValidationError( "This name must include non-punctuation characters." ) if ( self.instance.pk is None and slug and self.Meta.model.objects.filter(slug=slug).exists() ): raise forms.ValidationError("This name is already in use.") return name def clean(self): cleaned_data = super().clean() if self.instance.slug: del cleaned_data["slug"] else: name = cleaned_data.get("name") cleaned_data["slug"] = slugify(name) if cleaned_data["type"] != ExperimentConstants.TYPE_ROLLOUT: required_msg = "This field is required." required_fields = ( "data_science_issue_url", "public_description", ) for required_field in required_fields: if ( not cleaned_data.get(required_field) and required_field not in self._errors ): self._errors[required_field] = [required_msg] return cleaned_data def save(self, *args, **kwargs): created = not self.instance.id experiment = super().save(*args, **kwargs) if created and experiment.is_message_experiment: experiment.locales.add( *Locale.objects.filter(code__in=experiment.MESSAGE_DEFAULT_LOCALES) ) experiment.countries.add( *Country.objects.filter(code__in=experiment.MESSAGE_DEFAULT_COUNTRIES) ) return experiment