fix for out-of-date cached analytics not updating

This commit is contained in:
ggurdin 2024-06-04 10:00:04 -04:00
parent 98778abc51
commit 91f5fab0ea
5 changed files with 75 additions and 36 deletions

View file

@ -117,6 +117,7 @@ class AnalyticsController extends BaseController {
ChartAnalyticsModel? getAnalyticsLocal({
TimeSpan? timeSpan,
required AnalyticsSelected defaultSelected,
required DateTime? analyticsLastUpdated,
AnalyticsSelected? selected,
bool forceUpdate = false,
bool updateExpired = false,
@ -132,8 +133,11 @@ class AnalyticsController extends BaseController {
);
if (index != -1) {
final DateTime? cachedLastUpdate =
_cachedAnalyticsModels[index].summaryLastUpdated;
if ((updateExpired && _cachedAnalyticsModels[index].isExpired) ||
forceUpdate) {
forceUpdate ||
cachedLastUpdate != analyticsLastUpdated) {
_cachedAnalyticsModels.removeAt(index);
} else {
return _cachedAnalyticsModels[index].chartAnalyticsModel;
@ -146,6 +150,7 @@ class AnalyticsController extends BaseController {
void cacheAnalytics({
required ChartAnalyticsModel chartAnalyticsModel,
required AnalyticsSelected defaultSelected,
required DateTime? summaryLastUpdated,
AnalyticsSelected? selected,
TimeSpan? timeSpan,
}) {
@ -155,6 +160,7 @@ class AnalyticsController extends BaseController {
chartAnalyticsModel: chartAnalyticsModel,
defaultSelected: defaultSelected,
selected: selected,
summaryLastUpdated: summaryLastUpdated,
),
);
}
@ -273,10 +279,13 @@ class AnalyticsController extends BaseController {
bool forceUpdate = false,
}) async {
try {
final DateTime? analyticsLastUpdated = await _pangeaController.myAnalytics
.analyticsLastUpdated(PangeaEventTypes.summaryAnalytics);
final local = getAnalyticsLocal(
defaultSelected: defaultSelected,
selected: selected,
forceUpdate: forceUpdate,
analyticsLastUpdated: analyticsLastUpdated,
);
if (local != null && !forceUpdate) {
return local;
@ -330,6 +339,7 @@ class AnalyticsController extends BaseController {
defaultSelected: defaultSelected,
selected: selected,
timeSpan: currentAnalyticsTimeSpan,
summaryLastUpdated: analyticsLastUpdated,
);
}
@ -510,26 +520,36 @@ class AnalyticsController extends BaseController {
required TimeSpan timeSpan,
required ConstructType constructType,
required AnalyticsSelected defaultSelected,
required DateTime? constructsLastUpdated,
AnalyticsSelected? selected,
}) {
final cachedEntry = _cachedConstructs
.firstWhereOrNull(
(e) =>
e.timeSpan == timeSpan &&
e.type == constructType &&
e.defaultSelected.id == defaultSelected.id &&
e.defaultSelected.type == defaultSelected.type &&
e.selected?.id == selected?.id &&
e.selected?.type == selected?.type,
)
?.events;
return cachedEntry;
final index = _cachedConstructs.indexWhere(
(e) =>
e.timeSpan == timeSpan &&
e.type == constructType &&
e.defaultSelected.id == defaultSelected.id &&
e.defaultSelected.type == defaultSelected.type &&
e.selected?.id == selected?.id &&
e.selected?.type == selected?.type,
);
if (index > -1) {
if (_cachedConstructs[index].constructsLastUpdated !=
constructsLastUpdated) {
_cachedConstructs.removeAt(index);
return null;
}
return _cachedConstructs[index].events;
}
return null;
}
void cacheConstructs({
required ConstructType constructType,
required List<ConstructAnalyticsEvent> events,
required AnalyticsSelected defaultSelected,
required DateTime? constructsLastUpdated,
AnalyticsSelected? selected,
}) {
_cachedConstructs.add(
@ -539,6 +559,7 @@ class AnalyticsController extends BaseController {
events: events,
defaultSelected: defaultSelected,
selected: selected,
constructsLastUpdated: constructsLastUpdated,
),
);
}
@ -638,11 +659,14 @@ class AnalyticsController extends BaseController {
bool removeIT = false,
bool forceUpdate = false,
}) async {
final DateTime? constructsLastUpdated = await _pangeaController.myAnalytics
.analyticsLastUpdated(PangeaEventTypes.construct);
final List<ConstructAnalyticsEvent>? local = getConstructsLocal(
timeSpan: currentAnalyticsTimeSpan,
constructType: constructType,
defaultSelected: defaultSelected,
selected: selected,
constructsLastUpdated: constructsLastUpdated,
);
if (local != null && !forceUpdate) {
_constructs = local;
@ -691,6 +715,7 @@ class AnalyticsController extends BaseController {
events: _constructs!,
defaultSelected: defaultSelected,
selected: selected,
constructsLastUpdated: constructsLastUpdated,
);
}
@ -705,12 +730,14 @@ class ConstructCacheEntry {
final List<ConstructAnalyticsEvent> events;
final AnalyticsSelected defaultSelected;
AnalyticsSelected? selected;
final DateTime? constructsLastUpdated;
ConstructCacheEntry({
required this.timeSpan,
required this.type,
required this.events,
required this.defaultSelected,
required this.constructsLastUpdated,
this.selected,
});
}
@ -721,11 +748,13 @@ class AnalyticsCacheModel {
final AnalyticsSelected defaultSelected;
AnalyticsSelected? selected;
late DateTime _createdAt;
final DateTime? summaryLastUpdated;
AnalyticsCacheModel({
required this.timeSpan,
required this.chartAnalyticsModel,
required this.defaultSelected,
required this.summaryLastUpdated,
this.selected,
}) {
_createdAt = DateTime.now();

View file

@ -316,6 +316,25 @@ class MyAnalyticsController extends BaseController {
}
return aggregatedConstructs;
}
Future<DateTime?> analyticsLastUpdated(String type) async {
final List<Room> analyticsRooms =
_pangeaController.matrixState.client.allMyAnalyticsRooms;
if (analyticsRooms.isEmpty) return null;
final List<DateTime> lastUpdates = [];
for (final analyticsRoom in analyticsRooms) {
final AnalyticsEvent? lastEvent =
await analyticsRoom.getLastAnalyticsEvent(
type,
);
if (lastEvent?.content.lastUpdated != null) {
lastUpdates.add(lastEvent!.content.lastUpdated!);
}
}
return lastUpdates.reduce(
(value, element) => value.isAfter(element) ? value : element,
);
}
}
class AggregateConstructUses {

View file

@ -65,14 +65,8 @@ class BaseAnalyticsController extends State<BaseAnalyticsPage> {
AnalyticsSelected? params, {
forceUpdate = false,
}) async {
ChartAnalyticsModel? data = pangeaController.analytics.getAnalyticsLocal(
timeSpan: currentTimeSpan,
defaultSelected: widget.defaultSelected,
selected: params,
forceUpdate: forceUpdate,
);
data ??= await pangeaController.analytics.getAnalytics(
final ChartAnalyticsModel data =
await pangeaController.analytics.getAnalytics(
defaultSelected: widget.defaultSelected,
selected: params,
forceUpdate: forceUpdate,

View file

@ -95,11 +95,13 @@ class BaseAnalyticsView extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: controller.onRefresh,
tooltip: L10n.of(context)!.refresh,
),
if (controller.widget.defaultSelected.type ==
AnalyticsEntryType.student)
IconButton(
icon: const Icon(Icons.refresh),
onPressed: controller.onRefresh,
tooltip: L10n.of(context)!.refresh,
),
TimeSpanMenuButton(
value: controller.currentTimeSpan,
onChange: (TimeSpan value) =>

View file

@ -278,14 +278,16 @@ class ConstructListViewState extends State<ConstructListView> {
@override
Widget build(BuildContext context) {
debugPrint(
"constructs lengths: ${constructs?.map((x) => '${x.lemma}: ${x.uses.length}').toList()}",
);
if (!widget.init || fetchingUses) {
return const Expanded(
child: Center(child: CircularProgressIndicator()),
);
}
if ((constructs?.isEmpty ?? true) ||
(widget.controller.currentLemma != null && currentConstruct == null)) {
if (constructs?.isEmpty ?? true) {
return Expanded(
child: Center(child: Text(L10n.of(context)!.noDataFound)),
);
@ -551,14 +553,7 @@ class ConstructMessageMetadata extends StatelessWidget {
@override
Widget build(BuildContext context) {
final String roomName = msgEvent.event.room.name.isEmpty
? Matrix.of(context)
.client
.getRoomById(msgEvent.event.room.id)
?.getLocalizedDisplayname() ??
""
: msgEvent.event.room.name;
final String roomName = msgEvent.event.room.getLocalizedDisplayname();
return Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 30, 0),
child: Column(