Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #38

Merged
merged 5 commits into from
Jan 7, 2025
Merged

Dev #38

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

public interface ShopItemHistoryRepository extends JpaRepository<ShopItemHistoryEntry, Long> {

List<ShopItemHistoryEntry> findByUserId(String userId);

Page<ShopItemHistoryEntry> findByUserIdEquals(String username, Pageable pageable);

List<ShopItemHistoryEntry> findByUserIdAndTimestampBetween(String userId, Long startTimestamp,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package de.unipassau.fim.fsinfo.prost.data.repositories;

import de.unipassau.fim.fsinfo.prost.data.dao.ProstUser;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<ProstUser, String> {

List<ProstUser> findByHidden(Boolean hidden);

}
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,11 @@ public boolean consume(String itemId, String userId, int amount, String bearerId
historyRepository.save(historyEntry);
bearerLastBuy.put(bearerId, Instant.now().toEpochMilli());

AbstractMetricCollector.updateAllEntriesFor(ProstUser.class, user);
AbstractMetricCollector.updateAllEntriesFor(ShopItem.class, item);
AbstractMetricCollector.updateAllEntriesFor(ShopItemHistoryEntry.class, historyEntry);
if (!user.getHidden()) {
AbstractMetricCollector.updateAllEntriesFor(ProstUser.class, user);
AbstractMetricCollector.updateAllEntriesFor(ShopItem.class, item);
AbstractMetricCollector.updateAllEntriesFor(ShopItemHistoryEntry.class, historyEntry);
}
return true;
} else {
System.out.println("[SS] :: No Transaction found!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import de.unipassau.fim.fsinfo.prost.data.dao.ProstUser;
import de.unipassau.fim.fsinfo.prost.data.repositories.UserRepository;
import de.unipassau.fim.fsinfo.prost.service.statistics.AbstractMetricCollector;
import de.unipassau.fim.fsinfo.prost.service.statistics.MetricService;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
Expand All @@ -15,10 +16,12 @@
public class UserService {

private final UserRepository users;
private final MetricService metricService;

@Autowired
public UserService(UserRepository users) {
public UserService(UserRepository users, MetricService metricService) {
this.users = users;
this.metricService = metricService;
}

@Transactional
Expand Down Expand Up @@ -85,7 +88,6 @@ public boolean delete(String id) {

if (user.isPresent()) {
users.delete(user.get());
AbstractMetricCollector.removeAllEntriesFor(ProstUser.class, user.get());
return true;
}
return false;
Expand Down Expand Up @@ -140,6 +142,12 @@ public boolean setHidden(String id, boolean value) {
ProstUser u = user.get();
u.setHidden(value);
users.save(u);

if (value) {
metricService.removeFromMetrics(u);
} else {
metricService.addToMetrics(u);
}
return true;
}
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,17 @@ protected abstract BigDecimal calculateValue(T entity, TimeSpan timeSpan, Long s

protected abstract T findByKey(String key);

/**
* @param entity
* @return true if entity should be ignored in the metrics.
*/
protected abstract boolean filterOut(T entity);

protected void updateEntry(T entity) {
long now = Instant.now().toEpochMilli();
if (filterOut(entity)) {
return;
}
metricEntries_Weekly.put(getKey(entity),
calculateValue(entity, TimeSpan.WEEK, now - WEEK_MILLIS, now));
metricEntries_Monthly.put(getKey(entity),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import de.unipassau.fim.fsinfo.prost.data.repositories.UserRepository;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import org.springframework.beans.factory.annotation.Autowired;
Expand Down Expand Up @@ -45,7 +46,7 @@ public long resetMetric() {
"[MS] :: Resetting Metrics :: started at " + dateTimeFormatter.format(
LocalDateTime.now()));
long currentTime = System.currentTimeMillis();
AbstractMetricCollector.initAllCollectors(ProstUser.class, userRepository.findAll());
AbstractMetricCollector.initAllCollectors(ProstUser.class, userRepository.findByHidden(false));
AbstractMetricCollector.initAllCollectors(ShopItem.class, shopItemRepository.findAll());
AbstractMetricCollector.initAllCollectors(ShopItemHistoryEntry.class,
shopItemHistoryRepository.findAll());
Expand All @@ -54,4 +55,22 @@ public long resetMetric() {
return dur;
}

public void removeFromMetrics(ProstUser user) {
AbstractMetricCollector.removeAllEntriesFor(ProstUser.class, user);

List<ShopItemHistoryEntry> entry = shopItemHistoryRepository.findByUserId(user.getId());
for (ShopItemHistoryEntry e : entry) {
AbstractMetricCollector.removeAllEntriesFor(ShopItemHistoryEntry.class, e);
}
}

public void addToMetrics(ProstUser user) {
AbstractMetricCollector.updateAllEntriesFor(ProstUser.class, user);

List<ShopItemHistoryEntry> entry = shopItemHistoryRepository.findByUserId(user.getId());
for (ShopItemHistoryEntry e : entry) {
AbstractMetricCollector.updateAllEntriesFor(ShopItemHistoryEntry.class, e);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ public BigDecimal calculateValue(ShopItemHistoryEntry entity, TimeSpan timeSpan,
return valueO.orElse(BigDecimal.ZERO);
}

@Override
protected boolean filterOut(ShopItemHistoryEntry entity) {
Optional<ProstUser> userO = userRepository.findById(entity.getUserId());
if (userO.isPresent()) {
ProstUser user = userO.get();
return user.getHidden();
}
return false;
}

@Override
public String[] getKeys(ShopItemHistoryEntry entity) {
ZoneOffset offset = zone.getRules().getOffset(Instant.now());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ public ItemPurchaseMetricCollector(ShopItemHistoryRepository shopItemHistoryRepo
ShopItemRepository shopItemRepository, UserRepository userRepository) {
super(ShopItemHistoryEntry.class);
this.shopItemHistoryRepository = shopItemHistoryRepository;
initMetrics(shopItemHistoryRepository.findAll());
this.shopItemRepository = shopItemRepository;
this.userRepository = userRepository;
initMetrics(shopItemHistoryRepository.findAll());
}

@Override
Expand All @@ -49,6 +49,16 @@ public BigDecimal calculateValue(ShopItemHistoryEntry entity, TimeSpan timeSpan,
return valueO.orElse(BigDecimal.ZERO);
}

@Override
protected boolean filterOut(ShopItemHistoryEntry entity) {
Optional<ProstUser> userO = userRepository.findById(entity.getUserId());
if (userO.isPresent()) {
ProstUser user = userO.get();
return user.getHidden();
}
return false;
}

@Override
public String[] getKeys(ShopItemHistoryEntry entity) {
LocalDateTime entityTime = Instant.ofEpochMilli(entity.getTimestamp()).atZone(ZoneOffset.UTC)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,9 @@ public ShopItem findByKey(String key) {
Optional<ShopItem> result = shopItemRepository.findById(key);
return result.orElse(null);
}

@Override
protected boolean filterOut(ShopItem entity) {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,9 @@ public ProstUser findByKey(String key) {
Optional<ProstUser> result = userRepository.findById(key);
return result.orElse(null);
}

@Override
protected boolean filterOut(ProstUser entity) {
return entity.getHidden();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import de.unipassau.fim.fsinfo.prost.data.dao.ProstUser;
import de.unipassau.fim.fsinfo.prost.data.repositories.UserRepository;
import de.unipassau.fim.fsinfo.prost.service.statistics.MetricService;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -19,6 +20,9 @@ class UserServiceTest {

private UserService userService;

@Mock
private MetricService metricService;

@Mock
private UserRepository userRepository;

Expand All @@ -27,7 +31,7 @@ class UserServiceTest {
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
userService = new UserService(userRepository);
userService = new UserService(userRepository, metricService);

prostUser = new ProstUser("testuser", "User One", "[email protected]", true, false);
}
Expand Down
106 changes: 42 additions & 64 deletions frontend/src/Components/StatisticsTab/AllSystemStatistics.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
import { ScrollArea, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport } from "@radix-ui/react-scroll-area";
import { HistoryEntryDisplay } from "./HistoryEntryDisplay";
import { useEffect, useState } from "react";
import { ShopHistoryEntry } from "../../Types/ShopHistory";
import { User } from "../../Types/User";
import { ShopItem } from "../../Types/ShopItem";
import { getAllShopItems, getAllUsers, getHistory, getItemMetric } from "../../Queries";
import { formatMoney } from "../../Format";
import { MetricInfo } from "./MetricOverview";
import { isOnlyUser } from "../../SessionInfo";
import { CompositeMetricType, ItemMetricType, TimeSpan } from "../../Types/Statistics";
import { ItemMetricPieChart } from "../Chart/PieChart";
import { CompositeMetricLineChart } from "../Chart/LineChart";
import {useEffect, useState} from "react";
import {ShopHistoryEntry} from "../../Types/ShopHistory";
import {User} from "../../Types/User";
import {ShopItem} from "../../Types/ShopItem";
import {getAllShopItems, getAllUsers, getHistory, getItemMetric} from "../../Queries";
import {formatMoney} from "../../Format";
import {MetricInfo} from "./MetricOverview";
import {CompositeMetricType, ItemMetricType, TimeSpan} from "../../Types/Statistics";
import {ItemMetricPieChart} from "../Chart/PieChart";
import {CompositeMetricLineChart} from "../Chart/LineChart";

export function AllSystemStatistics(props: { timeSpan: TimeSpan }) {
const [history, setHistory] = useState<ShopHistoryEntry[]>([]);
const [users, setUsers] = useState<User[]>([]);
const [items, setItems] = useState<ShopItem[]>([]);
const [totalRevenue, setTotalRevenue] = useState<number>(0);
const { timeSpan } = props;
const {timeSpan} = props;

useEffect(reloadShopItems, []);

Expand All @@ -29,16 +26,16 @@ export function AllSystemStatistics(props: { timeSpan: TimeSpan }) {

useEffect(() => {
getAllUsers()
.then((userList) => {
if (userList === undefined) {
setUsers([]);
} else {
setUsers(userList);
}
})
.catch(() => {
.then((userList) => {
if (userList === undefined) {
setUsers([]);
});
} else {
setUsers(userList);
}
})
.catch(() => {
setUsers([]);
});
}, []);

useEffect(() => {
Expand Down Expand Up @@ -68,46 +65,27 @@ export function AllSystemStatistics(props: { timeSpan: TimeSpan }) {
}

return (
<>
{isOnlyUser() ? (
<></>
) : (
<ScrollArea className="DisplayCard">
<ScrollAreaViewport style={{ maxHeight: "20rem" }}>
<h3 className="bold">Kürzliche Käufe</h3>
<table className="Table">
<tbody>
{history.map((item) => (
<HistoryEntryDisplay entry={item} key={item.id} showHidden={false} />
))}
</tbody>
</table>
</ScrollAreaViewport>
<ScrollAreaScrollbar className="Scrollbar" orientation="vertical">
<ScrollAreaThumb className="ScrollbarThumb" />
</ScrollAreaScrollbar>
<ScrollAreaScrollbar className="Scrollbar" orientation="horizontal">
<ScrollAreaThumb className="ScrollbarThumb" />
</ScrollAreaScrollbar>
</ScrollArea>
)}
<div className="GridContainer" style={{ gridTemplateColumns: "repeat(auto-fill, minmax(24rem, 1fr))" }}>
<MetricInfo title="Nutzer" value={String(users.length)} desc="" />
<MetricInfo title="Gegenstände" value={String(items.length)} desc="" />
<MetricInfo title="Guthaben" value={formatMoney(getUserDebt())} desc="" />
<MetricInfo title="Einnahmen" value={formatMoney(totalRevenue)} desc="" />
</div>
<div className="" style={{ display: "flex", flexFlow: "row wrap", justifyContent: "space-around" }}>
<ItemMetricPieChart title="Verkaufsschlager" desc="" type={ItemMetricType.TOP_SELLING_ITEMS} time={timeSpan} />
<CompositeMetricLineChart
type={CompositeMetricType.HOURLY_ACTIVITY}
title="Aktivität"
desc="Käufe zur Tageszeit"
time={timeSpan}
filterFirst={false}
dataKey={undefined}
/>
</div>
</>
<>
<div className="GridContainer"
style={{gridTemplateColumns: "repeat(auto-fill, minmax(24rem, 1fr))"}}>
<MetricInfo title="Nutzer" value={String(users.length)} desc=""/>
<MetricInfo title="Gegenstände" value={String(items.length)} desc=""/>
<MetricInfo title="Guthaben" value={formatMoney(getUserDebt())} desc=""/>
<MetricInfo title="Einnahmen" value={formatMoney(totalRevenue)} desc=""/>
</div>
<div className=""
style={{display: "flex", flexFlow: "row wrap", justifyContent: "space-around"}}>
<ItemMetricPieChart title="Verkaufsschlager" desc=""
type={ItemMetricType.TOP_SELLING_ITEMS} time={timeSpan}/>
<CompositeMetricLineChart
type={CompositeMetricType.HOURLY_ACTIVITY}
title="Aktivität"
desc="Käufe zur Tageszeit"
time={timeSpan}
filterFirst={false}
dataKey={undefined}
/>
</div>
</>
);
}
Loading
Loading