You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
4.5 KiB
Dart
176 lines
4.5 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:http/http.dart';
|
|
|
|
import 'package:fluffychat/pangea/models/lemma.dart';
|
|
import 'package:fluffychat/pangea/network/urls.dart';
|
|
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
|
import 'package:fluffychat/widgets/matrix.dart';
|
|
import '../config/environment.dart';
|
|
import '../network/requests.dart';
|
|
|
|
class LemmaDefinitionRequest {
|
|
final Lemma _lemma;
|
|
final String partOfSpeech;
|
|
final String lemmaLang;
|
|
final String userL1;
|
|
|
|
LemmaDefinitionRequest({
|
|
required this.partOfSpeech,
|
|
required this.lemmaLang,
|
|
required this.userL1,
|
|
required Lemma lemma,
|
|
}) : _lemma = lemma;
|
|
|
|
String get lemma {
|
|
if (_lemma.text.isNotEmpty) {
|
|
return _lemma.text;
|
|
}
|
|
ErrorHandler.logError(
|
|
e: "Found lemma with empty text",
|
|
data: {
|
|
'lemma': _lemma,
|
|
'part_of_speech': partOfSpeech,
|
|
'lemma_lang': lemmaLang,
|
|
'user_l1': userL1,
|
|
},
|
|
);
|
|
return _lemma.form;
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'lemma': lemma,
|
|
'part_of_speech': partOfSpeech,
|
|
'lemma_lang': lemmaLang,
|
|
'user_l1': userL1,
|
|
};
|
|
}
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is LemmaDefinitionRequest &&
|
|
runtimeType == other.runtimeType &&
|
|
lemma == other.lemma &&
|
|
partOfSpeech == other.partOfSpeech &&
|
|
lemmaLang == other.lemmaLang &&
|
|
userL1 == other.userL1;
|
|
|
|
@override
|
|
int get hashCode =>
|
|
lemma.hashCode ^
|
|
partOfSpeech.hashCode ^
|
|
lemmaLang.hashCode ^
|
|
userL1.hashCode;
|
|
}
|
|
|
|
class LemmaDefinitionResponse {
|
|
final List<String> emoji;
|
|
final String definition;
|
|
|
|
LemmaDefinitionResponse({
|
|
required this.emoji,
|
|
required this.definition,
|
|
});
|
|
|
|
factory LemmaDefinitionResponse.fromJson(Map<String, dynamic> json) {
|
|
return LemmaDefinitionResponse(
|
|
emoji: (json['emoji'] as List<dynamic>).map((e) => e as String).toList(),
|
|
definition: json['definition'] as String,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'emoji': emoji,
|
|
'definition': definition,
|
|
};
|
|
}
|
|
|
|
@override
|
|
bool operator ==(Object other) =>
|
|
identical(this, other) ||
|
|
other is LemmaDefinitionResponse &&
|
|
runtimeType == other.runtimeType &&
|
|
emoji.length == other.emoji.length &&
|
|
emoji.every((element) => other.emoji.contains(element)) &&
|
|
definition == other.definition;
|
|
|
|
@override
|
|
int get hashCode =>
|
|
emoji.fold(0, (prev, element) => prev ^ element.hashCode) ^
|
|
definition.hashCode;
|
|
}
|
|
|
|
class LemmaDictionaryRepo {
|
|
// In-memory cache with timestamps
|
|
static final Map<LemmaDefinitionRequest, LemmaDefinitionResponse> _cache = {};
|
|
static final Map<LemmaDefinitionRequest, DateTime> _cacheTimestamps = {};
|
|
|
|
static const Duration _cacheDuration = Duration(days: 2);
|
|
|
|
static Future<LemmaDefinitionResponse> get(
|
|
LemmaDefinitionRequest request,
|
|
) async {
|
|
_clearExpiredEntries();
|
|
|
|
// Check the cache first
|
|
if (_cache.containsKey(request)) {
|
|
return _cache[request]!;
|
|
}
|
|
|
|
final Requests req = Requests(
|
|
choreoApiKey: Environment.choreoApiKey,
|
|
accessToken: MatrixState.pangeaController.userController.accessToken,
|
|
);
|
|
|
|
final requestBody = request.toJson();
|
|
final Response res = await req.post(
|
|
url: PApiUrls.lemmaDictionary,
|
|
body: requestBody,
|
|
);
|
|
|
|
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
|
final response = LemmaDefinitionResponse.fromJson(decodedBody);
|
|
|
|
// Store the response and timestamp in the cache
|
|
_cache[request] = response;
|
|
_cacheTimestamps[request] = DateTime.now();
|
|
|
|
return response;
|
|
}
|
|
|
|
/// From the cache, get a random set of cached definitions that are not for a specific lemma
|
|
static List<String> getDistractorDefinitions(
|
|
String lemma,
|
|
int count,
|
|
) {
|
|
_clearExpiredEntries();
|
|
|
|
final List<String> definitions = [];
|
|
for (final entry in _cache.entries) {
|
|
if (entry.key.lemma != lemma) {
|
|
definitions.add(entry.value.definition);
|
|
}
|
|
}
|
|
|
|
definitions.shuffle();
|
|
|
|
return definitions.take(count).toList();
|
|
}
|
|
|
|
static void _clearExpiredEntries() {
|
|
final now = DateTime.now();
|
|
final expiredKeys = _cacheTimestamps.entries
|
|
.where((entry) => now.difference(entry.value) > _cacheDuration)
|
|
.map((entry) => entry.key)
|
|
.toList();
|
|
|
|
for (final key in expiredKeys) {
|
|
_cache.remove(key);
|
|
_cacheTimestamps.remove(key);
|
|
}
|
|
}
|
|
}
|