Files
qbit-data-analysis/src/main/java/com/kingsrook/qbits/dataanalysis/model/DataAnalysisFieldPossibleValueProvider.java
T
james.maesandClaude Opus 5.5 43c7571864 feat(watches): anomaly watches through the alerts SPI, contingent pickers
Watches evaluate a metric per time bucket as their owner (SPIKE, DROP,
OUT_OF_RANGE, NEW_CATEGORY, NULL_RATE_CREEP with robust baselines) on a
hidden scheduled process, raise and resolve data.analysis.anomaly alerts
through QAlerts, and offer Evaluate now / Enable / Disable record actions
and a preview endpoint (extension 1.3.0). Pivot and crosstab labels are
human (Yes/No, Average Annual Salary). Contingent fields: a watch's saved
analysis depends on its table, table names use the core tables PVS, and
analyzable/excluded fields pick from the chosen table's fields.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:34:19 -05:00

113 lines
4.5 KiB
Java

/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2025. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* 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
*
* https://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 com.kingsrook.qbits.dataanalysis.model;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.model.actions.values.SearchPossibleValueSourceInput;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
/*******************************************************************************
** Fields of the table selected in the settings form. Core has no multi-select
** PVS field yet; settings retain their comma-separated TEXT representation.
*******************************************************************************/
public class DataAnalysisFieldPossibleValueProvider implements QCustomPossibleValueProvider<String>
{
public static final String NAME = "data_analysis_fields";
/*******************************************************************************
** Without form values the table is unknown; preserve names, including CSV lists.
*******************************************************************************/
@Override
public QPossibleValue<String> getPossibleValue(Serializable idValue)
{
String name = ValueUtils.getValueAsString(idValue);
return (name == null ? null : new QPossibleValue<>(name, name));
}
/*******************************************************************************
** Offer only fields of the selected table, honoring search text and id lookups.
*******************************************************************************/
@Override
public List<QPossibleValue<String>> search(SearchPossibleValueSourceInput input)
{
List<QPossibleValue<String>> values = new ArrayList<>();
QTableMetaData table = resolveTable(input.getOtherValues());
if(table == null)
{
return (values);
}
for(QFieldMetaData field : table.getFields().values())
{
String label = field.getLabel() == null ? field.getName() : field.getLabel();
QPossibleValue<String> value = new QPossibleValue<>(field.getName(), label + " (" + field.getName() + ")");
if(CollectionUtils.nullSafeHasContents(input.getIdList()))
{
if(input.getIdList().stream().anyMatch(id -> value.getId().equals(ValueUtils.getValueAsString(id))))
{
values.add(value);
}
}
else
{
String term = input.getSearchTerm();
if(!StringUtils.hasContent(term) || value.getLabel().toLowerCase(Locale.ROOT).contains(term.toLowerCase(Locale.ROOT)))
{
values.add(value);
}
}
}
return (values);
}
/*******************************************************************************
** Resolve the table from the frontend form values, never from the config table.
*******************************************************************************/
private QTableMetaData resolveTable(Map<String, Serializable> otherValues)
{
if(otherValues == null || QContext.getQInstance() == null)
{
return (null);
}
String tableName = ValueUtils.getValueAsString(otherValues.get("table_name"));
return (StringUtils.hasContent(tableName) ? QContext.getQInstance().getTable(tableName) : null);
}
}