1

initial member + event export

This commit is contained in:
Christoph
2020-04-17 16:20:50 +02:00
parent a27d3f6f27
commit d88402954e
6 changed files with 104 additions and 16 deletions

View File

@ -16,19 +16,23 @@ import mops.gruppen2.infrastructure.GroupCache;
import java.time.LocalDateTime;
import java.util.UUID;
import static com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import static com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
@Log4j2
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "class")
@JsonSubTypes({@JsonSubTypes.Type(value = AddMemberEvent.class, name = "ADDMEMBER"),
@JsonSubTypes.Type(value = CreateGroupEvent.class, name = "CREATEGROUP"),
@JsonSubTypes.Type(value = DestroyGroupEvent.class, name = "DESTROYGROUP"),
@JsonSubTypes.Type(value = KickMemberEvent.class, name = "KICKMEMBER"),
@JsonSubTypes.Type(value = SetDescriptionEvent.class, name = "SETDESCRIPTION"),
@JsonSubTypes.Type(value = SetInviteLinkEvent.class, name = "SETLINK"),
@JsonSubTypes.Type(value = SetLimitEvent.class, name = "SETLIMIT"),
@JsonSubTypes.Type(value = SetParentEvent.class, name = "SETPARENT"),
@JsonSubTypes.Type(value = SetTitleEvent.class, name = "SETTITLE"),
@JsonSubTypes.Type(value = SetTypeEvent.class, name = "SETTYPE"),
@JsonSubTypes.Type(value = UpdateRoleEvent.class, name = "UPDATEROLE")})
@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "class")
@JsonSubTypes({@Type(value = AddMemberEvent.class, name = "ADDMEMBER"),
@Type(value = CreateGroupEvent.class, name = "CREATEGROUP"),
@Type(value = DestroyGroupEvent.class, name = "DESTROYGROUP"),
@Type(value = KickMemberEvent.class, name = "KICKMEMBER"),
@Type(value = SetDescriptionEvent.class, name = "SETDESCRIPTION"),
@Type(value = SetInviteLinkEvent.class, name = "SETLINK"),
@Type(value = SetLimitEvent.class, name = "SETLIMIT"),
@Type(value = SetParentEvent.class, name = "SETPARENT"),
@Type(value = SetTitleEvent.class, name = "SETTITLE"),
@Type(value = SetTypeEvent.class, name = "SETTYPE"),
@Type(value = UpdateRoleEvent.class, name = "UPDATEROLE")})
@Getter
@NoArgsConstructor // Lombok needs a default constructor in the base class
public abstract class Event {
@ -93,6 +97,7 @@ public abstract class Event {
protected abstract void applyEvent(Group group) throws EventException;
@JsonIgnore
public abstract String format();
@JsonIgnore

View File

@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import mops.gruppen2.domain.event.Event;
import mops.gruppen2.domain.exception.BadPayloadException;
import mops.gruppen2.domain.exception.GroupNotFoundException;
import mops.gruppen2.domain.service.helper.JsonHelper;
import mops.gruppen2.persistance.EventRepository;
import mops.gruppen2.persistance.dto.EventDTO;
@ -12,6 +13,7 @@ import org.springframework.stereotype.Service;
import java.sql.Timestamp;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Log4j2
@ -98,7 +100,14 @@ public class EventStoreService {
return getEventsFromDTOs(eventStore.findAllEvents());
}
public List<Event> findGroupEvents(String groupId) {
return getEventsFromDTOs(eventStore.findGroupEvents(groupId));
public List<Event> findGroupEvents(UUID groupId) {
return getEventsFromDTOs(eventStore.findGroupEvents(groupId.toString()));
}
public String findGroupPayloads(UUID groupId) {
return eventStore.findGroupPayloads(groupId.toString()).stream()
.map(payload -> payload + "\n")
.reduce((String payloadA, String payloadB) -> payloadA + payloadB)
.orElseThrow(() -> new GroupNotFoundException("Keine Payloads gefunden."));
}
}

View File

@ -45,4 +45,20 @@ public final class CsvHelper {
return reader.<User>readValues(stream).readAll();
}
public static String writeCsvUserList(List<User> members) {
StringBuilder builder = new StringBuilder();
builder.append("id,givenname,familyname,email");
members.forEach(user -> builder.append(user.getId())
.append(",")
.append(user.getGivenname())
.append(",")
.append(user.getFamilyname())
.append(",")
.append(user.getEmail())
.append("\n"));
return builder.toString();
}
}

View File

@ -14,6 +14,7 @@ import mops.gruppen2.domain.service.helper.CsvHelper;
import mops.gruppen2.domain.service.helper.ValidationHelper;
import mops.gruppen2.infrastructure.GroupCache;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@ -25,7 +26,9 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.UUID;
@SuppressWarnings("SameReturnValue")
@ -102,11 +105,49 @@ public class GroupDetailsController {
Model model,
@PathVariable("id") String groupId) {
model.addAttribute("events", eventStoreService.findGroupEvents(groupId));
model.addAttribute("events",
eventStoreService.findGroupEvents(UUID.fromString(groupId)));
return "history";
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("details/{id}/export/history")
public void getDetailsExportHistory(HttpServletResponse response,
@PathVariable("id") String groupId) {
String filename = "eventlog-" + groupId + ".txt";
response.setContentType("text/txt");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"");
try {
response.getWriter().write(eventStoreService.findGroupPayloads(UUID.fromString(groupId)));
} catch (IOException e) {
log.error("Payloads konnten nicht geschrieben werden.", e);
}
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("details/{id}/export/members")
public void getDetailsExportMembers(HttpServletResponse response,
@PathVariable("id") String groupId) {
String filename = "teilnehmer-" + groupId + ".csv";
response.setContentType("text/csv");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"");
try {
response.getWriter()
.print(CsvHelper.writeCsvUserList(groupCache.group(UUID.fromString(groupId)).getMembers()));
} catch (IOException e) {
log.error("Teilnehmerliste konnte nicht geschrieben werden.", e);
}
}
@RolesAllowed({"ROLE_orga", "ROLE_studentin"})
@GetMapping("/details/{id}/edit")
public String getDetailsEdit(KeycloakAuthenticationToken token,
@ -183,6 +224,7 @@ public class GroupDetailsController {
Group group = groupCache.group(UUID.fromString(groupId));
ValidationHelper.throwIfNoAdmin(group, principal);
ValidationHelper.throwIfLastAdmin(group, principal);
groupService.toggleMemberRole(group, principal, target);

View File

@ -20,4 +20,7 @@ public interface EventRepository extends CrudRepository<EventDTO, Long> {
@Query("SELECT * FROM event WHERE group_id = :groupid")
List<EventDTO> findGroupEvents(@Param("groupid") String groupId);
@Query("SELECT event_payload FROM event WHERE group_id = :groupid")
List<String> findGroupPayloads(@Param("groupid") String groupId);
}

View File

@ -93,10 +93,23 @@
<span>Event-Historie</span>
</div>
<div class="row">
<a class="btn btn-primary btn-bar"
th:href="@{/gruppen2/details/{id}/history(id=${group.getId()})}">Event-Log</a>
<!--Spacer-->
<span class="col"></span>
<a class="btn btn-primary btn-bar" th:href="@{/gruppen2/details/{id}/history(id=${group.getId()})}">Event-Log</a>
<a class="btn btn-info btn-bar mr-2"
th:href="@{/gruppen2/details/{id}/export/history(id=${group.getId()})}"
title="Exportiert die gesamte Event-Historie dieser Gruppe. Kann beim erstellen importiert werden.">
Event-Log exportieren
</a>
<a class="btn btn-info btn-bar"
th:href="@{/gruppen2/details/{id}/export/members(id=${group.getId()})}"
title="Exportiert die Teilnehmerliste. Kann beim erstellen (oder nachträglich) importiert werden.">
Teilnehmer exportieren
</a>
</div>
</div>