completed parser for reparier-cafe jena
Build Docker Image / Docker-Build (push) Failing after 17s
Build Docker Image / Clean-Registry (push) Failing after 6s

Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
2026-08-31 17:46:00 +02:00
parent 3167065232
commit f3752a1939
4 changed files with 120 additions and 7 deletions
@@ -10,6 +10,11 @@ import org.json.JSONObject;
* @param longitude the longitude
*/
public record Coords(double latitude,double longitude) {
public static Coords from(String geo) {
var parts = geo.split(";");
return new Coords(Double.parseDouble(parts[0]), Double.parseDouble(parts[1]));
}
/**
* get a string representing this coords in the ICAL format, see <a href="https://datatracker.ietf.org/doc/html/rfc5545#section-3.8.1.6">iCalendar spec</a>
* @return the formatted coords
@@ -3,19 +3,27 @@ package de.srsoftware.cal;
import static de.srsoftware.tools.container.Container.transform;
import static de.srsoftware.tools.container.Error.error;
import static java.lang.System.Logger.Level.WARNING;
import static java.lang.System.getLogger;
import de.srsoftware.cal.api.Appointment;
import de.srsoftware.cal.api.Coords;
import de.srsoftware.cal.api.Importer;
import de.srsoftware.tools.container.Container;
import de.srsoftware.tools.container.Payload;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Stream;
public abstract class IcalImporter implements Importer {
private static final System.Logger LOG = getLogger(IcalImporter.class.getSimpleName());
@Override
public Stream<Appointment> fetch() throws IOException {
var programPage = Payload.of(url()).map(Util::url);
@@ -23,27 +31,90 @@ public abstract class IcalImporter implements Importer {
return programPage
.map(Util::open)
.map(Util::preload)
.map(IcalImporter::parse)
.map(this::parse)
.stream();
}
private static Container<List<Appointment>> parse(Container<InputStream> input) {
private Container<List<Appointment>> parse(Container<InputStream> input) {
if (input.optional().isEmpty()) return transform(input);
InputStream stream = input.optional().get();
var scanner = new Scanner(stream);
var lines = new ArrayList<String>();
while (scanner.hasNextLine()){
var line = scanner.nextLine();
System.out.println(line);
if (line.startsWith(" ")) line = lines.removeLast() + line.substring(1);
lines.add(line);
}
scanner.close();
try {
stream.close();
} catch (IOException e) {
throw new RuntimeException(e);
return error(e.getMessage());
}
return error("Not implemented");
return parseLines(lines);
}
private Container<List<Appointment>> parseLines(ArrayList<String> lines) {
var events = new ArrayList<Appointment>();
while (!lines.isEmpty()){
var line = lines.removeFirst();
if (line.equals("BEGIN:VEVENT")){
lines.addFirst(line);
parseEvent(lines).optional().ifPresent(events::add);
}
}
return Payload.of(events);
}
private Container<Appointment> parseEvent(ArrayList<String> lines) {
String summary = null;
String description = null;
Container<LocalDateTime> dtstart = error("No start date found"), dtend = error("No end date found");
String location = null;
String geo = null;
while (!lines.isEmpty()){
var line = lines.removeFirst();
if (line.equals("BEGIN:VEVENT")) continue;
if (line.startsWith("DTSTAMP:")) continue;
if (line.startsWith("UID:")) continue;
if (line.startsWith("X-APPLE-")) continue;
if (line.equals("END:VEVENT")) break;
if (line.startsWith("SUMMARY:")) {
summary = line.substring(8);
continue;
}
if (line.startsWith("DESCRIPTION:")) {
description = line.substring(12);
continue;
}
if (line.startsWith("DTSTART:")) {
dtstart = Util.parseIcalDateTime(line.substring(8));
continue;
}
if (line.startsWith("DTEND:")) {
dtend = Util.parseIcalDateTime(line.substring(6));
continue;
}
if (line.startsWith("LOCATION:")) {
location = line.substring(9);
continue;
}
if (line.startsWith("GEO:")) {
geo = line.substring(4);
continue;
}
LOG.log(WARNING,"Unknown entry in ICAL file: {0}",line);
}
description = description == null ? null : description.replace("\\n","\n").replace("\\, ",", ");
if (!(dtstart instanceof Payload<LocalDateTime> start)) return transform(dtstart);
var appointment = new BaseAppointment(0,summary,description,start.get(),dtend.optional().orElse(null),location);
if (geo != null) appointment.coords(Coords.from(geo));
appointment.tags(tagsFrom(description));
return Payload.of(appointment);
}
public abstract Collection<String> tagsFrom(String description);
public abstract String url();
}
@@ -47,6 +47,7 @@ public class Util {
public static final Pattern GERMAN_DATE_WITHOUT_YEAR = Pattern.compile("(\\d\\d?)\\.(\\d\\d?)");
public static final Pattern GERMAN_DATE_PATTERN_LONG = Pattern.compile("(\\d\\d?)\\.?\\s*(\\w+)\\s+(\\d{4})\\D");
public static final Pattern GERMAN_TIME_PATTERN = Pattern.compile("(\\d\\d?):(\\d\\d?)(:(\\d\\d?))?\\D");
public static final Pattern ICAL_DATE = Pattern.compile("(\\d{4})(\\d{2})(\\d{2})T(\\d{2})(\\d{2})(\\d{2})");
private static final Pattern BG_IMAGE_URL = Pattern.compile("background(-image)?:\\surl\\('?([^)]+)'?\\)");
private static final System.Logger LOG = System.getLogger(Util.class.getSimpleName());
@@ -268,4 +269,18 @@ public class Util {
}
return error("Failed to parse date from %s",string);
}
public static Container<LocalDateTime> parseIcalDateTime(String s) {
var matcher = ICAL_DATE.matcher(s);
if (matcher.find()){
int year = Integer.parseInt(matcher.group(1));
int mon = Integer.parseInt(matcher.group(2));
int day = Integer.parseInt(matcher.group(3));
int hour = Integer.parseInt(matcher.group(4));
int min = Integer.parseInt(matcher.group(5));
int sec = Integer.parseInt(matcher.group(6));
return Payload.of(LocalDateTime.of(year,mon,day,hour,min,sec));
}
return error("{0} does not match format {1}",s, ICAL_DATE);
}
}
@@ -3,14 +3,36 @@ package de.srsoftware.cal.importer.jena;
import de.srsoftware.cal.IcalImporter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public class ReparierCafe extends IcalImporter {
@Override
public String url() {
return "https://www.reparatur-initiativen.de/events/ical/2553.ics";
public Collection<String> tagsFrom(String description) {
var tags = new HashSet<String>();
tags.add("Jena");
tags.add("Repariercafe");
tags.add("Reparier-Café");
var lines = description.split("\n");
for (var line : lines){
if (line.startsWith("Kategorien:")) {
var kategorien = line.substring(11).trim().split(" ");
for (var kat : kategorien) tags.add(kat);
}
}
return tags;
}
@Override
public String description() {
return "Reparier-Café Jena";
}
@Override
public String url() {
return "https://www.reparatur-initiativen.de/events/ical/2553.ics";
}
}