completed parser for reparier-cafe jena
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -10,6 +10,11 @@ import org.json.JSONObject;
|
|||||||
* @param longitude the longitude
|
* @param longitude the longitude
|
||||||
*/
|
*/
|
||||||
public record Coords(double latitude,double 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>
|
* 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
|
* @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.Container.transform;
|
||||||
import static de.srsoftware.tools.container.Error.error;
|
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.Appointment;
|
||||||
|
import de.srsoftware.cal.api.Coords;
|
||||||
import de.srsoftware.cal.api.Importer;
|
import de.srsoftware.cal.api.Importer;
|
||||||
import de.srsoftware.tools.container.Container;
|
import de.srsoftware.tools.container.Container;
|
||||||
import de.srsoftware.tools.container.Payload;
|
import de.srsoftware.tools.container.Payload;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public abstract class IcalImporter implements Importer {
|
public abstract class IcalImporter implements Importer {
|
||||||
|
|
||||||
|
private static final System.Logger LOG = getLogger(IcalImporter.class.getSimpleName());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<Appointment> fetch() throws IOException {
|
public Stream<Appointment> fetch() throws IOException {
|
||||||
var programPage = Payload.of(url()).map(Util::url);
|
var programPage = Payload.of(url()).map(Util::url);
|
||||||
@@ -23,27 +31,90 @@ public abstract class IcalImporter implements Importer {
|
|||||||
return programPage
|
return programPage
|
||||||
.map(Util::open)
|
.map(Util::open)
|
||||||
.map(Util::preload)
|
.map(Util::preload)
|
||||||
.map(IcalImporter::parse)
|
.map(this::parse)
|
||||||
.stream();
|
.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);
|
if (input.optional().isEmpty()) return transform(input);
|
||||||
InputStream stream = input.optional().get();
|
InputStream stream = input.optional().get();
|
||||||
var scanner = new Scanner(stream);
|
var scanner = new Scanner(stream);
|
||||||
|
var lines = new ArrayList<String>();
|
||||||
while (scanner.hasNextLine()){
|
while (scanner.hasNextLine()){
|
||||||
var line = scanner.nextLine();
|
var line = scanner.nextLine();
|
||||||
System.out.println(line);
|
if (line.startsWith(" ")) line = lines.removeLast() + line.substring(1);
|
||||||
|
lines.add(line);
|
||||||
}
|
}
|
||||||
scanner.close();
|
scanner.close();
|
||||||
try {
|
try {
|
||||||
stream.close();
|
stream.close();
|
||||||
} catch (IOException e) {
|
} 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();
|
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_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_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 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 Pattern BG_IMAGE_URL = Pattern.compile("background(-image)?:\\surl\\('?([^)]+)'?\\)");
|
||||||
private static final System.Logger LOG = System.getLogger(Util.class.getSimpleName());
|
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);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-2
@@ -3,14 +3,36 @@ package de.srsoftware.cal.importer.jena;
|
|||||||
|
|
||||||
import de.srsoftware.cal.IcalImporter;
|
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 {
|
public class ReparierCafe extends IcalImporter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String url() {
|
public Collection<String> tagsFrom(String description) {
|
||||||
return "https://www.reparatur-initiativen.de/events/ical/2553.ics";
|
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
|
@Override
|
||||||
public String description() {
|
public String description() {
|
||||||
return "Reparier-Café Jena";
|
return "Reparier-Café Jena";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String url() {
|
||||||
|
return "https://www.reparatur-initiativen.de/events/ical/2553.ics";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user