profileShare

rasmusjy / tasteprint

Read-only snapshot

No repository description.

main default branch 169 files Expires Sep 13, 2026, 9:06 AM
SharingService.java 5,228 bytes
1 package com.tasteprint.social;
2
3 import java.util.Comparator;
4 import java.util.HashSet;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Set;
8 import java.util.UUID;
9 import java.util.function.Function;
10 import java.util.stream.Collectors;
11
12 import org.springframework.stereotype.Service;
13 import org.springframework.transaction.annotation.Transactional;
14
15 import com.tasteprint.account.AccountService;
16 import com.tasteprint.account.PublicAccountView;
17 import com.tasteprint.catalog.CatalogService;
18 import com.tasteprint.catalog.DestinationSummary;
19 import com.tasteprint.catalog.DishView;
20 import com.tasteprint.progress.ProgressService;
21 import com.tasteprint.progress.TasteSnapshot;
22 import com.tasteprint.tasting.TastingService;
23 import com.tasteprint.tasting.TastingView;
24
25 @Service
26 public class SharingService {
27
28 private final AccountService accounts;
29 private final CatalogService catalog;
30 private final TastingService tastings;
31 private final ProgressService progress;
32
33 SharingService(AccountService accounts, CatalogService catalog,
34 TastingService tastings, ProgressService progress) {
35 this.accounts = accounts;
36 this.catalog = catalog;
37 this.tastings = tastings;
38 this.progress = progress;
39 }
40
41 @Transactional(readOnly = true)
42 public PublicTasteprintView publicTasteprint(String shareSlug) {
43 PublicAccountView account = accounts.getPublic(shareSlug);
44 return new PublicTasteprintView(account, publicSnapshot(progress.snapshot(account.id())));
45 }
46
47 @Transactional(readOnly = true)
48 public TasteComparison compare(UUID currentUserId, String otherShareSlug) {
49 PublicAccountView other = accounts.getPublic(otherShareSlug);
50 Set<String> yours = tastings.triedDishSlugs(currentUserId);
51 Set<String> theirs = tastings.triedDishSlugs(other.id());
52 Set<String> sharedDishes = intersection(yours, theirs);
53 Set<String> union = new HashSet<>(yours);
54 union.addAll(theirs);
55
56 List<DishView> allDishes = catalog.allDishes();
57 Map<String, DishView> bySlug = allDishes.stream()
58 .collect(Collectors.toMap(DishView::slug, Function.identity()));
59 Set<String> yourCountries = destinationCodes(yours, bySlug);
60 Set<String> theirCountries = destinationCodes(theirs, bySlug);
61 Set<String> sharedCountries = intersection(yourCountries, theirCountries);
62 Set<String> yourUnique = difference(yourCountries, theirCountries);
63 Set<String> theirUnique = difference(theirCountries, yourCountries);
64 Map<String, DestinationSummary> destinations = catalog.destinations().stream()
65 .collect(Collectors.toMap(DestinationSummary::code, Function.identity()));
66
67 DishView suggestion = allDishes.stream()
68 .filter(dish -> theirs.contains(dish.slug()) && !yours.contains(dish.slug()))
69 .sorted(Comparator.comparingInt(DishView::importance).reversed())
70 .findFirst()
71 .orElseGet(() -> allDishes.stream()
72 .filter(dish -> !yours.contains(dish.slug()))
73 .max(Comparator.comparingInt(DishView::importance))
74 .orElse(null));
75
76 int overlap = union.isEmpty() ? 0 : (int) Math.round(sharedDishes.size() * 100.0 / union.size());
77 return new TasteComparison(
78 other, overlap, sharedDishes.size(), summaries(sharedCountries, destinations),
79 summaries(yourUnique, destinations), summaries(theirUnique, destinations), suggestion
80 );
81 }
82
83 private Set<String> destinationCodes(Set<String> dishSlugs, Map<String, DishView> bySlug) {
84 return dishSlugs.stream()
85 .map(bySlug::get)
86 .filter(java.util.Objects::nonNull)
87 .map(DishView::destinationCode)
88 .collect(Collectors.toSet());
89 }
90
91 private PublicTasteSnapshot publicSnapshot(TasteSnapshot snapshot) {
92 return new PublicTasteSnapshot(
93 snapshot.stats(), snapshot.destinations(), snapshot.importantMissing(),
94 snapshot.recentTastings().stream().map(this::publicTasting).toList()
95 );
96 }
97
98 private PublicTastingView publicTasting(TastingView tasting) {
99 return new PublicTastingView(
100 tasting.id(), tasting.dish(), tasting.city(), tasting.countryCode(), tasting.tastedOn(),
101 tasting.rating(), tasting.note(), tasting.photoUrl()
102 );
103 }
104
105 private List<DestinationSummary> summaries(Set<String> codes, Map<String, DestinationSummary> destinations) {
106 return codes.stream()
107 .map(destinations::get)
108 .filter(java.util.Objects::nonNull)
109 .sorted(Comparator.comparing(DestinationSummary::name))
110 .toList();
111 }
112
113 private <T> Set<T> intersection(Set<T> first, Set<T> second) {
114 Set<T> result = new HashSet<>(first);
115 result.retainAll(second);
116 return result;
117 }
118
119 private <T> Set<T> difference(Set<T> first, Set<T> second) {
120 Set<T> result = new HashSet<>(first);
121 result.removeAll(second);
122 return result;
123 }
124 }
125