All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
fi.evolver.ai.vaadin.view.AdminBaseView Maven / Gradle / Ivy
package fi.evolver.ai.vaadin.view;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serial;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.vaadin.olli.FileDownloadWrapper;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.BeforeEnterEvent;
import com.vaadin.flow.router.BeforeEnterObserver;
import com.vaadin.flow.server.StreamResource;
import fi.evolver.ai.vaadin.ChatRepository;
import fi.evolver.ai.vaadin.admin.ChatExportGenerator;
import fi.evolver.ai.vaadin.admin.ChatReportGenerator;
import fi.evolver.ai.vaadin.component.ComponentSource;
import fi.evolver.ai.vaadin.component.i18n.VaadinTranslations;
import fi.evolver.ai.vaadin.entity.Chat;
import fi.evolver.ai.vaadin.util.AuthUtils;
import jakarta.annotation.PostConstruct;
public class AdminBaseView extends VerticalLayout implements BeforeEnterObserver {
@Serial
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(AdminBaseView.class);
private final VerticalLayout layout = new VerticalLayout();
private final Locale localeFi = new Locale("fi", "FI");
private final Button fetchChatsButton;
private final Button fetchChatsFullButton;
private final Button generateReportButton;
private final DatePicker startDatePicker;
private final DatePicker endDatePicker;
private final ChatRepository chatRepository;
public enum ExportType {
ONLY_MESSAGES,
FULL,
REPORT
}
@Autowired(required = false)
private List> additionalComponents = new ArrayList<>();
public AdminBaseView(ChatRepository chatRepository) {
this.chatRepository = chatRepository;
this.fetchChatsButton = new Button(getTranslation("view.admin.fetchChats"));
this.fetchChatsFullButton = new Button(getTranslation("view.admin.fetchFullChats"));
this.generateReportButton = new Button(getTranslation("view.admin.generateReport"));
this.startDatePicker = new DatePicker(getTranslation("view.admin.starDate"));
this.endDatePicker = new DatePicker(getTranslation("view.admin.endDate"));
}
@PostConstruct
private void createUi() {
layout.add(
new H2(getTranslation("view.admin.headline")),
startDatePicker,
endDatePicker,
createFileDownloadWrapper(ExportType.ONLY_MESSAGES),
createFileDownloadWrapper(ExportType.FULL),
createFileDownloadWrapper(ExportType.REPORT));
additionalComponents.stream()
.sorted()
.map(ComponentSource::getComponent)
.forEach(layout::add);
layout.setAlignItems(FlexComponent.Alignment.START);
layout.setSpacing(true);
layout.setMargin(true);
add(layout);
startDatePicker.setLocale(localeFi);
endDatePicker.setLocale(localeFi);
startDatePicker.setMin(LocalDate.now().minusYears(1));
endDatePicker.setMax(LocalDate.now());
startDatePicker.setValue(getInitialStartDate());
endDatePicker.setValue(getInitialEndDate());
fetchChatsButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
fetchChatsFullButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
generateReportButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
}
@Override
public void beforeEnter(BeforeEnterEvent event) {
if (!AuthUtils.isAdminUser())
event.forwardTo("/");
}
private FileDownloadWrapper createFileDownloadWrapper(ExportType type) {
FileDownloadWrapper wrapper = new FileDownloadWrapper(createStreamResource(type));
wrapper.wrapComponent(getButton(type));
return wrapper;
}
private StreamResource createStreamResource(ExportType type) {
// For some reason locale becomes empty inside the StreamResource
final Locale l = getLocale();
final VaadinTranslations t = (s, params) -> getTranslation(l, s, params);
return new StreamResource(generateFilename(type), (o, s) -> generateContent(o, type, t)) {
@Serial
private static final long serialVersionUID = 1L;
@Override
public Map getHeaders() {
Map headers = new HashMap<>(super.getHeaders());
headers.put("Content-Disposition", "attachment; filename=\"" + generateFilename(type) + "\"");
return headers;
}
};
}
private Button getButton(ExportType type) {
return switch (type) {
case FULL -> fetchChatsFullButton;
case ONLY_MESSAGES -> fetchChatsButton;
case REPORT -> generateReportButton;
default -> throw new IllegalArgumentException("Unexpected value: " + type);
};
}
private String generateFilename(ExportType type) {
String dateRangePart = "%s-%s".formatted(
startDatePicker.getValue() != null ? startDatePicker.getValue() : getInitialStartDate(),
endDatePicker.getValue() != null ? endDatePicker.getValue() : getInitialEndDate());
return switch (type) {
case FULL -> "chats-full_%s.md".formatted(dateRangePart);
case ONLY_MESSAGES -> "chats_%s.md".formatted(dateRangePart);
case REPORT -> "chat-report_%s.xlsx".formatted(dateRangePart);
default -> throw new IllegalArgumentException("Unexpected value: " + type);
};
}
private void generateContent(OutputStream out, ExportType type, VaadinTranslations t) {
try {
if (!AuthUtils.isAdminUser()) {
showNotification(t.getTranslation("view.admin.forbiddenOperation"));
return;
}
LocalDate startDate = startDatePicker.getValue();
LocalDate endDate = endDatePicker.getValue();
LocalDateTime startTime = parseStartTime(startDate);
LocalDateTime endTime = parseEndTime(endDate);
if (!isValidReportRange(startTime, endTime)) {
showNotification(t.getTranslation("view.admin.invalidDateRange", startDate, endDate));
return;
}
List chats = chatRepository.findAllByStartTimeGreaterThanEqualAndStartTimeLessThanEqualOrderByIdDesc(
startTime,
endTime);
if (type == ExportType.REPORT)
ChatReportGenerator.generateChatReport(chats, startDate, endDate, out);
else
out.write(ChatExportGenerator.generateChatExport(chats, startDate, endDate, type, this::getTranslation).getBytes());
}
catch (IOException e) {
LOG.error("Failed generating file content", e);
showNotification(t.getTranslation("view.admin.errorGenerating"));
}
}
private static LocalDateTime parseEndTime(LocalDate endDate) {
return endDate != null ? endDate.atTime(LocalTime.MAX) : null;
}
private static LocalDateTime parseStartTime(LocalDate startDate) {
return startDate != null ? startDate.atStartOfDay() : null;
}
private static LocalDate getInitialStartDate() {
return LocalDate.now().with(TemporalAdjusters.firstDayOfMonth());
}
private static LocalDate getInitialEndDate() {
return LocalDate.now();
}
private static boolean isValidReportRange(LocalDateTime startTime, LocalDateTime endTime) {
return startTime != null && endTime != null && startTime.isBefore(endTime);
}
private void showNotification(String message) {
getUI().ifPresent(ui -> {
ui.access(() -> {
Notification.show(message, 6000, Notification.Position.MIDDLE);
});
});
}
}