Skip to content

Commit

Permalink
Merge pull request #338 from flairNLP/add_additional_attributes_to_un…
Browse files Browse the repository at this point in the history
…it_tests

Add `body` to unit tests
  • Loading branch information
MaxDall authored Apr 20, 2024
2 parents ff54845 + 607503d commit b10f975
Show file tree
Hide file tree
Showing 48 changed files with 2,059 additions and 26 deletions.
8 changes: 6 additions & 2 deletions scripts/generate_parser_test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def main() -> None:
arguments = parse_arguments()

# sort args.attributes for consistency
arguments.attributes = list(sorted(arguments.attributes)) or attributes_required_to_cover
arguments.attributes = sorted(set(arguments.attributes) or attributes_required_to_cover)

basic_logger.setLevel(WARN)

Expand Down Expand Up @@ -124,11 +124,15 @@ def main() -> None:
for html in html_mapping.values():
versioned_parser = html.publisher.parser(html.crawl_date)
extraction = versioned_parser.parse(html.content)
new = {attr: value for attr, value in extraction.items() if attr in arguments.attributes}
missing_attributes = set(arguments.attributes) - set(
test_data.get(type(versioned_parser).__name__) or {}
)
new = {attr: value for attr, value in extraction.items() if attr in missing_attributes}
if not (entry := test_data.get(type(versioned_parser).__name__)):
test_data[type(versioned_parser).__name__] = new
else:
entry.update(new)
test_data[type(versioned_parser).__name__] = dict(sorted(entry.items()))

test_data_file.write(test_data)
bar.update()
Expand Down
45 changes: 42 additions & 3 deletions src/fundus/parser/data.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from abc import ABC
from abc import ABC, abstractmethod
from dataclasses import dataclass, fields
from typing import (
Any,
Expand All @@ -14,7 +14,7 @@
overload,
)

from typing_extensions import TypeAlias
from typing_extensions import Self, TypeAlias

LDMappingValue: TypeAlias = Union[List[Dict[str, Any]], Dict[str, Any]]

Expand Down Expand Up @@ -179,9 +179,16 @@ def __repr__(self) -> str:
def __str__(self) -> str:
return "\n".join(self)

def __eq__(self, other: object) -> bool:
if not isinstance(other, TextSequence):
return NotImplemented
return self._data == other._data


@dataclass
class TextSequenceTree(ABC):
"""Base class to traverse and build trees of TextSequence."""

def as_text_sequence(self) -> TextSequence:
texts = [text for tl in self.df_traversal() for text in tl]
return TextSequence(texts)
Expand All @@ -202,7 +209,16 @@ def recursion(o: object):
for value in self:
yield from recursion(value)

def __iter__(self):
@abstractmethod
def serialize(self) -> Dict[str, Any]:
pass

@classmethod
@abstractmethod
def deserialize(cls, serialized: Dict[str, Any]) -> Self:
pass

def __iter__(self) -> Iterator[Any]:
field_values = [getattr(self, f.name) for f in fields(self)]
yield from field_values

Expand All @@ -218,8 +234,31 @@ class ArticleSection(TextSequenceTree):
headline: TextSequence
paragraphs: TextSequence

def serialize(self) -> Dict[str, Any]:
return {
"headline": list(self.headline),
"paragraphs": list(self.paragraphs),
}

@classmethod
def deserialize(cls, serialized: Dict[str, Any]) -> Self:
return cls(headline=TextSequence(serialized["headline"]), paragraphs=TextSequence(serialized["paragraphs"]))


@dataclass
class ArticleBody(TextSequenceTree):
summary: TextSequence
sections: List[ArticleSection]

def serialize(self) -> Dict[str, Any]:
return {
"summary": list(self.summary),
"sections": [section.serialize() for section in self.sections],
}

@classmethod
def deserialize(cls, serialized: Dict[str, Any]) -> Self:
return cls(
summary=TextSequence(serialized["summary"]),
sections=[ArticleSection.deserialize(section) for section in serialized["sections"]],
)
4 changes: 2 additions & 2 deletions src/fundus/publishers/de/waz.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class V1(BaseParser):
VALID_UNTIL = datetime.date(2024, 2, 21)
_paragraph_selector: XPath = CSSSelector(".article__body > p")
_summary_selector: XPath = CSSSelector(".article__header__intro__text")
_subheadline_selector = CSSSelector(".article__body > h3")
_subheadline_selector: XPath = CSSSelector(".article__body > h3")

@attribute
def body(self) -> ArticleBody:
Expand Down Expand Up @@ -55,4 +55,4 @@ class V1_1(V1):
namespaces={"re": "http://exslt.org/regular-expressions"},
)
_summary_selector = XPath("//div[@class='article-body'] /p[position()=1]")
_subheadline_selector = CSSSelector(".article-body > h3")
_subheadline_selector = XPath("//div[@class='article-body'] / h3[not(text()='Auch interessant')]")
4 changes: 3 additions & 1 deletion src/fundus/publishers/us/business_insider.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
class BusinessInsiderParser(ParserProxy):
class V1(BaseParser):
_summary_selector = CSSSelector("article ul[class^='summary-list'] > li")
_subheadline_selector = CSSSelector("article h2")
_subheadline_selector = CSSSelector("article h2, div.slideshow-slide-container h2")
_paragraph_selector = XPath(
"""
//article
Expand All @@ -25,6 +25,8 @@ class V1(BaseParser):
//article
//div[contains(@class, 'content-lock-content')]
/div[contains(@class, 'premium-content')]
/p[not(contains(@class, 'disclaimer'))] |
//div[@class='slide-layout clearfix']
/p[not(contains(@class, 'disclaimer'))]
"""
)
Expand Down
12 changes: 12 additions & 0 deletions tests/resources/parser/test_data/at/ORF.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
"authors": [
"ORF.at"
],
"body": {
"summary": [],
"sections": [
{
"headline": [],
"paragraphs": [
"Ein zwölfjähriges Mädchen ist heute bei einem Straßenbahnunfall in Wien-Favoriten schwer verletzt worden. Das Mädchen versuchte offenbar die Straße zu überqueren und wurde dabei von der Straßenbahn erfasst.",
"Mehr dazu in wien.ORF.at"
]
}
]
},
"publishing_date": "2023-04-28 18:03:46.463000+00:00",
"title": "Mädchen bei Straßenbahnunfall in Wien verletzt"
}
Expand Down
21 changes: 21 additions & 0 deletions tests/resources/parser/test_data/ch/SRF.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,27 @@
"srf/hosb",
"kesm"
],
"body": {
"summary": [],
"sections": [
{
"headline": [],
"paragraphs": [
"Iran hat seine Drohung wahr gemacht und Israel direkt angegriffen. Mehr als 300 Drohnen und Raketen sind gemäss israelischem Militär auf Israel abgefeuert worden; der grösste Teil davon konnte abgefangen werden, auch dank der Hilfe anderer Staaten wie Frankreich, Grossbritannien, Jordanien und den USA. Wie zentral der Konflikt für die USA und die Biden-Regierung ist, weiss USA-Kenner Thomas Jäger.",
"SRF News: Mehrere Staaten haben Israel unterstützt und Drohnen und Raketen abgefangen. Wie wichtig war dabei die Unterstützung der USA?",
"Thomas Jäger: Diese war ausschlaggebend. Man wusste, dass ein solcher Angriff bevorsteht. Die amerikanischen Dienste hatten festgestellt, dass der Iran ballistische Raketen in Stellung gebracht hatte.",
"In den USA interessiert sich nur eine kleine Zahl von Wählerinnen und Wählern für die Kriege im Nahen Osten und der Ukraine. Aber diese Kreise haben insbesondere bei den Demokraten ein erhebliches Gewicht.",
"Israel hat noch nicht auf den iranischen Angriff reagiert. Wie viel hat diese Zurückhaltung mit den USA und ihrem Einfluss auf Israel zu tun?",
"Sehr viel. In der israelischen Regierung ist es vor allem die religiöse Rechte, die Druck macht, dass man hart zurückschlagen müsste. Die rechten israelischen Parteien könnten als einzige Netanjahu zu Fall bringen, wenn sie die Koalition verlassen. Die Regierung aber ist in einer komfortablen Lage: Sie hat den Angriff aus Iran abgewehrt und möchte auf der anderen Seite den Krieg in Gaza weiterführen. Auch Joe Biden hat Interesse daran, dass ein wenig Ruhe einkehrt und der Krieg nicht weiter eskaliert, damit er seine Unterstützung für Israel in den USA besser vertreten kann. Wenn der Krieg nicht nur in Gaza, sondern mit dem Iran geführt wird, dann braucht Israel die Unterstützung der USA.",
"Für die USA geht es einerseits darum, eine grosse Eskalation zwischen Iran und Israel zu verhindern, und andererseits darum, den Gazakrieg nicht weiter ausarten zu lassen. Wie kann die Biden-Regierung damit umgehen?",
"Sie wird auf der einen Seite weiter dafür sorgen, dass die humanitäre Unterstützung in Gaza intensiviert wird. Auf der anderen Seite wird man die Streitkräfte in der Region behalten, um den Iran abzuschrecken. Denn auch wenn der Iran nun sagt, man habe Vergeltung geübt, so hat letztlich die Abschreckung der Vereinigten Staaten seit Oktober 2023 mit dazu geführt, den Krieg dort einzuhegen, weil jeder weiss, dass die Vereinigten Staaten, wenn es dazu kommt, Israel unterstützen würden. Ebenfalls muss Biden berücksichtigen, dass der Ölpreis einigermassen im Rahmen bleibt. Denn der Ölpreis und letztlich der Benzinpreis sind wichtige Faktoren, wenn in den Vereinigten Staaten im Spätherbst gewählt wird.",
"Welchen Einfluss hat die aktuelle Situation auf den Wahlkampf von Joe Biden?",
"In den USA interessiert sich nur eine kleine Zahl von Wählerinnen und Wählern für die Kriege im Nahen Osten und der Ukraine. Aber diese Kreise haben insbesondere bei den Demokraten ein erhebliches Gewicht. In diesem Zusammenhang ist interessant, was Donald Trump gemacht hat: Man könnte meinen, die entsprechenden Wählerinnen und Wähler entscheiden sich sowieso für Biden, weil Trump eng vertraut ist mit Netanjahu. Trump aber hat Netanjahu unter Druck gesetzt und gesagt, dass der israelische Ministerpräsident die Situation im Gazastreifen beenden müsse, um diesen linken Wählern den Impuls zu geben: Meinetwegen braucht ihr Biden nicht zu wählen, bleibt zu Hause. Das wäre für Biden ein ernstes Problem.",
"Das Gespräch führte Peter Hanselmann."
]
}
]
},
"publishing_date": "2024-04-15 13:38:00+02:00",
"title": "«Die israelische Zurückhaltung hat viel mit den USA zu tun»"
}
Expand Down
22 changes: 22 additions & 0 deletions tests/resources/parser/test_data/de/BSZ.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@
"authors": [
"Stefan Lienert"
],
"body": {
"summary": [
"Soltau. Erstes Geheimnis um „Katakomben-Tour“ gelüftet. Dann können Besucher die Attraktion erstmals ausprobieren. Und noch eine heißersehnte Info ist fix."
],
"sections": [
{
"headline": [],
"paragraphs": [
"Es ist aktuell noch eines der größten Geheimnisse im deutschen Freizeitpark-Sektor: Was passiert da momentan im Heide-Park in Soltau? Unweit des Fahrgeschäfts „Flug der Dämonen“ entsteht eine neue Attraktion. Doch noch hüllt sich der Park mit Details rund um den Neuzugang in Schweigen. Allerdings: Mittlerweile steht fest, ab wann die Freizeitpark-Besucher die neue Attraktion erleben können, die den Arbeitstitel „Katakomben-Tour“ bekommen hat. An Karfreitag, 29. März, also in exakt zwei Monaten, soll sie eröffnen, heißt es aus Soltau. Auf Nachfrage heißt es aus dem Park: „Die Holzachterbahn ‚Colossos‘ haben wir 2019 auch an Karfreitag eröffnet, den ‚Flug der Dämonen‘ ebenfalls am 29. März vor zehn Jahren.“ Also ein perfektes Datum für Zuwachs im Park.",
"Mit Videos und absolut kryptischen Texten halten die Freizeitpark-Macher auf Social Media die Fans seit Wochen bei Laune. „Die Indoor-Attraktion verspreche, das Leben der mutigen Gäste auf gruseligste Art und Weise zu verändern“, hieß es etwa bei der Ankündigung der neuen Attraktion im Oktober, die Teil des Themenbereichs „Transsilvanien“ werden soll. Ob es ein Darkride wird, wie ihn sich viele Heide-Park-Fans wünschen, ist noch immer unklar. Jedoch: Der Park möchte im Laufe des Februars bekanntgeben, um was es sich bei der Attraktion konkret handelt. Ein klein wenig gedulden müssen sich die Anhänger des größten Freizeitparks in Norddeutschland also noch."
]
},
{
"headline": [
"Saisonstart des Heide-Parks ist am 23. März"
],
"paragraphs": [
"Die Saison des Heide-Parks ist vom 23. März bis zum 2. November geplant. Tagestickets können sich Besucher aber schon jetzt online ab 37 Euro sichern, Jahreskarten gibt es ab 79 Euro. Neben der neuen Attraktion gibt es eine weitere Neuheit für den Park in diesem Jahr: Am 31. August und 1. September steigt das Heide-Park-Festival auf dem Gelände. The Chainsmokers, Don Diablo, Hardwell, Wincent Weiss und dutzende weitere Acts stehen bereits fest. Karten gibt es ab 99,99 Euro."
]
}
]
},
"publishing_date": "2024-01-29 18:09:51+00:00",
"title": "Heide-Park: Eröffnungstermin für neue Attraktion steht fest",
"topics": [
Expand Down
24 changes: 24 additions & 0 deletions tests/resources/parser/test_data/de/BerlinerZeitung.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,30 @@
"authors": [
"AFP"
],
"body": {
"summary": [
"Ein Mann bot sein Sperma mindestens zwölf Kliniken, über Internetplattformen und privat an. Er gab vor, bisher nur wenige Kinder gezeugt zu haben, was nicht stimmte."
],
"sections": [
{
"headline": [],
"paragraphs": [
"Nach der Zeugung von mindestens 550 Kindern hat ein niederländisches Gericht einen übereifrigen Samenspender gestoppt. Das Gericht untersagte dem Mann am Freitag jede weitere Samenspende. Bei einem Verstoß muss er 100.000 Euro Strafe zahlen. Eine Frau, die durch eine Samenspende des Mannes ein Kind bekam, und die Stiftung Donorkind hatten den 41-Jährigen verklagt. Weil er in Online-Netzwerken weiter seine Dienste anbot, wurde er nun im Eilverfahren verurteilt.",
"Der Mann hatte sein Sperma an mindestens zwölf Kliniken und über Internet-Plattformen auch privat an hunderte Paare gespendet. Die Klägerin, die ihn nach Angaben von Donorkind im Internet kennengelernt hatte, wirft ihm vor, sie über die Zahl der von ihm gezeugten Kinder getäuscht zu haben. Er gab demnach an, Vater von maximal 25 Kindern in zwölf Familien zu sein. Mehr ist in den Niederlanden zur Vermeidung von Inzest durch zu viele Halbgeschwister nicht erlaubt."
]
},
{
"headline": [
"Samenbetrug: Kinder wachsen jetzt mit Hunderten Halbgeschwistern auf"
],
"paragraphs": [
"Dem Urteil zufolge ist der Mann Vater von allein hundert Kindern, die in niederländischen Kliniken gezeugt wurden. Hinzu kommt demnach eine nicht genau bezifferbare Zahl von Kindern, die über privat organisierte Samenspenden und eine dänische Klinik gezeugt wurden, die sein Sperma an Paare in verschiedenen Ländern weitergeleitet hatte. Insgesamt hat der Mann nach Auffassung der Gerichts seit 2007 mindestens 550 bis 600 Kinder gezeugt.",
"Der Mann habe die Paare „bewusst“ über die Zahl der von ihm gezeugten Kinder getäuscht, urteilte das Gericht in Den Haag. „All diese Eltern sind nun mit der Tatsache konfrontiert, dass ihre Kinder Teil eines riesigen Verwandtschaftsnetzwerkes mit Hunderten von Halbgeschwistern sind“, erklärte das Gericht. Negative Folgen für die Kinder, etwa psychische Probleme aufgrund von Identitätsfragen und Angst vor Inzest, hält das Gericht für „hinreichend plausibel“.",
"Der Fall ist nicht der erste Samenspende-Skandal in den Niederlanden. Im Jahr 2020 kam heraus, dass ein inzwischen gestorbener Gynäkologe bei der künstlichen Befruchtung von Patientinnen mindestens 17 Kinder gezeugt hatte. Er hatte sein eigenes Sperma benutzt, die Frauen aber in dem Glauben gelassen, es stamme von anonymen Spendern."
]
}
]
},
"publishing_date": "2023-04-28 17:52:31.847000+00:00",
"title": "550 Kinder gezeugt: Gericht stoppt übereifrigen Samenspender",
"topics": [
Expand Down
16 changes: 16 additions & 0 deletions tests/resources/parser/test_data/de/Bild.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@
"authors": [
"JÖRG ZSCHOCHE"
],
"body": {
"summary": [
"Es war bereits der zwölfte Einsatz in der 2. Bundesliga von Fortuna Düsseldorfs Bubi Jona Niemiec (21). Kaum nach 67 Minuten eingewechselt, scheiterte der Stürmer nur an der Latte. Doch war er beim 0:0 auf Pauli eine Bereicherung."
],
"sections": [
{
"headline": [],
"paragraphs": [
"Endlich wieder – denn nach starken ersten Spielen mit seinem Tordebüt beim 3:1 über Braunschweig lief es nicht mehr ganz so rund.",
"Daniel Thioune (48) wundert es nicht: „Jeder Spieler hat seine Qualitäten, die letzten Partien haben ihm nicht so den Raum gegeben. Jetzt durch das hohe Pressing haben sich riesen Räume ergeben und er konnte sich zeigen.”",
"Der Trainer freut sich: „Ich bin froh, dass es wieder etwas mehr war. Wie müssen einfach deutlich geduldiger mit dem Jungen sein. Er muss sich anpassen, man sieht oft, dass er am durchschnaufen ist.”",
"Obwohl Niemiec mit seiner Schnelligkeit Fortuna guttun kann, muss er vor allem konditionell noch zulegen, um beständig so aufzutrumpfen wie die letzten 23 Minuten am Millerntor."
]
}
]
},
"publishing_date": "2023-05-15 11:52:09.860000+00:00",
"title": "Bubi-Bomber wieder da: Thioune fordert Geduld mit Niemiec",
"topics": [
Expand Down
31 changes: 31 additions & 0 deletions tests/resources/parser/test_data/de/BusinessInsiderDE.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,37 @@
"authors": [
"Matthew Loh"
],
"body": {
"summary": [
"Evergrande, einer der größten Immobilienentwickler Chinas, ist mit 300 Milliarden Dollar (277 Milliarden Euro) verschuldet.",
"Das ist die größte Schuldenlast, die ein Unternehmen derzeit weltweit hat.",
"Analysten befürchten, dass der Zusammenbruch von Evergrande Schockwellen durch die chinesische Wirtschaft schicken würde."
],
"sections": [
{
"headline": [],
"paragraphs": [
"Evergrande, einst der zweitgrößte Immobilienentwickler Chinas nach Umsatz, wird am Montag auf Anordnung eines Hongkonger Gerichts liquidiert.",
"Die Entscheidung markiert den Abschluss einer zweijährigen Krise des in Shenzhen ansässigen Bauträgers, der im September 2021 signalisierte, dass er Kredite in Höhe von 300 Milliarden Dollar (277 Milliarden Euro) an Hunderte von Banken und Finanzunternehmen nicht mehr zurückzahlen könne.",
"Analysten befürchten seit langem, dass der Zusammenbruch des umkämpften Immobiliengiganten Schockwellen über Chinas bereits ins Stocken geratene Wirtschaft aussenden und möglicherweise sogar ausländische Firmen destabilisieren würde. Experten sprachen damals von Chinas „Lehman Brothers Moment“.",
"Um euch das Ausmaß des ehemaligen chinesischen Immobilienlieblings zu verdeutlichen, haben wir einige Statistiken und Vergleiche zusammengestellt, die das Ganze ins rechte Licht rücken."
]
},
{
"headline": [
"Statistiken, die die enorme Größe von Evergrande, Chinas sterbendem Immobilienriesen, verdeutlichen",
"1. Evergrande besaß mehr als 1300 Immobilienprojekte in China.",
"2. Evergrande war mit 300 Milliarden Dollar (277 Milliarden Euro) verschuldet, so hoch wie kein anderes Unternehmen der Welt.",
"3. Mindestens zwölf Millionen Hausbesitzer lebten in Immobilien, die von Evergrande gebaut wurden.",
"4. Evergrande verfügt über genügend Land, um die gesamte Fläche von Manhattan zu bebauen.",
"5. Evergrande war indirekt für 3,8 Millionen Arbeitsplätze verantwortlich, was genug ist, um ganz Los Angeles zu beschäftigen.",
"6. Die Schuldenlast von Evergrande entsprach 1,6 Prozent des chinesischen BIP.",
"Evergrande wurde von den Hausbesitzern verunglimpft und fiel vor zwei Jahren in China in Ungnade."
],
"paragraphs": []
}
]
},
"publishing_date": "2024-01-29 19:00:45+00:00",
"title": "6 Statistiken, die die enorme Größe von Evergrande, Chinas hoch verschuldeten Immobilienriesen verdeutlichen",
"topics": [
Expand Down
Loading

0 comments on commit b10f975

Please sign in to comment.