bug6095: minor refactoring in the com.sap.sailing.news bundle;

it may come in handy as a basis for placing AI-generated "news" in
the event feed
This commit is contained in:
Axel Uhl
2025-02-21 14:34:57 +01:00
parent 2317784cb1
commit 25dc01b2f6
15 changed files with 84 additions and 121 deletions
+4
View File
@@ -337,4 +337,8 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
id="com.sap.sailing.domain.queclinkadapter"
version="0.0.0"/>
<plugin
id="com.sap.sailing.aiagent"
version="0.0.0"/>
</feature>
@@ -84,16 +84,15 @@ public class GetEventOverviewNewsAction implements SailingAction<ResultWithTTL<L
@GwtIncompatible
private List<NewsEntryDTO> getNews(SailingDispatchContext dispatchContext, Event event) {
List<EventNewsItem> newsItems = dispatchContext.getEventNewsService().getNews(event);
if(this.limit > 0 && newsItems.size() > limit) {
if (this.limit > 0 && newsItems.size() > limit) {
newsItems = newsItems.subList(0, limit);
}
List<NewsEntryDTO> news = new ArrayList<>(newsItems.size());
for(EventNewsItem newsItem: newsItems) {
if(newsItem instanceof InfoEventNewsItem) {
final List<NewsEntryDTO> news = new ArrayList<>(newsItems.size());
for (EventNewsItem newsItem : newsItems) {
if (newsItem instanceof InfoEventNewsItem) {
news.add(new InfoNewsEntryDTO((InfoEventNewsItem) newsItem, dispatchContext.getClientLocale()));
}
if(newsItem instanceof LeaderboardUpdateNewsItem) {
if (newsItem instanceof LeaderboardUpdateNewsItem) {
news.add(new LeaderboardNewsEntryDTO((LeaderboardUpdateNewsItem) newsItem));
}
}
@@ -17,7 +17,6 @@ public class Activator implements BundleActivator {
EventNewsService newsService = new EventNewsServiceImpl(providerRegistry);
context.registerService(EventNewsService.class, newsService, null);
context.registerService(EventNewsProviderRegistry.class, providerRegistry, null);
// The json event news provider is only a fallback and should not be activated per default
// providerRegistry.registerNewsProvider(new JsonEventNewsProvider());
providerRegistry.registerNewsProvider(new LeaderboardUpdateEventNewsProvider());
@@ -9,8 +9,4 @@ public interface EventNewsProvider {
Collection<? extends EventNewsItem> getNews(Event event);
Collection<? extends EventNewsItem> getNews(Event event, Date startingFrom);
boolean hasNews(Event event, Date startingFrom);
boolean hasNews(Event event);
}
@@ -4,16 +4,18 @@ import java.net.URL;
import java.util.Date;
import java.util.UUID;
import com.sap.sse.common.Util;
/**
* An interface for a generic news item
* @author Frank
* An interface for a generic news item. The natural ordering, implementing the {@link Comparable} interface,
* is defined by the {@link #getCreatedAtDate() creation time point}.
*
* @author Frank Mittag
*
*/
public interface NewsItem extends Comparable<NewsItem> {
UUID getId();
String getCategory();
String getTitle();
String getMessage();
@@ -21,4 +23,10 @@ public interface NewsItem extends Comparable<NewsItem> {
URL getRelatedItemLink();
Date getCreatedAtDate();
@Override
default int compareTo(NewsItem o) {
final Date otherCreatedAtDate = o.getCreatedAtDate();
return -Util.compareToWithNull(getCreatedAtDate(), otherCreatedAtDate, /* nullIsLess */ true);
}
}
@@ -9,16 +9,11 @@ import com.sap.sailing.news.EventNewsItem;
public abstract class AbstractEventNewsItem extends AbstractNewsItem implements EventNewsItem {
private UUID eventId;
public AbstractEventNewsItem(UUID eventId, String title, String message, Date createdAtDate, String category, URL relatedItemLink) {
super(title, message, createdAtDate, category, relatedItemLink);
protected AbstractEventNewsItem(UUID eventId, String title, String message, Date createdAtDate, URL relatedItemLink) {
super(title, message, createdAtDate, relatedItemLink);
this.eventId = eventId;
}
public AbstractEventNewsItem(UUID eventId, String title, String message, Date createdAtDate, String category) {
super(title, message, createdAtDate, category);
this.eventId = eventId;
}
@Override
public UUID getEventUUID() {
return eventId;
@@ -6,11 +6,9 @@ import java.util.UUID;
import com.sap.sailing.news.NewsItem;
public class AbstractNewsItem implements NewsItem {
public abstract class AbstractNewsItem implements NewsItem {
private final UUID id;
private String category;
private String title;
private String message;
@@ -19,55 +17,36 @@ public class AbstractNewsItem implements NewsItem {
private Date createdAtDate;
public AbstractNewsItem(String title, String message, Date createdAtDate, String category) {
this(title, message, createdAtDate, category, null);
}
public AbstractNewsItem(String title, String message, Date createdAtDate, String category, URL relatedItemLink) {
protected AbstractNewsItem(String title, String message, Date createdAtDate, URL relatedItemLink) {
this.title = title;
this.message = message;
this.category = category;
this.createdAtDate = createdAtDate;
this.relatedItemLink = relatedItemLink;
this.id = UUID.randomUUID();
}
public String getCategory() {
return category;
}
@Override
public String getTitle() {
return title;
}
@Override
public String getMessage() {
return message;
}
@Override
public URL getRelatedItemLink() {
return relatedItemLink;
}
@Override
public Date getCreatedAtDate() {
return createdAtDate;
}
@Override
public UUID getId() {
return id;
}
@Override
public int compareTo(NewsItem o) {
Date otherCreatedAtDate = o.getCreatedAtDate();
if(createdAtDate == otherCreatedAtDate) {
return 0;
}
if(createdAtDate == null) {
return 1;
}
if(otherCreatedAtDate == null) {
return -1;
}
return -createdAtDate.compareTo(otherCreatedAtDate);
}
}
@@ -13,6 +13,7 @@ import com.sap.sailing.news.EventNewsProviderRegistry;
import com.sap.sailing.news.EventNewsService;
public class EventNewsServiceImpl implements EventNewsService {
private final static int LIMIT = 25;
private final EventNewsProviderRegistry providerRegistry;
public EventNewsServiceImpl(EventNewsProviderRegistry providerRegistry) {
@@ -21,13 +22,13 @@ public class EventNewsServiceImpl implements EventNewsService {
@Override
public List<EventNewsItem> getNews(Event event) {
List<EventNewsItem> result = new ArrayList<>();
for(EventNewsProvider provider: providerRegistry.getEventNewsProvider()) {
final List<EventNewsItem> result = new ArrayList<>();
for (EventNewsProvider provider : providerRegistry.getEventNewsProvider()) {
Collection<? extends EventNewsItem> news = provider.getNews(event);
result.addAll(news);
}
Collections.sort(result);
if(result.size() <= 25) {
if (result.size() <= 25) {
return result;
}
return result.subList(0, 25);
@@ -40,11 +41,8 @@ public class EventNewsServiceImpl implements EventNewsService {
Collection<? extends EventNewsItem> news = provider.getNews(event, startingFrom);
result.addAll(news);
}
Collections.sort(result);
if(result.size() <= 25) {
return result;
}
return result.subList(0, 25);
Collections.sort(result); // sorts by the NewsItem natural order, based on creation date
return result.subList(0, Math.min(result.size(), LIMIT));
}
@Override
@@ -10,9 +10,9 @@ public class InfoEventNewsItem extends AbstractEventNewsItem {
private Map<Locale, String> titles;
private Map<Locale, String> messages;
public InfoEventNewsItem(UUID eventId, String title, String message, Date createdAtDate, String category,
URL relatedItemLink, Map<Locale, String> titles, Map<Locale, String> messages) {
super(eventId, title, message, createdAtDate, category, relatedItemLink);
public InfoEventNewsItem(UUID eventId, String title, String message, Date createdAtDate, URL relatedItemLink,
Map<Locale, String> titles, Map<Locale, String> messages) {
super(eventId, title, message, createdAtDate, relatedItemLink);
this.titles = titles;
this.messages = messages;
}
@@ -79,7 +79,7 @@ public class JsonEventNewsProvider implements EventNewsProvider {
Map<Locale, String> messages = readI18nText(eventAsJson, "messages");
String newsURL = (String) eventAsJson.get("url");
UUID eventId = UUID.fromString(eventIdAsString);
InfoEventNewsItem newsItem = new InfoEventNewsItem(eventId, title, message, new Date(timestamp.longValue()), null, newsURL == null ? null : new URL(newsURL), titles, messages);
InfoEventNewsItem newsItem = new InfoEventNewsItem(eventId, title, message, new Date(timestamp.longValue()), newsURL == null ? null : new URL(newsURL), titles, messages);
newNews.add(newsItem);
}
news = newNews;
@@ -128,15 +128,4 @@ public class JsonEventNewsProvider implements EventNewsProvider {
public Collection<? extends EventNewsItem> getNews(Event event, Date startingFrom) {
return getNews(event);
}
@Override
public boolean hasNews(Event event, Date startingFrom) {
return news.size() > 0;
}
@Override
public boolean hasNews(Event event) {
return news.size() > 0;
}
}
@@ -8,78 +8,56 @@ import java.util.List;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
import com.sap.sailing.domain.leaderboard.ScoreCorrection;
import com.sap.sailing.news.EventNewsItem;
import com.sap.sailing.news.EventNewsProvider;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
/**
* An event news provider looking at leaderboard updates triggered by changes of timePointOfLatestModification
* @author Frank
* An event news provider looking at leaderboard updates triggered by changes of timePointOfLatestModification
*
* @author Frank Mittag
*
*/
public class LeaderboardUpdateEventNewsProvider implements EventNewsProvider {
private final static int LIMIT = 10;
@Override
public Collection<? extends EventNewsItem> getNews(Event event) {
List<LeaderboardUpdateNewsItem> result = new LinkedList<>();
Iterable<LeaderboardGroup> leaderboardGroups = event.getLeaderboardGroups();
final List<LeaderboardUpdateNewsItem> result = new LinkedList<>();
final Iterable<LeaderboardGroup> leaderboardGroups = event.getLeaderboardGroups();
for (LeaderboardGroup leaderboardGroup : leaderboardGroups) {
for (Leaderboard leaderboard : leaderboardGroup.getLeaderboards()) {
if (!isPartOfEvent(event, leaderboard)) {
continue;
}
SettableScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
if (scoreCorrection != null) {
TimePoint timePointOfLatestModification = scoreCorrection.getTimePointOfLastCorrectionsValidity();
if (timePointOfLatestModification != null) {
String displayName = leaderboard.getDisplayName() != null ? leaderboard.getDisplayName()
: leaderboard.getName();
String boatClassName = null;
if (leaderboard instanceof RegattaLeaderboard) {
BoatClass boatClass = ((RegattaLeaderboard) leaderboard).getRegatta().getBoatClass();
if (boatClass != null) {
boatClassName = boatClass.getName();
if (leaderboard.isPartOfEvent(event)) {
final ScoreCorrection scoreCorrection = leaderboard.getScoreCorrection();
if (scoreCorrection != null) {
final TimePoint timePointOfLatestModification = scoreCorrection.getTimePointOfLastCorrectionsValidity();
if (timePointOfLatestModification != null) {
String displayName = leaderboard.getDisplayName() != null ? leaderboard.getDisplayName()
: leaderboard.getName();
String boatClassName = null;
if (leaderboard instanceof RegattaLeaderboard) {
final BoatClass boatClass = ((RegattaLeaderboard) leaderboard).getRegatta().getBoatClass();
if (boatClass != null) {
boatClassName = boatClass.getName();
}
}
result.add(new LeaderboardUpdateNewsItem(event.getId(), timePointOfLatestModification.asDate(), leaderboard.getName(), displayName, boatClassName));
}
result.add(new LeaderboardUpdateNewsItem(event.getId(), timePointOfLatestModification.asDate(), leaderboard.getName(), displayName, boatClassName));
}
}
}
}
Collections.sort(result);
if (result.size() <= 10) {
return result;
}
return result.subList(0, 11);
return result.subList(0, Math.min(LIMIT, result.size()));
}
// TODO duplicate code taken from HomeServiceUtil
private boolean isPartOfEvent(EventBase event, Leaderboard regattaEntity) {
return Util.isEmpty(regattaEntity.getCourseAreas())
|| Util.containsAny(event.getVenue().getCourseAreas(), regattaEntity.getCourseAreas());
}
@Override
public Collection<? extends EventNewsItem> getNews(Event event, Date startingFrom) {
// TODO correct implementation
return getNews(event);
}
@Override
public boolean hasNews(Event event, Date startingFrom) {
// TODO correct implementation
return true;
}
@Override
public boolean hasNews(Event event) {
return true;
}
}
@@ -8,7 +8,7 @@ public class LeaderboardUpdateNewsItem extends AbstractEventNewsItem {
private String boatClass;
public LeaderboardUpdateNewsItem(UUID eventId, Date updateDate, String leaderboardName, String leaderboardDisplayName, String boatClass) {
super(eventId, leaderboardDisplayName, null, updateDate, null, null);
super(eventId, leaderboardDisplayName, /* message */ null, updateDate, /* relatedItemLink */ null);
this.leaderboardName = leaderboardName;
this.boatClass = boatClass;
}
@@ -31,7 +31,7 @@ public class TestWithSecretCredentials {
@Before
public void setUp() throws MalformedURLException, ParseException {
final String credentialsJsonString = System.getProperty("sap.aicore.credentials");
final String credentialsJsonString = System.getProperty(AICore.CREDENTIALS_SYSTEM_PROPERTY_NAME);
credentials = CredentialsParser.create().parse(credentialsJsonString);
aiCore = AICore.create(credentials);
}
@@ -1,6 +1,7 @@
package com.sap.sse.aicore;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.Optional;
@@ -27,7 +28,7 @@ import com.sap.sse.common.Util;
*
* Example:
* <pre>
* final AICore aiCore = AICore.create(CredentialsParser.create().parse(System.getProperty("sap.ai.core.credentials")));
* final AICore aiCore = AICore.create(CredentialsParser.create().parse(System.getProperty(CREDENTIALS_SYSTEM_PROPERTY_NAME)));
* final ChatSession chatSession = aiCore.createChatSession("gpt-4o-mini").get();
* final String response = chatSession
* .addSystemPrompt("You are a teacher.")
@@ -39,6 +40,22 @@ import com.sap.sse.common.Util;
*
*/
public interface AICore {
/**
* Name of the system property in which we look for default credentials that will be used by the
* {@link #getDefault} method to obtain valid credentials.
*/
String CREDENTIALS_SYSTEM_PROPERTY_NAME = "sap.aicore.credentials";
/**
* Produces a default {@link AICore} instance using credentials from the system property whose name
* is specified by {@link #CREDENTIALS_SYSTEM_PROPERTY_NAME}. If that property is not set, {@code null}
* is returned.
*/
static AICore getDefault() throws MalformedURLException, ParseException {
final String systemProperty = System.getProperty(CREDENTIALS_SYSTEM_PROPERTY_NAME);
return systemProperty == null ? null : AICore.create(CredentialsParser.create().parse(systemProperty));
}
static AICore create(final Credentials credentials) {
return new AICoreImpl(credentials);
}
+1
View File
@@ -163,6 +163,7 @@
<module>com.sap.sailing.domain.swisstimingadapter</module>
<module>com.sap.sailing.domain.swisstimingadapter.persistence</module>
<module>com.sap.sailing.domain.swisstimingreplayadapter</module>
<module>com.sap.sailing.aiagent</module>
<module>com.sap.sailing.news</module>
<module>com.sap.sailing.declination</module>
<module>com.sap.sailing.udpconnector</module>