From ed86612c52b3aea74197c6c09c0195a9cacd2fef Mon Sep 17 00:00:00 2001 From: =?utf8?q?David=20=E2=80=98Bombe=E2=80=99=20Roden?= Date: Fri, 24 Apr 2026 12:16:39 +0200 Subject: [PATCH] =?utf8?q?=E2=9C=A8=20Parse=20vacancies=20from=20multiple?= =?utf8?q?=20HTML=20states?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- .../webpages/dasenbrook/DasenbrookFilter.kt | 22 +- .../webpages/dasenbrook/DasenbrookFilterTest.kt | 24 + .../filters/webpages/dasenbrook/dasenbrook2.html | 1116 ++++++++++++++++++++ 3 files changed, 1153 insertions(+), 9 deletions(-) create mode 100644 src/test/resources/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/dasenbrook2.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 index d245963..52c04ed 100644 --- a/src/main/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilter.kt +++ b/src/main/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilter.kt @@ -1,6 +1,7 @@ package net.pterodactylus.rhynodge.filters.webpages.dasenbrook import java.time.LocalDate +import kotlin.reflect.cast import net.pterodactylus.rhynodge.Filter import net.pterodactylus.rhynodge.State import net.pterodactylus.rhynodge.states.HtmlState @@ -8,15 +9,18 @@ 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 - } + val allHtmlStates = listOf(state as HtmlState) + state.additionalStates.filter(HtmlState::class::isInstance).map(HtmlState::class::cast) + val vacancies = allHtmlStates.map(HtmlState::document).flatMap { + it.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) 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 index bd1914f..a94a467 100644 --- a/src/test/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilterTest.kt +++ b/src/test/kotlin/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/DasenbrookFilterTest.kt @@ -27,9 +27,33 @@ class DasenbrookFilterTest { )) } + @Test + fun `dasenbrook filter can parse multiple states`() { + htmlState.addState(secondHtmlState) + 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, 5, 14)), + Vacancy(LocalDate.of(2027, 6, 25), LocalDate.of(2027, 7, 9)), + Vacancy(LocalDate.of(2027, 9, 10), LocalDate.of(2027, 10, 25)), + Vacancy(LocalDate.of(2027, 11, 6), LocalDate.of(2028, 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) } + private val secondHtmlState = ResourceLoader + .loadDocument(javaClass, "dasenbrook2.html", "https://www.ostsee-reisen.de/api/availability-calendar/v2/apartment/11343.html?p=2") + .let { HtmlState("https://www.ostsee-reisen.de/api/availability-calendar/v2/apartment/11343.html?p=2", it) } } diff --git a/src/test/resources/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/dasenbrook2.html b/src/test/resources/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/dasenbrook2.html new file mode 100644 index 0000000..997d2ff --- /dev/null +++ b/src/test/resources/net/pterodactylus/rhynodge/filters/webpages/dasenbrook/dasenbrook2.html @@ -0,0 +1,1116 @@ + + + + +Ostsee-Reisen.de - Belegungsplan + + + + + +
+
+
+
+
Belegt
+
 
+
+
+
+
An-/Abreise
+
 
+
+
Reservierung
+
+
Copyright © 2026 Ostsee-Reisen.de
+
+
+Apr 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

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Mai 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

+

+

+

+

+

+

+
+
+
+
+
+Jun 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

+

+

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Jul 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

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Aug 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

+

+

+

+

+

+
+
+
+
+
+Sep 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

+

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Okt 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

+
+

+

+

+

+

+

+

+
+
+
+
+
+Nov 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

+

+

+

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Dez 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

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Jan 2028 +
+
+

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 2028 +
+
+

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

+

+

+

+

+

+
+

+

+

+

+

+

+

+
+
+
+
+
+Mrz 2028 +
+
+

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