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