feat: level up summary (#2182)
* remove send local analytics to matrix on level up * complete implementation of level up summary * generated * fix model key issues that prevents parsing request and response * fix env * generated * improve level up summary to utilize existing state event * generated --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: ggurdin <46800240+ggurdin@users.noreply.github.com>
This commit is contained in:
parent
bf102a33ef
commit
b104069d31
10 changed files with 449 additions and 112 deletions
|
|
@ -4814,8 +4814,18 @@
|
|||
"home": "Home",
|
||||
"join": "Join",
|
||||
"learnByTexting": "Learn by texting",
|
||||
"levelSummaryTrigger": "View summary",
|
||||
"levelSummaryPopupTitle": "Level {level} Summary",
|
||||
"@levelSummaryPopupTitle": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"level": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
},
|
||||
"startChatting": "Start chatting",
|
||||
"referFriends": "Refer friends",
|
||||
"referFriendDialogTitle": "Invite a friend to your conversation",
|
||||
"referFriendDialogDesc": "Do you have a friend who is excited to learn a new language with you? Then copy and send this invitation link to join and start chatting with you today."
|
||||
}
|
||||
}
|
||||
|
|
@ -3793,5 +3793,15 @@
|
|||
"@createASpace": {
|
||||
"type": "String",
|
||||
"placeholders": {}
|
||||
},
|
||||
"levelSummaryTrigger": "Đọc báo cáo",
|
||||
"levelSummaryPopupTitle": "Tóm tắt cấp {level}",
|
||||
"@levelSummaryPopupTitle": {
|
||||
"type": "String",
|
||||
"placeholders": {
|
||||
"level": {
|
||||
"type": "int"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -365,18 +365,30 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
|
||||
_levelSubscription = pangeaController.getAnalytics.stateStream
|
||||
.where(
|
||||
(update) =>
|
||||
update is Map<String, dynamic> && update['level_up'] != null,
|
||||
)
|
||||
(update) => update is Map<String, dynamic> && update['level_up'] != null,
|
||||
)
|
||||
// .listen(
|
||||
// (update) => Future.delayed(
|
||||
// const Duration(milliseconds: 500),
|
||||
// () => LevelUpUtil.showLevelUpDialog(
|
||||
// update['level_up'],
|
||||
// context,
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
.listen(
|
||||
(update) => Future.delayed(
|
||||
const Duration(milliseconds: 500),
|
||||
() => LevelUpUtil.showLevelUpDialog(
|
||||
update['level_up'],
|
||||
context,
|
||||
),
|
||||
),
|
||||
// remove delay now that GetAnalyticsController._onLevelUp
|
||||
// is async is should take roughly 500ms to make requests anyway
|
||||
(update) {
|
||||
LevelUpUtil.showLevelUpDialog(
|
||||
update['level_up'],
|
||||
update['analytics_room_id'],
|
||||
update["construct_summary_state_event_id"],
|
||||
update['construct_summary'],
|
||||
context,
|
||||
);
|
||||
},
|
||||
);
|
||||
// Pangea#
|
||||
_tryLoadTimeline();
|
||||
if (kIsWeb) {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import 'package:fluffychat/pangea/common/constants/local.key.dart';
|
|||
import 'package:fluffychat/pangea/common/controllers/base_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_repo.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
|
||||
|
||||
|
|
@ -141,8 +143,12 @@ class GetAnalyticsController extends BaseController {
|
|||
if (analyticsUpdate.type == AnalyticsUpdateType.server) {
|
||||
await _getConstructs(forceUpdate: true);
|
||||
}
|
||||
if (oldLevel < constructListModel.level) _onLevelUp();
|
||||
if (oldLevel > constructListModel.level) await _onLevelDown(oldLevel);
|
||||
if (oldLevel < constructListModel.level) {
|
||||
await _onLevelUp(oldLevel, constructListModel.level);
|
||||
}
|
||||
if (oldLevel > constructListModel.level) {
|
||||
await _onLevelDown(constructListModel.level, oldLevel);
|
||||
}
|
||||
_updateAnalyticsStream(origin: analyticsUpdate.origin);
|
||||
// Update public profile each time that new analytics are added.
|
||||
// If the level hasn't changed, this will not send an update to the server.
|
||||
|
|
@ -158,13 +164,22 @@ class GetAnalyticsController extends BaseController {
|
|||
}) =>
|
||||
analyticsStream.add(AnalyticsStreamUpdate(origin: origin));
|
||||
|
||||
void _onLevelUp() {
|
||||
setState({'level_up': constructListModel.level});
|
||||
Future<void> _onLevelUp(final int lowerLevel, final int upperLevel) async {
|
||||
final result = await _generateLevelUpAnalyticsAndSaveToStateEvent(
|
||||
lowerLevel,
|
||||
upperLevel,
|
||||
);
|
||||
setState({
|
||||
'level_up': constructListModel.level,
|
||||
'analytics_room_id': _client.analyticsRoomLocal(_l2!)?.id,
|
||||
"construct_summary_state_event_id": result?.stateEventId,
|
||||
"construct_summary": result?.summary,
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _onLevelDown(final prevLevel) async {
|
||||
Future<void> _onLevelDown(final int lowerLevel, final int upperLevel) async {
|
||||
final offset =
|
||||
_calculateMinXpForLevel(prevLevel) - constructListModel.totalXP;
|
||||
_calculateMinXpForLevel(lowerLevel) - constructListModel.totalXP;
|
||||
await _pangeaController.userController.addXPOffset(offset);
|
||||
constructListModel.updateConstructs(
|
||||
[],
|
||||
|
|
@ -344,6 +359,90 @@ class GetAnalyticsController extends BaseController {
|
|||
);
|
||||
_cache.add(entry);
|
||||
}
|
||||
|
||||
Future<GenerateConstructSummaryResult?>
|
||||
_generateLevelUpAnalyticsAndSaveToStateEvent(
|
||||
final int lowerLevel,
|
||||
final int upperLevel,
|
||||
) async {
|
||||
// generate level up analytics as a construct summary
|
||||
ConstructSummary summary;
|
||||
try {
|
||||
final int maxXP = _calculateMinXpForLevel(upperLevel);
|
||||
final int minXP = _calculateMinXpForLevel(lowerLevel);
|
||||
int diffXP = maxXP - minXP;
|
||||
if (diffXP < 0) diffXP = 0;
|
||||
|
||||
// compute construct use of current level
|
||||
final List<OneConstructUse> constructUseOfCurrentLevel = [];
|
||||
int score = 0;
|
||||
for (final use in constructListModel.uses) {
|
||||
constructUseOfCurrentLevel.add(use);
|
||||
score += use.pointValue;
|
||||
if (score >= diffXP) break;
|
||||
}
|
||||
|
||||
// extract construct use message bodies for analytics
|
||||
List<String?>? constructUseMessageContentBodies = [];
|
||||
for (final use in constructUseOfCurrentLevel) {
|
||||
try {
|
||||
final useMessage = await use.getEvent(_client);
|
||||
final useMessageBody = useMessage?.content["body"];
|
||||
if (useMessageBody is String) {
|
||||
constructUseMessageContentBodies.add(useMessageBody);
|
||||
} else {
|
||||
constructUseMessageContentBodies.add(null);
|
||||
}
|
||||
} catch (e) {
|
||||
constructUseMessageContentBodies.add(null);
|
||||
}
|
||||
}
|
||||
if (constructUseMessageContentBodies.length !=
|
||||
constructUseOfCurrentLevel.length) {
|
||||
constructUseMessageContentBodies = null;
|
||||
}
|
||||
|
||||
final request = ConstructSummaryRequest(
|
||||
constructs: constructUseOfCurrentLevel,
|
||||
constructUseMessageContentBodies: constructUseMessageContentBodies,
|
||||
language: _l2!.langCodeShort,
|
||||
upperLevel: upperLevel,
|
||||
lowerLevel: lowerLevel,
|
||||
);
|
||||
|
||||
final response = await ConstructRepo.generateConstructSummary(request);
|
||||
summary = response.summary;
|
||||
} catch (e) {
|
||||
debugPrint("Error generating level up analytics: $e");
|
||||
ErrorHandler.logError(e: e, data: {'e': e});
|
||||
return null;
|
||||
}
|
||||
String stateEventId;
|
||||
try {
|
||||
final Room? analyticsRoom = _client.analyticsRoomLocal(_l2!);
|
||||
if (analyticsRoom == null) {
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
data: {'e': e, 'message': "Analytics room not found for user"},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
stateEventId = await _client.setRoomStateWithKey(
|
||||
analyticsRoom.id,
|
||||
PangeaEventTypes.constructSummary,
|
||||
'',
|
||||
summary.toJson(),
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint("Error saving construct summary room: $e");
|
||||
ErrorHandler.logError(e: e, data: {'e': e});
|
||||
return null;
|
||||
}
|
||||
return GenerateConstructSummaryResult(
|
||||
stateEventId: stateEventId,
|
||||
summary: summary,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AnalyticsCacheEntry {
|
||||
|
|
|
|||
87
lib/pangea/analytics_misc/level_summary.dart
Normal file
87
lib/pangea/analytics_misc/level_summary.dart
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/constructs/construct_repo.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
// New component renamed to ConstructSummaryAlertDialog with a max width
|
||||
class ConstructSummaryAlertDialog extends StatelessWidget {
|
||||
final String title;
|
||||
final String content;
|
||||
|
||||
const ConstructSummaryAlertDialog({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.content,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(title),
|
||||
content: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 400),
|
||||
child: Text(content),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(L10n.of(context).close),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LevelSummaryDialog extends StatelessWidget {
|
||||
final int level;
|
||||
final String analyticsRoomId;
|
||||
final String summaryStateEventId;
|
||||
final ConstructSummary? constructSummary;
|
||||
|
||||
const LevelSummaryDialog({
|
||||
super.key,
|
||||
required this.analyticsRoomId,
|
||||
required this.level,
|
||||
required this.summaryStateEventId,
|
||||
this.constructSummary,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Client client = Matrix.of(context).client;
|
||||
final futureSummary = client
|
||||
.getOneRoomEvent(analyticsRoomId, summaryStateEventId)
|
||||
.then((rawEvent) => ConstructSummary.fromJson(rawEvent.content));
|
||||
if (constructSummary != null) {
|
||||
return ConstructSummaryAlertDialog(
|
||||
title: L10n.of(context).levelSummaryPopupTitle(level),
|
||||
content: constructSummary!.textSummary,
|
||||
);
|
||||
} else {
|
||||
return FutureBuilder<ConstructSummary>(
|
||||
future: futureSummary,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError) {
|
||||
return ConstructSummaryAlertDialog(
|
||||
title: L10n.of(context).levelSummaryPopupTitle(level),
|
||||
content: L10n.of(context).error502504Desc,
|
||||
);
|
||||
} else if (snapshot.hasData) {
|
||||
final constructSummary = snapshot.data!;
|
||||
return ConstructSummaryAlertDialog(
|
||||
title: L10n.of(context).levelSummaryPopupTitle(level),
|
||||
content: constructSummary.textSummary,
|
||||
);
|
||||
} else {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,15 @@ import 'package:http/http.dart' as http;
|
|||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/analytics_constants.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_repo.dart';
|
||||
import 'level_summary.dart';
|
||||
|
||||
class LevelUpUtil {
|
||||
static void showLevelUpDialog(
|
||||
int level,
|
||||
String? analyticsRoomId,
|
||||
String? summaryStateEventId,
|
||||
ConstructSummary? constructSummary,
|
||||
BuildContext context,
|
||||
) {
|
||||
final player = AudioPlayer();
|
||||
|
|
@ -26,6 +31,9 @@ class LevelUpUtil {
|
|||
context: context,
|
||||
builder: (context) => LevelUpAnimation(
|
||||
level: level,
|
||||
analyticsRoomId: analyticsRoomId,
|
||||
summaryStateEventId: summaryStateEventId,
|
||||
constructSummary: constructSummary,
|
||||
),
|
||||
).then((_) => player.dispose());
|
||||
}
|
||||
|
|
@ -33,9 +41,17 @@ class LevelUpUtil {
|
|||
|
||||
class LevelUpAnimation extends StatefulWidget {
|
||||
final int level;
|
||||
final String? analyticsRoomId;
|
||||
final String? summary;
|
||||
final String? summaryStateEventId;
|
||||
final ConstructSummary? constructSummary;
|
||||
|
||||
const LevelUpAnimation({
|
||||
required this.level,
|
||||
required this.analyticsRoomId,
|
||||
this.summary,
|
||||
this.summaryStateEventId,
|
||||
this.constructSummary,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
|
@ -43,11 +59,7 @@ class LevelUpAnimation extends StatefulWidget {
|
|||
LevelUpAnimationState createState() => LevelUpAnimationState();
|
||||
}
|
||||
|
||||
class LevelUpAnimationState extends State<LevelUpAnimation>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _animationController;
|
||||
late Animation<Offset> _slideAnimation;
|
||||
|
||||
class LevelUpAnimationState extends State<LevelUpAnimation> {
|
||||
Uint8List? bytes;
|
||||
final imageURL =
|
||||
"${AppConfig.assetsBaseURL}/${AnalyticsConstants.levelUpImageFileName}";
|
||||
|
|
@ -55,52 +67,13 @@ class LevelUpAnimationState extends State<LevelUpAnimation>
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadImageData().then((resp) {
|
||||
if (bytes == null) return;
|
||||
_animationController.forward().then((_) {
|
||||
if (mounted) Navigator.of(context).pop();
|
||||
});
|
||||
}).catchError((e) {
|
||||
_loadImageData().catchError((e) {
|
||||
if (mounted) Navigator.of(context).pop();
|
||||
});
|
||||
|
||||
_animationController = AnimationController(
|
||||
duration: const Duration(milliseconds: 2500),
|
||||
vsync: this,
|
||||
);
|
||||
|
||||
_slideAnimation = TweenSequence<Offset>(
|
||||
<TweenSequenceItem<Offset>>[
|
||||
// Slide up from the bottom of the screen to the middle
|
||||
TweenSequenceItem<Offset>(
|
||||
tween: Tween<Offset>(begin: const Offset(0, 2), end: Offset.zero)
|
||||
.chain(CurveTween(curve: Curves.easeInOut)),
|
||||
weight: 2.0, // Adjust weight for the duration of the slide-up
|
||||
),
|
||||
// Pause in the middle
|
||||
TweenSequenceItem<Offset>(
|
||||
tween: Tween<Offset>(begin: Offset.zero, end: Offset.zero)
|
||||
.chain(CurveTween(curve: Curves.linear)),
|
||||
weight: 8.0, // Adjust weight for the pause duration
|
||||
),
|
||||
// Slide up and off the screen
|
||||
TweenSequenceItem<Offset>(
|
||||
tween: Tween<Offset>(begin: Offset.zero, end: const Offset(0, -2))
|
||||
.chain(CurveTween(curve: Curves.easeInOut)),
|
||||
weight: 2.0, // Adjust weight for the slide-off duration
|
||||
),
|
||||
],
|
||||
).animate(
|
||||
CurvedAnimation(
|
||||
parent: _animationController,
|
||||
curve: Curves.linear, // Keep overall animation smooth
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -119,45 +92,67 @@ class LevelUpAnimationState extends State<LevelUpAnimation>
|
|||
return const SizedBox();
|
||||
}
|
||||
|
||||
Widget content = Image.memory(
|
||||
bytes!,
|
||||
height: kIsWeb ? 350 : 250,
|
||||
);
|
||||
|
||||
if (!kIsWeb) {
|
||||
content = OverflowBox(
|
||||
maxWidth: double.infinity,
|
||||
child: content,
|
||||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onDoubleTap: Navigator.of(context).pop,
|
||||
child: Dialog.fullscreen(
|
||||
backgroundColor: Colors.transparent,
|
||||
child: Center(
|
||||
child: SlideTransition(
|
||||
position: _slideAnimation,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
content,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 100),
|
||||
child: Text(
|
||||
L10n.of(context).levelPopupTitle(widget.level),
|
||||
style: const TextStyle(
|
||||
fontSize: kIsWeb ? 40 : 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
return Dialog(
|
||||
backgroundColor: Colors.transparent,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Banner image
|
||||
Image.memory(
|
||||
bytes!,
|
||||
height: kIsWeb ? 350 : 250,
|
||||
width: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
// Overlay: centered title and close button
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: kIsWeb ? 200 : 100,
|
||||
), // Added hardcoded padding above the text
|
||||
child: Text(
|
||||
L10n.of(context).levelPopupTitle(widget.level),
|
||||
style: const TextStyle(
|
||||
fontSize: kIsWeb ? 40 : 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(L10n.of(context).close),
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
if (widget.summaryStateEventId != null &&
|
||||
widget.analyticsRoomId != null)
|
||||
// Show summary button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => LevelSummaryDialog(
|
||||
level: widget.level,
|
||||
analyticsRoomId: widget.analyticsRoomId!,
|
||||
summaryStateEventId: widget.summaryStateEventId!,
|
||||
constructSummary: widget.constructSummary,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(L10n.of(context).levelSummaryTrigger),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -304,18 +304,13 @@ class PutAnalyticsController extends BaseController<AnalyticsStream> {
|
|||
sendLocalAnalyticsToAnalyticsRoom();
|
||||
return;
|
||||
}
|
||||
|
||||
final int newLevel =
|
||||
_pangeaController.getAnalytics.constructListModel.level;
|
||||
newLevel > prevLevel
|
||||
? sendLocalAnalyticsToAnalyticsRoom()
|
||||
: analyticsUpdateStream.add(
|
||||
AnalyticsUpdate(
|
||||
AnalyticsUpdateType.local,
|
||||
newConstructs,
|
||||
origin: origin,
|
||||
),
|
||||
);
|
||||
analyticsUpdateStream.add(
|
||||
AnalyticsUpdate(
|
||||
AnalyticsUpdateType.local,
|
||||
newConstructs,
|
||||
origin: origin,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Clears the local cache of recently sent constructs. Called before updating analytics
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@ class PApiUrls {
|
|||
"${PApiUrls.choreoEndpoint}/activity_plan/search";
|
||||
|
||||
static String morphFeaturesAndTags = "${PApiUrls.choreoEndpoint}/morphs";
|
||||
static String constructSummary =
|
||||
"${PApiUrls.choreoEndpoint}/construct_summary";
|
||||
|
||||
///-------------------------------- revenue cat --------------------------
|
||||
static String rcAppsChoreo = "${PApiUrls.subscriptionEndpoint}/app_ids";
|
||||
|
|
|
|||
126
lib/pangea/constructs/construct_repo.dart
Normal file
126
lib/pangea/constructs/construct_repo.dart
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/network/requests.dart';
|
||||
import 'package:fluffychat/pangea/common/network/urls.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class ConstructSummary {
|
||||
final int upperLevel;
|
||||
final int lowerLevel;
|
||||
final String language;
|
||||
final String textSummary;
|
||||
|
||||
ConstructSummary({
|
||||
required this.upperLevel,
|
||||
required this.lowerLevel,
|
||||
required this.language,
|
||||
required this.textSummary,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'upper_level': upperLevel,
|
||||
'lower_level': lowerLevel,
|
||||
'language': language,
|
||||
'text_summary': textSummary,
|
||||
};
|
||||
}
|
||||
|
||||
factory ConstructSummary.fromJson(Map<String, dynamic> json) {
|
||||
return ConstructSummary(
|
||||
upperLevel: json['upper_level'],
|
||||
lowerLevel: json['lower_level'],
|
||||
language: json['language'],
|
||||
textSummary: json['text_summary'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConstructSummaryRequest {
|
||||
final List<OneConstructUse> constructs;
|
||||
final List<String?>? constructUseMessageContentBodies;
|
||||
final String language;
|
||||
final int upperLevel;
|
||||
final int lowerLevel;
|
||||
|
||||
ConstructSummaryRequest({
|
||||
required this.constructs,
|
||||
this.constructUseMessageContentBodies,
|
||||
required this.language,
|
||||
required this.upperLevel,
|
||||
required this.lowerLevel,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'constructs': constructs.map((construct) => construct.toJson()).toList(),
|
||||
'construct_use_message_content_bodies': constructUseMessageContentBodies,
|
||||
'language': language,
|
||||
'upper_level': upperLevel,
|
||||
'lower_level': lowerLevel,
|
||||
};
|
||||
}
|
||||
|
||||
factory ConstructSummaryRequest.fromJson(Map<String, dynamic> json) {
|
||||
return ConstructSummaryRequest(
|
||||
constructs: (json['constructs'] as List)
|
||||
.map((construct) => OneConstructUse.fromJson(construct))
|
||||
.toList(),
|
||||
constructUseMessageContentBodies:
|
||||
List<String>.from(json['construct_use_message_content_bodies']),
|
||||
language: json['language'],
|
||||
upperLevel: json['upper_level'],
|
||||
lowerLevel: json['lower_level'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConstructSummaryResponse {
|
||||
final ConstructSummary summary;
|
||||
|
||||
ConstructSummaryResponse({
|
||||
required this.summary,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'summary': summary.toJson(),
|
||||
};
|
||||
}
|
||||
|
||||
factory ConstructSummaryResponse.fromJson(Map<String, dynamic> json) {
|
||||
return ConstructSummaryResponse(
|
||||
summary: ConstructSummary.fromJson(json['summary']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class GenerateConstructSummaryResult {
|
||||
final String stateEventId;
|
||||
final ConstructSummary summary;
|
||||
|
||||
GenerateConstructSummaryResult({
|
||||
required this.stateEventId,
|
||||
required this.summary,
|
||||
});
|
||||
}
|
||||
|
||||
class ConstructRepo {
|
||||
static Future<ConstructSummaryResponse> generateConstructSummary(
|
||||
ConstructSummaryRequest request,
|
||||
) async {
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: MatrixState.pangeaController.userController.accessToken,
|
||||
);
|
||||
final Response res =
|
||||
await req.post(url: PApiUrls.constructSummary, body: request.toJson());
|
||||
final decodedBody = jsonDecode(utf8.decode(res.bodyBytes));
|
||||
final response = ConstructSummaryResponse.fromJson(decodedBody);
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ class PangeaEventTypes {
|
|||
// static const studentAnalyticsSummary = "pangea.usranalytics";
|
||||
static const summaryAnalytics = "pangea.summaryAnalytics";
|
||||
static const construct = "pangea.construct";
|
||||
static const constructSummary = "pangea.construct_summary";
|
||||
static const userChosenEmoji = "p.emoji";
|
||||
|
||||
static const translation = "pangea.translation";
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue