Skip to content

Commit

Permalink
Merge pull request #1146 from givtnl/feat/kids-1508-fetch-acts-recs
Browse files Browse the repository at this point in the history
Feature: Fetch recommended acts of service (kids-1508)
  • Loading branch information
Daniela510 authored Oct 9, 2024
2 parents 30458e5 + cba67a1 commit acb8ad5
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,35 @@ import 'dart:convert';

import 'package:equatable/equatable.dart';
import 'package:givt_app/features/family/features/recommendation/tags/models/tag.dart';
import 'package:givt_app/features/family/features/reflect/data/recommendation_types.dart';

class Organisation extends Equatable {
const Organisation({
required this.guid,
required this.collectGroupId,
required this.type,
required this.name,
required this.namespace,
required this.qrCodeURL,
required this.organisationLogoURL,
required this.promoPictureUrl,
required this.shortDescription,
required this.longDescription,
required this.tags,
required this.collectGroupId,
required this.namespace,
required this.qrCodeURL,
});

factory Organisation.fromMap(Map<String, dynamic> map) {
return Organisation(
guid: map['guid'] as String,
collectGroupId: map['collectGroupId'] as String,
name: map['name'] as String,
namespace: map['namespace'] as String,
qrCodeURL: map['qrCodeURL'] as String,
organisationLogoURL: map['organisationLogoURL'] as String,
promoPictureUrl: map['promoPictureUrl'] as String,
shortDescription: map['shortDescription'] as String,
longDescription: map['longDescription'] as String,
guid: (map['guid'] ?? '') as String,
name: (map['name'] ?? 'Organisation Name') as String,
organisationLogoURL: (map['organisationLogoURL'] ?? '') as String,
promoPictureUrl: (map['promoPictureUrl'] ?? '') as String,
shortDescription: (map['shortDescription'] ?? '') as String,
longDescription: (map['longDescription'] ?? '') as String,
collectGroupId: (map['collectGroupId'] ?? '') as String,
namespace: (map['namespace'] ?? '') as String,
qrCodeURL: (map['qrCodeURL'] ?? '') as String,
type: RecommendationTypes.fromString((map['type'] ?? '') as String),
tags: List<Tag>.from(
(map['tags'] as List<dynamic>)
.map((e) => Tag.fromMap(e as Map<String, dynamic>)),
Expand All @@ -36,21 +39,22 @@ class Organisation extends Equatable {
}

final String guid;
final String collectGroupId;
final RecommendationTypes type;
final String name;
final String namespace;
final String qrCodeURL;
final String organisationLogoURL;
final String promoPictureUrl;
final String shortDescription;
final String longDescription;
final List<Tag> tags;

final String collectGroupId;
final String namespace;
final String qrCodeURL;
@override
List<Object?> get props => [
guid,
collectGroupId,
name,
type,
namespace,
qrCodeURL,
organisationLogoURL,
Expand All @@ -61,6 +65,7 @@ class Organisation extends Equatable {
];

Organisation copyWith({
RecommendationTypes? type,
String? guid,
String? collectGroupId,
String? name,
Expand All @@ -73,6 +78,7 @@ class Organisation extends Equatable {
List<Tag>? tags,
}) =>
Organisation(
type: type ?? this.type,
guid: guid ?? this.guid,
collectGroupId: collectGroupId ?? this.collectGroupId,
name: name ?? this.name,
Expand All @@ -89,6 +95,7 @@ class Organisation extends Equatable {
return {
'guid': guid,
'collectGroupId': collectGroupId,
'type': type.value,
'name': name,
'namespace': namespace,
'qrCodeURL': qrCodeURL,
Expand Down
15 changes: 7 additions & 8 deletions lib/features/family/features/reflect/bloc/grateful_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,12 @@ class GratefulCubit extends CommonCubit<GratefulUIModel, GratefulCustom> {
.toList(),
),
recommendationsUIModel: RecommendationsUIModel(
isLoading: _isLoadingRecommendations,
hasError: _hasRecommendationsError,
organisations: _currentRecommendations,
name:
_profiles.elementAtOrNull(_currentProfileIndex)?.firstName ?? '',
category: _profiles.elementAt(_currentProfileIndex).gratitude
),
isLoading: _isLoadingRecommendations,
hasError: _hasRecommendationsError,
organisations: _currentRecommendations,
name: _profiles.elementAtOrNull(_currentProfileIndex)?.firstName ??
'',
category: _profiles.elementAt(_currentProfileIndex).gratitude),
),
);
}
Expand All @@ -129,7 +128,7 @@ class GratefulCubit extends CommonCubit<GratefulUIModel, GratefulCustom> {
_isLoadingRecommendations = true;
_emitData();
_currentRecommendations = await _gratefulRecommendationsRepository
.getGratefulRecommendations(_getCurrentProfile());
.getOrganisationsRecommendations(_getCurrentProfile());
_hasRecommendationsError = false;
} catch (e, s) {
_hasRecommendationsError = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
enum RecommendationTypes {
organisation('collectgroup'),
church('church'),
actOfService('actsofservice');

const RecommendationTypes(this.value);

final String value;

static RecommendationTypes fromString(String value) {
final lowercaseValue = value.toLowerCase();
return RecommendationTypes.values.firstWhere(
(element) => element.value == lowercaseValue,
orElse: () => RecommendationTypes.organisation,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ abstract class GratefulRecommendationsRepository {
List<GameProfile> profiles,
);

Future<List<Organisation>> getGratefulRecommendations(
Future<List<Organisation>> getOrganisationsRecommendations(
GameProfile profile,
);

Future<List<Organisation>> getActsRecommendations(
GameProfile profile,
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,29 @@ class GratefulRecommendationsRepositoryImpl
);

final FamilyAPIService _familyApiService;
final Map<GameProfile, List<Organisation>> _gratefulRecommendations = {};
final Map<GameProfile, List<Organisation>> _organisationRecommendations = {};
final Map<GameProfile, List<Organisation>> _actsRecommendations = {};

@override
Future<void> fetchGratefulRecommendationsForMultipleProfiles(
List<GameProfile> profiles,
) async {
try {
final results = await Future.wait(
profiles.map((profile) async {
final orgsList = await _getOrganisationsForProfile(profile);
final sortedList = sortOrganisationsByChurchTag(orgsList);
return MapEntry(profile, sortedList);
}),
final organisations = await Future.wait(
profiles.map(
(profile) => _fetchAndSortRecommendations(
profile, _getOrganisationsForProfile),
),
);
_organisationRecommendations.addEntries(organisations);

_gratefulRecommendations.addEntries(results);
final acts = await Future.wait(
profiles.map(
(profile) =>
_fetchAndSortRecommendations(profile, _getActsForProfile),
),
);
_actsRecommendations.addEntries(acts);
} catch (e, s) {
LoggingInfo.instance.error(
e.toString(),
Expand All @@ -35,8 +42,19 @@ class GratefulRecommendationsRepositoryImpl
}
}

List<Organisation> sortOrganisationsByChurchTag(
List<Organisation> organisations) {
Future<MapEntry<GameProfile, List<Organisation>>>
_fetchAndSortRecommendations(
GameProfile profile,
Future<List<Organisation>> Function(GameProfile) fetchFunction,
) async {
final recommendationsList = await fetchFunction(profile);
final sortedList = sortRecommendationsByChurchTag(recommendationsList);
return MapEntry(profile, sortedList);
}

List<Organisation> sortRecommendationsByChurchTag(
List<Organisation> organisations,
) {
organisations.sort((a, b) {
final aHasChurchTag = a.tags.any((tag) => tag.key == "CHURCH");
final bHasChurchTag = b.tags.any((tag) => tag.key == "CHURCH");
Expand Down Expand Up @@ -67,16 +85,53 @@ class GratefulRecommendationsRepositoryImpl
return orgsList.toList();
}

@override
Future<List<Organisation>> getGratefulRecommendations(
Future<List<Organisation>> _getActsForProfile(GameProfile profile) async {
final interests = profile.gratitude?.tags;
final response = await _familyApiService.getRecommendedAOS(
{
'pageSize': 10,
'tags': interests?.map((tag) => tag.key).toList(),
'includePreferredChurch': true,
},
);

final actsList = response
.map((org) => Organisation.fromMap(org as Map<String, dynamic>));
return actsList.toList();
}

Future<List<Organisation>> _getRecommendations(
GameProfile profile,
Map<GameProfile, List<Organisation>> cache,
Future<List<Organisation>> Function(GameProfile) fetchFunction,
) async {
if (!_gratefulRecommendations.containsKey(profile)) {
final result = await _getOrganisationsForProfile(profile);
final sortedList = sortOrganisationsByChurchTag(result);
_gratefulRecommendations[profile] = sortedList;
if (!cache.containsKey(profile)) {
final result = await fetchFunction(profile);
final sortedList = sortRecommendationsByChurchTag(result);
cache[profile] = sortedList;
}
return cache[profile] ?? [];
}

@override
Future<List<Organisation>> getOrganisationsRecommendations(
GameProfile profile,
) async {
return _getRecommendations(
profile,
_organisationRecommendations,
_getOrganisationsForProfile,
);
}

return _gratefulRecommendations[profile] ?? [];
@override
Future<List<Organisation>> getActsRecommendations(
GameProfile profile,
) async {
return _getRecommendations(
profile,
_actsRecommendations,
_getActsForProfile,
);
}
}
22 changes: 22 additions & 0 deletions lib/features/family/network/api_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,28 @@ class FamilyAPIService {
return decodedBody['items'] as List<dynamic>;
}

Future<List<dynamic>> getRecommendedAOS(
Map<String, dynamic> body,
) async {
final url = Uri.https(_apiURL, '/givtservice/v1/game/aos-recommendation');

final response = await client.post(
url,
headers: {'Content-Type': 'application/json'},
body: jsonEncode(body),
);

if (response.statusCode >= 400) {
throw GivtServerFailure(
statusCode: response.statusCode,
body: jsonDecode(response.body) as Map<String, dynamic>,
);
}

final decodedBody = json.decode(response.body) as Map<String, dynamic>;
return decodedBody['items'] as List<dynamic>;
}

Future<List<dynamic>> fetchAvatars() async {
final url = Uri.https(_apiURL, '/givtservice/v1/profiles/avatars');

Expand Down

0 comments on commit acb8ad5

Please sign in to comment.