From b37074459ea57c91aefbcf2d3e7f747941ebf0e6 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Tue, 12 Nov 2024 16:33:59 -0500 Subject: [PATCH] guess grammar category when missing, saving form when available --- assets/l10n/intl_en.arb | 4 +- .../morph_categories_and_labels.dart | 167 ++++++++++++++++++ .../controllers/put_analytics_controller.dart | 1 + .../models/analytics/constructs_model.dart | 96 ++++++---- .../practice_activity_record_model.dart | 2 + .../models/representation_content_model.dart | 3 + .../utils/{ => grammar}/get_grammar_copy.dart | 2 +- .../analytics_popup/analytics_xp_tile.dart | 2 +- 8 files changed, 240 insertions(+), 37 deletions(-) create mode 100644 lib/pangea/constants/morph_categories_and_labels.dart rename lib/pangea/utils/{ => grammar}/get_grammar_copy.dart (99%) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 2d0a3c823..e4290abd3 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4158,7 +4158,9 @@ "goToSpace": "Go to space: {space}", "@goToSpace": { "type": "text", - "space": {} + "placeholders": { + "space": {} + } }, "markAsUnread": "Mark as unread", "userLevel": "{level} - User", diff --git a/lib/pangea/constants/morph_categories_and_labels.dart b/lib/pangea/constants/morph_categories_and_labels.dart new file mode 100644 index 000000000..6eb056a53 --- /dev/null +++ b/lib/pangea/constants/morph_categories_and_labels.dart @@ -0,0 +1,167 @@ +const Map> morphCategoriesAndLabels = { + "Pos": [ + "ADJ", + "ADP", + "ADV", + "AFFIX", + "AUX", + "CCONJ", + "DET", + "INTJ", + "NOUN", + "NUM", + "PART", + "PRON", + "PROPN", + "PUNCT", + "SCONJ", + "SPACE", + "SYM", + "VERB", + "X", + ], + "AdvType": ["Adverbial", "Tim"], + "Aspect": [ + "Imp", + "Perf", + "Prog", + "Hab", + ], + "Case": [ + "Nom", + "Acc", + "Dat", + "Gen", + "Voc", + "Abl", + "Loc", + "All", + "Ins", + "Ess", + "Tra", + "Com", + "Par", + "Adv", + "Ref", + "Rel", + "Equ", + "Dis", + "Abs", + "Erg", + "Cau", + "Ben", + "Sub", + "Sup", + "Tem", + "Obl", + "Acc,Dat", + "Acc,Nom", + "Pre", + ], + "ConjType": ["Coord", "Sub", "Cmp"], + "Definite": ["Def", "Ind", "Cons"], + "Degree": [ + "Pos", + "Cmp", + "Sup", + "Abs", + ], + "Evident": ["Fh", "Nfh"], + "Foreign": ["Yes"], + "Gender": ["Masc", "Fem", "Neut", "Com"], + "Mood": [ + "Ind", + "Imp", + "Sub", + "Cnd", + "Opt", + "Jus", + "Adm", + "Des", + "Nec", + "Pot", + "Prp", + "Qot", + "Int", + ], + "NounType": ["Prop", "Comm", "Not_proper"], + "NumForm": [ + "Digit", + "Word", + "Roman", + "Letter", + ], + "NumType": [ + "Card", + "Ord", + "Mult", + "Frac", + "Sets", + "Range", + "Dist", + ], + "Number": [ + "Sing", + "Plur", + "Dual", + "Tri", + "Pauc", + "Grpa", + "Grpl", + "Inv", + ], + "Number[psor]": ["Sing", "Plur", "Dual"], + "Person": [ + "0", + "1", + "2", + "3", + "4", + ], + "Polarity": ["Pos", "Neg"], + "Polite": ["Infm", "Form", "Elev", "Humb"], + "Poss": ["Yes"], + "PrepCase": ["Npr"], + "PronType": [ + "Prs", + "Int", + "Rel", + "Dem", + "Tot", + "Neg", + "Art", + "Emp", + "Exc", + "Ind", + "Rcp", + "Int,Rel", + ], + "PunctSide": ["Ini", "Fin"], + "PunctType": [ + "Brck", + "Dash", + "Excl", + "Peri", + "Qest", + "Quot", + "Semi", + "Colo", + "Comm", + ], + "Reflex": ["Yes"], + "Tense": ["Pres", "Past", "Fut", "Imp", "Pqp", "Aor", "Eps", "Prosp"], + "VerbForm": [ + "Fin", + "Inf", + "Sup", + "Part", + "Conv", + "Vnoun", + "Ger", + "Adn", + "Lng", + ], + "VerbType": ["Mod", "Caus"], + "Voice": ["Act", "Mid", "Pass", "Antip", "Cau", "Dir", "Inv", "Rcp", "Caus"], + "X": ["X"], +}; diff --git a/lib/pangea/controllers/put_analytics_controller.dart b/lib/pangea/controllers/put_analytics_controller.dart index f484520e8..26e0507a4 100644 --- a/lib/pangea/controllers/put_analytics_controller.dart +++ b/lib/pangea/controllers/put_analytics_controller.dart @@ -189,6 +189,7 @@ class PutAnalyticsController extends BaseController { OneConstructUse( useType: useType, lemma: entry.value, + form: token.text.content, category: entry.key, constructType: ConstructTypeEnum.morph, metadata: metadata, diff --git a/lib/pangea/models/analytics/constructs_model.dart b/lib/pangea/models/analytics/constructs_model.dart index b6b13399b..22fee9ee5 100644 --- a/lib/pangea/models/analytics/constructs_model.dart +++ b/lib/pangea/models/analytics/constructs_model.dart @@ -1,5 +1,6 @@ import 'dart:developer'; +import 'package:fluffychat/pangea/constants/morph_categories_and_labels.dart'; import 'package:fluffychat/pangea/enum/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; @@ -49,12 +50,15 @@ class ConstructAnalyticsModel { } class OneConstructUse { - String? lemma; + String lemma; + + // practice activities do not currently include form in the target_construct info + // for that, this is nullable String? form; /// For vocab constructs, this is the POS. For morph /// constructs, this is the morphological category. - String? category; + String category; ConstructTypeEnum constructType; ConstructUseTypeEnum useType; @@ -70,8 +74,8 @@ class OneConstructUse { required this.lemma, required this.constructType, required this.metadata, - this.category, - this.form, + required this.category, + required this.form, this.id, }); @@ -80,27 +84,20 @@ class OneConstructUse { DateTime get timeStamp => metadata.timeStamp; factory OneConstructUse.fromJson(Map json) { - final constructType = json['constructType'] != null - ? ConstructTypeUtil.fromString(json['constructType']) - : null; - debugger(when: kDebugMode && constructType == null); + debugger(when: kDebugMode && json['constructType'] == null); - final categoryEntry = json['cat'] ?? json['categories']; - String? category; - if (categoryEntry != null) { - if ((categoryEntry is List) && categoryEntry.isNotEmpty) { - category = categoryEntry.first; - } else if (categoryEntry is String) { - category = categoryEntry; - } - } + final ConstructTypeEnum constructType = json['constructType'] != null + ? ConstructTypeUtil.fromString(json['constructType']) + : ConstructTypeEnum.vocab; return OneConstructUse( useType: ConstructUseTypeUtil.fromString(json['useType']), lemma: json['lemma'], form: json['form'], - category: category, - constructType: constructType ?? ConstructTypeEnum.vocab, + category: constructType == ConstructTypeEnum.morph + ? getCategory(json) + : "Other", + constructType: constructType, id: json['id'], metadata: ConstructUseMetaData( eventId: json['msgId'], @@ -110,21 +107,52 @@ class OneConstructUse { ); } - Map toJson() { - final Map data = { - 'useType': useType.string, - 'chatId': metadata.roomId, - 'timeStamp': metadata.timeStamp.toIso8601String(), - 'form': form, - 'msgId': metadata.eventId, - }; + Map toJson() => { + 'useType': useType.string, + 'chatId': metadata.roomId, + 'timeStamp': metadata.timeStamp.toIso8601String(), + 'form': form, + 'msgId': metadata.eventId, + 'lemma': lemma, + 'constructType': constructType.string, + 'categories': category, + 'id': id, + }; + + static String getCategory(Map json) { + final categoryEntry = json['cat'] ?? json['categories']; - data['lemma'] = lemma!; - data['constructType'] = constructType.string; + if (categoryEntry == null) { + return _guessGrammarCategory(json["lemma"]); + } - if (id != null) data['id'] = id; - data['categories'] = category; - return data; + if ((categoryEntry is List)) { + if (categoryEntry.isEmpty) { + return _guessGrammarCategory(json["lemma"]); + } + return categoryEntry.first; + } else if (categoryEntry is String) { + return categoryEntry; + } + + debugPrint( + "Category entry is not a list or string -${json['cat'] ?? json['categories']}-", + ); + return _guessGrammarCategory(json["lemma"]); + } + + static String _guessGrammarCategory(String morphLemma) { + for (final String category in morphCategoriesAndLabels.keys) { + if (morphCategoriesAndLabels[category]!.contains(morphLemma)) { + debugPrint( + "found missing construct category for $morphLemma: $category"); + return category; + } + } + ErrorHandler.logError( + m: "Morph construct lemma $morphLemma not found in morph categories and labels", + ); + return "Other"; } Room? getRoom(Client client) { @@ -140,9 +168,9 @@ class OneConstructUse { int get pointValue => useType.pointValue; ConstructIdentifier get identifier => ConstructIdentifier( - lemma: lemma!, + lemma: lemma, type: constructType, - category: category ?? "", + category: category, ); } diff --git a/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart b/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart index ffa6fd30b..74f2d0d64 100644 --- a/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart +++ b/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart @@ -143,6 +143,8 @@ class ActivityRecordResponse { .map( (construct) => OneConstructUse( lemma: construct.lemma, + // TODO - add form to practiceActivity target_construct data somehow + form: null, constructType: construct.type, useType: useType, metadata: metadata, diff --git a/lib/pangea/models/representation_content_model.dart b/lib/pangea/models/representation_content_model.dart index c3f649069..3d21d7ff6 100644 --- a/lib/pangea/models/representation_content_model.dart +++ b/lib/pangea/models/representation_content_model.dart @@ -159,6 +159,7 @@ class PangeaRepresentation { OneConstructUse( useType: useType, lemma: entry.value, + form: token.text.content, category: entry.key, constructType: ConstructTypeEnum.morph, metadata: metadata, @@ -210,6 +211,7 @@ class PangeaRepresentation { OneConstructUse( useType: ConstructUseTypeEnum.ga, lemma: entry.value, + form: token.text.content, category: entry.key, constructType: ConstructTypeEnum.morph, metadata: metadata, @@ -227,6 +229,7 @@ class PangeaRepresentation { OneConstructUse( useType: ConstructUseTypeEnum.wa, lemma: entry.value, + form: token.text.content, category: entry.key, constructType: ConstructTypeEnum.morph, metadata: metadata, diff --git a/lib/pangea/utils/get_grammar_copy.dart b/lib/pangea/utils/grammar/get_grammar_copy.dart similarity index 99% rename from lib/pangea/utils/get_grammar_copy.dart rename to lib/pangea/utils/grammar/get_grammar_copy.dart index fa7178cf9..193995b80 100644 --- a/lib/pangea/utils/get_grammar_copy.dart +++ b/lib/pangea/utils/grammar/get_grammar_copy.dart @@ -458,6 +458,6 @@ String? getGrammarCopy({ 'context': context, }, ); - return key; // Fallback to the key itself if no match is found + return lemma; // Fallback to the lemma itself if no match is found } } diff --git a/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_xp_tile.dart b/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_xp_tile.dart index b297418f7..1d89ecafd 100644 --- a/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_xp_tile.dart +++ b/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_xp_tile.dart @@ -2,7 +2,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; import 'package:fluffychat/pangea/enum/progress_indicators_enum.dart'; import 'package:fluffychat/pangea/models/analytics/construct_list_model.dart'; -import 'package:fluffychat/pangea/utils/get_grammar_copy.dart'; +import 'package:fluffychat/pangea/utils/grammar/get_grammar_copy.dart'; import 'package:flutter/material.dart'; class ConstructUsesXPTile extends StatelessWidget {