cf32c1c70b4eaf389e4daba7247180934342a458
[rhynodge.git] / src / main / java / net / pterodactylus / rhynodge / filters / webpages / savoy / SavoyTicketsFilter.java
1 package net.pterodactylus.rhynodge.filters.webpages.savoy;
2
3 import static com.google.common.base.Preconditions.checkArgument;
4 import static java.time.format.DateTimeFormatter.ofPattern;
5 import static java.util.Optional.empty;
6 import static java.util.Optional.of;
7 import static net.pterodactylus.rhynodge.filters.webpages.savoy.Movie.byName;
8 import static net.pterodactylus.rhynodge.filters.webpages.savoy.Movie.withPerformances;
9 import static net.pterodactylus.rhynodge.filters.webpages.savoy.Performance.byTime;
10 import static org.jsoup.nodes.Document.createShell;
11 import static org.jsoup.parser.Tag.valueOf;
12
13 import java.time.LocalDate;
14 import java.time.format.DateTimeFormatter;
15 import java.util.Collection;
16 import java.util.List;
17 import java.util.Optional;
18 import java.util.stream.Collectors;
19
20 import net.pterodactylus.rhynodge.Filter;
21 import net.pterodactylus.rhynodge.State;
22 import net.pterodactylus.rhynodge.states.HtmlState;
23 import net.pterodactylus.rhynodge.states.OutputState;
24
25 import org.apache.log4j.Logger;
26 import org.jetbrains.annotations.NotNull;
27 import org.jsoup.nodes.Document;
28 import org.jsoup.nodes.Element;
29 import org.jsoup.nodes.Node;
30
31 /**
32  * {@link Filter} implementation that creates a list of movies running at the
33  * Savoy theatre in Hamburg.
34  *
35  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
36  */
37 public class SavoyTicketsFilter implements Filter {
38
39         private static final MovieExtractor movieExtractor = new MovieExtractor();
40         private DateTimeFormatter dateFormatter = ofPattern("dd.MM.uuuu");
41         private DateTimeFormatter timeFormatter = ofPattern("HH:mm");
42         private DateTimeFormatter dateTimeFormatter = ofPattern("dd.MM.uuuu, HH:mm");
43
44         @NotNull
45         @Override
46         public State filter(@NotNull State state) {
47                 checkArgument(state instanceof HtmlState, "state must be HTML");
48
49                 HtmlState htmlState = (HtmlState) state;
50                 Collection<Movie> movies = movieExtractor.getMovies(htmlState.document());
51                 Logger.getLogger(SavoyTicketsFilter.class).debug("Parsed Movies: " + movies);
52                 return new OutputState(getPlainTextOutput(movies), getHtmlOutput(movies));
53         }
54
55         private Optional<String> getHtmlOutput(Collection<Movie> movies) {
56                 Document document = createShell("");
57                 document.head().appendElement("style").attr("type", "text/css").text(generateStyleSheet());
58                 document.body().appendElement("h1").text("Kinoprogramm: Savoy");
59                 document.body().appendElement("h2").text("Filme");
60                 movies.stream().filter(withPerformances).sorted(byName).forEach(movie -> {
61                         document.body().appendChild(createMovieNode(movie));
62                 });
63                 document.body().appendElement("h2").text("Zeiten");
64                 movies.stream().flatMap(movie -> movie.getPerformances().stream().map(performance -> new Presentation(movie, performance))).sorted((leftPresentation, rightPresentation) -> leftPresentation.getTicketLink().getTime().compareTo(rightPresentation.getTicketLink().getTime())).collect(Collectors.<Presentation, LocalDate>groupingBy(presentation -> presentation.getTicketLink().getTime().toLocalDate())).entrySet().stream().sorted((leftEntry, rightEntry) -> leftEntry.getKey().compareTo(rightEntry.getKey())).forEach(dateEntry -> {
65                         document.body().appendChild(createDayNode(dateEntry.getKey(), dateEntry.getValue()));
66                 });
67                 document.body().appendElement("h2").text("Vorschau");
68                 movies.stream().filter(withPerformances.negate()).sorted(byName).forEach(movie -> {
69                         document.body().appendElement("div").attr("class", "name").text(movie.getName());
70                 });
71                 return of(document.toString());
72         }
73
74         private String generateStyleSheet() {
75                 StringBuilder styleSheet = new StringBuilder();
76                 styleSheet.append(".movie .name { font-weight: bold; }\n");
77                 styleSheet.append(".day:first-child { margin-top: 1em; }\n");
78                 styleSheet.append(".date { display: table-cell; font-weight: bold; }\n");
79                 styleSheet.append(".presentation { display: inline; }\n");
80                 styleSheet.append(".time, .movie { display: inline; }\n");
81                 return styleSheet.toString();
82         }
83
84         private Node createMovieNode(Movie movie) {
85                 Element movieNode = new Element(valueOf("div"), "");
86                 movieNode.attr("class", "movie");
87                 movieNode.appendElement("div").attr("class", "name").text(movie.getName());
88                 movie.getPerformances().stream().sorted(byTime).forEach(performance -> {
89                         Element presentationNode = movieNode.appendElement("div").attr("class", "presentation");
90                         presentationNode.appendElement("div").attr("class", "time").text("» ").appendElement("a").attr("href", performance.getLink()).text(performance.getTime().format(dateTimeFormatter));
91                 });
92                 return movieNode;
93         }
94
95         private Node createDayNode(LocalDate date, List<Presentation> presentations) {
96                 Element dayNode = new Element(valueOf("div"), "").attr("class", "day");
97                 dayNode.appendElement("div").attr("class", "date").text(date.format(dateFormatter));
98                 presentations.stream().forEach(presentation -> {
99                         Element presentationNode = dayNode.appendElement("div").attr("class", "presentation");
100                         presentationNode.appendElement("div").attr("class", "time").text("» " + presentation.getTicketLink().getTime().format(timeFormatter));
101                         presentationNode.appendElement("div").attr("class", "movie").appendElement("a").attr("href", presentation.getTicketLink().getLink()).text(presentation.getMovie().getName());
102                 });
103                 return dayNode;
104         }
105
106         private Optional<String> getPlainTextOutput(Collection<Movie> movies) {
107                 return empty();
108         }
109
110         private static class Presentation {
111
112                 private final Movie movie;
113                 private final Performance performance;
114
115                 private Presentation(Movie movie, Performance performance) {
116                         this.movie = movie;
117                         this.performance = performance;
118                 }
119
120                 private Movie getMovie() {
121                         return movie;
122                 }
123
124                 private Performance getTicketLink() {
125                         return performance;
126                 }
127
128         }
129
130 }