Skip to content

Commit

Permalink
feat: Improve display of rewards detail list - MEED-7632 - Meeds-io/M…
Browse files Browse the repository at this point in the history
…IPs#154 (#597)

This PR will improve display of rewards detail list by using pagination.
  • Loading branch information
AzmiTouil authored and boubaker committed Oct 24, 2024
1 parent 5c45ba5 commit 73a30db
Show file tree
Hide file tree
Showing 28 changed files with 975 additions and 206 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public boolean hasErrorTransactions() {
return getFailedTransactionCount() > 0;
}

public boolean isCompletelyProceeded() {
public boolean isCompletelyProcessed() {
// Can be greater if in the mean time of transaction confirmation, a member
// has been invalidated / disabled / deleted
return getTransactionsCount() > 0 && getSuccessTransactionCount() >= getValidRewardCount();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* This file is part of the Meeds project (https://meeds.io/).
*
* Copyright (C) 2020 - 2024 Meeds Lab contact@meedslab.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package io.meeds.wallet.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class RewardReportStatus {

private long sentDate;

private RewardPeriod period;

private long participantsCount;

private long recipientsCount;

private int achievementsCount;

private double tokensSent;

private double tokensToSend;

private boolean completelyProcessed;

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,30 @@
package io.meeds.wallet.reward.service;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.List;
import java.util.Map;
import java.util.Set;

import io.meeds.wallet.model.*;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import org.exoplatform.social.core.identity.model.Identity;

import io.meeds.wallet.model.DistributionForecast;
import io.meeds.wallet.model.RewardPeriod;
import io.meeds.wallet.model.RewardPeriodType;
import io.meeds.wallet.model.RewardReport;
import io.meeds.wallet.model.RewardSettings;
import io.meeds.wallet.model.WalletReward;

/**
* A storage service to save/load reward settings
*/
public interface RewardReportService {

/**
* Gets rewards report switch {@link RewardPeriod}
*
* @param rewardPeriod a {@link RewardPeriod}
* @return a {@link RewardReportStatus}
*/
RewardReportStatus getReport(RewardPeriod rewardPeriod);

/**
* Compute rewards swicth configurations for the list of identities passed in
* parameters
Expand Down Expand Up @@ -88,6 +92,14 @@ public interface RewardReportService {
*/
RewardPeriod getRewardPeriod(RewardPeriodType periodType, LocalDate date);

/**
* Return the stored reward period by id
*
* @param rewardPeriodId reward period id
* @return {@link RewardPeriod}
*/
RewardPeriod getRewardPeriodById(long rewardPeriodId);

/**
* Retrieve a {@link RewardReport} corresponding to a period identified by its id
*
Expand Down Expand Up @@ -164,4 +176,28 @@ public interface RewardReportService {
*/
void replaceRewardTransactions(String oldHash, String newHash);

/**
* Gets wallet rewards by PeriodId and status
* first one
*
* @param periodId Reward Period id
* @param status Wallet reward status
* @param zoneId Wallet reward status
*/
Page<WalletReward> findWalletRewardsByPeriodIdAndStatus(long periodId, String status, ZoneId zoneId, Pageable pageable);


/**
* Set isChanged data map for periods not sent
*
* @param updatedSettings map of isChanged data for periods not sent
*
*/
void setRewardSettingChanged(Map<Long, Boolean> updatedSettings);

/**
* Gets isChanged data map for periods not sent
*
*/
Map<Long, Boolean> getRewardSettingChanged();
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import jakarta.transaction.Transactional;
import io.meeds.wallet.reward.entity.WalletRewardEntity;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
Expand All @@ -37,6 +38,14 @@ public interface RewardDAO extends JpaRepository<WalletRewardEntity, Long> {
""")
List<WalletRewardEntity> findRewardsByPeriodId(@Param("periodId") long periodId);

@Query("""
SELECT rw FROM Reward rw WHERE rw.period.id = :periodId AND
(:isValid = TRUE AND (rw.tokensSent > 0 OR rw.tokensToSend > 0) OR :isValid = FALSE AND (rw.tokensSent <= 0 AND rw.tokensToSend <= 0))
""")
Page<WalletRewardEntity> findWalletRewardsByPeriodIdAndStatus(@Param("periodId") long periodId,
@Param("isValid") boolean isValid,
Pageable pageable);

List<WalletRewardEntity> findWalletRewardEntitiesByIdentityId(long identityId, Pageable pageable);

double countWalletRewardEntitiesByIdentityId(long identityId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* This file is part of the Meeds project (https://meeds.io/).
*
* Copyright (C) 2020 - 2024 Meeds Lab contact@meedslab.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package io.meeds.wallet.reward.listener;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import io.meeds.wallet.model.RewardPeriod;
import io.meeds.wallet.model.RewardSettings;
import io.meeds.wallet.reward.service.RewardReportService;
import io.meeds.wallet.reward.service.RewardSettingsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import org.exoplatform.services.listener.Event;
import org.exoplatform.services.listener.Listener;
import org.exoplatform.services.listener.ListenerService;

import io.meeds.common.ContainerTransactional;

import jakarta.annotation.PostConstruct;

import static io.meeds.gamification.utils.Utils.*;
import static io.meeds.wallet.reward.service.WalletRewardSettingsService.REWARD_SETTINGS_UPDATED;
import static io.meeds.wallet.utils.WalletUtils.MODIFY_ADDRESS_ASSOCIATED_EVENT;
import static io.meeds.wallet.utils.WalletUtils.NEW_ADDRESS_ASSOCIATED_EVENT;

/**
* A listener that is triggered to update estimated reward report for current
* period.
*/
@Component
public class RewardReportUpdateListener extends Listener<Object, Map<String, String>> {

private static final List<String> EVENT_NAMES = Arrays.asList(NEW_ADDRESS_ASSOCIATED_EVENT,
MODIFY_ADDRESS_ASSOCIATED_EVENT,
REWARD_SETTINGS_UPDATED,
POST_CREATE_ANNOUNCEMENT_EVENT,
POST_UPDATE_ANNOUNCEMENT_EVENT,
POST_CANCEL_ANNOUNCEMENT_EVENT,
POST_REALIZATION_CREATE_EVENT,
POST_REALIZATION_UPDATE_EVENT,
POST_REALIZATION_CANCEL_EVENT);

@Autowired
private RewardReportService rewardReportService;

@Autowired
private RewardSettingsService rewardSettingsService;

@Autowired
private ListenerService listenerService;

@PostConstruct
public void init() {
EVENT_NAMES.forEach(name -> listenerService.addListener(name, this));
}

@ContainerTransactional
@Override
public void onEvent(Event<Object, Map<String, String>> event) {
String eventName = event.getEventName();
Map<Long, Boolean> updatedSettings = rewardReportService.getRewardSettingChanged();
if (REWARD_SETTINGS_UPDATED.equals(eventName)) {
List<RewardPeriod> rewardPeriods = rewardReportService.getRewardPeriodsNotSent();
updatedSettings = rewardPeriods.stream().collect(Collectors.toMap(RewardPeriod::getId, rewardPeriod -> true));
} else {
RewardSettings rewardSettings = rewardSettingsService.getSettings();
RewardPeriod rewardPeriod = rewardReportService.getRewardPeriod(rewardSettings.getPeriodType(), LocalDate.now());
updatedSettings.put(rewardPeriod.getId(), true);
}
rewardReportService.setRewardSettingChanged(updatedSettings);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@
import org.exoplatform.commons.api.notification.model.NotificationInfo;
import org.exoplatform.commons.api.notification.service.template.TemplateContext;
import org.exoplatform.commons.notification.template.TemplateUtils;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.social.notification.plugin.SocialNotificationUtils;
import org.exoplatform.webui.utils.TimeConvertUtils;

Expand All @@ -50,14 +47,10 @@

public class RewardSuccessTemplateBuilder extends AbstractTemplateBuilder {

private static final Log LOG = ExoLogger.getLogger(RewardSuccessTemplateBuilder.class);

private ChannelKey channelKey;

private boolean pushNotification;

private ExoContainer container;

private RewardSettingsService rewardSettingsService;

public RewardSuccessTemplateBuilder(RewardSettingsService rewardSettingsService, ChannelKey channelKey) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.PagedModel;
Expand All @@ -75,32 +77,23 @@ public class RewardReportREST {
@GetMapping(path = "compute")
@Secured("rewarding")
@Operation(
summary = "Compute rewards of wallets per a chosen period of time",
summary = "Gets rewards report per a chosen period of time",
method = "GET",
description = "returns a set of wallet reward object")
description = "returns a list of reward report")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Request fulfilled"),
@ApiResponse(responseCode = "400", description = "Invalid query input"),
@ApiResponse(responseCode = "401", description = "Unauthorized operation"),
@ApiResponse(responseCode = "500", description = "Internal server error") })
public List<RewardReport> computeRewards(@Parameter(description = "Page")
@RequestParam(value = "page", defaultValue = "0", required = false)
int page,
@Parameter(description = "Page size")
@RequestParam(value = "size", defaultValue = "12", required = false)
int size) {
public List<RewardReportStatus> getReportsStatus(@Parameter(description = "Page")
@RequestParam(value = "page", defaultValue = "0", required = false)
int page,
@Parameter(description = "Page size")
@RequestParam(value = "size", defaultValue = "12", required = false)
int size) {
int skip = page * size;
List<RewardPeriod> periods = generatePreviousPeriods(skip + size).subList(skip, skip + size);
List<RewardReport> rewardReports = periods.parallelStream()
.map(period -> {
RewardReport rewardReport =
rewardReportService.computeRewards(period.getPeriodMedianDate());
rewardReport.setPeriod(new RewardPeriodWithFullDate(rewardReport.getPeriod()));
return rewardReport;
})
.toList();
rewardReports.forEach(rewardReport -> rewardReport.setPeriod(new RewardPeriodWithFullDate(rewardReport.getPeriod())));
return rewardReports;
return periods.parallelStream().map(period -> rewardReportService.getReport(period)).toList();
}

@PostMapping(path = "period/compute")
Expand All @@ -121,6 +114,39 @@ public RewardReport computeRewardsByPeriod(@RequestBody
return rewardReport;
}

@GetMapping(path = "rewards")
@Secured("rewarding")
@Operation(summary = "Gets rewards of wallets per a chosen period of time", method = "GET", description = "returns the list of wallets per a chosen period of time")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Request fulfilled"),
@ApiResponse(responseCode = "400", description = "Invalid query input"),
@ApiResponse(responseCode = "401", description = "Unauthorized operation"),
@ApiResponse(responseCode = "500", description = "Internal server error") })
public PagedModel<EntityModel<WalletReward>> getWalletRewards(Pageable pageable,
PagedResourcesAssembler<WalletReward> assembler,
@Parameter(description = "Period id", required = true)
@RequestParam("periodId")
long periodId,
@Parameter(description = "Wallet reward status filtering, possible values: VALId and INVALID. Default value = VALId.")
@RequestParam(value = "status", defaultValue = "VALID")
String status,
@Parameter(description = "Field to sort by. Possible values: 'tokensSent', 'points'. Default is 'tokensSent'.")
@RequestParam(value = "sortField", defaultValue = "tokensSent") String sortField,
@Parameter(description = "Sort direction for tokensToSend field. Possible values: 'asc' (ascending) or 'desc' (descending). Default value = asc.")
@RequestParam(value = "sortDir", defaultValue = "asc") String sortDir) {

Sort sort = sortDir.equalsIgnoreCase("desc") ? Sort.by(sortField).descending() : Sort.by(sortField).ascending();

Pageable sortedPageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), sort);

Page<WalletReward> walletRewards;
walletRewards = rewardReportService.findWalletRewardsByPeriodIdAndStatus(periodId,
status,
rewardSettingsService.getSettings().zoneId(),
sortedPageable);
return assembler.toModel(walletRewards);
}

@PostMapping(path = "forecast")
@Secured("rewarding")
@Operation(
Expand Down Expand Up @@ -268,8 +294,8 @@ private List<RewardPeriod> generatePreviousPeriods(int count) {
ZonedDateTime currentDateTime = ZonedDateTime.now(zoneId);

return IntStream.range(0, count).mapToObj(i -> {
ZonedDateTime start;
ZonedDateTime end;
ZonedDateTime start = null;
ZonedDateTime end = null;

switch (periodType) {
case WEEK -> {
Expand All @@ -287,16 +313,14 @@ private List<RewardPeriod> generatePreviousPeriods(int count) {
.truncatedTo(ChronoUnit.DAYS);
end = start.plusMonths(3);
}
default -> throw new UnsupportedOperationException("Unknown period type");
}

RewardPeriod rewardPeriod = new RewardPeriod();
rewardPeriod.setRewardPeriodType(periodType);
rewardPeriod.setStartDateInSeconds(timeToSecondsAtDayStart(LocalDate.from(start), currentDateTime.getZone()));
rewardPeriod.setEndDateInSeconds(timeToSecondsAtDayStart(LocalDate.from(end), currentDateTime.getZone()));

return rewardPeriod;
}).collect(Collectors.toList());
}).toList();
}

}
Loading

0 comments on commit 73a30db

Please sign in to comment.