ec73e1c17794897cf6c42d2ca6dc131381bc6dc0
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html
| ... | ... | @@ -17,6 +17,9 @@ |
| 17 | 17 | their dampened COG/SOG values based on the newer fixes. This sacrifices short |
| 18 | 18 | maneuver calculation delays for better performance due to fewer re-calculations |
| 19 | 19 | and reliable results with no difference between live and replay analysis.</li> |
| 20 | + <li>The polar data export service now allows clients to filter the result down to one or |
|
| 21 | + more boat classes. See <a href="/polars/webservices/api/polar_data.html">https://dev.sapsailing.com/polars/webservices/api/polar_data.html</a> |
|
| 22 | + for more details.</li> |
|
| 20 | 23 | </ul> |
| 21 | 24 | <h5 class="articleSubheadline">December 2025</h5> |
| 22 | 25 | <ul class="bulletList"> |
java/com.sap.sailing.landscape/resources/stringmessages/SailingLandscape_StringMessages_cs.properties
| ... | ... | @@ -16,3 +16,5 @@ FinishedToArchiveReplicaSetSubject=Archivace sady replik {0} je dokončena. |
| 16 | 16 | FinishedToArchiveReplicaSetBody=Archivace sady replik {0} je dokončena.\nPokud jste to vyžadovali, původní sada replik byla odebrána.\n\nTento e-mail vám byl doručen, protože máte oprávnění správce pro {0}.\nNechcete-li taková hlášení nadále dostávat, na <https://sapsailing.com/gwt/AdminConsole.html#UserManagementPlace:> tato oprávnění odeberte. |
| 17 | 17 | NewArchiveCandidateReadyForSpotChecksAndRotationSubject=Nový kandidát {0} je připraven na namátkové kontroly |
| 18 | 18 | NewArchiveCandidateReadyForSpotChecksAndRotationBody=Spustili jsme následující kontroly:\n{3}.\nNový kandidát {0} je připraven na namátkové kontroly, a pokud je to v pořádku, rotace se stane novou produkcí {0}.\nSpusťte namátkové kontroly v <https://{1}/gwt/Home.html#EventsPlace:> a porovnejte s <https://{2}/gwt/Home.html#EventsPlace:>.\nPo úspěšných kontrolách spusťte rotaci na nový produkční server na adrese <{4}/gwt/AdminConsole.html#LandscapeManagementPlace:>. |
| 19 | +NewArchiveCandidateFailedSubject=Kontrola {1} během spouštění {0} se nezdařila |
|
| 20 | +NewArchiveCandidateFailedBody=Kontrola „{1}“ při spouštění {0} se nezdařila se zprávou „{2}“. Zkontrolujte infrastrukturu a proveďte manuální opravu. |
java/com.sap.sailing.landscape/resources/stringmessages/SailingLandscape_StringMessages_da.properties
| ... | ... | @@ -16,3 +16,5 @@ FinishedToArchiveReplicaSetSubject=Arkivering af et replikasæt {0} er afsluttet |
| 16 | 16 | FinishedToArchiveReplicaSetBody=Arkivering af et replikasæt {0} er afsluttet.\nHvis du har anmodet om dette, er det oprindelige replikasæt fjernet.\n\nDu modtager denne e-mail, fordi du har administrationsrettigheder til {0}.\nFjern disse rettigheder på <https://sapsailing.com/gwt/AdminConsole.html#UserManagementPlace:>, hvis du ikke længere vil modtage disse meddelelser. |
| 17 | 17 | NewArchiveCandidateReadyForSpotChecksAndRotationSubject=Den nye {0}-kandidat er klar til stikprøvekontrol |
| 18 | 18 | NewArchiveCandidateReadyForSpotChecksAndRotationBody=Vi har kørt følgende kontroller:\n{3}.\nDen nye {0}-kandidat er klar til stikprøvekontroller, og hvis OK, er rotationen klar til at blive den nye produktion {0}.\nKør dine stikprøvekontroller på <https://{1}/gwt/Home.html#EventsPlace:>, og sammenlign med <https://{2}/gwt/Home.html#EventsPlace:>.\nStart rotationen til den nye produktionsserver på <{4}/gwt/AdminConsole.html#LandscapeManagementPlace:> efter fuldførte kontroller. |
| 19 | +NewArchiveCandidateFailedSubject=Kontrollen af {1} mislykkedes under opstart af {0} |
|
| 20 | +NewArchiveCandidateFailedBody=Kontrollen "{1}" mislykkedes med meddelelsen "{2}" under opstart af {0}. Kontroller landskabet, og ret manuelt. |
java/com.sap.sailing.landscape/resources/stringmessages/SailingLandscape_StringMessages_es.properties
| ... | ... | @@ -16,3 +16,5 @@ FinishedToArchiveReplicaSetSubject=Archivo de conjunto de réplicas en {0} final |
| 16 | 16 | FinishedToArchiveReplicaSetBody=El archivo de un conjunto de réplicas {0} ha finalizado.\nSi así lo ha solicitado, el conjunto de réplicas original se ha quitado.\n\nEstá recibiendo este correo porque tiene permisos administrativos para {0}.\nRetire esos permisos en <https://sapsailing.com/gwt/AdminConsole.html#UserManagementPlace:> si ya no desea recibir estos mensajes. |
| 17 | 17 | NewArchiveCandidateReadyForSpotChecksAndRotationSubject=El candidato nuevo {0} está listo para los controles puntuales |
| 18 | 18 | NewArchiveCandidateReadyForSpotChecksAndRotationBody=Hemos ejecutado los siguientes controles:\n{3}.\nEl candidato nuevo {0} está listo para los controles puntuales y, si es correcto, la rotación para convertirse en la producción nueva {0}.\nEjecute sus controles puntuales en <https://{1}/gwt/Home.html#EventsPlace:> y compárelos con <https://{2}/gwt/Home.html#EventsPlace:>.\nInicie la rotación al servidor de producción nuevo en <{4}/gwt/AdminConsole.html#LandscapeManagementPlace:> tras los controles correctos. |
| 19 | +NewArchiveCandidateFailedSubject=La verificación {1} ha fallado durante el inicio de {0} |
|
| 20 | +NewArchiveCandidateFailedBody=La verificación "{1}" ha fallado con el mensaje "{2}" al iniciar {0}. Verifique la infraestructura y corríjala manualmente. |
java/com.sap.sailing.landscape/resources/stringmessages/SailingLandscape_StringMessages_fr.properties
| ... | ... | @@ -16,3 +16,5 @@ FinishedToArchiveReplicaSetSubject=Archivage de l''ensemble de réplicas {0} ter |
| 16 | 16 | FinishedToArchiveReplicaSetBody=L''archivage de l''ensemble de réplicas {0} est terminé.\nSi vous en avez fait la demande, l''ensemble de réplicas d''origine a été supprimé.\n\nVous recevez cet e-mail car vous disposez des autorisations de gestion pour {0}.\nSi vous ne voulez plus recevoir ces messages, retirez ces autorisations sur <https://sapsailing.com/gwt/AdminConsole.html#UserManagementPlace>. |
| 17 | 17 | NewArchiveCandidateReadyForSpotChecksAndRotationSubject=Le nouveau candidat {0} est prêt pour des contrôles ponctuels. |
| 18 | 18 | NewArchiveCandidateReadyForSpotChecksAndRotationBody=Nous avons exécuté les contrôles suivants :\n{3}.\nLe nouveau candidat {0} est prêt pour les contrôles ponctuels et, s''ils donnent de bons résultats, pour la rotation afin de devenir le nouvel élément {0} de production.\nEffectuez vos contrôles ponctuels via le lien <https://{1}/gwt/Home.html#EventsPlace:> et comparez-les à <https://{2}/gwt/Home.html#EventsPlace:>.\nSi les contrôles sont réussis, lancez la rotation vers le nouveau serveur de production via le lien <{4}/gwt/AdminConsole.html#LandscapeManagementPlace:>. |
| 19 | +NewArchiveCandidateFailedSubject=Échec de la vérification {1} lors du lancement de {0} |
|
| 20 | +NewArchiveCandidateFailedBody=La vérification "{1}" a échoué avec le message "{2}" lors du lancement de {0}. Vérifiez l''infrastructure et corrigez l''erreur manuellement. |
java/com.sap.sailing.landscape/resources/stringmessages/SailingLandscape_StringMessages_it.properties
| ... | ... | @@ -16,3 +16,5 @@ FinishedToArchiveReplicaSetSubject=Archiviazione di un set di replica {0} comple |
| 16 | 16 | FinishedToArchiveReplicaSetBody=L''archiviazione di un set di replica {0} è stata completata.\nSe richiesto, il set di replica originale è stato rimosso.\n\nSi riceve questa e-mail perché si dispone di autorizzazioni amministrative per {0}.\nRimuovere queste autorizzazioni all''indirizzo <https://sapsailing.com/gwt/AdminConsole.html#UserManagementPlace:> se non si desidera ricevere più questi messaggi. |
| 17 | 17 | NewArchiveCandidateReadyForSpotChecksAndRotationSubject=Il nuovo candidato {0} è pronto per i controlli a campione |
| 18 | 18 | NewArchiveCandidateReadyForSpotChecksAndRotationBody=Sono stati eseguiti i seguenti controlli:\n{3}.\nIl nuovo candidato {0} è pronto per i controlli a campione e, se li supera, inizierà la rotazione per diventare il nuovo {0} di produzione .\nEseguire i controlli a campione all''indirizzo <https://{1}/gwt/Home.html#EventsPlace:> e confrontarli con <https://{2}/gwt/Home.html#EventsPlace:>.\nAvviare la rotazione al nuovo server di produzione all''indirizzo <{4}/gwt/AdminConsole.html#LandscapeManagementPlace:> dopo aver eseguito correttamente i controlli. |
| 19 | +NewArchiveCandidateFailedSubject=Controllo {1} non riuscito durante l’avvio di {0} |
|
| 20 | +NewArchiveCandidateFailedBody=Il controllo "{1}" non è riuscito con il messaggio "{2}" durante l''avvio di {0}. Controllare l''infrastruttura e correggere l’errore manualmente. |
java/com.sap.sailing.landscape/resources/stringmessages/SailingLandscape_StringMessages_ja.properties
| ... | ... | @@ -16,3 +16,5 @@ FinishedToArchiveReplicaSetSubject=複製セット {0} のアーカイブが終 |
| 16 | 16 | FinishedToArchiveReplicaSetBody=複製セット {0} のアーカイブが終了しました。\nそのように依頼している場合には、オリジナルの複製セットは削除されています。\n\nこのメールを受信しているのは、{0} の管理権限をお持ちであるためです。\nこれらのメッセージをもう受信しないようにするには、<https://sapsailing.com/gwt/AdminConsole.html#UserManagementPlace:> でそれらの権限を削除してください。 |
| 17 | 17 | NewArchiveCandidateReadyForSpotChecksAndRotationSubject=新規 {0} 候補のスポットチェックの準備が完了 |
| 18 | 18 | NewArchiveCandidateReadyForSpotChecksAndRotationBody=以下のチェックが実行されました:\n{3}。\n新規 {0} 候補はスポットチェックの準備が完了しており、OK の場合は、ローテーションして新規本稼動 {0} になります。\nスポットチェックを <https://{1}/gwt/Home.html#EventsPlace:> で実行し、<https://{2}/gwt/Home.html#EventsPlace:> と比較します。\nチェックが正常に終了した後、<{4}/gwt/AdminConsole.html#LandscapeManagementPlace:> で新規本稼動サーバへのローテーションを開始します。 |
| 19 | +NewArchiveCandidateFailedSubject=チェック {1} が {0} 起動時に失敗 |
|
| 20 | +NewArchiveCandidateFailedBody={0} の起動時に、チェック "{1}" が失敗し、メッセージ "{2}" が表示されました。ランドスケープを確認し、マニュアルで修正してください。 |
java/com.sap.sailing.landscape/resources/stringmessages/SailingLandscape_StringMessages_pt.properties
| ... | ... | @@ -16,3 +16,5 @@ FinishedToArchiveReplicaSetSubject=Arquivamento de conjunto de réplicas {0} con |
| 16 | 16 | FinishedToArchiveReplicaSetBody=O arquivamento de conjunto de réplicas {0} foi concluído.\nSe você tiver solicitado, o conjunto de réplicas original terá sido removido.\n\nVocê está recebendo este correio porque tem permissões administrativas para {0}.\nRemova essas permissões em <https://sapsailing.com/gwt/AdminConsole.html#UserManagementPlace:> se você não quiser mais receber estas mensagens. |
| 17 | 17 | NewArchiveCandidateReadyForSpotChecksAndRotationSubject=O novo candidato {0} está pronto para verificações pontuais |
| 18 | 18 | NewArchiveCandidateReadyForSpotChecksAndRotationBody=Executamos as seguintes verificações:\n{3}.\nO novo candidato {0} está pronto para verificações pontuais e, se OK, rotação para se tornar o novo {0} de produção.\nExecute suas verificações pontuais em <https://{1}/gwt/Home.html#EventsPlace:> e compare com <https://{2}/gwt/Home.html#EventsPlace:>.\nInicie a rotação para o novo servidor de produção em <{4}/gwt/AdminConsole.html#LandscapeManagementPlace:> após verificações com êxito. |
| 19 | +NewArchiveCandidateFailedSubject=A verificação {1} falhou durante a inicialização {0} |
|
| 20 | +NewArchiveCandidateFailedBody=A verificação "{1}" falhou com a mensagem "{2}" ao inicializar {0}. Verifique a estrutura e corrija manualmente. |
java/com.sap.sailing.landscape/resources/stringmessages/SailingLandscape_StringMessages_ru.properties
| ... | ... | @@ -16,3 +16,5 @@ FinishedToArchiveReplicaSetSubject=Завершена архивация наб |
| 16 | 16 | FinishedToArchiveReplicaSetBody=Завершена архивация набора реплик {0}.\nИсходный набор реплик был удален, если вы запросили удаление.\n\nВы получили это письмо, поскольку имеете полномочия администратора для {0}.\nЕсли вы больше не хотите получать эти письма, отмените свои полномочия на странице https://sapsailing.com/gwt/AdminConsole.html#UserManagementPlace:> . |
| 17 | 17 | NewArchiveCandidateReadyForSpotChecksAndRotationSubject=Новый кандидат {0} готов к выборочным проверкам |
| 18 | 18 | NewArchiveCandidateReadyForSpotChecksAndRotationBody=Выполнены следующие проверки:\n{3}.\nНовый кандидат {0} готов к выборочным проверкам и, если все в порядке, к переводу в производство {0}.\nВыполните выборочные проверки по адресу <https://{1}/gwt/Home.html#EventsPlace:> и выполните сравнение с <https://{2}/gwt/Home.html#EventsPlace:>.\nПосле успешных проверок запустите переход на новый продуктивный сервер по адресу <{4}/gwt/AdminConsole.html#LandscapeManagementPlace:> . |
| 19 | +NewArchiveCandidateFailedSubject=Проверка {1} не удалась при запуске {0} |
|
| 20 | +NewArchiveCandidateFailedBody=Проверка "{1}" не удалась (сообщение: "{2}") при запуске {0}. Проверьте ландшафт и исправьте вручную. |
java/com.sap.sailing.landscape/resources/stringmessages/SailingLandscape_StringMessages_sl.properties
| ... | ... | @@ -16,3 +16,5 @@ FinishedToArchiveReplicaSetSubject=Arhiviranje niza replik {0} je končano |
| 16 | 16 | FinishedToArchiveReplicaSetBody=Arhiviranje niza replik v {0} je končano.\nČe ste to zahtevali, je bil izvirni replik odstranjen.\n\nTo elektronsko sporočilo ste prejeli, ker imate upravljavska dovoljenja za {0}.\nČe teh sporočil ne želite več prejemati, odstranite ta dovoljenja na <https://sapsailing.com/gwt/AdminConsole.html#UserManagementPlace:>. |
| 17 | 17 | NewArchiveCandidateReadyForSpotChecksAndRotationSubject=Novi kandidat za {0} je pripravljen na preglede |
| 18 | 18 | NewArchiveCandidateReadyForSpotChecksAndRotationBody=Izvedeni so naslednji pregledi:\n{3}.\nNovi kandidat za {0} je pripravljen za preglede in v primeru ustreznosti za rotacijo v proizvodnjo kot novi {0}.\nIzvedite preglede na <https://{1}/gwt/Home.html#EventsPlace:> in jih primerjajte z <https://{2}/gwt/Home.html#EventsPlace:>.\nPo uspešnih pregledih zaženite rotacijo v novi proizvodni strežnik na <{4}/gwt/AdminConsole.html#LandscapeManagementPlace:>. |
| 19 | +NewArchiveCandidateFailedSubject=Preverjanje {1} ni uspelo med zagonom {0} |
|
| 20 | +NewArchiveCandidateFailedBody=Preverjanje "{1}" ni uspelo s sporočilom "{2}" med zagonom {0}. Preverite infrastrukturo in ročno popravite. |
java/com.sap.sailing.landscape/resources/stringmessages/SailingLandscape_StringMessages_zh.properties
| ... | ... | @@ -16,3 +16,5 @@ FinishedToArchiveReplicaSetSubject=归档复本集 {0} 已完成 |
| 16 | 16 | FinishedToArchiveReplicaSetBody=归档复本集 {0} 已完成。\n如果您要求这样做,原始复本集已移除。\n\n您之所以收到此邮件,是因为您具有 {0} 的管理权限。\n如果不想再收到这些消息,请在 <https://sapsailing.com/gwt/AdminConsole.html#UserManagementPlace:> 移除这些权限。 |
| 17 | 17 | NewArchiveCandidateReadyForSpotChecksAndRotationSubject=新 {0} 候选项已准备好进行抽查 |
| 18 | 18 | NewArchiveCandidateReadyForSpotChecksAndRotationBody=我们已运行以下检查:\n{3}。\n 新 {0} 候选项已准备好进行抽查,如果良好,则轮换到新生产{0}。\n在 <https://{1}/gwt/Home.html#EventsPlace:> 上运行抽查,并与 <https://{2}/gwt/Home.html#EventsPlace:> 进行比较。\n检查成功后,在 <{4}/gwt/AdminConsole.html#LandscapeManagementPlace:> 上启动轮换到新生产服务器。 |
| 19 | +NewArchiveCandidateFailedSubject={1} 启动期间的检查 {0} 失败 |
|
| 20 | +NewArchiveCandidateFailedBody=检查 "{1}" 在启动 {0} 时失败,并显示消息 "{2}"。请检查架构并手动修复。 |
java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java
| ... | ... | @@ -74,6 +74,13 @@ public class PolarDataServiceImpl extends AbstractReplicableWithObjectInputStrea |
| 74 | 74 | resetState(); |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | + public PolarDataServiceImpl filterToBoatClasses(Iterable<BoatClass> boatClassesToFilterTo) { |
|
| 78 | + final PolarDataMiner filteredPolarDataMiner = polarDataMiner.filterToBoatClasses(boatClassesToFilterTo); |
|
| 79 | + final PolarDataServiceImpl filteredService = new PolarDataServiceImpl(); |
|
| 80 | + filteredService.polarDataMiner = filteredPolarDataMiner; |
|
| 81 | + return filteredService; |
|
| 82 | + } |
|
| 83 | + |
|
| 77 | 84 | @Override |
| 78 | 85 | public void resetState() { |
| 79 | 86 | PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createBackendPolarSettings(); |
java/com.sap.sailing.polars/src/com/sap/sailing/polars/jaxrs/api/PolarDataResource.java
| ... | ... | @@ -2,11 +2,13 @@ package com.sap.sailing.polars.jaxrs.api; |
| 2 | 2 | |
| 3 | 3 | import java.io.IOException; |
| 4 | 4 | import java.io.OutputStream; |
| 5 | +import java.util.List; |
|
| 5 | 6 | import java.util.logging.Logger; |
| 6 | 7 | |
| 7 | 8 | import javax.ws.rs.GET; |
| 8 | 9 | import javax.ws.rs.Path; |
| 9 | 10 | import javax.ws.rs.Produces; |
| 11 | +import javax.ws.rs.QueryParam; |
|
| 10 | 12 | import javax.ws.rs.WebApplicationException; |
| 11 | 13 | import javax.ws.rs.core.Response; |
| 12 | 14 | import javax.ws.rs.core.StreamingOutput; |
| ... | ... | @@ -14,9 +16,12 @@ import javax.ws.rs.core.StreamingOutput; |
| 14 | 16 | import org.apache.shiro.SecurityUtils; |
| 15 | 17 | import org.apache.shiro.subject.Subject; |
| 16 | 18 | |
| 19 | +import com.sap.sailing.domain.base.BoatClass; |
|
| 17 | 20 | import com.sap.sailing.domain.common.security.SecuredDomainType; |
| 21 | +import com.sap.sailing.polars.impl.PolarDataServiceImpl; |
|
| 18 | 22 | import com.sap.sailing.polars.jaxrs.AbstractPolarResource; |
| 19 | 23 | import com.sap.sse.ServerInfo; |
| 24 | +import com.sap.sse.common.Util; |
|
| 20 | 25 | import com.sap.sse.security.shared.HasPermissions.DefaultActions; |
| 21 | 26 | import com.sap.sse.security.shared.TypeRelativeObjectIdentifier; |
| 22 | 27 | |
| ... | ... | @@ -26,7 +31,7 @@ public class PolarDataResource extends AbstractPolarResource { |
| 26 | 31 | |
| 27 | 32 | @GET |
| 28 | 33 | @Produces("application/octet-stream;charset=UTF-8") |
| 29 | - public Response getRegressions() throws IOException { |
|
| 34 | + public Response getRegressions(@QueryParam("boatClassName") final List<String> boatClassNames) throws IOException { |
|
| 30 | 35 | final Subject subject = SecurityUtils.getSubject(); |
| 31 | 36 | logger.info("Polar Data requested by "+ (subject.getPrincipal() == null ? "anonymous user" : subject.getPrincipal().toString())); |
| 32 | 37 | subject.checkPermission(SecuredDomainType.POLAR_DATA.getStringPermissionForTypeRelativeIdentifier(DefaultActions.READ, |
| ... | ... | @@ -34,7 +39,24 @@ public class PolarDataResource extends AbstractPolarResource { |
| 34 | 39 | return Response.ok(new StreamingOutput() { |
| 35 | 40 | @Override |
| 36 | 41 | public void write(OutputStream output) throws IOException, WebApplicationException { |
| 37 | - getPolarDataServiceImpl().serializeForInitialReplication(output); |
|
| 42 | + PolarDataServiceImpl polarDataService = getPolarDataServiceImpl(); |
|
| 43 | + @SuppressWarnings("unchecked") |
|
| 44 | + final Iterable<BoatClass>[] boatClassesToFilterTo = new Iterable[1]; |
|
| 45 | + if (boatClassNames == null || boatClassNames.isEmpty()) { |
|
| 46 | + boatClassesToFilterTo[0] = null; |
|
| 47 | + } else { |
|
| 48 | + try { |
|
| 49 | + polarDataService.runWithDomainFactory(domainFactory->{ |
|
| 50 | + boatClassesToFilterTo[0] = Util.map(boatClassNames, bcn->domainFactory.getBoatClass(bcn)); |
|
| 51 | + }); |
|
| 52 | + } catch (InterruptedException e) { |
|
| 53 | + throw new RuntimeException(e); |
|
| 54 | + } |
|
| 55 | + } |
|
| 56 | + if (boatClassesToFilterTo[0] != null) { |
|
| 57 | + polarDataService = polarDataService.filterToBoatClasses(boatClassesToFilterTo[0]); |
|
| 58 | + } |
|
| 59 | + polarDataService.serializeForInitialReplication(output); |
|
| 38 | 60 | } |
| 39 | 61 | }).header("Content-Type", "application/octet-stream").build(); |
| 40 | 62 | } |
java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/CubicRegressionPerCourseProcessor.java
| ... | ... | @@ -18,11 +18,13 @@ import com.sap.sailing.domain.common.Tack; |
| 18 | 18 | import com.sap.sailing.domain.common.polars.NotEnoughDataHasBeenAddedException; |
| 19 | 19 | import com.sap.sailing.domain.polars.PolarsChangedListener; |
| 20 | 20 | import com.sap.sse.common.Speed; |
| 21 | +import com.sap.sse.common.Util; |
|
| 21 | 22 | import com.sap.sse.datamining.components.AdditionalResultDataBuilder; |
| 22 | 23 | import com.sap.sse.datamining.components.Processor; |
| 23 | 24 | import com.sap.sse.datamining.factories.GroupKeyFactory; |
| 24 | 25 | import com.sap.sse.datamining.impl.components.GroupedDataEntry; |
| 25 | 26 | import com.sap.sse.datamining.shared.GroupKey; |
| 27 | +import com.sap.sse.datamining.shared.impl.GenericGroupKey; |
|
| 26 | 28 | |
| 27 | 29 | /** |
| 28 | 30 | * Groups incoming fixes by boatclass and legtype into {@link AngleAndSpeedRegression} instances and |
| ... | ... | @@ -45,6 +47,40 @@ public class CubicRegressionPerCourseProcessor implements |
| 45 | 47 | */ |
| 46 | 48 | private transient ConcurrentMap<BoatClass, Set<PolarsChangedListener>> listeners; |
| 47 | 49 | |
| 50 | + public CubicRegressionPerCourseProcessor filterToBoatClasses(Iterable<BoatClass> boatClasses) { |
|
| 51 | + final Set<BoatClass> allowedBoatClasses = Util.asSet(boatClasses); |
|
| 52 | + final CubicRegressionPerCourseProcessor filteredProcessor = new CubicRegressionPerCourseProcessor(); |
|
| 53 | + for (final Map.Entry<GroupKey, AngleAndSpeedRegression> entry : regressions.entrySet()) { |
|
| 54 | + GroupKey key = entry.getKey(); |
|
| 55 | + BoatClass boatClass = extractBoatClass(key); |
|
| 56 | + if (boatClass != null && allowedBoatClasses.contains(boatClass)) { |
|
| 57 | + filteredProcessor.regressions.put(key, entry.getValue()); |
|
| 58 | + } |
|
| 59 | + } |
|
| 60 | + return filteredProcessor; |
|
| 61 | + } |
|
| 62 | + |
|
| 63 | + private BoatClass extractBoatClass(GroupKey key) { |
|
| 64 | + final BoatClass result; |
|
| 65 | + if (key.hasSubKeys()) { |
|
| 66 | + // In the compound key, BoatClass is the second dimension (index 1) |
|
| 67 | + GroupKey boatClassKey = key.getKeys().get(1); |
|
| 68 | + if (boatClassKey instanceof GenericGroupKey) { |
|
| 69 | + Object value = ((GenericGroupKey<?>) boatClassKey).getValue(); |
|
| 70 | + if (value instanceof BoatClass) { |
|
| 71 | + result = (BoatClass) value; |
|
| 72 | + } else { |
|
| 73 | + result = null; |
|
| 74 | + } |
|
| 75 | + } else { |
|
| 76 | + result = null; |
|
| 77 | + } |
|
| 78 | + } else { |
|
| 79 | + result = null; |
|
| 80 | + } |
|
| 81 | + return result; |
|
| 82 | + } |
|
| 83 | + |
|
| 48 | 84 | @Override |
| 49 | 85 | public boolean canProcessElements() { |
| 50 | 86 | return true; |
| ... | ... | @@ -109,7 +145,6 @@ public class CubicRegressionPerCourseProcessor implements |
| 109 | 145 | |
| 110 | 146 | private GroupKey createGroupKey(final BoatClass boatClass, final LegType legType) { |
| 111 | 147 | LegTypePolarClusterKey key = new LegTypePolarClusterKey() { |
| 112 | - |
|
| 113 | 148 | @Override |
| 114 | 149 | public BoatClass getBoatClass() { |
| 115 | 150 | return boatClass; |
java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java
| ... | ... | @@ -40,7 +40,6 @@ public class PolarDataDimensionCollectionFactory { |
| 40 | 40 | Collection<Function<?>> dimensions = new ArrayList<>(); |
| 41 | 41 | FunctionFactory functionFactory = new FunctionFactory(); |
| 42 | 42 | addTackAndLegTypeDimensions(dimensions, functionFactory); |
| 43 | - |
|
| 44 | 43 | addPolarBaseDimension(dimensions, functionFactory); |
| 45 | 44 | return dimensions; |
| 46 | 45 | } |
java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java
| ... | ... | @@ -136,6 +136,13 @@ public class PolarDataMiner { |
| 136 | 136 | throw new RuntimeException(e); |
| 137 | 137 | } |
| 138 | 138 | } |
| 139 | + |
|
| 140 | + public PolarDataMiner filterToBoatClasses(Iterable<BoatClass> boatClasses) { |
|
| 141 | + return new PolarDataMiner(backendPolarSheetGenerationSettings, |
|
| 142 | + cubicRegressionPerCourseProcessor.filterToBoatClasses(boatClasses), |
|
| 143 | + speedRegressionPerAngleClusterProcessor.filterToBoatClasses(boatClasses), |
|
| 144 | + angleClusterGroup); |
|
| 145 | + } |
|
| 139 | 146 | |
| 140 | 147 | private void setUpWorkflow() throws ClassCastException, NoSuchMethodException, SecurityException { |
| 141 | 148 | Collection<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>> regressionPerCourseGrouperResultReceivers = new ArrayList<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>>(); |
java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedRegressionPerAngleClusterProcessor.java
| ... | ... | @@ -22,6 +22,7 @@ import com.sap.sailing.polars.regression.IncrementalLeastSquares; |
| 22 | 22 | import com.sap.sailing.polars.regression.impl.IncrementalAnyOrderLeastSquaresImpl; |
| 23 | 23 | import com.sap.sse.common.Bearing; |
| 24 | 24 | import com.sap.sse.common.Speed; |
| 25 | +import com.sap.sse.common.Util; |
|
| 25 | 26 | import com.sap.sse.common.Util.Pair; |
| 26 | 27 | import com.sap.sse.common.impl.DegreeBearingImpl; |
| 27 | 28 | import com.sap.sse.common.impl.KnotSpeedImpl; |
| ... | ... | @@ -32,6 +33,7 @@ import com.sap.sse.datamining.data.ClusterGroup; |
| 32 | 33 | import com.sap.sse.datamining.factories.GroupKeyFactory; |
| 33 | 34 | import com.sap.sse.datamining.impl.components.GroupedDataEntry; |
| 34 | 35 | import com.sap.sse.datamining.shared.GroupKey; |
| 36 | +import com.sap.sse.datamining.shared.impl.GenericGroupKey; |
|
| 35 | 37 | |
| 36 | 38 | /** |
| 37 | 39 | * Holds one speed regression per BoatClass, WindSpeed, Beat Angle Range combination and provides means for adding and |
| ... | ... | @@ -63,6 +65,49 @@ public class SpeedRegressionPerAngleClusterProcessor implements |
| 63 | 65 | this.angleClusterGroup = angleClusterGroup; |
| 64 | 66 | } |
| 65 | 67 | |
| 68 | + public SpeedRegressionPerAngleClusterProcessor filterToBoatClasses(Iterable<BoatClass> boatClasses) { |
|
| 69 | + final Set<BoatClass> allowedBoatClasses = Util.asSet(boatClasses); |
|
| 70 | + final SpeedRegressionPerAngleClusterProcessor filteredProcessor = new SpeedRegressionPerAngleClusterProcessor(angleClusterGroup); |
|
| 71 | + synchronized (regressions) { |
|
| 72 | + for (Map.Entry<GroupKey, IncrementalLeastSquares> entry : regressions.entrySet()) { |
|
| 73 | + GroupKey key = entry.getKey(); |
|
| 74 | + BoatClass boatClass = extractBoatClass(key); |
|
| 75 | + if (boatClass != null && allowedBoatClasses.contains(boatClass)) { |
|
| 76 | + filteredProcessor.regressions.put(key, entry.getValue()); |
|
| 77 | + } |
|
| 78 | + } |
|
| 79 | + } |
|
| 80 | + synchronized (fixCountPerBoatClass) { |
|
| 81 | + for (Map.Entry<BoatClass, Long> entry : fixCountPerBoatClass.entrySet()) { |
|
| 82 | + if (allowedBoatClasses.contains(entry.getKey())) { |
|
| 83 | + filteredProcessor.fixCountPerBoatClass.put(entry.getKey(), entry.getValue()); |
|
| 84 | + } |
|
| 85 | + } |
|
| 86 | + } |
|
| 87 | + return filteredProcessor; |
|
| 88 | + } |
|
| 89 | + |
|
| 90 | + private BoatClass extractBoatClass(GroupKey key) { |
|
| 91 | + final BoatClass result; |
|
| 92 | + if (key.hasSubKeys()) { |
|
| 93 | + // In the compound key, BoatClass is the first dimension (index 0) |
|
| 94 | + GroupKey boatClassKey = key.getKeys().get(0); |
|
| 95 | + if (boatClassKey instanceof GenericGroupKey) { |
|
| 96 | + Object value = ((GenericGroupKey<?>) boatClassKey).getValue(); |
|
| 97 | + if (value instanceof BoatClass) { |
|
| 98 | + result = (BoatClass) value; |
|
| 99 | + } else { |
|
| 100 | + result = null; |
|
| 101 | + } |
|
| 102 | + } else { |
|
| 103 | + result = null; |
|
| 104 | + } |
|
| 105 | + } else { |
|
| 106 | + result = null; |
|
| 107 | + } |
|
| 108 | + return result; |
|
| 109 | + } |
|
| 110 | + |
|
| 66 | 111 | @Override |
| 67 | 112 | public boolean canProcessElements() { |
| 68 | 113 | // TODO Auto-generated method stub |
java/com.sap.sailing.polars/webservices/api/polar_data.html
| ... | ... | @@ -13,7 +13,9 @@ |
| 13 | 13 | |
| 14 | 14 | <b>Description:</b> |
| 15 | 15 | <p> |
| 16 | -Gets the binary representation of the server's polar data for all boat classes. |
|
| 16 | +Gets the binary representation of the server's polar data. By default, data for all boat classes is returned. |
|
| 17 | +Optionally, the results can be filtered to specific boat classes using the <tt>boatClassName</tt> query parameter, |
|
| 18 | +which may be specified multiple times to include several boat classes. |
|
| 17 | 19 | </p> |
| 18 | 20 | <p> |
| 19 | 21 | You need the <tt>POLAR_DATA:READ</tt> permission for this. The output can be deserialized into a Java object of type |
| ... | ... | @@ -36,9 +38,29 @@ class. |
| 36 | 38 | <td>Binary Java Object</td> |
| 37 | 39 | </tr> |
| 38 | 40 | <tr> |
| 39 | - <td>Example:</td> |
|
| 40 | - <td><a href="https://www.sapsailing.com/polars/api/polar_data"> |
|
| 41 | - curl -o polar.dat -H 'Authorization: Bearer {your-access-token-here}' <a href="https://www.sapsailing.com/polars/api/polar_data">https://www.sapsailing.com/polars/api/polar_data</a> |
|
| 41 | + <td valign="top">Query Parameters:</td> |
|
| 42 | + <td> |
|
| 43 | + <table border="1" cellpadding="4" cellspacing="0"> |
|
| 44 | + <tr> |
|
| 45 | + <th>Name</th><th>Required</th><th>Description</th> |
|
| 46 | + </tr> |
|
| 47 | + <tr> |
|
| 48 | + <td><tt>boatClassName</tt></td> |
|
| 49 | + <td>No</td> |
|
| 50 | + <td>Name of a boat class to include in the response. May be specified multiple times |
|
| 51 | + to request data for several boat classes. If omitted, data for all boat classes is returned.</td> |
|
| 52 | + </tr> |
|
| 53 | + </table> |
|
| 54 | + </td> |
|
| 55 | + </tr> |
|
| 56 | + <tr> |
|
| 57 | + <td valign="top">Examples:</td> |
|
| 58 | + <td> |
|
| 59 | + All boat classes:<br/> |
|
| 60 | + <tt>curl -o polar.dat -H 'Authorization: Bearer {your-access-token-here}' <a href="https://www.sapsailing.com/polars/api/polar_data">https://www.sapsailing.com/polars/api/polar_data</a></tt> |
|
| 61 | + <br/><br/> |
|
| 62 | + Filtered to specific boat classes:<br/> |
|
| 63 | + <tt>curl -o polar.dat -H 'Authorization: Bearer {your-access-token-here}' "<a href="https://www.sapsailing.com/polars/api/polar_data?boatClassName=49er&boatClassName=Nacra17">https://www.sapsailing.com/polars/api/polar_data?boatClassName=49er&boatClassName=Nacra17</a>"</tt> |
|
| 42 | 64 | </td> |
| 43 | 65 | </tr> |
| 44 | 66 | </table> |