def validate()

in jetstream/analysis.py [0:0]


    def validate(self) -> None:
        self.check_runnable()
        assert self.config.experiment.start_date is not None  # for mypy

        dates_enrollment = self.config.experiment.enrollment_period + 1

        if self.config.experiment.end_date is not None:
            end_date = self.config.experiment.end_date
            analysis_length_dates = (
                (end_date - self.config.experiment.start_date).days - dates_enrollment + 1
            )
        else:
            analysis_length_dates = 21  # arbitrary
            end_date = self.config.experiment.start_date + timedelta(
                days=analysis_length_dates + dates_enrollment - 1
            )

        if analysis_length_dates < 0:
            logging.error(
                "Proposed enrollment longer than analysis dates length:"
                + f"{self.config.experiment.normandy_slug}"
            )
            raise Exception("Cannot validate experiment")

        limits = TimeLimits.for_single_analysis_window(
            last_date_full_data=end_date.strftime("%Y-%m-%d"),
            analysis_start_days=0,
            analysis_length_dates=analysis_length_dates,
            first_enrollment_date=self.config.experiment.start_date.strftime("%Y-%m-%d"),
            num_dates_enrollment=dates_enrollment,
        )

        exp = mozanalysis.experiment.Experiment(
            experiment_slug=self.config.experiment.normandy_slug,
            start_date=self.config.experiment.start_date.strftime("%Y-%m-%d"),
            app_id=self._app_id_to_bigquery_dataset(self.config.experiment.app_id),
            analysis_unit=self.config.experiment.analysis_unit,
        )

        metrics = set()
        for v in self.config.metrics.values():
            for metric_config in v:
                if metric_config.metric.select_expression:
                    metrics.add(
                        Metric.from_metric_config(metric_config.metric).to_mozanalysis_metric()
                    )

        exposure_signal = None
        if self.config.experiment.exposure_signal:
            exposure_signal = ExposureSignal.from_exposure_signal_config(
                self.config.experiment.exposure_signal
            )
            exposure_signal = exposure_signal.to_mozanalysis_exposure_signal(limits)

        segments = []
        for segment in self.config.experiment.segments:
            segments.append(Segment.from_segment_config(segment).to_mozanalysis_segment())

        enrollments_sql = exp.build_enrollments_query(
            limits,
            PLATFORM_CONFIGS[self.config.experiment.app_name].enrollments_query_type,
            self.config.experiment.enrollment_query,
            None,
            exposure_signal,
            segments,
            self.config.experiment.sample_size or None,
        )

        self._write_sql_output(
            f"enrollments_{bq_normalize_name(self.config.experiment.normandy_slug)}",
            enrollments_sql,
        )

        dry_run_query(enrollments_sql)
        print(f"Dry running enrollments query for {self.config.experiment.normandy_slug}:")
        print(enrollments_sql)

        metrics_sql = exp.build_metrics_query(
            metrics, limits, "enrollments_table", AnalysisBasis.ENROLLMENTS
        )

        # enrollments_table doesn't get created when performing a dry run;
        # the metrics SQL is modified to include a subquery for a mock enrollments_table
        # A UNION ALL is required here otherwise the dry run fails with
        # "cannot query over table without filter over columns"
        metrics_sql = metrics_sql.replace(
            "WITH analysis_windows AS (",