From 95e8e5b3b597495a32557ee00865df6ae2eebdcb Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Mon, 20 Apr 2026 16:15:40 +0200 Subject: [PATCH] =?utf8?q?=F0=9F=9A=A7=20Add=20filter=20for=20Dasenbrook?= =?utf8?q?=E2=80=99s=20vacation=20home=20vacancy=20site?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../webpages/dasenbrook/DasenbrookFilter.kt | 36 + .../webpages/dasenbrook/DasenbrookFilterTest.kt | 35 + .../filters/webpages/dasenbrook/dasenbrook.html | 1116 ++++++++++++++++++++ 3 files changed, 1187 insertions(+) create mode 100644 src/main/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilter.kt create mode 100644 src/test/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilterTest.kt create mode 100644 src/test/resources/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/dasenbrook.html diff --git a/src/main/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilter.kt b/src/main/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilter.kt new file mode 100644 index 0000000..d245963 --- /dev/null +++ b/src/main/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilter.kt @@ -0,0 +1,36 @@ +package net.pterodactylus.rhynodge.filters.webpages.dasenbrook + +import java.time.LocalDate +import net.pterodactylus.rhynodge.Filter +import net.pterodactylus.rhynodge.State +import net.pterodactylus.rhynodge.states.HtmlState + +class DasenbrookFilter(private val home: String) : Filter { + + override fun filter(state: State): State { + val vacancies = (state as HtmlState).document().select("div.mb").flatMap { month -> + val firstOfMonth = month.select("div.mh").text().toLocalDate() + month.select(".dbs .db p, .dbs .db .da-d") + .filter { dayCell -> dayCell.parents().none { it.classNames() == setOf("db", "e") } } + .map { dayCell -> + val day = firstOfMonth.withDayOfMonth(dayCell.text().toInt()) + val free = dayCell.parents().any { it.classNames() == setOf("db", "a") } + free to day + } + }.fold(false to listOf()) { (yesterdayWasFree, vacancies), (todayIsFree, date) -> + todayIsFree to when { + yesterdayWasFree -> vacancies.dropLast(1) + Vacancy(vacancies.last().begin, date) + todayIsFree -> vacancies + Vacancy(date, date) + else -> vacancies + } + }.second + return VacationHomeState(home, vacancies) + } + +} + +private fun String.toLocalDate() = split(" ").let { (month, year) -> + LocalDate.of(year.toInt(), monthNames.indexOf(month), 1) +} + +private val monthNames = listOf("-", "Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez") diff --git a/src/test/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilterTest.kt b/src/test/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilterTest.kt new file mode 100644 index 0000000..bd1914f --- /dev/null +++ b/src/test/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilterTest.kt @@ -0,0 +1,35 @@ +package net.pterodactylus.rhynodge.filters.webpages.dasenbrook + +import java.time.LocalDate +import net.pterodactylus.rhynodge.filters.ResourceLoader +import net.pterodactylus.rhynodge.states.HtmlState +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers +import org.hamcrest.Matchers.containsInAnyOrder +import org.junit.jupiter.api.Test + +class DasenbrookFilterTest { + + @Test + fun `dasenbrook filter can parse state`() { + val dasenbrookState = dasenbrookFilter.filter(htmlState) as VacationHomeState + assertThat(dasenbrookState.vacancies, containsInAnyOrder( + Vacancy(LocalDate.of(2026, 4, 25), LocalDate.of(2026, 4, 26)), + Vacancy(LocalDate.of(2026, 5, 17), LocalDate.of(2026, 5, 21)), + Vacancy(LocalDate.of(2026, 6, 27), LocalDate.of(2026, 6, 30)), + Vacancy(LocalDate.of(2026, 8, 16), LocalDate.of(2026, 8, 20)), + Vacancy(LocalDate.of(2026, 11, 7), LocalDate.of(2026, 12, 27)), + Vacancy(LocalDate.of(2027, 1, 2), LocalDate.of(2027, 1, 11)), + Vacancy(LocalDate.of(2027, 1, 19), LocalDate.of(2027, 2, 7)), + Vacancy(LocalDate.of(2027, 2, 14), LocalDate.of(2027, 3, 14)), + Vacancy(LocalDate.of(2027, 3, 19), LocalDate.of(2027, 3, 20)), + Vacancy(LocalDate.of(2027, 3, 27), LocalDate.of(2027, 3, 31)), + )) + } + + private val dasenbrookFilter = DasenbrookFilter("Test Home") + private val htmlState = ResourceLoader + .loadDocument(javaClass, "dasenbrook.html", "https://www.ostsee-reisen.de/api/availability-calendar/v2/apartment/11343.html") + .let { HtmlState("https://www.ostsee-reisen.de/api/availability-calendar/v2/apartment/11343.html", it) } + +} diff --git a/src/test/resources/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/dasenbrook.html b/src/test/resources/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/dasenbrook.html new file mode 100644 index 0000000..fa6218a --- /dev/null +++ b/src/test/resources/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/dasenbrook.html @@ -0,0 +1,1116 @@ + + + + +Ostsee-Reisen.de - Belegungsplan + + + + + +
+
+
+
+
Belegt
+
 
+
+
+
+
An-/Abreise
+
 
+
+
Reservierung
+
+
Copyright © 2026 Ostsee-Reisen.de
+
+
+Apr 2026 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

+

+

1

+

2

+

3

+

4

+

5

+
+

6

+

7

+

8

+

9

+

10

+

11

+

12

+
+

13

+

14

+

15

+

16

+

17

+

18

+

19

+
+

20

+

21

+

22

+

23

+

24

+
25
+
26
+
+

27

+

28

+

29

+

30

+

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Mai 2026 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

+

+

+

+

1

+

2

+

3

+
+

4

+

5

+

6

+

7

+

8

+

9

+

10

+
+

11

+

12

+

13

+

14

+

15

+

16

+
17
+
+

18

+

19

+

20

+
21
+

22

+

23

+

24

+
+

25

+

26

+

27

+

28

+

29

+

30

+

31

+
+

+

+

+

+

+

+

+
+
+
+
+
+Jun 2026 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+
+

8

+

9

+

10

+

11

+

12

+

13

+

14

+
+

15

+

16

+

17

+

18

+

19

+

20

+

21

+
+

22

+

23

+

24

+

25

+

26

+
27
+

28

+
+

29

+
30
+

+

+

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Jul 2026 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

+

+

1

+

2

+

3

+

4

+

5

+
+

6

+

7

+

8

+

9

+

10

+

11

+

12

+
+

13

+

14

+

15

+

16

+

17

+

18

+

19

+
+

20

+

21

+

22

+

23

+

24

+

25

+

26

+
+

27

+

28

+

29

+

30

+

31

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Aug 2026 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

+

+

+

+

+

1

+

2

+
+

3

+

4

+

5

+

6

+

7

+

8

+

9

+
+

10

+

11

+

12

+

13

+

14

+

15

+
16
+
+

17

+

18

+

19

+
20
+

21

+

22

+

23

+
+

24

+

25

+

26

+

27

+

28

+

29

+

30

+
+

31

+

+

+

+

+

+

+
+
+
+
+
+Sep 2026 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

+

1

+

2

+

3

+

4

+

5

+

6

+
+

7

+

8

+

9

+

10

+

11

+

12

+

13

+
+

14

+

15

+

16

+

17

+

18

+

19

+

20

+
+

21

+

22

+

23

+

24

+

25

+

26

+

27

+
+

28

+

29

+

30

+

+

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Okt 2026 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

+

+

+

1

+

2

+

3

+

4

+
+

5

+

6

+

7

+

8

+

9

+

10

+

11

+
+

12

+

13

+

14

+

15

+

16

+

17

+

18

+
+

19

+

20

+

21

+

22

+

23

+

24

+

25

+
+

26

+

27

+

28

+

29

+

30

+

31

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Nov 2026 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

+

+

+

+

+

+

1

+
+

2

+

3

+

4

+

5

+

6

+
7
+

8

+
+

9

+

10

+

11

+

12

+

13

+

14

+

15

+
+

16

+

17

+

18

+

19

+

20

+

21

+

22

+
+

23

+

24

+

25

+

26

+

27

+

28

+

29

+
+

30

+

+

+

+

+

+

+
+
+
+
+
+Dez 2026 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

+

1

+

2

+

3

+

4

+

5

+

6

+
+

7

+

8

+

9

+

10

+

11

+

12

+

13

+
+

14

+

15

+

16

+

17

+

18

+

19

+

20

+
+

21

+

22

+

23

+

24

+

25

+

26

+
27
+
+

28

+

29

+

30

+

31

+

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Jan 2027 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

+

+

+

+

1

+
2
+

3

+
+

4

+

5

+

6

+

7

+

8

+

9

+

10

+
+
11
+

12

+

13

+

14

+

15

+

16

+

17

+
+

18

+
19
+

20

+

21

+

22

+

23

+

24

+
+

25

+

26

+

27

+

28

+

29

+

30

+

31

+
+

+

+

+

+

+

+

+
+
+
+
+
+Feb 2027 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

1

+

2

+

3

+

4

+

5

+

6

+
7
+
+

8

+

9

+

10

+

11

+

12

+

13

+
14
+
+

15

+

16

+

17

+

18

+

19

+

20

+

21

+
+

22

+

23

+

24

+

25

+

26

+

27

+

28

+
+

+

+

+

+

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Mrz 2027 +
+
+

Mo

+

Di

+

Mi

+

Do

+

Fr

+

Sa

+

So

+
+
+
+

1

+

2

+

3

+

4

+

5

+

6

+

7

+
+

8

+

9

+

10

+

11

+

12

+

13

+
14
+
+

15

+

16

+

17

+

18

+
19
+
20
+

21

+
+

22

+

23

+

24

+

25

+

26

+
27
+

28

+
+

29

+

30

+

31

+

+

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+ + + + +
+ + -- 2.7.4