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.
fluffychat/lib/pangea/extensions/client_extension/client_analytics_extension....

157 lines
5.4 KiB
Dart

part of "client_extension.dart";
extension AnalyticsClientExtension on Client {
/// Get the logged in user's analytics room matching
/// a given langCode. If not present, create it.
Future<Room?> _getMyAnalyticsRoom(String langCode) async {
final Room? analyticsRoom = _analyticsRoomLocal(langCode);
if (analyticsRoom != null) return analyticsRoom;
return _makeAnalyticsRoom(langCode);
}
/// Get local analytics room for a given langCode and
/// optional userId (if not specified, uses current user).
/// If user is invited to the room, joins the room.
Room? _analyticsRoomLocal(String langCode, [String? userIdParam]) {
final Room? analyticsRoom = rooms.firstWhereOrNull((e) {
return e.isAnalyticsRoom &&
e.isAnalyticsRoomOfUser(userIdParam ?? userID!) &&
e.isMadeForLang(langCode);
});
if (analyticsRoom != null &&
analyticsRoom.membership == Membership.invite) {
debugger(when: kDebugMode);
analyticsRoom.join().onError(
(error, stackTrace) =>
ErrorHandler.logError(e: error, s: stackTrace),
);
return analyticsRoom;
}
return analyticsRoom;
}
/// Creates an analytics room with the specified language code and returns the created room.
/// Additionally, the room is added to the user's spaces and all teachers are invited to the room.
///
/// If the room does not appear immediately after creation, this method waits for it to appear in sync.
/// Returns the created [Room] object.
Future<Room?> _makeAnalyticsRoom(String langCode) async {
if (userID == null || userID == BotName.byEnvironment) {
return null;
}
final String roomID = await createRoom(
creationContent: {
'type': PangeaRoomTypes.analytics,
ModelKey.langCode: langCode,
},
name: "$userID $langCode Analytics",
topic: "This room stores learning analytics for $userID.",
invite: [
...(await myTeachers).map((e) => e.id),
BotName.byEnvironment,
],
);
if (getRoomById(roomID) == null) {
// Wait for room actually appears in sync
await waitForRoomInSync(roomID, join: true);
}
final Room? analyticsRoom = getRoomById(roomID);
// add this analytics room to all spaces so teachers can join them
// via the space hierarchy
analyticsRoom?.addAnalyticsRoomToSpaces();
// and invite all teachers to new analytics room
analyticsRoom?.inviteTeachersToAnalyticsRoom();
return getRoomById(roomID)!;
}
/// Get all my analytics rooms
List<Room> get _allMyAnalyticsRooms => rooms
.where(
(e) => e.isAnalyticsRoomOfUser(userID!),
)
.toList();
// migration function to change analytics rooms' vsibility to public
// so they will appear in the space hierarchy
Future<void> _updateAnalyticsRoomVisibility() async {
if (userID == null || userID == BotName.byEnvironment) return;
await Future.wait(
allMyAnalyticsRooms.map((room) async {
final visability = await getRoomVisibilityOnDirectory(room.id);
if (visability != Visibility.public) {
await setRoomVisibilityOnDirectory(
room.id,
visibility: Visibility.public,
);
}
}),
);
}
/// Add all the users' analytics room to all the spaces the user is studying in
/// so teachers can join them via space hierarchy.
/// Allows teachers to join analytics rooms without being invited.
void _addAnalyticsRoomsToAllSpaces() {
if (userID == null || userID == BotName.byEnvironment) return;
for (final Room room in allMyAnalyticsRooms) {
room.addAnalyticsRoomToSpaces();
}
}
/// Invite teachers to all my analytics room.
/// Handles case when students cannot add analytics room to space(s)
/// so teacher is still able to get analytics data for this student
void _inviteAllTeachersToAllAnalyticsRooms() {
if (userID == null || userID == BotName.byEnvironment) return;
for (final Room room in allMyAnalyticsRooms) {
room.inviteTeachersToAnalyticsRoom();
}
}
// Join all analytics rooms in all spaces
// Allows teachers to join analytics rooms without being invited
Future<void> _joinAnalyticsRoomsInAllSpaces() async {
for (final Room space in _spacesImTeaching) {
// Each call to joinAnalyticsRoomsInSpace calls getSpaceHierarchy, which has a
// strict rate limit. So we wait a second between each call to prevent a 429 error.
await Future.delayed(
const Duration(seconds: 1),
() => space.joinAnalyticsRoomsInSpace(),
);
}
}
/// Join invited analytics rooms.
/// Checks for invites to any student analytics rooms.
/// Handles case of analytics rooms that can't be added to some space(s).
void _joinInvitedAnalyticsRooms() {
Future.wait(
rooms
.where(
(room) =>
room.membership == Membership.invite && room.isAnalyticsRoom,
)
.map(
(room) => room.join().catchError((err, s) {
ErrorHandler.logError(e: err, s: s);
}),
),
);
}
/// Helper function to join all relevant analytics rooms
/// and set up those rooms to be joined by other users.
void _migrateAnalyticsRooms() {
_updateAnalyticsRoomVisibility().then((_) {
_addAnalyticsRoomsToAllSpaces();
_inviteAllTeachersToAllAnalyticsRooms();
_joinInvitedAnalyticsRooms();
_joinAnalyticsRoomsInAllSpaces();
});
}
}