add categories to analytics display

pull/1476/head
ggurdin 1 year ago
parent 6b643a841a
commit da6d64972b
No known key found for this signature in database
GPG Key ID: A01CB41737CBB478

@ -23,8 +23,8 @@ class ConstructListModel {
List<OneConstructUse> get uses => List<OneConstructUse> get uses =>
_uses.where((use) => use.constructType == type || type == null).toList(); _uses.where((use) => use.constructType == type || type == null).toList();
/// All unique lemmas used in the construct events // /// All unique lemmas used in the construct events
List<String> get lemmas => constructList.map((e) => e.lemma).toSet().toList(); // List<String> get lemmas => constructList.map((e) => e.lemma).toSet().toList();
/// All unique lemmas used in the construct events with non-zero points /// All unique lemmas used in the construct events with non-zero points
List<String> get lemmasWithPoints => List<String> get lemmasWithPoints =>
@ -36,8 +36,13 @@ class ConstructListModel {
final Map<String, List<OneConstructUse>> lemmaToUses = {}; final Map<String, List<OneConstructUse>> lemmaToUses = {};
for (final use in uses) { for (final use in uses) {
if (use.lemma == null) continue; if (use.lemma == null) continue;
lemmaToUses[use.lemma! + use.constructType.string] ??= []; lemmaToUses[use.lemma! +
lemmaToUses[use.lemma! + use.constructType.string]!.add(use); use.constructType.string +
(use.category ?? "Other")] ??= [];
lemmaToUses[use.lemma! +
use.constructType.string +
(use.category ?? "Other")]!
.add(use);
} }
_constructMap = lemmaToUses.map( _constructMap = lemmaToUses.map(
@ -47,6 +52,7 @@ class ConstructListModel {
uses: value, uses: value,
constructType: value.first.constructType, constructType: value.first.constructType,
lemma: value.first.lemma!, lemma: value.first.lemma!,
category: value.first.category,
), ),
), ),
); );
@ -68,7 +74,7 @@ class ConstructListModel {
_constructList = _constructMap!.values.toList(); _constructList = _constructMap!.values.toList();
_constructList!.sort((a, b) { _constructList!.sort((a, b) {
final comp = b.uses.length.compareTo(a.uses.length); final comp = b.points.compareTo(a.points);
if (comp != 0) return comp; if (comp != 0) return comp;
return a.lemma.compareTo(b.lemma); return a.lemma.compareTo(b.lemma);
}); });
@ -79,6 +85,15 @@ class ConstructListModel {
List<ConstructUses> get constructListWithPoints => List<ConstructUses> get constructListWithPoints =>
constructList.where((constructUse) => constructUse.points > 0).toList(); constructList.where((constructUse) => constructUse.points > 0).toList();
Map<String, List<ConstructUses>> get categoriesToUses {
final Map<String, List<ConstructUses>> categoriesMap = {};
for (final use in constructListWithPoints) {
categoriesMap[use.category] ??= [];
categoriesMap[use.category]!.add(use);
}
return categoriesMap;
}
get maxXPPerLemma { get maxXPPerLemma {
return type != null return type != null
? type!.maxXPPerLemma ? type!.maxXPPerLemma
@ -141,12 +156,14 @@ class ConstructUses {
final List<OneConstructUse> uses; final List<OneConstructUse> uses;
final ConstructTypeEnum constructType; final ConstructTypeEnum constructType;
final String lemma; final String lemma;
final String? _category;
ConstructUses({ ConstructUses({
required this.uses, required this.uses,
required this.constructType, required this.constructType,
required this.lemma, required this.lemma,
}); required category,
}) : _category = category;
// Total points for all uses of this lemma // Total points for all uses of this lemma
int get points { int get points {
@ -165,6 +182,8 @@ class ConstructUses {
}); });
return _lastUsed = lastUse; return _lastUsed = lastUse;
} }
String get category => _category ?? "Other";
} }
/// One lemma, a use type, and a list of uses /// One lemma, a use type, and a list of uses

@ -74,7 +74,11 @@ class ConstructAnalyticsModel {
class OneConstructUse { class OneConstructUse {
String? lemma; String? lemma;
String? form; String? form;
/// For vocab constructs, this is the POS. For morph
/// constructs, this is the morphological category.
String? category; String? category;
ConstructTypeEnum constructType; ConstructTypeEnum constructType;
ConstructUseTypeEnum useType; ConstructUseTypeEnum useType;

@ -16,8 +16,76 @@ class AnalyticsPopup extends StatelessWidget {
super.key, super.key,
}); });
List<MapEntry<String, List<ConstructUses>>> get categoriesToUses {
final entries = constructsModel.categoriesToUses.entries.toList();
// Sort the list with custom logic
entries.sort((a, b) {
// Check if one of the keys is 'Other'
if (a.key == 'Other') return 1;
if (b.key == 'Other') return -1;
// Sort by the length of the list in descending order
final aTotalPoints = a.value.fold<int>(
0,
(previousValue, element) => previousValue + element.points,
);
final bTotalPoints = b.value.fold<int>(
0,
(previousValue, element) => previousValue + element.points,
);
return bTotalPoints.compareTo(aTotalPoints);
});
return entries;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Widget? dialogContent;
final bool hasNoData = constructsModel.constructListWithPoints.isEmpty;
final bool hasNoCategories = constructsModel.categoriesToUses.length == 1 &&
constructsModel.categoriesToUses.keys.first == "Other";
if (hasNoData) {
dialogContent = Center(child: Text(L10n.of(context)!.noDataFound));
} else if (hasNoCategories) {
dialogContent = ListView.builder(
itemCount: constructsModel.constructListWithPoints.length,
itemBuilder: (context, index) {
return ConstructUsesXPTile(
indicator: indicator,
constructsModel: constructsModel,
constructUses: constructsModel.constructListWithPoints[index],
);
},
);
} else {
dialogContent = ListView.builder(
itemCount: categoriesToUses.length,
itemBuilder: (context, index) {
final category = categoriesToUses[index];
return Column(
children: [
ExpansionTile(
title: Text(
category.key != 'Other'
? getGrammarCopy(category.key, context)
: category.key,
),
children: category.value.map((constructUses) {
return ConstructUsesXPTile(
indicator: indicator,
constructsModel: constructsModel,
constructUses: constructUses,
);
}).toList(),
),
const Divider(height: 1),
],
);
},
);
}
return Dialog( return Dialog(
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints( constraints: const BoxConstraints(
@ -36,46 +104,49 @@ class AnalyticsPopup extends StatelessWidget {
), ),
body: Padding( body: Padding(
padding: const EdgeInsets.symmetric(vertical: 20), padding: const EdgeInsets.symmetric(vertical: 20),
child: constructsModel.constructListWithPoints.isEmpty child: dialogContent,
? Center( ),
child: Text(L10n.of(context)!.noDataFound), ),
) ),
: ListView.builder( ),
itemCount: constructsModel.constructListWithPoints.length, );
itemBuilder: (context, index) { }
}
class ConstructUsesXPTile extends StatelessWidget {
final ProgressIndicatorEnum indicator;
final ConstructListModel constructsModel;
final ConstructUses constructUses;
const ConstructUsesXPTile({
required this.indicator,
required this.constructsModel,
required this.constructUses,
super.key,
});
@override
Widget build(BuildContext context) {
final lemma = constructUses.lemma;
return Tooltip( return Tooltip(
message: message: "${constructUses.points} / ${constructsModel.maxXPPerLemma}",
"${constructsModel.constructListWithPoints[index].points} / ${constructsModel.maxXPPerLemma}",
child: ListTile( child: ListTile(
onTap: () {}, onTap: () {},
title: Text( title: Text(
constructsModel.type == ConstructTypeEnum.morph constructsModel.type == ConstructTypeEnum.morph
? getGrammarCopy( ? getGrammarCopy(lemma, context)
constructsModel : lemma,
.constructListWithPoints[index].lemma,
context,
)
: constructsModel
.constructListWithPoints[index].lemma,
), ),
subtitle: LinearProgressIndicator( subtitle: LinearProgressIndicator(
value: constructsModel value: constructUses.points / constructsModel.maxXPPerLemma,
.constructListWithPoints[index].points /
constructsModel.maxXPPerLemma,
minHeight: 20, minHeight: 20,
borderRadius: const BorderRadius.all( borderRadius: const BorderRadius.all(
Radius.circular(AppConfig.borderRadius), Radius.circular(AppConfig.borderRadius),
), ),
color: indicator.color(context), color: indicator.color(context),
), ),
contentPadding: contentPadding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric(horizontal: 20), horizontal: 20,
),
);
},
),
),
),
), ),
), ),
); );

Loading…
Cancel
Save