func()

in pkg/server/clickhouse.go [245:356]


func (t *StatsServer) CreateProcessMetricDataHandler() http.HandlerFunc {
	return func(w http.ResponseWriter, request *http.Request) {
		type requestParams struct {
			TestName   string `json:"testName"`
			Branch     string `json:"branch"`
			Machine    string `json:"machine"`
			Product    string `json:"product"`
			MetricName string `json:"metricName"`
			Mode       string `json:"mode"`
		}

		var params requestParams
		decoder := json.NewDecoder(request.Body)
		defer request.Body.Close()
		if err := decoder.Decode(&params); err != nil {
			http.Error(w, "Invalid request body: "+err.Error(), http.StatusBadRequest)
			return
		}

		// Replace all matches with %
		machine := removeLastPart(params.Machine) + "%"
		// Map product to table - this is a simplified mapping, adjust as needed
		table := mapProductToTable(params.Product)
		if table == "" {
			http.Error(w, "Unknown product: "+params.Product, http.StatusBadRequest)
			return
		}

		// Query the database for metric values
		sql := fmt.Sprintf(`
		SELECT groupArray(metric_value) AS MetricValues
		FROM (
			SELECT measures.value as metric_value
			FROM perfintDev.%s ARRAY JOIN measures
			WHERE branch = '%s'
			AND measures.name = '%s'
			AND machine LIKE '%s'
			AND project = '%s'
			AND mode = '%s'
			AND generated_time >= now() - INTERVAL 1 MONTH
			ORDER BY generated_time
		)
	`, table, params.Branch, params.MetricName, machine, params.TestName, params.Mode)

		db, err := t.openDatabaseConnection()
		if err != nil {
			http.Error(w, "Failed to open database: "+err.Error(), http.StatusInternalServerError)
			return
		}
		defer func(db driver.Conn) {
			_ = db.Close()
		}(db)

		var queryResult struct {
			MetricValues []int
		}

		err = db.QueryRow(request.Context(), sql).Scan(&queryResult.MetricValues)
		if err != nil {
			http.Error(w, "Database query failed: "+err.Error(), http.StatusInternalServerError)
			return
		}

		if len(queryResult.MetricValues) == 0 {
			http.Error(w, "No data found for the specified parameters", http.StatusNotFound)
			return
		}

		// Run Change Point algorithm
		changePoints := statistic.GetChangePointIndexes(queryResult.MetricValues, 3)

		// Median difference and effect size thresholds (same as degradation detector)
		medianDifferenceThreshold := 10.0
		effectSizeThreshold := 2.0

		// Filter change points based on median difference and effect size
		validChangePoints := filterValidChangePoints(queryResult.MetricValues, changePoints, medianDifferenceThreshold, effectSizeThreshold)

		// Get the segment to analyze for max value
		// Strategy: use data after the last significant behavior change
		var segmentForAnalysis []int
		if len(validChangePoints) == 0 {
			// No valid change points - use all data
			segmentForAnalysis = queryResult.MetricValues
		} else {
			// Use data after the last valid change point
			lastChangePoint := validChangePoints[len(validChangePoints)-1]
			segmentForAnalysis = queryResult.MetricValues[lastChangePoint:]
		}

		// Remove outliers using MAD-based detection (windowSize=5, threshold=3)
		processedData := outlier_detection.RemoveOutliers(segmentForAnalysis, 5, 3.0)

		type processMetricResponse struct {
			MaxValue int `json:"maxValue"`
		}

		response := processMetricResponse{
			MaxValue: slices.Max(processedData),
		}

		jsonData, err := json.Marshal(response)
		if err != nil {
			http.Error(w, "Failed to marshal response: "+err.Error(), http.StatusInternalServerError)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		_, _ = w.Write(jsonData)
	}
}