diff --git a/flow/src/org/labkey/flow/FlowMetricArea.java b/flow/src/org/labkey/flow/FlowMetricArea.java new file mode 100644 index 000000000..d22903384 --- /dev/null +++ b/flow/src/org/labkey/flow/FlowMetricArea.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2026 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.labkey.flow; + +import org.labkey.api.usageMetrics.SimpleMetricArea; + +public enum FlowMetricArea implements SimpleMetricArea +{ + SAMPLE_IMPORT("SampleImport", "Analysis import wizard events, by metric name: StudyLinkChanged (submitted after the user changed the target study)"); + + private final Definition _definition; + + FlowMetricArea(String areaName, String description) + { + _definition = new Definition(FlowModule.NAME, areaName, description); + } + + @Override + public Definition getDefinition() + { + return _definition; + } +} diff --git a/flow/src/org/labkey/flow/FlowModule.java b/flow/src/org/labkey/flow/FlowModule.java index 78f9c323f..4427a90d0 100644 --- a/flow/src/org/labkey/flow/FlowModule.java +++ b/flow/src/org/labkey/flow/FlowModule.java @@ -39,6 +39,7 @@ import org.labkey.api.search.SearchService; import org.labkey.api.security.permissions.Permission; import org.labkey.api.security.permissions.PlatformDeveloperPermission; +import org.labkey.api.usageMetrics.SimpleMetricsService; import org.labkey.api.usageMetrics.UsageMetricsService; import org.labkey.api.util.PageFlowUtil; import org.labkey.api.view.DefaultWebPartFactory; @@ -304,8 +305,9 @@ protected void startupAfterSpringConfig(ModuleContext moduleContext) if (null != svc) { FlowManager mgr = FlowManager.get(); - svc.registerUsageMetrics(NAME, mgr::getUsageMetrics); + svc.registerUsageMetrics(NAME, mgr::addUsageMetrics); } + SimpleMetricsService.get().describeFeatureAreas(FlowMetricArea.values()); FlowSchema.registerContainerListener(); } diff --git a/flow/src/org/labkey/flow/controllers/executescript/AnalysisScriptController.java b/flow/src/org/labkey/flow/controllers/executescript/AnalysisScriptController.java index c0153212c..6585b9102 100644 --- a/flow/src/org/labkey/flow/controllers/executescript/AnalysisScriptController.java +++ b/flow/src/org/labkey/flow/controllers/executescript/AnalysisScriptController.java @@ -68,7 +68,7 @@ import org.labkey.api.view.RedirectException; import org.labkey.api.view.ViewBackgroundInfo; import org.labkey.api.view.template.PageConfig; -import org.labkey.flow.FlowModule; +import org.labkey.flow.FlowMetricArea; import org.labkey.flow.FlowPreference; import org.labkey.flow.analysis.model.ExternalAnalysis; import org.labkey.flow.analysis.model.FCS; @@ -1458,8 +1458,8 @@ private void stepChooseAnalysis(ImportAnalysisForm form, BindException errors) Container targetStudy = getTargetStudy(form.getTargetStudy(), errors); if (form.getStudyChanged()) { - // Estabilish & increment a metric to measure usage of the Study Linkage feature of the Flow data import process - SimpleMetricsService.get().increment(FlowModule.NAME, "SampleImport", "StudyLinkChanged"); + // Establish & increment a metric to measure usage of the Study Linkage feature of the Flow data import process + SimpleMetricsService.get().increment(FlowMetricArea.SAMPLE_IMPORT, "StudyLinkChanged"); } if (errors.hasErrors()) diff --git a/flow/src/org/labkey/flow/persist/FlowManager.java b/flow/src/org/labkey/flow/persist/FlowManager.java index 4650d5c50..0c1e33c5e 100644 --- a/flow/src/org/labkey/flow/persist/FlowManager.java +++ b/flow/src/org/labkey/flow/persist/FlowManager.java @@ -63,6 +63,7 @@ import org.labkey.api.security.permissions.AbstractContainerScopingTest; import org.labkey.api.security.roles.EditorRole; import org.labkey.api.security.roles.ReaderRole; +import org.labkey.api.usageMetrics.UsageMetrics; import org.labkey.api.util.DateUtil; import org.labkey.api.util.FileUtil; import org.labkey.api.util.IntegerUtils; @@ -1264,6 +1265,30 @@ public Map getUsageMetrics() }); } + private static final Map USAGE_METRIC_DESCRIPTIONS = Map.ofEntries( + Map.entry("flowTempTableCount", "Flow temp tables (ffo* and fbg*) currently in the database's temp schema"), + Map.entry("keyword", "Keyword attributes across the server: total, aliases, distinct names, and distinct names ignoring case; aliascount is null when the table is empty"), + Map.entry("statistic", "Statistic attributes across the server: total, aliases, distinct names, and distinct names ignoring case; aliascount is null when the table is empty"), + Map.entry("graph", "Graph attributes across the server: total, aliases, distinct names, and distinct names ignoring case; aliascount is null when the table is empty"), + Map.entry("scriptRuns", "Runs created by an analysis script, summed across Flow-enabled folders: run count, latest created date, and FCS analysis, compensation control, and FCS file counts"), + Map.entry("workspaceRuns", "Runs imported from a FlowJo workspace, summed across Flow-enabled folders: run count, latest created date, and FCS analysis, compensation control, and FCS file counts"), + Map.entry("externalRuns", "Runs imported from an external analysis archive, summed across Flow-enabled folders: run count, latest created date, and FCS analysis, compensation control, and FCS file counts"), + Map.entry("fcsFileRuns", "Keywords (FCS file import) runs, summed across Flow-enabled folders: run count, latest created date, and FCS analysis, compensation control, and FCS file counts"), + Map.entry("analysisFolderCount", "Flow analysis folders, summed across Flow-enabled folders"), + Map.entry("analysisScriptCount", "Flow analysis scripts, summed across Flow-enabled folders"), + Map.entry("protocol", "Flow-enabled folders whose protocol has each setting: ICS metadata, complete study metadata, complete background, sample join fields, case-sensitive keywords, case-sensitive stats and graphs"), + Map.entry("sampleCount", "Samples in flow protocol sample types, summed across Flow-enabled folders; absent when no protocol has a sample type") + ); + + public UsageMetrics addUsageMetrics(UsageMetrics metrics) + { + // Per-folder keys are absent when no folder has Flow enabled + Map values = getUsageMetrics(); + USAGE_METRIC_DESCRIPTIONS.forEach((key, description) -> + metrics.reportedIf(values.containsKey(key)).put(key, values.get(key), description)); + return metrics; + } + /** * merge the values from map into the target map. * - numbers are summed diff --git a/protein/src/org/labkey/protein/ProteinModule.java b/protein/src/org/labkey/protein/ProteinModule.java index 74d39960f..26b4dfca4 100644 --- a/protein/src/org/labkey/protein/ProteinModule.java +++ b/protein/src/org/labkey/protein/ProteinModule.java @@ -54,7 +54,6 @@ import java.util.Collection; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.Set; public class ProteinModule extends DefaultModule @@ -122,7 +121,7 @@ public void doStartup(ModuleContext moduleContext) ContainerManager.addContainerListener(new ProteinContainerListener()); PipelineService service = PipelineService.get(); service.registerPipelineProvider(new ProteinAnnotationPipelineProvider(this)); - UsageMetricsService.get().registerUsageMetrics(getName(), () -> Map.of("hasGeneOntologyData", new TableSelector(ProteinSchema.getTableInfoGoTerm()).exists())); + UsageMetricsService.get().registerUsageMetrics(getName(), metrics -> metrics.put("hasGeneOntologyData", () -> new TableSelector(ProteinSchema.getTableInfoGoTerm()).exists(), "Whether Gene Ontology terms have been loaded (server-wide)")); ProteinController.registerAdminConsoleLinks(); FileContentService fcs = FileContentService.get();