From f3752a1939e112fa2c45f55f2d217f75dc2c327d Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Mon, 31 Aug 2026 17:46:00 +0200 Subject: [PATCH] completed parser for reparier-cafe jena Signed-off-by: Stephan Richter --- .../java/de/srsoftware/cal/api/Coords.java | 5 ++ .../java/de/srsoftware/cal/IcalImporter.java | 81 +++++++++++++++++-- .../src/main/java/de/srsoftware/cal/Util.java | 15 ++++ .../cal/importer/jena/ReparierCafe.java | 26 +++++- 4 files changed, 120 insertions(+), 7 deletions(-) diff --git a/de.srsoftware.cal.api/src/main/java/de/srsoftware/cal/api/Coords.java b/de.srsoftware.cal.api/src/main/java/de/srsoftware/cal/api/Coords.java index 7cf24b7..7b0883a 100644 --- a/de.srsoftware.cal.api/src/main/java/de/srsoftware/cal/api/Coords.java +++ b/de.srsoftware.cal.api/src/main/java/de/srsoftware/cal/api/Coords.java @@ -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 iCalendar spec * @return the formatted coords diff --git a/de.srsoftware.cal.base/src/main/java/de/srsoftware/cal/IcalImporter.java b/de.srsoftware.cal.base/src/main/java/de/srsoftware/cal/IcalImporter.java index b817e74..669db1a 100644 --- a/de.srsoftware.cal.base/src/main/java/de/srsoftware/cal/IcalImporter.java +++ b/de.srsoftware.cal.base/src/main/java/de/srsoftware/cal/IcalImporter.java @@ -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 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> parse(Container input) { + private Container> parse(Container input) { if (input.optional().isEmpty()) return transform(input); InputStream stream = input.optional().get(); var scanner = new Scanner(stream); + var lines = new ArrayList(); 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> parseLines(ArrayList lines) { + var events = new ArrayList(); + 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 parseEvent(ArrayList lines) { + String summary = null; + String description = null; + Container 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 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 tagsFrom(String description); public abstract String url(); } diff --git a/de.srsoftware.cal.base/src/main/java/de/srsoftware/cal/Util.java b/de.srsoftware.cal.base/src/main/java/de/srsoftware/cal/Util.java index 133e241..dd0bdee 100644 --- a/de.srsoftware.cal.base/src/main/java/de/srsoftware/cal/Util.java +++ b/de.srsoftware.cal.base/src/main/java/de/srsoftware/cal/Util.java @@ -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 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); + } } diff --git a/de.srsoftware.cal.importer/src/main/java/de/srsoftware/cal/importer/jena/ReparierCafe.java b/de.srsoftware.cal.importer/src/main/java/de/srsoftware/cal/importer/jena/ReparierCafe.java index a411fa2..b90422b 100644 --- a/de.srsoftware.cal.importer/src/main/java/de/srsoftware/cal/importer/jena/ReparierCafe.java +++ b/de.srsoftware.cal.importer/src/main/java/de/srsoftware/cal/importer/jena/ReparierCafe.java @@ -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 tagsFrom(String description) { + var tags = new HashSet(); + 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"; + } }