71 lines
2.2 KiB
Dart
71 lines
2.2 KiB
Dart
import 'question_source_preferences.dart';
|
|
import 'questions_db.dart';
|
|
|
|
/// Picks the next question source to match configured mix percentages over time.
|
|
abstract final class QuestionSourceMix {
|
|
/// Source id with the largest deficit vs configured effective mix (sequential balance).
|
|
static String pickNextSourceId({
|
|
required Map<String, int> effectiveMixPercent,
|
|
required Map<String, int> deliveredCounts,
|
|
}) {
|
|
if (effectiveMixPercent.isEmpty) {
|
|
return 'market_history';
|
|
}
|
|
|
|
final int totalDelivered =
|
|
deliveredCounts.values.fold<int>(0, (int a, int b) => a + b);
|
|
if (totalDelivered == 0) {
|
|
return effectiveMixPercent.entries
|
|
.reduce(
|
|
(
|
|
MapEntry<String, int> a,
|
|
MapEntry<String, int> b,
|
|
) =>
|
|
a.value >= b.value ? a : b,
|
|
)
|
|
.key;
|
|
}
|
|
|
|
String? pick;
|
|
var maxDeficit = double.negativeInfinity;
|
|
for (final MapEntry<String, int> entry in effectiveMixPercent.entries) {
|
|
final double target = entry.value / 100.0;
|
|
final double actual =
|
|
(deliveredCounts[entry.key] ?? 0) / totalDelivered;
|
|
final double deficit = target - actual;
|
|
if (deficit > maxDeficit) {
|
|
maxDeficit = deficit;
|
|
pick = entry.key;
|
|
}
|
|
}
|
|
return pick ?? effectiveMixPercent.keys.first;
|
|
}
|
|
|
|
static Map<String, int> effectiveMixFromResolved(
|
|
List<ResolvedQuestionSourcePreference> prefs,
|
|
) {
|
|
return QuestionSourcePreferences.effectiveMix(prefs);
|
|
}
|
|
|
|
/// Orders unanswered rows so [preferredSourceId] is shown first (stable FIFO within source).
|
|
static List<Map<String, dynamic>> orderUnansweredByPreferredSource(
|
|
List<Map<String, dynamic>> rows, {
|
|
required String preferredSourceId,
|
|
}) {
|
|
if (rows.length <= 1) {
|
|
return rows;
|
|
}
|
|
|
|
final List<Map<String, dynamic>> preferred = <Map<String, dynamic>>[];
|
|
final List<Map<String, dynamic>> rest = <Map<String, dynamic>>[];
|
|
for (final Map<String, dynamic> row in rows) {
|
|
if (QuestionsDb.resolveSourceId(row) == preferredSourceId) {
|
|
preferred.add(row);
|
|
} else {
|
|
rest.add(row);
|
|
}
|
|
}
|
|
return <Map<String, dynamic>>[...preferred, ...rest];
|
|
}
|
|
}
|