Merge pull request #714 from pangeachat/analytics-notes

responded to comment from Will in client code, added fix for mini ana…
pull/1398/head
ggurdin 1 year ago committed by GitHub
commit 74fceeb82c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:math';
import 'package:fluffychat/pangea/constants/class_default_values.dart';
import 'package:fluffychat/pangea/constants/local.key.dart';
@ -42,8 +43,30 @@ class GetAnalyticsController {
/// Get the current level based on the number of xp points
/// The formula is calculated from XP and modeled on RPG games
// int get level => 1 + sqrt((1 + 8 * currentXP / 100) / 2).floor();
int get level => currentXP ~/ 10;
int get level => 1 + sqrt((1 + 8 * currentXP / 100) / 2).floor();
// the minimum XP required for a given level
double get minXPForLevel {
return 12.5 * (2 * pow(level - 1, 2) - 1);
}
// the minimum XP required for the next level
double get minXPForNextLevel {
return 12.5 * (2 * pow(level, 2) - 1);
}
// the progress within the current level as a percentage (0.0 to 1.0)
double get levelProgress {
final progress =
(currentXP - minXPForLevel) / (minXPForNextLevel - minXPForLevel);
return progress >= 0 ? progress : 0;
}
double get serverLevelProgress {
final progress =
(serverXP - minXPForLevel) / (minXPForNextLevel - minXPForLevel);
return progress >= 0 ? progress : 0;
}
void initialize() {
_analyticsUpdateSubscription ??= _pangeaController

@ -42,7 +42,7 @@ class MyAnalyticsController extends BaseController<AnalyticsStream> {
final int _maxMessagesCached = 10;
/// the number of minutes before an automatic update is triggered
final int _minutesBeforeUpdate = 2;
final int _minutesBeforeUpdate = 5;
/// the time since the last update that will trigger an automatic update
final Duration _timeSinceUpdate = const Duration(days: 1);
@ -120,9 +120,6 @@ class MyAnalyticsController extends BaseController<AnalyticsStream> {
}
if (filtered.isEmpty) return;
// @ggurdin - are we sure this isn't happening twice? it's also above
filtered.addAll(_getDraftUses(data.roomId));
final level = _pangeaController.analytics.level;
_addLocalMessage(eventID, filtered).then(
@ -179,8 +176,6 @@ class MyAnalyticsController extends BaseController<AnalyticsStream> {
}
}
// @ggurdin - if the point of draft uses is that we don't want to send them twice,
// then, if this is triggered here, couldn't that make a problem?
final level = _pangeaController.analytics.level;
_addLocalMessage('draft$roomID', uses).then(
(_) => _decideWhetherToUpdateAnalyticsRoom(level),
@ -201,21 +196,20 @@ class MyAnalyticsController extends BaseController<AnalyticsStream> {
/// Add a list of construct uses for a new message to the local
/// cache of recently sent messages
Future<void> _addLocalMessage(
String eventID,
// @ggurdin - why is this an eventID and not a roomID?
String cacheKey,
List<OneConstructUse> constructs,
) async {
try {
final currentCache = _pangeaController.analytics.messagesSinceUpdate;
constructs.addAll(currentCache[eventID] ?? []);
currentCache[eventID] = constructs;
constructs.addAll(currentCache[cacheKey] ?? []);
currentCache[cacheKey] = constructs;
await _setMessagesSinceUpdate(currentCache);
} catch (e, s) {
ErrorHandler.logError(
e: PangeaWarningError("Failed to add message since update: $e"),
s: s,
m: 'Failed to add message since update for eventId: $eventID',
m: 'Failed to add message since update for eventId: $cacheKey',
);
}
}
@ -248,7 +242,18 @@ class MyAnalyticsController extends BaseController<AnalyticsStream> {
/// Clears the local cache of recently sent constructs. Called before updating analytics
void clearMessagesSinceUpdate() {
_pangeaController.pStoreService.delete(PLocalKey.messagesSinceUpdate);
final localCache = _pangeaController.analytics.messagesSinceUpdate;
final draftKeys = localCache.keys.where((key) => key.startsWith('draft'));
if (draftKeys.isEmpty) {
_pangeaController.pStoreService.delete(PLocalKey.messagesSinceUpdate);
return;
}
final Map<String, List<OneConstructUse>> newCache = {};
for (final key in draftKeys) {
newCache[key] = localCache[key]!;
}
_setMessagesSinceUpdate(newCache);
}
/// Save the local cache of recently sent constructs to the local storage

@ -78,6 +78,10 @@ class OneConstructUse {
List<String> categories;
ConstructTypeEnum constructType;
ConstructUseTypeEnum useType;
/// Used to unqiuely identify the construct use. Useful in the case
/// that a users makes the same type of mistake multiple times in a
/// message, and those uses need to be disinguished.
String? id;
ConstructUseMetaData metadata;

@ -145,7 +145,6 @@ class ChoreoRecord {
lemma: name,
form: name,
constructType: ConstructTypeEnum.grammar,
// @ggurdin what is this used for?
id: "${metadata.eventId}_${step.acceptedOrIgnoredMatch!.match.offset}_${step.acceptedOrIgnoredMatch!.match.length}",
metadata: metadata,
),

@ -1,5 +1,4 @@
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/constants/analytics_constants.dart';
import 'package:fluffychat/pangea/widgets/animations/progress_bar/animated_level_dart.dart';
import 'package:fluffychat/pangea/widgets/animations/progress_bar/progress_bar_details.dart';
import 'package:flutter/material.dart';
@ -21,17 +20,11 @@ class LevelBar extends StatefulWidget {
class LevelBarState extends State<LevelBar> {
double prevWidth = 0;
double get width {
const perLevel = AnalyticsConstants.xpPerLevel;
final percent = (widget.details.currentPoints % perLevel) / perLevel;
return widget.progressBarDetails.totalWidth * percent;
}
@override
void didUpdateWidget(covariant LevelBar oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.details.currentPoints != widget.details.currentPoints) {
setState(() => prevWidth = width);
setState(() => prevWidth = widget.details.width);
}
}
@ -40,7 +33,7 @@ class LevelBarState extends State<LevelBar> {
return AnimatedLevelBar(
height: widget.progressBarDetails.height,
beginWidth: prevWidth,
endWidth: width,
endWidth: widget.details.width,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(
Radius.circular(AppConfig.borderRadius),

@ -3,10 +3,12 @@ import 'dart:ui';
class LevelBarDetails {
final Color fillColor;
final int currentPoints;
final double width;
const LevelBarDetails({
required this.fillColor,
required this.currentPoints,
required this.width,
});
}

@ -348,7 +348,6 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
nextEvent: widget._nextEvent,
previousEvent: widget._prevEvent,
),
// TODO for @ggurdin - move reactions and toolbar here
// MessageReactions(widget._event, widget.chatController.timeline!),
// const SizedBox(height: 6),
// MessagePadding(

@ -8,7 +8,7 @@ import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dar
import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart';
import 'package:flutter/material.dart';
class ToolbarButtons extends StatefulWidget {
class ToolbarButtons extends StatelessWidget {
final MessageOverlayController overlayController;
final double width;
@ -18,13 +18,8 @@ class ToolbarButtons extends StatefulWidget {
super.key,
});
@override
ToolbarButtonsState createState() => ToolbarButtonsState();
}
class ToolbarButtonsState extends State<ToolbarButtons> {
PangeaMessageEvent get pangeaMessageEvent =>
widget.overlayController.pangeaMessageEvent;
overlayController.pangeaMessageEvent;
List<MessageMode> get modes => MessageMode.values
.where((mode) => mode.isValidMode(pangeaMessageEvent.event))
@ -32,31 +27,23 @@ class ToolbarButtonsState extends State<ToolbarButtons> {
static const double iconWidth = 36.0;
MessageOverlayController get overlayController => widget.overlayController;
// @ggurdin - maybe this can be stateless now?
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final double barWidth = widget.width - iconWidth;
final double barWidth = width - iconWidth;
if (widget.overlayController.pangeaMessageEvent.isAudioMessage) {
if (overlayController.pangeaMessageEvent.isAudioMessage) {
return const SizedBox();
}
return SizedBox(
width: widget.width,
width: width,
child: Stack(
alignment: Alignment.center,
children: [
Stack(
children: [
Container(
width: widget.width,
width: width,
height: 12,
decoration: BoxDecoration(
color: MessageModeExtension.barAndLockedButtonColor(context),
@ -87,18 +74,18 @@ class ToolbarButtonsState extends State<ToolbarButtons> {
child: IconButton(
iconSize: 20,
icon: Icon(mode.icon),
color: mode == widget.overlayController.toolbarMode
color: mode == overlayController.toolbarMode
? Colors.white
: null,
isSelected: mode == widget.overlayController.toolbarMode,
isSelected: mode == overlayController.toolbarMode,
style: ButtonStyle(
backgroundColor: WidgetStateProperty.all(
mode.iconButtonColor(
context,
index,
widget.overlayController.toolbarMode,
overlayController.toolbarMode,
pangeaMessageEvent.numberOfActivitiesCompleted,
widget.overlayController.isPracticeComplete,
overlayController.isPracticeComplete,
),
),
),
@ -107,8 +94,7 @@ class ToolbarButtonsState extends State<ToolbarButtons> {
pangeaMessageEvent.numberOfActivitiesCompleted,
overlayController.isPracticeComplete,
)
? () =>
widget.overlayController.updateToolbarMode(mode)
? () => overlayController.updateToolbarMode(mode)
: null,
),
),

@ -1,7 +1,6 @@
import 'dart:async';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/constants/analytics_constants.dart';
import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/enum/construct_type_enum.dart';
import 'package:fluffychat/pangea/enum/progress_indicators_enum.dart';
@ -60,7 +59,7 @@ class LearningProgressIndicatorsState
_pangeaController.analytics.locallyCachedConstructs,
);
int get serverXP => currentXP - localXP;
int get level => currentXP ~/ AnalyticsConstants.xpPerLevel;
int get level => _pangeaController.analytics.level;
@override
void initState() {
@ -147,10 +146,13 @@ class LearningProgressIndicatorsState
? const Color.fromARGB(255, 0, 190, 83)
: Theme.of(context).colorScheme.primary,
currentPoints: currentXP,
width: levelBarWidth * _pangeaController.analytics.levelProgress,
),
LevelBarDetails(
fillColor: Theme.of(context).colorScheme.primary,
currentPoints: serverXP,
width:
levelBarWidth * _pangeaController.analytics.serverLevelProgress,
),
],
progressBarDetails: ProgressBarDetails(
@ -242,15 +244,19 @@ class LearningProgressIndicatorsState
],
),
),
Container(
height: 36,
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Stack(
alignment: Alignment.center,
children: [
Positioned(left: 16, right: 0, child: progressBar),
Positioned(left: 0, child: levelBadge),
],
Center(
child: SizedBox(
height: 36,
child: SizedBox(
width: levelBarWidth + 16,
child: Stack(
alignment: Alignment.center,
children: [
Positioned(left: 16, right: 0, child: progressBar),
Positioned(left: 0, child: levelBadge),
],
),
),
),
),
const SizedBox(height: 16),

@ -143,11 +143,9 @@ class SpanCardState extends State<SpanCard> {
}
}
/// @ggurdin - this seems like it would be including the correct answer as well
/// we only want to give this kind of points for ignored distractors
/// Returns the list of choices that are not selected
/// Returns the list of distractor choices that are not selected
List<SpanChoice>? get ignoredMatches => widget.scm.pangeaMatch?.match.choices
?.where((choice) => !choice.selected)
?.where((choice) => choice.isDistractor && !choice.selected)
.toList();
/// Returns the list of tokens from choices that are not selected

Loading…
Cancel
Save