manaviel85370 commited on
Commit
83f1514
·
1 Parent(s): 9b0e5e0

extract dates and times

Browse files
.gitattributes CHANGED
@@ -17,8 +17,6 @@
17
  *.ot filter=lfs diff=lfs merge=lfs -text
18
  *.parquet filter=lfs diff=lfs merge=lfs -text
19
  *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
  *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
  *.rar filter=lfs diff=lfs merge=lfs -text
 
17
  *.ot filter=lfs diff=lfs merge=lfs -text
18
  *.parquet filter=lfs diff=lfs merge=lfs -text
19
  *.pb filter=lfs diff=lfs merge=lfs -text
 
 
20
  *.pt filter=lfs diff=lfs merge=lfs -text
21
  *.pth filter=lfs diff=lfs merge=lfs -text
22
  *.rar filter=lfs diff=lfs merge=lfs -text
.gitignore CHANGED
@@ -1,3 +1,4 @@
1
  .idea
2
  .venv
3
- .env
 
 
1
  .idea
2
  .venv
3
+ .env
4
+ /src/nlp/playground/models/
pages/6_Pipeline.py CHANGED
@@ -1,17 +1,29 @@
1
  import streamlit as st
2
  import streamlit.components.v1 as components
3
-
4
  from src.configuration.config import SessionStateConfig
5
- from src.nlp.playground.Event import Event
6
  from src.nlp.playground.ner import GlinerHandler
 
7
  from src.nlp.playground.pipelines.description_extractor import DescriptionExtractor
8
- from src.nlp.playground.textclassification import ZeroShotClassifier, CategoryMode, CustomMode
9
  from src.nlp.playground.pipelines.title_extractor import TitleExtractor
10
  from src.persistence.db import init_db
11
  from src.utils.apis.gpt_api import remove_boilerplate
12
- from src.utils.helpers import normalize_data
13
  from src.utils.markdown_processing.CustomMarkdownAnalyzer.MarkdownAnalyzer import MarkdownAnalyzer
14
  from src.utils.markdown_processing.md_preprocessing import convert_html_to_md
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  @st.cache_resource
17
  def init_connection():
@@ -30,108 +42,127 @@ if SessionStateConfig.ZERO_SHOT_CLASSIFIER not in st.session_state:
30
  st.session_state[SessionStateConfig.ZERO_SHOT_CLASSIFIER] = ZeroShotClassifier()
31
  if SessionStateConfig.GLINER_HANDLER not in st.session_state:
32
  st.session_state[SessionStateConfig.GLINER_HANDLER] = GlinerHandler()
 
 
33
 
34
  db = init_connection()
35
  data = init_data()
36
- element = next(data,None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
 
39
- st.subheader("Bereinigtes HTML")
40
- st.write(element["url"])
41
- st.components.v1.html(element["cleaned_html"], height=500, scrolling=True)
42
 
43
- start_pipeline = st.button("Starte Pipeline")
44
- if element:
45
  title_extractor = st.session_state.title_extractor
46
  description_extractor = st.session_state.description_extractor
47
  classifier = st.session_state[SessionStateConfig.ZERO_SHOT_CLASSIFIER]
48
  gliner_handler = st.session_state.gliner_handler
49
- if start_pipeline:
50
- html = element["cleaned_html"]
51
- md = convert_html_to_md(html)
52
- with st.expander("Markdown"):
53
- with st.container(border=True, height=400):
54
- st.markdown(md)
55
- with st.expander("Markdown Code"):
56
- with st.container(height=400):
57
- st.code(md)
58
-
59
- cleaned_md = remove_boilerplate(md)
60
- st.info("Remove boilerplate with GPT API")
61
- with st.expander("Gekürztes Markdown"):
62
- with st.container(border=True, height=400):
63
- st.markdown(cleaned_md)
64
-
65
- normalized_md = normalize_data(cleaned_md)
66
- with st.expander("Normalisiertes Markdown"):
67
- with st.container(border=True, height=400):
68
- st.markdown(normalized_md)
69
-
70
-
71
- text = normalized_md
72
- analyzer = MarkdownAnalyzer(text)
73
- results = analyzer.identify_all()["block_elements"]
74
- table_data = [{"Class": r.__class__.__name__, "Markdown": r.markdown} for r in results]
75
- with st.expander("Markdown Elemente"):
76
- st.table(table_data)
77
-
78
- with st.expander("Markdown Segmente"):
79
- segments = analyzer.segmentation()
80
- for s in segments:
81
- with st.container(border=True):
82
- for e in s:
83
- st.markdown(e.markdown)
84
-
85
- extracted_event = Event()
86
-
87
- st.info("Extracting title...")
88
- extracted_event.title = title_extractor.extract_title(cleaned_md)
89
-
90
- # extracted_event.categories = ZeroShotClassifier().classify(text, CategoryMode())
91
- extracted_event.categories = []
92
- st.info("Extracting Categories...")
93
- family_category = [cat.label for cat in classifier.classify(text,CustomMode(["Kinder_und_Familie","Adults_only"],"Die Veranstaltung ist für {}")) if cat.score >= 0.8]
94
- topic_category = [classifier.classify(text,CustomMode(
95
- ["Kunst","Kultur", "Musik", "Sport", "Bildung", "Tanz", "Wissenschaft", "Unterhaltung", "Gesundheit", "Wellness", "Business", "Politik","Religion"],
96
- "In der Veranstaltung geht es um {}"))[0].label]
97
- type_category = [classifier.classify(text,CustomMode(
98
- ["Oper", "Theater", "Konzert", "Gottesdienst", "Ausstellung", "Museum", "Planetarium", "Führung", "Film", "Vortrag", "Show", "Turnier", "Wettkampf", "Markt", "Feier", "Party"],
99
- "Die Art der Veranstaltung ist {}"))[0].label]
100
- time_category = [classifier.classify(text,CustomMode(
101
- ["Mehrere Tage","Einen Tag"],
102
- "Die Veranstaltung findet über {} statt."))[0].label]
103
-
104
- extracted_event.categories.extend(family_category)
105
- extracted_event.categories.extend(topic_category)
106
- extracted_event.categories.extend(type_category)
107
- extracted_event.categories.extend(time_category)
108
-
109
- st.info("Extracting Organizer and Location...")
110
- entities = gliner_handler.extract_entities(text, ["EVENT_ORGANIZER","EVENT_LOCATION_NAME","EVENT_ADDRESS"])
111
- extracted_event.organizers = list(set([item["text"] for item in entities if item["label"] == "EVENT_ORGANIZER"]))
112
- extracted_event.locations = list(set([item["text"] for item in entities if item["label"] == "EVENT_LOCATION_NAME"]))
113
- extracted_event.address = list(set([item["text"] for item in entities if item["label"] == "EVENT_ADDRESS"]))
114
- st.info("Extracting Price Information...")
115
- price_entities = gliner_handler.extract_entities(text, ["KOSTEN"])
116
- extracted_event.prices = list(set([item["text"] for item in price_entities if any(char.isdigit() for char in item["text"])]))
117
-
118
- st.info("Extracting Description...")
119
- extracted_event.description = description_extractor.extract_description(text, extracted_event.title)
120
-
121
- event_data = []
122
- event_data.append({'Field': 'Title', 'Value': extracted_event.title})
123
- event_data.append({'Field': 'Categories', 'Value': ", ".join(extracted_event.categories)})
124
- event_data.append({'Field': 'Organizers', 'Value': ", ".join(extracted_event.organizers)})
125
- event_data.append({'Field': 'Locations', 'Value': ", ".join(extracted_event.locations)})
126
- event_data.append({'Field': 'Address', 'Value': ", ".join(extracted_event.address)})
127
- event_data.append({'Field': 'Prices', 'Value': ", ".join(extracted_event.prices)})
128
-
129
-
130
- st.subheader("Extrahierte Daten")
131
- st.table(event_data)
132
- st.write("Event Description:")
133
  with st.container(border=True, height=400):
134
- st.markdown(extracted_event.description)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
 
137
 
 
1
  import streamlit as st
2
  import streamlit.components.v1 as components
 
3
  from src.configuration.config import SessionStateConfig
4
+ from src.utils.Event import Event
5
  from src.nlp.playground.ner import GlinerHandler
6
+ from src.nlp.playground.pipelines.date_extractor import DateTimeExtractor
7
  from src.nlp.playground.pipelines.description_extractor import DescriptionExtractor
8
+ from src.nlp.playground.textclassification import ZeroShotClassifier, CustomMode
9
  from src.nlp.playground.pipelines.title_extractor import TitleExtractor
10
  from src.persistence.db import init_db
11
  from src.utils.apis.gpt_api import remove_boilerplate
12
+ from src.utils.helpers import normalize_data, clean_html
13
  from src.utils.markdown_processing.CustomMarkdownAnalyzer.MarkdownAnalyzer import MarkdownAnalyzer
14
  from src.utils.markdown_processing.md_preprocessing import convert_html_to_md
15
+ import requests
16
+ from bs4 import BeautifulSoup
17
+
18
+ def scrape_url(url):
19
+ try:
20
+ response = requests.get(url)
21
+ response.raise_for_status()
22
+ soup = BeautifulSoup(response.text, 'html.parser')
23
+ cleaned_html = soup.prettify()
24
+ return cleaned_html
25
+ except requests.exceptions.RequestException as e:
26
+ return f"Fehler beim Abrufen der Seite: {e}"
27
 
28
  @st.cache_resource
29
  def init_connection():
 
42
  st.session_state[SessionStateConfig.ZERO_SHOT_CLASSIFIER] = ZeroShotClassifier()
43
  if SessionStateConfig.GLINER_HANDLER not in st.session_state:
44
  st.session_state[SessionStateConfig.GLINER_HANDLER] = GlinerHandler()
45
+ if "date_time_extractor" not in st.session_state:
46
+ st.session_state["date_time_extractor"] = DateTimeExtractor()
47
 
48
  db = init_connection()
49
  data = init_data()
50
+
51
+ with st.form("url_form"):
52
+ url = st.text_input("Gebe eine URL einer Veranstaltungsseite ein (Oder leer lassen um Seite aus der Datenbank zu nehmen).")
53
+
54
+ submit_button = st.form_submit_button("Starte Pipeline")
55
+
56
+ if submit_button:
57
+ if url:
58
+ html = scrape_url(url)
59
+ html = clean_html(html)
60
+ else:
61
+ element = next(data, None)
62
+ url = element["url"]
63
+ html = element["cleaned_html"]
64
+
65
 
66
 
67
+ st.subheader("Bereinigtes HTML")
68
+ st.write(url)
69
+ st.components.v1.html(html, height=500, scrolling=True)
70
 
 
 
71
  title_extractor = st.session_state.title_extractor
72
  description_extractor = st.session_state.description_extractor
73
  classifier = st.session_state[SessionStateConfig.ZERO_SHOT_CLASSIFIER]
74
  gliner_handler = st.session_state.gliner_handler
75
+ date_time_extractor = st.session_state.date_time_extractor
76
+
77
+ # html = element["cleaned_html"]
78
+ md = convert_html_to_md(html)
79
+ with st.expander("Markdown"):
80
+ with st.container(border=True, height=400):
81
+ st.markdown(md)
82
+ with st.expander("Markdown Code"):
83
+ with st.container(height=400):
84
+ st.code(md)
85
+
86
+ cleaned_md = remove_boilerplate(md)
87
+ st.info("Remove boilerplate with GPT API")
88
+ with st.expander("Gekürztes Markdown"):
89
+ with st.container(border=True, height=400):
90
+ st.markdown(cleaned_md)
91
+
92
+ normalized_md = normalize_data(cleaned_md)
93
+ with st.expander("Normalisiertes Markdown"):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  with st.container(border=True, height=400):
95
+ st.markdown(normalized_md)
96
+
97
+
98
+ text = normalized_md
99
+ analyzer = MarkdownAnalyzer(text)
100
+ results = analyzer.identify_all()["block_elements"]
101
+ table_data = [{"Class": r.__class__.__name__, "Markdown": r.markdown} for r in results]
102
+ with st.expander("Markdown Elemente"):
103
+ st.table(table_data)
104
+
105
+ with st.expander("Markdown Segmente"):
106
+ segments = analyzer.segmentation()
107
+ for s in segments:
108
+ with st.container(border=True):
109
+ for e in s:
110
+ st.markdown(e.markdown)
111
+
112
+ extracted_event = Event()
113
+
114
+ st.info("Extracting title...")
115
+ extracted_event.title = title_extractor.extract_title(cleaned_md)
116
+
117
+ # extracted_event.categories = ZeroShotClassifier().classify(text, CategoryMode())
118
+ extracted_event.categories = []
119
+ st.info("Extracting Categories...")
120
+ family_category = [cat.label for cat in classifier.classify(text,CustomMode(["Kinder_und_Familie","Adults_only"],"Die Veranstaltung ist für {}")) if cat.score >= 0.8]
121
+ topic_category = [classifier.classify(text,CustomMode(
122
+ ["Kunst","Kultur", "Musik", "Sport", "Bildung", "Tanz", "Wissenschaft", "Unterhaltung", "Gesundheit", "Wellness", "Business", "Politik","Religion"],
123
+ "In der Veranstaltung geht es um {}"))[0].label]
124
+ type_category = [classifier.classify(text,CustomMode(
125
+ ["Oper", "Theater", "Konzert", "Gottesdienst", "Ausstellung", "Museum", "Planetarium", "Führung", "Film", "Vortrag", "Show", "Turnier", "Wettkampf", "Markt", "Feier", "Party"],
126
+ "Die Art der Veranstaltung ist {}"))[0].label]
127
+ time_category = [classifier.classify(text,CustomMode(
128
+ ["Mehrere Tage","Einen Tag"],
129
+ "Die Veranstaltung findet über {} statt."))[0].label]
130
+
131
+ extracted_event.categories.extend(family_category)
132
+ extracted_event.categories.extend(topic_category)
133
+ extracted_event.categories.extend(type_category)
134
+ extracted_event.categories.extend(time_category)
135
+
136
+ st.info("Extracting Organizer and Location...")
137
+ entities = gliner_handler.extract_entities(text, ["EVENT_ORGANIZER","EVENT_LOCATION_NAME","EVENT_ADDRESS"])
138
+ extracted_event.organizers = list(set([item["text"] for item in entities if item["label"] == "EVENT_ORGANIZER"]))
139
+ extracted_event.locations = list(set([item["text"] for item in entities if item["label"] == "EVENT_LOCATION_NAME"]))
140
+ extracted_event.address = list(set([item["text"] for item in entities if item["label"] == "EVENT_ADDRESS"]))
141
+
142
+ st.info("Extracting Price Information...")
143
+ price_entities = gliner_handler.extract_entities(text, ["KOSTEN"])
144
+ extracted_event.prices = list(set([item["text"] for item in price_entities if any(char.isdigit() for char in item["text"])]))
145
+
146
+ st.info("Extracting Description...")
147
+ extracted_event.description = description_extractor.extract_description(cleaned_md, extracted_event.title)
148
+
149
+ st.info("Extracting Dates and Times...")
150
+ date_times = date_time_extractor.extract(cleaned_md)
151
+ st.write(date_times)
152
+ event_data = []
153
+ event_data.append({'Field': 'Title', 'Value': extracted_event.title})
154
+ event_data.append({'Field': 'Categories', 'Value': ", ".join(extracted_event.categories)})
155
+ event_data.append({'Field': 'Organizers', 'Value': ", ".join(extracted_event.organizers)})
156
+ event_data.append({'Field': 'Locations', 'Value': ", ".join(extracted_event.locations)})
157
+ event_data.append({'Field': 'Address', 'Value': ", ".join(extracted_event.address)})
158
+ event_data.append({'Field': 'Prices', 'Value': ", ".join(extracted_event.prices)})
159
+
160
+
161
+ st.subheader("Extrahierte Daten")
162
+ st.table(event_data)
163
+ st.write("Event Description:")
164
+ with st.container(border=True, height=400):
165
+ st.markdown(extracted_event.description)
166
 
167
 
168
 
src/nlp/experimental/custom_dependecy_parser.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # import spacy
2
+ # from spacy import displacy
3
+ # from spacy.training import Example
4
+ # from spacy.tokens import Span
5
+ #
6
+ #
7
+ # nlp = spacy.blank("de") # Leeres Modell
8
+ # config = {"moves": None}
9
+ # parser = nlp.add_pipe("parser", config=config)
10
+ #
11
+ # # Definiere eigene Labels
12
+ # for label in ["ROOT", "nsubj", "obl", "det", "case", "punct"]:
13
+ # parser.add_label(label)
14
+ #
15
+ # # Erstelle Trainingsdaten
16
+ # TRAIN_DATA = [("Die Veranstaltung beginnt am 24.12.2025 um 16:00.",
17
+ # {"heads": [1, 2, 2, 2, 4, 5, 5, 2, 7],
18
+ # "deps": ["det", "nsubj", "ROOT", "case", "obl", "case", "obl", "punct"]})]
19
+ #
20
+ # # Trainingspipeline starten
21
+ # optimizer = nlp.begin_training()
22
+ # for epoch in range(50): # Mehr Epochen für bessere Ergebnisse
23
+ # for text, annotations in TRAIN_DATA:
24
+ # doc = nlp.make_doc(text)
25
+ # example = Example.from_dict(doc, annotations)
26
+ # nlp.update([example], drop=0.1, losses={})
27
+ #
28
+
29
+
30
+
31
+
32
+
33
+
34
+ import spacy
35
+ from spacy.tokens import Doc
36
+ from spacy.language import Language
37
+
38
+ nlp = spacy.load("de_core_news_lg")
39
+
40
+ @Language.component("custom_dependency_parser")
41
+ def custom_dependency_parser(doc):
42
+ for token in doc:
43
+ # Falls ein Token eine Zeitangabe ist, hänge es an das nächste Verb
44
+ if token.ent_type_ in ["TIME", "DATE"]:
45
+ for child in token.children:
46
+ if child.pos_ == "VERB":
47
+ token.head = child
48
+ token.dep_ = "time_modifier"
49
+ break
50
+ return doc
51
+
52
+ # Füge den Custom Parser nach dem Standard-Parser hinzu
53
+ nlp.add_pipe("custom_dependency_parser", after="parser")
54
+
55
+ doc = nlp("Die Veranstaltung beginnt am 24.12.2025 um 16:00, der Einlass startet ab 15:00. Beginn: 20:00, Ende: 21:00, Einlass: 19:00")
56
+
57
+
58
+
59
+ sub_sentences = []
60
+ current_sentence = []
61
+
62
+ for token in doc:
63
+ # Haupt-Subjekt identifizieren
64
+ if token.dep_ == "sb":
65
+ if current_sentence: # Falls bereits Tokens gesammelt wurden, speichere den bisherigen Satz
66
+ sub_sentences.append(" ".join([t for t in current_sentence]))
67
+ current_sentence = [token.text] # Starte einen neuen Satz
68
+ else:
69
+ current_sentence.append(token.text)
70
+
71
+ # Letzten Satz hinzufügen
72
+ if current_sentence:
73
+ sub_sentences.append(" ".join([t for t in current_sentence]))
74
+
75
+ # Ausgabe der getrennten Sätze
76
+ for i, sentence in enumerate(sub_sentences):
77
+ print(f"Satz {i+1}: {sentence}")
78
+
79
+ # Erlaubte Dependency-Typen
80
+ allowed_deps = {"sb", "oa", "da", "nk"}
81
+ allowed_pos = {"NOUN", "PROPN"} # Nomen & Eigennamen
82
+
83
+ # Gefilterte Tokens
84
+ filtered_tokens = [token for token in doc if token.dep_ in allowed_deps or token.pos_ in allowed_pos]
85
+ print(filtered_tokens)
86
+
87
+ # Ausgabe der Abhängigkeiten
88
+ relations = [] # Hier speichern wir die gefundenen Relationen
89
+
90
+ for token in doc:
91
+ print(f"{token.text} --({token.dep_})--> {token.head.text}")
92
+
src/nlp/experimental/ner/create_spacy_annotations_date_and_time.py ADDED
File without changes
src/nlp/experimental/textclassification/classy_classifier.py CHANGED
@@ -416,7 +416,7 @@ test_data = {"Veranstaltungstitel": [d for d in test_data["Veranstaltungstitel"]
416
  # with open("./classifier.pkl", "wb") as f:
417
  # pickle.dump(classifier, f)
418
 
419
- f = open("./classifier.pkl", "rb")
420
  classifier = pickle.load(f)
421
  classifier("I am looking for kitchen appliances.")
422
 
 
416
  # with open("./classifier.pkl", "wb") as f:
417
  # pickle.dump(classifier, f)
418
 
419
+ f = open("../../playground/models/title_classifier.pkl", "rb")
420
  classifier = pickle.load(f)
421
  classifier("I am looking for kitchen appliances.")
422
 
src/nlp/experimental/textclassification/classy_classifier_date.py CHANGED
@@ -10,7 +10,7 @@ from src.nlp.playground.textclassification import ZeroShotClassifier, CustomMode
10
  from src.utils.helpers import normalize_data
11
  from src.utils.markdown_processing.CustomMarkdownAnalyzer.MarkdownAnalyzer import MarkdownAnalyzer
12
 
13
- classifier_train_data = {
14
  "EVENT_DATE": [
15
  "Termin: [DATE], 19:00",
16
  "[DATE]",
@@ -131,7 +131,101 @@ classifier_train_data = {
131
  ]
132
  }
133
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
  nlp = spacy.blank("de")
137
  nlp.add_pipe('sentencizer')
@@ -170,7 +264,7 @@ ruler.add_patterns(patterns)
170
 
171
  # Prepare Training Data: Use Placeholders for Times and Dates
172
  classifier_train_data_cleaned = {"EVENT_DATE": [], "OTHER":[]}
173
- for text in classifier_train_data["EVENT_DATE"]:
174
  text = normalize_data(text)
175
  doc = nlp(text)
176
  for ent in doc.ents:
@@ -179,7 +273,7 @@ for text in classifier_train_data["EVENT_DATE"]:
179
  if ent.label_ == "TIME":
180
  text = text.replace(ent.text, "[TIME]")
181
  classifier_train_data_cleaned["EVENT_DATE"].append(text)
182
- for text in classifier_train_data["OTHER"]:
183
  text = normalize_data(text)
184
  doc = nlp(text)
185
  for ent in doc.ents:
@@ -193,51 +287,64 @@ for text in classifier_train_data["OTHER"]:
193
  classifier_train_data_cleaned["EVENT_DATE"] = list(set(classifier_train_data_cleaned["EVENT_DATE"]))
194
  classifier_train_data_cleaned["OTHER"] = list(set(classifier_train_data_cleaned["OTHER"]))
195
  print(classifier_train_data_cleaned["EVENT_DATE"])
 
196
 
197
  classifier = ClassyClassifier(data=classifier_train_data_cleaned)
198
  classifier.set_embedding_model(model="stsb-xlm-r-multilingual")
 
 
199
 
 
 
200
 
201
  for text in TEXTS:
202
  text = normalize_data(text)
203
  analyzer = MarkdownAnalyzer(text)
 
 
 
204
  md_elements = analyzer.identify_all().get("block_elements")
 
 
205
  for md_element in md_elements:
206
  doc = nlp(md_element.text)
207
-
208
  # Prüfe Tokenisierung
209
  # print("Tokens:", [token.text for token in doc])
210
-
211
  if doc.ents:
212
- print("*" * 100)
213
-
214
- # Extrahiere erkannte Entitäten
215
  modified_text = md_element.text
 
 
216
  for ent in doc.ents:
217
- print(ent.text, ent.label_)
218
  if ent.label_ == "DATE":
219
  modified_text = modified_text.replace(ent.text, "[DATE]")
220
  if ent.label_ == "TIME":
221
  modified_text = modified_text.replace(ent.text, "[TIME]")
222
- cats = classifier(modified_text)
223
- print(modified_text)
224
- print(f"{max(cats, key=cats.get)}")
225
- print("*" * 100)
226
-
227
-
228
- # for text in test_data["EVENT_DATE"]:
229
- # print(text)
230
- # print("*"*100)
231
- # # print(nlp(text)._.cats)
232
- # cats = classifier(text)
233
- # print(f"{max(cats,key=cats.get)}")
234
- # print("*"*100)
235
- #
236
- # print("\n\n\n\n\n")
237
- # for text in test_data["OTHER"]:
238
- # print(text)
239
- # print("*"*100)
240
- # # print(nlp(text)._.cats)
241
- # cats = classifier(text)
242
- # print(f"{max(cats,key=cats.get)}")
243
- # print("*"*100)
 
 
 
 
 
 
10
  from src.utils.helpers import normalize_data
11
  from src.utils.markdown_processing.CustomMarkdownAnalyzer.MarkdownAnalyzer import MarkdownAnalyzer
12
 
13
+ date_classifier_train_data = {
14
  "EVENT_DATE": [
15
  "Termin: [DATE], 19:00",
16
  "[DATE]",
 
131
  ]
132
  }
133
 
134
+ time_classifier_train_data = {
135
+ "EVENT_TIME": [
136
+ "**Wann?** 12.05.2024, 19:00-21:00",
137
+ "So. 12.08.2024 12:15 - 13:15 CET",
138
+ "13:00 - 14:00",
139
+ "Termin: [DATE], 19:00",
140
+ "[DATE]",
141
+ "Unser Meetup ist am [DATE] um 18:30 Uhr.",
142
+ "Die Show findet am [DATE] um 20:00 Uhr statt.",
143
+ "Das Webinar beginnt am [DATE] um 16:00 Uhr.",
144
+ "Fußballspiel: [DATE], 15:30 Uhr",
145
+ "[DATE] um 20:00 Uhr",
146
+ "Termin: [DATE], 18:00 Uhr",
147
+ "Wann? [DATE], 19:00 bis 20:00 Uhr"
148
+ "Konzert am [DATE], 20:00 Uhr",
149
+ "Networking-Event am [DATE], 17:00 Uhr",
150
+ "Workshop: [DATE], 14:00 Uhr",
151
+ "Firmenfeier: [DATE] ab 18:30 Uhr",
152
+ "Seminar: [DATE], Beginn um 10:00 Uhr",
153
+ "Schulung am [DATE] um 15:00 Uhr",
154
+ "Jubiläumsfeier am [DATE], 19:30 Uhr",
155
+ "[DATE] 23:00",
156
+ "[DATE] 23:00",
157
+ "[DATE] 23:00",
158
+ "Datum: [DATE], Startzeit: 10:00, Endzeit: 12:00",
159
+ "Samstag, [DATE], Einlass: 15:59, Beginn: 17:30, Preis: 65,73 EUR",
160
+ "Samstag, [DATE], Einlass: 17:00, Beginn: 19:00, Preis: 74,99 EUR",
161
+ "Samstag, [DATE], Einlass: 18:00, Beginn: 20:00",
162
+ "Freitag, [DATE], Einlass: 17:00, Beginn: 19:00, Preis: 77,93 EUR",
163
+ "Samstag, [DATE], Einlass: 16:30, Beginn: 18:30, Preis: ab 69,99 Euro",
164
+ "Gestört aber GeiL – Das Festival • [DATE], 16:00 • Berlin",
165
+ "Samstag, [DATE], Einlass: 17:00, Beginn: 19:00",
166
+ "Kaufberatung: [DATE] um 19:00",
167
+ "Bedienung: [DATE] um 19:00",
168
+ "[DATE] Ganztägig",
169
+ "ab dem [DATE]",
170
+ "Mittwoch, [DATE], von 18:00-22:00",
171
+ "Mittwoch, [DATE], von 18:00-22:00",
172
+ "Augsburger Friedensgespräche am [DATE]",
173
+ "Augsburger Friedensgespräche am [DATE]",
174
+ "Augsburger Friedensgespräche am [DATE]",
175
+ "[DATE] - [DATE]",
176
+ "Am [DATE] endet der Weihnachtsmarkt bereits um 19:00.",
177
+ "Winzerglühwein Do [DATE] 17:00 - 19:00",
178
+ "Winzerglühwein Fr [DATE] 16:30 - 18:30",
179
+ "Winzerglühwein Sa [DATE] 15:30 - 15:30",
180
+ "Lessons and Carols Sa [DATE] 19:30 - 21:00",
181
+ "[DATE] - [DATE]",
182
+ "Vom [DATE] - [DATE]",
183
+ "[DATE] - [DATE]",
184
+ "Am [DATE] endet der Weihnachtsmarkt bereits um 19:00.",
185
+ "Do [DATE] 17:00 - 19:00",
186
+ "Fr [DATE] 16:30 - 18:30",
187
+ "Sa [DATE] 15:30 - 15:30",
188
+ "Sa [DATE] 19:30 - 21:00",
189
+ "[DATE]",
190
+ "[DATE] - [DATE]",
191
+ "[DATE]",
192
+ "[DATE]",
193
+ "[DATE] und [DATE]",
194
+ "[DATE]",
195
+ "[DATE] - [DATE]",
196
+ "Am [DATE] endet der Weihnachtsmarkt bereits um 19:00.",
197
+ "Vom [DATE] - [DATE]",
198
+ "[DATE] und [DATE]",
199
+ "Am [DATE] ab 19:00",
200
+ "Am [DATE] ab 19:00",
201
+ "Am [DATE]",
202
+ "[DATE]",
203
+ "[DATE]",
204
+ "[DATE]",
205
+ "[DATE] 16:00 – [DATE] 17:00",
206
+ "[DATE] 10:15 – [DATE] 12:30",
207
+ "[DATE] 10:00 – [DATE] 18:00",
208
+ "[DATE]",
209
+ "[DATE] 11:00 – [DATE] 18:00",
210
+ "[DATE] - [DATE]",
211
+ "[DATE] | 19:30",
212
+ "[DATE]",
213
+ "[DATE] bis einschließlich [DATE]",
214
+ "[DATE], [DATE], [DATE] und [DATE]",
215
+ "[DATE] 18:00",
216
+ "[DATE] 13:00-21:00",
217
 
218
+ ],
219
+ "OTHER":[
220
+ "Einlass: 19:00",
221
+ "Abendkasse ab 20:00 Uhr",
222
+ "Tageskarten können ab 18:00 Uhr gekauft werden.",
223
+ "Öffnungszeiten: Mo-Fr 09:00 - 17:00",
224
+ "Kartenverkauf ab 17:30 Uhr",
225
+ "Einlass beginnt um 18:45",
226
+ "Reservierung erforderlich bis 12:00 Uhr",
227
+ ]
228
+ }
229
 
230
  nlp = spacy.blank("de")
231
  nlp.add_pipe('sentencizer')
 
264
 
265
  # Prepare Training Data: Use Placeholders for Times and Dates
266
  classifier_train_data_cleaned = {"EVENT_DATE": [], "OTHER":[]}
267
+ for text in date_classifier_train_data["EVENT_DATE"]:
268
  text = normalize_data(text)
269
  doc = nlp(text)
270
  for ent in doc.ents:
 
273
  if ent.label_ == "TIME":
274
  text = text.replace(ent.text, "[TIME]")
275
  classifier_train_data_cleaned["EVENT_DATE"].append(text)
276
+ for text in date_classifier_train_data["OTHER"]:
277
  text = normalize_data(text)
278
  doc = nlp(text)
279
  for ent in doc.ents:
 
287
  classifier_train_data_cleaned["EVENT_DATE"] = list(set(classifier_train_data_cleaned["EVENT_DATE"]))
288
  classifier_train_data_cleaned["OTHER"] = list(set(classifier_train_data_cleaned["OTHER"]))
289
  print(classifier_train_data_cleaned["EVENT_DATE"])
290
+ print(classifier_train_data_cleaned["OTHER"])
291
 
292
  classifier = ClassyClassifier(data=classifier_train_data_cleaned)
293
  classifier.set_embedding_model(model="stsb-xlm-r-multilingual")
294
+ with open("../../playground/models/date_classifier.pkl", "wb") as f:
295
+ pickle.dump(classifier, f)
296
 
297
+ zero_shot_classifier = ZeroShotClassifier()
298
+ TEXTS = ["Tickets können ab dem 03.12.2020 erworben werden"]
299
 
300
  for text in TEXTS:
301
  text = normalize_data(text)
302
  analyzer = MarkdownAnalyzer(text)
303
+ print("*"*100)
304
+ print(text)
305
+ print("\n\n\n")
306
  md_elements = analyzer.identify_all().get("block_elements")
307
+ md_elements = []
308
+ dates = {"dates": [], "times": []}
309
  for md_element in md_elements:
310
  doc = nlp(md_element.text)
 
311
  # Prüfe Tokenisierung
312
  # print("Tokens:", [token.text for token in doc])
313
+ print(doc.ents)
314
  if doc.ents:
315
+ print(md_element.text)
 
 
316
  modified_text = md_element.text
317
+
318
+ # Replace TIME and DATE entities with placeholders
319
  for ent in doc.ents:
 
320
  if ent.label_ == "DATE":
321
  modified_text = modified_text.replace(ent.text, "[DATE]")
322
  if ent.label_ == "TIME":
323
  modified_text = modified_text.replace(ent.text, "[TIME]")
324
+
325
+ date_entities = [ent.text for ent in doc.ents if ent.label_ == "DATE"]
326
+ # Classify Date category
327
+ if date_entities:
328
+ print("DATES: ",date_entities )
329
+ cats = classifier(modified_text)
330
+ date_category = max(cats, key=cats.get)
331
+ print("Date Category: ", date_category)
332
+ if date_category == "EVENT_DATE":
333
+ dates["dates"].extend(date_entities)
334
+
335
+ time_entities = [ent.text for ent in doc.ents if ent.label_ == "TIME"]
336
+ if time_entities:
337
+ # Classify Time category
338
+ print("ZEITEN: ", time_entities)
339
+ time_category = zero_shot_classifier.classify(modified_text, CustomMode(
340
+ labels=["BEGINN", "EINLASS", "ABLAUF"],
341
+ hypothesis_template="Der Text geht um {} einer Veranstaltung"))[0].label
342
+ print("Time Category: ", time_category)
343
+ if time_category == "BEGINN":
344
+ dates["times"].extend(time_entities)
345
+ print("\n")
346
+
347
+ print(dates)
348
+ print("*" * 100)
349
+
350
+
src/nlp/experimental/textclassification/classy_classifier_time.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from classy_classification import ClassyClassifier
2
+ import pickle
3
+ import spacy
4
+ from spacy import displacy
5
+ from spacy.tokenizer import Tokenizer
6
+ from spacy.util import compile_prefix_regex, compile_suffix_regex, compile_infix_regex
7
+ from nltk import Tree
8
+ from src.nlp.data.test_texts import TEXTS
9
+ from src.nlp.playground.textclassification import ZeroShotClassifier, CustomMode
10
+ from src.utils.helpers import normalize_data
11
+ from src.utils.markdown_processing.CustomMarkdownAnalyzer.MarkdownAnalyzer import MarkdownAnalyzer
12
+
13
+ train_data = {
14
+ "EVENT_TIME": [
15
+ "Die Veranstaltung beginnt am 24.12.2025 16:00",
16
+ "Wann: 03.03.2020 16:00",
17
+ "25.04.2025 - 23.05.2020 , 15:00 - 16:00",
18
+ "22.06.2022 18:00",
19
+ "23.12.2030 17:00 - 18:00",
20
+ "12.05.2024 , 19:00 - 21:00",
21
+ "So. 12.08.2024 12:15 - 13:15",
22
+ "**24.12.2025 16:00 **",
23
+ "15.01.2025 18:00",
24
+ "18.01.2025 11:00 - 18:00",
25
+ "**Der Nikolaus kommt am Freitag 17:00 !**",
26
+ "Sa 12.00 + 16:00",
27
+ "So 12:00",
28
+ "Kindertheater Sa + So 15:00",
29
+ "| Beginn | Freitag 16:00 - 20:00",
30
+ "/ Samstag 11:00 - 20:00",
31
+ "/ Sonntag 11:00 - 18:00",
32
+ "19:00",
33
+ "Beginn: 15:00"
34
+ ],
35
+ "ADMITTANCE_TIME": [
36
+ "Einlass ist 15:00",
37
+ "Einlass ist 15:30",
38
+ "Einlass: 15:00",
39
+ "Abendkasse 14:30",
40
+ "Einlass: 14:30",
41
+ "Tageskasse 15:00 im Museum",
42
+ "Einlass beginnt um 16:00",
43
+ "Der Einlass erfolgt um 17:30",
44
+ "Zutritt ab 18:00",
45
+ "Gäste dürfen ab 14:45 eintreten",
46
+ "Türöffnung: 19:00",
47
+ "Einlass erfolgt ab 20:15",
48
+ "Einlass für VIPs: 13:30",
49
+ "Normaler Einlass: 16:45",
50
+ "Die Türen öffnen sich um 12:00",
51
+ "Zugang ab 17:00 möglich",
52
+ "Einlasskontrolle beginnt um 18:30",
53
+ "Einlass erst ab 19:15 gestattet"
54
+ ]
55
+ }
56
+
57
+
58
+ nlp = spacy.blank("de")
59
+ nlp.add_pipe('sentencizer')
60
+
61
+ # 1️⃣ Punkt als Suffix & Infix definieren (damit er zwischen Zahlen trennt)
62
+ suffixes = list(nlp.Defaults.suffixes) + [r"\."] # Punkt als Suffix hinzufügen
63
+ infixes = list(nlp.Defaults.infixes) + [r"(?<=\d)\.(?=\d)"] + [r"(?<=\d)\:(?=\d)"] # Punkt zwischen Zahlen trennen
64
+
65
+
66
+ # Regex-Objekte kompilieren
67
+ suffix_re = compile_suffix_regex(suffixes)
68
+ infix_re = compile_infix_regex(infixes)
69
+
70
+ # Angepasste Tokenizer-Funktion setzen
71
+ nlp.tokenizer = Tokenizer(nlp.vocab, suffix_search=suffix_re.search, infix_finditer=infix_re.finditer)
72
+ # 2️⃣ Entity Ruler für Datumsangaben hinzufügen
73
+ ruler = nlp.add_pipe("entity_ruler")
74
+
75
+ patterns = [
76
+ {
77
+ "label": "DATE",
78
+ "pattern": [
79
+ {"SHAPE": "dd"}, {"ORTH": "."}, {"SHAPE": "dd"}, {"ORTH": "."}, {"SHAPE": "dddd"}
80
+ ]
81
+ },
82
+ {
83
+ "label": "TIME",
84
+ "pattern": [
85
+ {"SHAPE": "dd"}, {"ORTH": ":"}, {"SHAPE": "dd"}
86
+ ]
87
+ }
88
+ ]
89
+
90
+ ruler.add_patterns(patterns)
91
+
92
+
93
+ # Prepare Training Data: Use Placeholders for Times and Dates
94
+ classifier_train_data_cleaned = {"EVENT_TIME": [], "ADMITTANCE_TIME": []}
95
+ for text in train_data["EVENT_TIME"]:
96
+ text = normalize_data(text)
97
+ doc = nlp(text)
98
+ for ent in doc.ents:
99
+ if ent.label_ == "DATE":
100
+ text = text.replace(ent.text, "[DATE]")
101
+ if ent.label_ == "TIME":
102
+ text = text.replace(ent.text, "[TIME]")
103
+ classifier_train_data_cleaned["EVENT_TIME"].append(text)
104
+ for text in train_data["ADMITTANCE_TIME"]:
105
+ text = normalize_data(text)
106
+ doc = nlp(text)
107
+ for ent in doc.ents:
108
+ if ent.label_ == "DATE":
109
+ text = text.replace(ent.text, "[DATE]")
110
+ if ent.label_ == "TIME":
111
+ text = text.replace(ent.text, "[TIME]")
112
+ classifier_train_data_cleaned["ADMITTANCE_TIME"].append(text)
113
+
114
+ # remove duplicates
115
+ classifier_train_data_cleaned["EVENT_TIME"] = list(set(classifier_train_data_cleaned["EVENT_TIME"]))
116
+ classifier_train_data_cleaned["ADMITTANCE_TIME"] = list(set(classifier_train_data_cleaned["ADMITTANCE_TIME"]))
117
+
118
+
119
+ classifier = ClassyClassifier(data=classifier_train_data_cleaned)
120
+ classifier.set_embedding_model(model="stsb-xlm-r-multilingual")
121
+ with open("../../playground/models/time_classifier.pkl", "wb") as f:
122
+ pickle.dump(classifier, f)
123
+
124
+ TEXTS = ["Einlass: ab 15:00", "Beginn: 18:00", "Konzert: 20:00 bis 21:00", "Start: 20:00, Ende: 21:00"]
125
+
126
+ for text in TEXTS:
127
+ print(text)
128
+ text = normalize_data(text)
129
+ analyzer = MarkdownAnalyzer(text)
130
+
131
+ md_elements = analyzer.identify_all().get("block_elements")
132
+ dates = {"dates": [], "times": []}
133
+ for md_element in md_elements:
134
+ doc = nlp(md_element.text)
135
+ # Prüfe Tokenisierung
136
+ # print("Tokens:", [token.text for token in doc])
137
+ if doc.ents:
138
+ print("Found Entities: ", doc.ents)
139
+ modified_text = md_element.text
140
+
141
+ # Replace TIME and DATE entities with placeholders
142
+ for ent in doc.ents:
143
+ if ent.label_ == "DATE":
144
+ modified_text = modified_text.replace(ent.text, "[DATE]")
145
+ if ent.label_ == "TIME":
146
+ modified_text = modified_text.replace(ent.text, "[TIME]")
147
+
148
+ cats = classifier(modified_text)
149
+ time_category = max(cats, key=cats.get)
150
+ print(time_category)
151
+ print("*"*100)
152
+
153
+
154
+
src/nlp/playground/pipelines/date_extractor.py ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pickle
2
+ import os
3
+ from dotenv import load_dotenv
4
+ import spacy
5
+ from spacy.util import compile_suffix_regex, compile_infix_regex
6
+ from spacy.tokenizer import Tokenizer
7
+ from spacy.language import Language
8
+
9
+ from src.nlp.data.test_texts import TEXTS
10
+ from src.nlp.playground.textclassification import ZeroShotClassifier, CustomMode
11
+ from src.utils.helpers import normalize_data
12
+ from src.utils.markdown_processing.CustomMarkdownAnalyzer.MarkdownAnalyzer import MarkdownAnalyzer
13
+ from spacy.tokens import Span
14
+ from huggingface_hub import hf_hub_download
15
+ import joblib
16
+ from huggingface_hub import login
17
+
18
+ load_dotenv()
19
+ token = os.getenv("HUGGING_FACE_SPACES_TOKEN")
20
+ login(token=token)
21
+
22
+
23
+ class NLPProcessor:
24
+ def __init__(self, language="de"):
25
+ self.nlp = spacy.blank(language)
26
+ self._configure_pipeline()
27
+
28
+ def _configure_pipeline(self):
29
+ self.nlp.add_pipe("sentencizer", config={"punct_chars": [".", "!", "?"]})
30
+ self.nlp.add_pipe("custom_sentence_boundary", after="sentencizer")
31
+ self._setup_tokenizer()
32
+ self._setup_entity_ruler()
33
+
34
+ def _setup_tokenizer(self):
35
+ suffixes = list(self.nlp.Defaults.suffixes) + [r"\.", r"\,"]
36
+ infixes = list(self.nlp.Defaults.infixes) + [r"(?<=\d)\.(?=\d)", r"(?<=\d)\:(?=\d)"]
37
+
38
+ suffix_re = compile_suffix_regex(suffixes)
39
+ infix_re = compile_infix_regex(infixes)
40
+
41
+ self.nlp.tokenizer = Tokenizer(self.nlp.vocab, suffix_search=suffix_re.search, infix_finditer=infix_re.finditer)
42
+
43
+ def _setup_entity_ruler(self):
44
+ ruler = self.nlp.add_pipe("entity_ruler")
45
+ ruler.add_patterns([
46
+ {"label": "DATE", "pattern": [{"SHAPE": "dd"}, {"ORTH": "."}, {"SHAPE": "dd"}, {"ORTH": "."}, {"SHAPE": "dddd"}]},
47
+ {"label": "TIME", "pattern": [{"SHAPE": "dd"}, {"ORTH": ":"}, {"SHAPE": "dd"}]}
48
+ ])
49
+
50
+ @staticmethod
51
+ @Language.component("custom_sentence_boundary")
52
+ def custom_sentence_boundary(doc):
53
+ for token in doc[:-1]:
54
+ if token.text.endswith(".") and token.nbor(1).is_digit:
55
+ doc[token.i + 1].is_sent_start = False
56
+ return doc
57
+
58
+ class Token:
59
+ def __init__(self, text):
60
+ self.text = text
61
+
62
+ def __repr__(self):
63
+ return self.text
64
+
65
+ class DateTimeExtractor(NLPProcessor):
66
+ def __init__(self):
67
+ super().__init__()
68
+ self.date_classifier = self._load_classifier("adojode/date_classifier","date_classifier")
69
+ self.time_classifier = self._load_classifier("adojode/time_classifier","time_classifier")
70
+
71
+ @staticmethod
72
+ def _load_classifier(repo_id,model_name):
73
+ # classifier_path = os.path.join(os.path.dirname(__file__), model_name + ".pkl")
74
+ # with open(classifier_path, "rb") as f:
75
+ # return pickle.load(f)
76
+ return joblib.load(
77
+ hf_hub_download(repo_id=repo_id, filename=model_name + ".pkl")
78
+ )
79
+
80
+ def __extract_tokens_between_entities(self, doc):
81
+ entities = list(doc.ents) # Alle Entitäten im Text
82
+ tokens_between = []
83
+
84
+ if len(entities) < 2:
85
+ return tokens_between # Wenn es weniger als 2 Entitäten gibt, gibt es kein "Dazwischen"
86
+
87
+ for i in range(len(entities) - 1):
88
+ ent1 = entities[i]
89
+ ent2 = entities[i + 1]
90
+
91
+ # Speichere nur Tokens zwischen ent1 und ent2
92
+ tokens_between.append(doc[ent1.end:ent2.start])
93
+
94
+ return tokens_between
95
+
96
+ def __find_strong_relations(self, sequence):
97
+ sequence = [s for s in sequence if len(s.text)>1]
98
+ strong_relations = []
99
+ curr_relation = [sequence[0]]
100
+ sequence.pop(0)
101
+ for i,token in enumerate(sequence):
102
+ if isinstance(token, Span):
103
+ label = token.label_
104
+ curr_relation.append(token)
105
+ elif i+1 < len(sequence):
106
+ if isinstance(sequence[i+1], Span):
107
+ strong_relations.append(curr_relation)
108
+ curr_relation=[token]
109
+ else:
110
+ curr_relation.append(token)
111
+ strong_relations.append(curr_relation)
112
+
113
+ return strong_relations
114
+
115
+ def __preprocess(self, text):
116
+ text = normalize_data(text)
117
+ replacements = {" und ": " + ", " bis ": " - ", " um ": " ", " ab ": " "}
118
+ for old, new in replacements.items():
119
+ text = text.replace(old, new)
120
+ return text
121
+
122
+ def text_segmentation(self, text):
123
+ analyzer = MarkdownAnalyzer(text)
124
+ md_elements = analyzer.identify_all().get("block_elements")
125
+ segments = []
126
+ for element in md_elements:
127
+ text = self.__preprocess(element.text)
128
+ # text = remove_stopwords(text)
129
+ doc = self.nlp(text)
130
+ for sent in doc.sents:
131
+ doc_small = self.nlp(sent.text)
132
+ if doc_small.ents:
133
+
134
+ if len(doc_small.ents) == 1:
135
+ entity = doc_small.ents[0]
136
+ before = Token(text=doc_small[:entity.start].text) if entity.start > 0 else None
137
+ entity_token = entity # Die Entität bleibt als eigenes Token
138
+ after = Token(text=doc_small[entity.end:].text) if entity.end < len(doc_small) else None
139
+
140
+ # Erstelle das Segment mit separater Entität
141
+ segment = [t for t in [before, entity_token, after] if t] # Entferne None-Werte
142
+ segments.append(segment)
143
+ continue
144
+
145
+ tokens_between_entities = self.__extract_tokens_between_entities(doc_small)
146
+
147
+ sequence = [Token(text=doc_small[:doc_small.ents[0].start].text)]
148
+ entities = list(doc_small.ents)
149
+ # entities = [Entity(e.text, e.label_) for e in doc_small.ents]
150
+ sequence.append(entities[0])
151
+ for i, tokens in enumerate(tokens_between_entities):
152
+ connector = tokens.text.strip()
153
+ if len(connector) > 1:
154
+ if "," in connector:
155
+ sequence.extend([Token(text=c) for c in connector.split(",")])
156
+ else:
157
+ sequence.extend([Token(text=c) for c in connector.split("+")])
158
+ else:
159
+ sequence.append(Token(connector))
160
+ sequence.append(entities[i + 1])
161
+ sequence.append(Token(doc_small[doc_small.ents[len(entities) - 1].end:].text))
162
+
163
+ strong_relations = self.__find_strong_relations(sequence)
164
+ final_relations = []
165
+ remaining_sequence = sequence[:] # Eine Kopie von `sequence` behalten
166
+
167
+ for relation in strong_relations:
168
+ complete_group = []
169
+ for r in relation:
170
+ if r.text in [s.text for s in remaining_sequence]:
171
+ i = remaining_sequence.index(r) # Index in der verbleibenden Liste finden
172
+ s = remaining_sequence[:i] # Alles davor nehmen
173
+ complete_group.extend(s) # In die aktuelle Gruppe packen
174
+ complete_group.append(r) # Das aktuelle `r` hinzufügen
175
+ remaining_sequence = remaining_sequence[i + 1:] # Kürze die Liste ab
176
+ else:
177
+ print(f"Warnung: '{r}' nicht in der verbleibenden Sequence gefunden!")
178
+
179
+ final_relations.append(complete_group)
180
+ segments.extend(final_relations)
181
+ return segments
182
+
183
+ def extract(self,text):
184
+ segments = self.text_segmentation(text)
185
+ date_times = []
186
+ for segment in segments:
187
+ context = " ".join([s.text for s in segment])
188
+
189
+ classifier_input = self.prepare_for_classifier(context)
190
+
191
+ # doc = self.nlp(" ".join([s.text for s in segment]))
192
+ date_entities = [token for token in segment if isinstance(token, spacy.tokens.Span) and token.label_ == "DATE"]
193
+ time_entities = [token for token in segment if isinstance(token, spacy.tokens.Span) and token.label_ == "TIME"]
194
+ if date_entities:
195
+ date_time = {}
196
+ print("Textsegment: ", context)
197
+ date_class = self.date_classifier(classifier_input)
198
+ best_label, best_score = max(date_class.items(), key=lambda x: x[1])
199
+ if best_label == "EVENT_DATE" and best_score > 0.8:
200
+ print("Datum: ",date_entities)
201
+ date_time["dates"] = date_entities
202
+ print(f"Label: {best_label}, Score: {best_score:.4f}")
203
+ if time_entities:
204
+ print("Uhrzeit:", time_entities)
205
+ date_time["times"] = time_entities
206
+ date_times.append(date_time)
207
+ elif time_entities:
208
+ date_time = {}
209
+ print("Textsegment ", context)
210
+ time_class = self.time_classifier(classifier_input)
211
+ best_label, best_score = max(time_class.items(), key=lambda x: x[1])
212
+ print(best_label, " ", best_score)
213
+ if best_label == "EVENT_TIME" and best_score > 0.8:
214
+ date_time["times"] = time_entities
215
+ else:
216
+ print("Einlass Zeit:", time_entities)
217
+ date_time["admittance"] = time_entities
218
+ date_times.append(date_time)
219
+ return date_times
220
+
221
+ def prepare_for_classifier(self,text):
222
+ doc = self.nlp(text)
223
+ for ent in doc.ents:
224
+ if ent.label_ == "DATE":
225
+ text = text.replace(ent.text, "[DATE]")
226
+ if ent.label_ == "TIME":
227
+ text = text.replace(ent.text, "[TIME]")
228
+ return text
229
+
230
+ # texts = [
231
+ # "Tickets können ab dem 03.12.2020 erworben werden und die Veranstaltung startet um 15:00",
232
+ # "Am 01.01.2020 findet die Veranstaltung statt, Einlass ist um 15:00",
233
+ # "** Die Veranstaltung beginnt am 24.12.2025 um 16:00, Einlass ist ab 15:30. **",
234
+ # "Wann: 03.03.2020 16:00, Einlass: 15:00",
235
+ # "Wann? 25.04.2025 - 23.05.2020, 15:00 bis 16:00. Abendkasse ab 14:30",
236
+ # """
237
+ # Termine: \n
238
+ # 23.05.2024 19:00 \n
239
+ # 22.06.2022 18:00 \n
240
+ # 23.12.2030 17:00-18:00
241
+ # """,
242
+ # "# Technik-Salon an der TIB: „FLY ROCKET FLY“\n\nÜber den Aufstieg und Fall des Raketenpioniers Lutz Kayser – ein Film- und\nGesprächsabend mit dem Dokumentarfilmer Oliver Schwehm\n\nEin Start-Up aus dem Schwäbischen, 170 Millionen D-Mark Wagniskapital, ein\nprivates Testgelände im afrikanischen Dschungel – so trat Lutz Kayser in den\n1970 er-Jahren an, die Raumfahrt zu revolutionieren.\n\nWarum das gut hätten klappen können und schließlich doch scheiterte, schildert\nOliver Schwehm in seiner bildreichen Dokumentation. Im Rahmen der\nJubiläumstour zu Ehren von Lutz Kayser („50 Jahre OTRAG – Oribital Transport\nund Raketen AG“) machen Film und Regisseur am 05.12.2024 im Technik-\nSalon Station in der TIB in Hannover. Der Eintritt für die Veranstaltung „FLY\nROCKET FLY“ ist frei (Spendenbox).\n\n**Wann?** 05.12.2024, 19:00-21:00 \n**Wo?** Lesesaal im Marstallgebäude, TIB\n\n",
243
+ # "# 7. Workshop Retrodigitalisierung\n\nThema: Digitalisierte Sammlungen präsentieren – Konzeptionierung, Darstellung\nund Vermittlung\n\nDer siebte Workshop Retrodigitalisierung findet am 20.03.2025 und 21.03.2025 bei\nZB MED – Informationszentrum Lebenswissenschaften in Köln statt. Er richtet\nsich an Praktiker:innen, die sich in Bibliotheken mit der Retrodigitalisierung\nbefassen. Wie in den Vorjahren bietet der Workshop ein breites Spektrum an\ninteressanten Vorträgen zur Praxis der Retrodigitalisierung. Dafür brauchen\nwir Sie und Ihre Einreichungen!\n\nIm Fokus des nächsten Workshops steht die zeitgemäße Präsentation\ndigitalisierter Sammlungen. Das Programm widmet sich insbesondere den Themen\nKonzeptionierung, Darstellung und Vermittlung von digitalisierten Sammlungen\nund Beständen über die Präsentationsplattformen der Einrichtungen rund um die\nNutzung von Digitalisaten.\n\nDer Call for Presentations läuft noch bis zum 18.10.2024. Wir freuen uns\nauf Ihren Beitrag!\n\nDer Workshop Retrodigitalisierung wird gemeinsam von den drei deutschen\nZentralen Fachbibliotheken TIB – Leibniz-Informationszentrum Technik und\nNaturwissenschaften, ZB MED – Informationszentrum Lebenswissenschaften und ZBW\n– Leibniz-Informationszentrum Wirtschaft sowie der Staatsbibliothek zu Berlin\n– Preußischer Kulturbesitz durchgeführt.\n\n**Wann?** 20.03.2025 - 21.03.2025 \n**Wo?** ZB MED in Köln\n\n",
244
+ # "# „ACM WSDM 2025“: renommierte Konferenz zu Websuche und Data Mining in\nHannover\n\nWissenschaftlicher Austausch und technologische Innovation im Fokus der 18.\nACM International Conference on Web Search and Data Mining\n\nDie 18. ACM International Conference on Web Search and Data Mining (WSDM 2025)\nwird vom 10.03.2025 - 14.03.2025 in Hannover stattfinden. Die WSDM zählt zu den\nführenden Konferenzen in den Bereichen Websuche, Data Mining, Maschinelles\nLernen und Künstliche Intelligenz. Sie bietet eine Plattform, auf der weltweit\nführende Wissenschaftler:innen, Fachleute und Praktiker:innen ihre neuesten\nForschungsergebnisse präsentieren und zukunftsweisende Ideen austauschen\nkönnen.\n\nDie Konferenz wird sich auf ein breites Spektrum aktueller Themen\nkonzentrieren, darunter:\n\n * Web of Things, ortsunabhängige und mobile Datenverarbeitung (Ubiquitous and Mobile Computing) \n * Datenschutz, Fairness, Interpretierbarkeit \n * Soziale Netzwerke \n * Intelligente Assistenten \n * Crowdsourcing und menschliche Datenverarbeitung \n\n",
245
+ # "# Infoveranstaltung für geistliche Mütter und Väter\n\n**Als Kirche wollen wir uns in die junge Generation investieren und sie\nfördern. Dazu gebraucht Gott reife geistliche Mütter und Väter.**\n\nVon **Christliches Zentrum Darmstadt**\n\n## Datum und Uhrzeit\n\nSo. 08.12.2024 12:15 - 13:15 CET\n\n## Veranstaltungsort\n\nChristliches Zentrum Darmstadt\n\nRöntgenstraße 18 64291 Darmstadt\n\n## Zu diesem Event\n\n**Infoveranstaltung für geistliche Mütter und Väter**\n\nAls Kirche wollen wir uns in die junge Generation investieren und sie fördern.\nDazu gebraucht Gott reife geistliche Mütter und Väter. Wenn du im altern von\nüber 55 Jahren bist und einen Unterschied im Leben einer jungen Person machen\nmöchtest, bist du herzlich zu dieser Infoveranstaltung eingeladen.\n\nWir wollen uns gemeinsam austauschen was unsere ältere Generation auf ihrer\nReise mit Gott benötigt und welches Erbe Gott ihnen gegeben hat. Zudem\nsprechen wir über das kommende Jahr und welche Schritte wir gehen dürfen,\ndamit die junge Generation fest in Jesus verwurzelt ist.\n\nWir starten mit einem kleinen Mittagessen und laden dich herzlich ein.\n\n",
246
+ # "**HEILIGABEND | FILM-GOTTESDIENST**\n\nErlebe einen besonderen Film-Gottesdienst mit ansprechendem Programm. Es\nerwartet dich eine Atmosphäre mit weihnachtlichen Liedern und einer\nermutigenden Botschaft.\n\n**24.12.2025 um 16:00**\n\nDer Film \"Klaus\" zeigt, dass kleine Gesten der Freundlichkeit und des Gebens\neine große positive Wirkung auf die Welt haben können. Es wird deutlich, wie\nselbst ein einzelner, gutherziger Mensch eine Kettenreaktion auslösen kann,\ndie das Leben anderer verbessert und Konflikte überwindet.\n\n**Infos für Familien: \n** Der Gottesdienst ist für die gesamte Familie geeignet und dauert ca. eine\nStunde. Es gibt kein extra Kids Programm.\n\n",
247
+ # "# Termin für öffentliche Besichtigung\n\n07.01.2025\n\n * Am 01.07.2025 \n * Von 18:00 bis Uhr \n * Tasköprüstraße 10 (ehemalige Selgros-Markthalle) \n * Termin im Kalender speichern \n\nIn einer Informationsveranstaltung wurde bereits über die Planung zur\nErrichtung eines Notstandortes für die Unterbringung Asyl- und/oder\nSchutzsuchender in der ehemaligen Selgros-Markthalle im Bezirk Altona\ninformiert. Nun steht der Termin für die öffentliche Besichtigung fest.\n\nInformationen zum Notstandort gibt es außerdem in der Präsentation , die im\nRahmen der Informationsveranstaltung gezeigt worden ist.\n\nErgänzende Informationen zum Standort und den Terminen finden Sie auch in der\nNachbarschaftsinformation von Fördern & Wohnen (November 2024).\n\n",
248
+ # "# Cornelia Poletto Palazzo\n\n**Wann?** 07.11.2024 - 09.03.2025 \n**Wo?** Spiegelpalast in Hamburg-Altona, Waidmannstraße 26\n\nIn diesem Jahr geht der Cornelia Poletto Palazzo bereits in die neunte\nSpielzeit in Hamburg. Neben einem neuen Menü und neuem Programm erwartet Gäste\nder Dinner-Show auch ein neuer Standort: In der Saison 2024/25 ist der\nSpiegelpalast vom 07.11.2024 - 09.03.2025 in Hamburg-Altona zu\nfinden.\n\nTickets sind hier erhältlich: **Tickets buchen**\n\n## Das Menü von Cornelia Poletto\n\nDie Hamburger Starköchin Cornelia Poletto hat für die Dinner-Show erneut ein\nVier-Gänge-Menü entworfen. Als Vorspeise werden ein Insalata nizzarda mit\nGrenaille-Kartoffeln, Thunfisch-Tatar, Artischocke und Kräutersud gereicht.\nDen Zwischengang bildet eine grüne Minestrone mit Burratacreme, Pistazien und\nMinzöl. Als Hauptgang folgt ein „Brasato di Festa“, ein italienischer, in\nBarolo geschmorter Rinderbraten mit cremiger Polenta, Balsamico-Apfel-Rotkohl\nund Maronen. Zum Schluss wird als Dessert die „Zitronen-Jette“ serviert:\nCrostata mit Amalfizitrone, Vanillecreme und Joghurteis. Für Vegetarier*innen\nsteht ein alternatives Menü zur Verfügung.\n\n## Das Programm: „Family Affairs“\n\nNicht nur das Menü und der Standort sind neu: Auch das Programm zeigt sich in\nneuem Gewand. Unter dem Namen „Family Affairs“ erschafft das Ensemble den\nSchauplatz einer vermeintlich gewöhnlichen Familienfeier. Eine über die ganze\nWelt verteilte, ungleiche und meist auch uneinige Verwandtschaft trifft sich\nzu einer spannungsreichen und amüsanten Zusammenkunft. In diesem Rahmen können\ndie Zuschauer*innen sich auf Akrobatik, Comedy, Slapstick und Live-Musik\nfreuen.\n\n## Über Palazzo\n\nDer Cornelia Poletto Palazzo ist zum neunten Mal in Hamburg. Palazzo-Shows mit\nMenüs anderer Köch*innen finden in Berlin, Nürnberg, Stuttgart und Wien statt.\nDas Konzept von Zirkus- bzw. Varieté-Shows, kombiniert mit einem gehobenen\ngastronomischen Angebot in der Atmosphäre eines Spiegelpalastes geht auf den\nSchweizer Produzenten und Gründer des „Zirkus Aladin“ Ueli Hirzel zurück.\nHirzel bereiste mit seinem Zirkus ab 1981 die Schweiz. 1990 brachten Bernhard\nPaul, Hans-Peter Wodarz und Alfons Schuhbeck das Konzept nach Deutschland.\n\n",
249
+ # "# Leonardo da Vinci in Hamburg\n\nNach dem großen Erfolg der immersiven Show Tutanchamun startete am 17.09.2024 die neue Ausstellung \"Leonardo da Vinci – uomo universale\" in\nHamburg.\n\nLeonard da Vinci - uomo universalis\n\nAlegria Exhibition GmbH\n\n## Eine Reise durch das Leben des Genies\n\nMit einer aufwändigen immersiven Inszenierung nimmt die Ausstellung die\nBesucherinnen und Besucher mit auf eine Reise durch das Leben des Genies.\nGezeigt werden unter anderem Nachbildungen seiner zahlreichen Erfindungen und\nalle 16 Bilder, die ihm heute sicher zugeschrieben werden können. Das\nHerzstück der Ausstellung ist die immersive Inszenierung. Mittels Projektoren\nerwachen Werk und Welt des Universalgenies zu neuem Leben.\n\n**Tickets sind hier erhältlich:\n\nTickets buchen*\n\n**\n\n## Erster unter den uomini universali\n\nLeonardo da Vinci (1452 - 1519) hatte durchaus berühmte Zeitgenossen, wie\nMichelangelo, Raffael oder Botticelli, doch niemand war so vielseitig.\n\nLeonardo war ein Universalgenie, war begnadeter Künstler, Wissenschaftler,\nIngenieur und Erfinder. Erster unter den uomini universali (Erster unter den\nUniversalmenschen)wurde Leonardo genannt. In der Renaissance beschrieb man so\ndas Idealbild eines Menschen.\n\n## Berühmte Gemälde und Erfindungen\n\nZu Leonardos berühmtesten Gemälden zählen die _Dame mit Hermelin, Das letzte\nAbendmahl, Mona Lisa_ oder der _Vitruvianische Mensch._ Zu seinen Erfindungen\nzählen Fluggeräte, ein Helikopter, Fallschirm, mechanische Roboter, Panzer und\nvieles mehr.\n\n## Info-Spalte\n\n## Kontakt\n\nGaußhöfe\n\n * Gaußstraße 190 22765 Hamburg \n\nEmpfohlene Besuchsdauer: ca. 65-75 Minuten\n\n * +49 40 391771 \n\n * Webauftritt aufrufen \n\n * Kontakt speichern \n\n * Adresse auf Karte anzeigen \n\n * Anfahrt \n\n### Termine\n\nTermin\n\n 01.09.2025 - 26.12.2024 \n\n### Altersempfehlung:\n\nAltersempfehlung:\n\nAlle sind willkommen! Kinder unter 14 Jahren müssen in Begleitung ihrer\nEltern sein.\n\n### Barrierefreiheit\n\nBarrierefreiheit\n\nDie Ausstellung ist barrierefrei.\n\nDisclaimer\n\n### *Hinweis\n\n*Über die Einbindung dieses mit *Sternchen markierten Angebots erhalten wir beim Kauf möglicherweise eine Provision vom Händler. Die gekauften Produkte werden dadurch für Sie als Nutzerinnen und Nutzer nicht teurer. \n\n",
250
+ # "# MJ – Das Michael Jackson Musical\n\nDas Erfolgsmusical über den Ausnahmekünstler Michael Jackson ist seit Dezember\n2024 in Hamburg zu sehen! Tickets und Hotel können Sie hier bequem online\nbuchen.\n\nBenét Monteiro als Michael Jackson in MJ – Das Michael Jackson Musical.\n\nMatthew Murphy / Stage Entertainment\n\n## Inhaltsverzeichnis\n\n * Tickets buchen \n\n * Tickets und Hotel buchen \n\n * MJ – Das Michael Jackson Musical in Hamburg \n\n * Die Entstehung der „Dangerous“-Welttournee \n\n## **Tickets buchen**\n\nErleben Sie das mehrfach ausgezeichnete Musical MJ – Das Michael Jackson\nMusical.\n\nTickets buchen ab 63,99€*\n\n## **Tickets und Hotel buchen**\n\nSie möchten Ihren Musical-Besuch mit einer Reise nach Hamburg verbinden? Hier\nkönnen Sie Ihre Tickets und Hotelübernachtung im Paket buchen:\n\nReisepaket: MJ – Das Michael Jackson Musical ab 134,60€*\n\nAlle Vorteile auf einen Blick:\n\n * Übernachtung im ausgewählten Hotel inkl. Frühstück, Zusatznächte buchbar \n * Musical-Ticket in der gewählten Preiskategorie \n * Hamburg Card (3 Tage) – Ihr Entdeckerticket für freie Fahrt mit Bus und Bahn im Wert von 31,90€ \n\n## MJ – Das Michael Jackson Musical in Hamburg\n\nMit Hits wie \"Billy Jean\", \"Beat it\" oder \"Thriller\" wurde Michael Jackson\nweltberühmt. Sein künstlerisches Talent und Charisma werden in dem Broadway-\nMusical über den Entertainer gewürdigt. Das Stück erhielt vier Tony Awards,\neine Grammy Nominierung für das Cast Album und hat bereits fast eine Million\nZuschauer*innen in New York begeistert.\n\nAm 01.12.2024 feierte MJ – Das Michael Jackson Musical jetzt seine\nDeutschlandpremiere! Das Stage Theater an der Elbe ist die Bühne für das\nMusical-Spektakel über die Musiklegende Michael Jackson.\n\n## Trailer für MJ – Das Michael Jackson Musical\n\n### Youtube Video aktivieren\n\nMit dem Laden des Videos akzeptierst Du die\n\naktuelle Datenschutzerklärung\n\nDieses Video aktivieren\n\nZu den Datenschutz-Bestimmungen\n\n## Die Entstehung der „Dangerous“-Welttournee\n\nRund um die Entstehungsgeschichte der 1992 er „Dangerous“-Welttournee zeigt MJ\n– Das Michael Jackson Musical einige der beliebtesten und meistverkauften\nSongs der Musikgeschichte überhaupt. Während der Proben zur Tournee nimmt\nMichael Jackson das Publikum mit auf eine Reise durch sein Leben. Das Publikum\nkann sich auf die Moves und den unverwechselbaren Sound des Stars freuen –\nerhält aber auch seltene Einblicke in den Teamgeist und die schöpferische\nKraft, die Michael Jackson zur Legende werden ließen.\n\nFür die deutsche Produktion werden die Songs von Michael Jackson im englischen\nOriginal aufgeführt, während die Dialoge ins Deutsche übersetzt sind. Für die\nBesetzung der Hauptrolle in MJ – Das Michael Jackson Musical fanden\numfangreiche Castings statt. Am Ende konnte sich Musicaldarsteller Benét\nMonteiro durchsetzen. Der gebürtige Brasilianer steht als Michael Jackson auf\nder Musicalbühne.\n\n## Termine für MJ – Das Michael Jackson Musical\n\n## Anfahrt\n\nMJ – Das Michael Jackson Musical | Stage Theater an der Elbe \n\n * [Norderelbstraße 8 \n\n20457 Hamburg](#)\n\n * Kontakt speichern \n\n * Adresse auf Karte anzeigen \n\n * Anfahrt \n\n### Termine\n\nTermin\n\nPremiere am 01.12.2024\n\n",
251
+ # "# Liedernachmittag\n\nLieder von R. Schubert, R. Franz, A. Webern, H. Wolf\n\n© MAK/LHH\n\nVortrag im Museum August Kestner\n\nMonika Abel, Sopran / Kathrin Isabelle Klein, Klavier\n\nTermine\n\n01.11.2025 ab 16:00\n\nOrt\n\nMuseum August Kestner\n\nPlatz der Menschenrechte 3\n\n30159 Hannover\n\nKonzertkarten: [email protected] und Tageskasse ab 15:00 im Museum\n\nUnter der Schirmherrschaft von Kammersängerin Helen Donath.\n\nBis zu viermal im Jahr laden wir zu einem Liedernachmittag im Museum ein.\n\nIn Zusammenarbeit mit: Lohmann-Stiftung für Liedgesang e.V., Hannover;\nFreundes- und Förderkreis des Museum August Kestner „Antike & Gegenwart e.V.“\n\n",
252
+ # "## Die goldene Stadt\n\n**Rom im Spiegel seiner Medaillen**\n\n© MAK/LHH\n\nVortrag im Museum August Kestner\n\nMit Dr. Martin Hersch (München)\n\nIn Kooperation mit dem Freundeskreis Antike und Gegenwart\n\n**Termine**\n\n15.01.2025 ab 18:00\n\n**Ort**\n\nMuseum August Kestner\n\nPlatz der Menschenrechte 3\n\n30159 Hannover\n\n**Eintritt**\n\n5,00 €\n\n**ermäßigter Eintritt**\n\n4,00 €\n\n",
253
+ # "###\n\nVeranstaltungen\n\n# Stadtansichten\n\n(Reise-)Geschichten zu Münzen aus Antike und Barock\n\n© Historisches Museum Hannover\n\nPostkarte Gruß aus Hannover, 1954\n\nErleben Sie bei einem Besuch der Sonderausstellung Städtetrip vielfältige und\nspannende Lyrik und Prosa verschiedener Autor*innen, nicht nur zum Thema\nReisen. Ausgewählt und vorgetragen von der Literarischen Komponistin und\nRezitatorin Marie Dettmer.\n\nTermine\n\n01.12.2025 ab 14:00-15:00\n\nOrt\n\nLandeshauptstadt Hannover\n\nTrammplatz 2\n\n30159 Hannover\n\n",
254
+ # "# Finissage der Sonderausstellung Bartmann, Bier und Tafelzier. Steinzeug in\nder niederländischen Malerei\n\n© Museum August Kestner\n\nDiverse Krüge aus der Ausstellung\n\nMit Kuratorinnenführung\n\n**Termine**\n\n18.01.2025 ab 11:00-18:00\n\n**Ort**\n\nMuseum August Kestner\n\nPlatz der Menschenrechte 3\n\n30159 Hannover\n\n",
255
+ # "# Nikolausmarkt\n\n## Veranstaltungsinformationen\n\n 01.12.2024 – 08.12.2024 \n\n 2. Kulturhof \n\n© Stadt Bottrop\n\nWie in jedem Jahr organisiert das Kulturamt am zweiten Adventswochenende den\nNikolausmarkt im Kulturhof und im Umfeld des Kulturzentrums. Dabei gibt es ein\nbuntes Programm für die ganze Familie. Für die Kleinen gibt's die Möglichkeit,\nWeihnachtsgeschenke zu basteln. Das Spielmobil \"Rollmobs\" des Jugendamtes ist\nauch mit dabei und bietet jede Menge Bastel-, Back- und Nähaktionen an. In der\nLebendigen Bibliothek gibt es ein Lese- und Bastelangebot für Kinder und auch\ndie MEWA Nähstube ist hier zu finden.\n\nPrivate Kunsthandwerkerinnen und Kunsthandwerker geben einen Einblick in ihr\nSchaffen und verkaufen selbstgestalteten Schmuck, Strick- und Filzwaren. Auch\ngemeinnützige Einrichtungen sind mit von der Partie und verkaufen\nselbstgemachte Produkte. Ein Holzschnitzer aus Kirchhellen stellt zahlreiche\nHolzarbeiten aus und zeigt auch live, wie verschiedene Motive hergestellt\nwerden.\n\nIm Kulturhof wird wieder das RWW Weihnachtshaus aufgebaut, wo es heiße und\nkalte Getränke gibt. Außerdem gibt es ein weihnachtlich-gastronomisches\nAngebot auf dem Vorplatz des Kulturzentrums.\n\nDer ELE Winter-Express zieht seine Runden rund um das Kulturzentrum August\nEverding.\n\nIm B12 findet der Kunstmarkt „ARTvent“ statt, bei dem Bottroper KünstlerInnen\nihre Werke ausstellen und verkaufen.\n\nAlle kulturellen und spielerischen Angebote auf dem Nikolausmarkt sind\nkostenlos.\n\n* * *\n\n## Programm\n\n**Der Nikolaus kommt am Freitag um 17:00!**\n\n**Filmforum:** \nWeihnachtskino \n\"Arthur Weihnachtsmann\" \nSa 12.00 und 16:00, So 12:00 \nKindertheater \nSa und So 15:00 \n**B12:** ARTvent \n**Lebendige Bibliothek:** \nLesungen und Basteln \nMEWA Nähstube \n**Kulturhof:** \nKunsthandwerk, Sterne basteln \n**Außerdem:** Der ELE Winter-Express \nDas Spielmobil \nMusikalische Walkacts \nRiesenrad & Karussell\n\n## Informationen\n\n| Beginn | Freitag 16:00-20:00 / Samstag 11:00-20:00 / Sonntag 11:00-18:00 | | --- | --- | \n\n## Veranstaltungsort\n\nKulturhof\n\n## Veranstalter\n\nKulturamt der Stadt Bottrop\n\n",
256
+ # "# Verleihung Erlanger Theaterpreis\n\nTheater in der Garage\n\nEinmal jährlich vergibt der Förderverein den Erlanger Theaterpreis für\nherausragende künstlerische Leistungen am Schauspiel Erlangen.\n\n## Daten\n\nSo.\n\n8.12.\n\nkeine Anmeldung nötig\n\nTheater in der Garage\n\n19:00\n\nEintritt frei\n\n## Infos\n\nMehr Infos zum Förderverein Theater Erlangen.\n\n",
257
+ # "# 🎉 SILVESTER 2024 IM FOODKLUB 🎉\n\nFeiern Sie mit uns ins neue Jahr mit einem exquisiten levantinischen\nFestbuffet! Was Sie erwartet: • Reichhaltiges levantinisches Buffet • 48€ pro\nPerson • Erlesene Weine aus der Region • Erstklassiger fränkischer Secco •\nOptional: Champagner (auf Vorbestellung) ⚠️ Limitierte Plätze verfügbar!\nSichern Sie sich jetzt Ihren Tisch für einen unvergesslichen Jahresabschluss.\nReservierung unter: 📞 09131/9171462 📧 [email protected] Foodklub Ebrardstr.\n30, 91054 Erlangen\n\n",
258
+ # "PIANOKLÄNGE & Herzgeschichten So, 15.12.2024\n===================================================\n\nVeranstalter: Stiftung Gemeinsam für Halle\n\nWo findet diese Veranstaltung statt? Lichthaus Halle \nDreyhauptstraße 3 \n06108 Halle (Saale)\n\nWann findet diese Veranstaltung statt? So, 15.12.2024\n\nBeginn: 15:00\n\nEinlass: 14:30\n\nZum Kalender hinzufügen\n\n### Tickets\n\n#### Ticket (Vorverkauf)\n\nAktuell verfügbar: 21\n\n18,00 € inkl. 7% MwSt.\n\n\\-\n\n\\+\n\nZur Kasse\n\n",
259
+ #
260
+ # ]
261
+ #
262
+ # # texts = TEXTS
263
+ #
264
+ #
265
+ # date_time_extractor = DateTimeExtractor()
266
+ #
267
+ # for text in texts:
268
+ # print("*"*100)
269
+ # print(text)
270
+ # dates = date_time_extractor.extract(text)
271
+ # print(dates)
272
+ # print("*"*100)
273
+ #
274
+ # # segments = date_time_extractor.text_segmentation(text)
275
+ # # # Ausgabe der finalen Gruppen
276
+ # # print("*"*100)
277
+ # # print(text)
278
+ # # print("\n\n")
279
+ # # for segment in segments:
280
+ # # print("Relation: ", " ".join([element.text for element in segment]))
281
+ # # print("*" * 100)
282
+
src/nlp/playground/pipelines/date_extractor_v2.py ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pickle
2
+
3
+ import spacy
4
+ from spacy.util import compile_suffix_regex, compile_infix_regex
5
+ from spacy.tokenizer import Tokenizer
6
+ from spacy.language import Language
7
+
8
+ from src.nlp.data.test_texts import TEXTS
9
+ from src.nlp.playground.textclassification import ZeroShotClassifier, CustomMode
10
+ from src.utils.helpers import normalize_data
11
+ from src.utils.markdown_processing.CustomMarkdownAnalyzer.MarkdownAnalyzer import MarkdownAnalyzer
12
+ from spacy.tokens import Span
13
+
14
+ class NLPProcessor:
15
+ def __init__(self, language="de"):
16
+ self.nlp = spacy.blank(language)
17
+ self._configure_pipeline()
18
+
19
+ def _configure_pipeline(self):
20
+ self.nlp.add_pipe("sentencizer", config={"punct_chars": [".", "!", "?"]})
21
+ self.nlp.add_pipe("custom_sentence_boundary", after="sentencizer")
22
+ self._setup_tokenizer()
23
+ self._setup_entity_ruler()
24
+
25
+ def _setup_tokenizer(self):
26
+ suffixes = list(self.nlp.Defaults.suffixes) + [r"\.", r"\,"]
27
+ infixes = list(self.nlp.Defaults.infixes) + [r"(?<=\d)\.(?=\d)", r"(?<=\d)\:(?=\d)"]
28
+
29
+ suffix_re = compile_suffix_regex(suffixes)
30
+ infix_re = compile_infix_regex(infixes)
31
+
32
+ self.nlp.tokenizer = Tokenizer(self.nlp.vocab, suffix_search=suffix_re.search, infix_finditer=infix_re.finditer)
33
+
34
+ def _setup_entity_ruler(self):
35
+ ruler = self.nlp.add_pipe("entity_ruler")
36
+ ruler.add_patterns([
37
+ {"label": "DATE", "pattern": [{"SHAPE": "dd"}, {"ORTH": "."}, {"SHAPE": "dd"}, {"ORTH": "."}, {"SHAPE": "dddd"}]},
38
+ {"label": "TIME", "pattern": [{"SHAPE": "dd"}, {"ORTH": ":"}, {"SHAPE": "dd"}]}
39
+ ])
40
+
41
+ @staticmethod
42
+ @Language.component("custom_sentence_boundary")
43
+ def custom_sentence_boundary(doc):
44
+ for token in doc[:-1]:
45
+ if token.text.endswith(".") and token.nbor(1).is_digit:
46
+ doc[token.i + 1].is_sent_start = False
47
+ return doc
48
+
49
+ class Token:
50
+ def __init__(self, text):
51
+ self.text = text
52
+
53
+ def __repr__(self):
54
+ return self.text
55
+
56
+
57
+ class EntityGroup(Token):
58
+ def __init__(self, sequence):
59
+ self.sequence = sequence
60
+ text = " ".join([s.text for s in sequence])
61
+ super().__init__(text)
62
+
63
+ class DateTime:
64
+ def __init__(self):
65
+ self._startDate = None
66
+ self._endDate = None
67
+ self._startTime = None
68
+ self._endTime = None
69
+ self._context = None
70
+
71
+ @property
72
+ def start_date(self):
73
+ return self._start_date
74
+
75
+ @start_date.setter
76
+ def start_date(self, value):
77
+ self._start_date = value
78
+
79
+ @property
80
+ def end_date(self):
81
+ return self._end_date
82
+
83
+ @end_date.setter
84
+ def end_date(self, value):
85
+ self._end_date = value
86
+
87
+ @property
88
+ def start_time(self):
89
+ return self._start_time
90
+
91
+ @start_time.setter
92
+ def start_time(self, value):
93
+ self._start_time = value
94
+
95
+ @property
96
+ def end_time(self):
97
+ return self._end_time
98
+
99
+ @end_time.setter
100
+ def end_time(self, value):
101
+ self._end_time = value
102
+
103
+ @property
104
+ def context(self):
105
+ return self._con
106
+
107
+ @context.setter
108
+ def context(self, value):
109
+ self.context = value
110
+
111
+
112
+
113
+ class DateTimeExtractor(NLPProcessor):
114
+ def __init__(self):
115
+ super().__init__()
116
+ self.date_classifier = self._load_classifier()
117
+
118
+ @staticmethod
119
+ def _load_classifier():
120
+ """Lädt den trainierten Klassifikator aus einer Datei."""
121
+ with open("../models/title_classifier.pkl", "rb") as f:
122
+ return pickle.load(f)
123
+
124
+ def __extract_tokens_between_entities(self, doc):
125
+ entities = list(doc.ents) # Alle Entitäten im Text
126
+ tokens_between = []
127
+
128
+ if len(entities) < 2:
129
+ return tokens_between # Wenn es weniger als 2 Entitäten gibt, gibt es kein "Dazwischen"
130
+
131
+ for i in range(len(entities) - 1):
132
+ ent1 = entities[i]
133
+ ent2 = entities[i + 1]
134
+
135
+ # Speichere nur Tokens zwischen ent1 und ent2
136
+ tokens_between.append(doc[ent1.end:ent2.start])
137
+
138
+ return tokens_between
139
+
140
+ def __find_strong_relations(self, sequence):
141
+ sequence = [s for s in sequence if len(s.text)>1]
142
+ strong_relations = []
143
+ curr_relation = [sequence[0]]
144
+ sequence.pop(0)
145
+ for i,token in enumerate(sequence):
146
+ if isinstance(token, Span):
147
+ label = token.label_
148
+ curr_relation.append(token)
149
+ elif i+1 < len(sequence):
150
+ if isinstance(sequence[i+1], Span):
151
+ strong_relations.append(curr_relation)
152
+ curr_relation=[token]
153
+ else:
154
+ curr_relation.append(token)
155
+ strong_relations.append(curr_relation)
156
+
157
+ return strong_relations
158
+
159
+ def __preprocess(self, text):
160
+ text = normalize_data(text)
161
+ replacements = {" und ": " + ", " bis ": " - ", " um ": " ", " ab ": " "}
162
+ for old, new in replacements.items():
163
+ text = text.replace(old, new)
164
+ return text
165
+
166
+ def date_time_segmentation(self, text):
167
+ analyzer = MarkdownAnalyzer(text)
168
+ md_elements = analyzer.identify_all().get("block_elements")
169
+ segments = []
170
+ date_times = []
171
+ for element in md_elements:
172
+ text = self.__preprocess(element.text)
173
+ sents = self.nlp(text).sents
174
+ for sent in sents:
175
+ doc = self.nlp(sent.text)
176
+ if doc.ents:
177
+ date_time = DateTime()
178
+ if len(doc.ents) == 1:
179
+ entity = doc.ents[0]
180
+ if entity.label_ == "DATE":
181
+ date_time.start_date = doc.text
182
+ else:
183
+ date_time.start_time = doc.text
184
+ date_time.context = sent.text
185
+ # before = Token(text=doc[:entity.start].text) if entity.start > 0 else None
186
+ # entity_token = entity
187
+ # after = Token(text=doc[entity.end:].text) if entity.end < len(doc) else None
188
+ # segment = [t for t in [before, entity_token, after] if t]
189
+ # segments.append(segment)
190
+ date_times.append(date_time)
191
+ continue
192
+
193
+ tokens_between_entities = self.__extract_tokens_between_entities(doc)
194
+
195
+ sequence = [Token(text=doc[:doc.ents[0].start].text)]
196
+ entities = list(doc.ents)
197
+ # entities = [Entity(e.text, e.label_) for e in doc_small.ents]
198
+ sequence.append(entities[0])
199
+ if entities[0].label_ == "DATE":
200
+ date_time.start_date = doc.text
201
+ else:
202
+ date_time.start_time = doc.text
203
+
204
+ for i, tokens in enumerate(tokens_between_entities):
205
+ connector = tokens.text.strip()
206
+ if len(connector) > 1:
207
+ sequence.extend([Token(text=c) for c in connector.split(",")])
208
+ else:
209
+ sequence.append(Token(connector))
210
+ sequence.append(entities[i + 1])
211
+ sequence.append(Token(doc[doc.ents[len(entities) - 1].end:].text))
212
+
213
+ strong_relations = self.__find_strong_relations(sequence)
214
+ final_relations = []
215
+ remaining_sequence = sequence[:] # Eine Kopie von `sequence` behalten
216
+
217
+ for relation in strong_relations:
218
+ complete_group = []
219
+ for r in relation:
220
+ if r.text in [s.text for s in remaining_sequence]:
221
+ i = remaining_sequence.index(r) # Index in der verbleibenden Liste finden
222
+ s = remaining_sequence[:i] # Alles davor nehmen
223
+ complete_group.extend(s) # In die aktuelle Gruppe packen
224
+ complete_group.append(r) # Das aktuelle `r` hinzufügen
225
+ remaining_sequence = remaining_sequence[i + 1:] # Kürze die Liste ab
226
+ else:
227
+ print(f"Warnung: '{r}' nicht in der verbleibenden Sequence gefunden!")
228
+
229
+ final_relations.append(complete_group)
230
+ segments.extend(final_relations)
231
+ return segments
232
+
233
+ def extract(self,text):
234
+ segments = self.date_time_segmentation(text)
235
+ for segment in segments:
236
+ # doc = self.nlp(" ".join([s.text for s in segment]))
237
+ date_entities = [token for token in segment if isinstance(token, spacy.tokens.Span) and token.label_ == "DATE"]
238
+ time_entities = [token for token in segment if isinstance(token, spacy.tokens.Span) and token.label_ == "TIME"]
239
+ if date_entities:
240
+ print("Textsegment: ", " ".join([s.text for s in segment]))
241
+ date_class = self.date_classifier(" ".join([s.text for s in segment]))
242
+ best_label, best_score = max(date_class.items(), key=lambda x: x[1])
243
+ if best_label == "EVENT_DATE" and best_score > 0.8:
244
+ print("Datum: ",date_entities)
245
+ print(f"Label: {best_label}, Score: {best_score:.4f}")
246
+ if time_entities:
247
+ print("Uhrzeit:", time_entities)
248
+ elif time_entities:
249
+ print(" ".join([s.text for s in segment]))
250
+ # Todo Time classification
251
+ print("Andere Uhrzeit:", time_entities)
252
+
253
+
254
+ texts = [
255
+ "Tickets können ab dem 03.12.2020 erworben werden",
256
+ "Am 01.01.2020 findet die Veranstaltung statt, Einlass ist um 15:00",
257
+ "** Die Veranstaltung beginnt am 24.12.2025 um 16:00, Einlass ist ab 15:30. **",
258
+ "Wann: 03.03.2020 16:00, Einlass: 15:00",
259
+ "Wann? 25.04.2025 - 23.05.2020, 15:00 bis 16:00. Abendkasse ab 14:30",
260
+ """
261
+ Termine: \n
262
+ 23.05.2024 19:00 \n
263
+ 22.06.2022 18:00 \n
264
+ 23.12.2030 17:00-18:00
265
+ """,
266
+ "# Technik-Salon an der TIB: „FLY ROCKET FLY“\n\nÜber den Aufstieg und Fall des Raketenpioniers Lutz Kayser – ein Film- und\nGesprächsabend mit dem Dokumentarfilmer Oliver Schwehm\n\nEin Start-Up aus dem Schwäbischen, 170 Millionen D-Mark Wagniskapital, ein\nprivates Testgelände im afrikanischen Dschungel – so trat Lutz Kayser in den\n1970 er-Jahren an, die Raumfahrt zu revolutionieren.\n\nWarum das gut hätten klappen können und schließlich doch scheiterte, schildert\nOliver Schwehm in seiner bildreichen Dokumentation. Im Rahmen der\nJubiläumstour zu Ehren von Lutz Kayser („50 Jahre OTRAG – Oribital Transport\nund Raketen AG“) machen Film und Regisseur am 05.12.2024 im Technik-\nSalon Station in der TIB in Hannover. Der Eintritt für die Veranstaltung „FLY\nROCKET FLY“ ist frei (Spendenbox).\n\n**Wann?** 05.12.2024, 19:00-21:00 \n**Wo?** Lesesaal im Marstallgebäude, TIB\n\n",
267
+ "# 7. Workshop Retrodigitalisierung\n\nThema: Digitalisierte Sammlungen präsentieren – Konzeptionierung, Darstellung\nund Vermittlung\n\nDer siebte Workshop Retrodigitalisierung findet am 20.03.2025 und 21.03.2025 bei\nZB MED – Informationszentrum Lebenswissenschaften in Köln statt. Er richtet\nsich an Praktiker:innen, die sich in Bibliotheken mit der Retrodigitalisierung\nbefassen. Wie in den Vorjahren bietet der Workshop ein breites Spektrum an\ninteressanten Vorträgen zur Praxis der Retrodigitalisierung. Dafür brauchen\nwir Sie und Ihre Einreichungen!\n\nIm Fokus des nächsten Workshops steht die zeitgemäße Präsentation\ndigitalisierter Sammlungen. Das Programm widmet sich insbesondere den Themen\nKonzeptionierung, Darstellung und Vermittlung von digitalisierten Sammlungen\nund Beständen über die Präsentationsplattformen der Einrichtungen rund um die\nNutzung von Digitalisaten.\n\nDer Call for Presentations läuft noch bis zum 18.10.2024. Wir freuen uns\nauf Ihren Beitrag!\n\nDer Workshop Retrodigitalisierung wird gemeinsam von den drei deutschen\nZentralen Fachbibliotheken TIB – Leibniz-Informationszentrum Technik und\nNaturwissenschaften, ZB MED – Informationszentrum Lebenswissenschaften und ZBW\n– Leibniz-Informationszentrum Wirtschaft sowie der Staatsbibliothek zu Berlin\n– Preußischer Kulturbesitz durchgeführt.\n\n**Wann?** 20.03.2025 - 21.03.2025 \n**Wo?** ZB MED in Köln\n\n",
268
+ "# „ACM WSDM 2025“: renommierte Konferenz zu Websuche und Data Mining in\nHannover\n\nWissenschaftlicher Austausch und technologische Innovation im Fokus der 18.\nACM International Conference on Web Search and Data Mining\n\nDie 18. ACM International Conference on Web Search and Data Mining (WSDM 2025)\nwird vom 10.03.2025 - 14.03.2025 in Hannover stattfinden. Die WSDM zählt zu den\nführenden Konferenzen in den Bereichen Websuche, Data Mining, Maschinelles\nLernen und Künstliche Intelligenz. Sie bietet eine Plattform, auf der weltweit\nführende Wissenschaftler:innen, Fachleute und Praktiker:innen ihre neuesten\nForschungsergebnisse präsentieren und zukunftsweisende Ideen austauschen\nkönnen.\n\nDie Konferenz wird sich auf ein breites Spektrum aktueller Themen\nkonzentrieren, darunter:\n\n * Web of Things, ortsunabhängige und mobile Datenverarbeitung (Ubiquitous and Mobile Computing) \n * Datenschutz, Fairness, Interpretierbarkeit \n * Soziale Netzwerke \n * Intelligente Assistenten \n * Crowdsourcing und menschliche Datenverarbeitung \n\n",
269
+ "# Infoveranstaltung für geistliche Mütter und Väter\n\n**Als Kirche wollen wir uns in die junge Generation investieren und sie\nfördern. Dazu gebraucht Gott reife geistliche Mütter und Väter.**\n\nVon **Christliches Zentrum Darmstadt**\n\n## Datum und Uhrzeit\n\nSo. 08.12.2024 12:15 - 13:15 CET\n\n## Veranstaltungsort\n\nChristliches Zentrum Darmstadt\n\nRöntgenstraße 18 64291 Darmstadt\n\n## Zu diesem Event\n\n**Infoveranstaltung für geistliche Mütter und Väter**\n\nAls Kirche wollen wir uns in die junge Generation investieren und sie fördern.\nDazu gebraucht Gott reife geistliche Mütter und Väter. Wenn du im altern von\nüber 55 Jahren bist und einen Unterschied im Leben einer jungen Person machen\nmöchtest, bist du herzlich zu dieser Infoveranstaltung eingeladen.\n\nWir wollen uns gemeinsam austauschen was unsere ältere Generation auf ihrer\nReise mit Gott benötigt und welches Erbe Gott ihnen gegeben hat. Zudem\nsprechen wir über das kommende Jahr und welche Schritte wir gehen dürfen,\ndamit die junge Generation fest in Jesus verwurzelt ist.\n\nWir starten mit einem kleinen Mittagessen und laden dich herzlich ein.\n\n",
270
+ "**HEILIGABEND | FILM-GOTTESDIENST**\n\nErlebe einen besonderen Film-Gottesdienst mit ansprechendem Programm. Es\nerwartet dich eine Atmosphäre mit weihnachtlichen Liedern und einer\nermutigenden Botschaft.\n\n**24.12.2025 um 16:00**\n\nDer Film \"Klaus\" zeigt, dass kleine Gesten der Freundlichkeit und des Gebens\neine große positive Wirkung auf die Welt haben können. Es wird deutlich, wie\nselbst ein einzelner, gutherziger Mensch eine Kettenreaktion auslösen kann,\ndie das Leben anderer verbessert und Konflikte überwindet.\n\n**Infos für Familien: \n** Der Gottesdienst ist für die gesamte Familie geeignet und dauert ca. eine\nStunde. Es gibt kein extra Kids Programm.\n\n",
271
+ "# Termin für öffentliche Besichtigung\n\n07.01.2025\n\n * Am 01.07.2025 \n * Von 18:00 bis Uhr \n * Tasköprüstraße 10 (ehemalige Selgros-Markthalle) \n * Termin im Kalender speichern \n\nIn einer Informationsveranstaltung wurde bereits über die Planung zur\nErrichtung eines Notstandortes für die Unterbringung Asyl- und/oder\nSchutzsuchender in der ehemaligen Selgros-Markthalle im Bezirk Altona\ninformiert. Nun steht der Termin für die öffentliche Besichtigung fest.\n\nInformationen zum Notstandort gibt es außerdem in der Präsentation , die im\nRahmen der Informationsveranstaltung gezeigt worden ist.\n\nErgänzende Informationen zum Standort und den Terminen finden Sie auch in der\nNachbarschaftsinformation von Fördern & Wohnen (November 2024).\n\n",
272
+ "# Cornelia Poletto Palazzo\n\n**Wann?** 07.11.2024 - 09.03.2025 \n**Wo?** Spiegelpalast in Hamburg-Altona, Waidmannstraße 26\n\nIn diesem Jahr geht der Cornelia Poletto Palazzo bereits in die neunte\nSpielzeit in Hamburg. Neben einem neuen Menü und neuem Programm erwartet Gäste\nder Dinner-Show auch ein neuer Standort: In der Saison 2024/25 ist der\nSpiegelpalast vom 07.11.2024 - 09.03.2025 in Hamburg-Altona zu\nfinden.\n\nTickets sind hier erhältlich: **Tickets buchen**\n\n## Das Menü von Cornelia Poletto\n\nDie Hamburger Starköchin Cornelia Poletto hat für die Dinner-Show erneut ein\nVier-Gänge-Menü entworfen. Als Vorspeise werden ein Insalata nizzarda mit\nGrenaille-Kartoffeln, Thunfisch-Tatar, Artischocke und Kräutersud gereicht.\nDen Zwischengang bildet eine grüne Minestrone mit Burratacreme, Pistazien und\nMinzöl. Als Hauptgang folgt ein „Brasato di Festa“, ein italienischer, in\nBarolo geschmorter Rinderbraten mit cremiger Polenta, Balsamico-Apfel-Rotkohl\nund Maronen. Zum Schluss wird als Dessert die „Zitronen-Jette“ serviert:\nCrostata mit Amalfizitrone, Vanillecreme und Joghurteis. Für Vegetarier*innen\nsteht ein alternatives Menü zur Verfügung.\n\n## Das Programm: „Family Affairs“\n\nNicht nur das Menü und der Standort sind neu: Auch das Programm zeigt sich in\nneuem Gewand. Unter dem Namen „Family Affairs“ erschafft das Ensemble den\nSchauplatz einer vermeintlich gewöhnlichen Familienfeier. Eine über die ganze\nWelt verteilte, ungleiche und meist auch uneinige Verwandtschaft trifft sich\nzu einer spannungsreichen und amüsanten Zusammenkunft. In diesem Rahmen können\ndie Zuschauer*innen sich auf Akrobatik, Comedy, Slapstick und Live-Musik\nfreuen.\n\n## Über Palazzo\n\nDer Cornelia Poletto Palazzo ist zum neunten Mal in Hamburg. Palazzo-Shows mit\nMenüs anderer Köch*innen finden in Berlin, Nürnberg, Stuttgart und Wien statt.\nDas Konzept von Zirkus- bzw. Varieté-Shows, kombiniert mit einem gehobenen\ngastronomischen Angebot in der Atmosphäre eines Spiegelpalastes geht auf den\nSchweizer Produzenten und Gründer des „Zirkus Aladin“ Ueli Hirzel zurück.\nHirzel bereiste mit seinem Zirkus ab 1981 die Schweiz. 1990 brachten Bernhard\nPaul, Hans-Peter Wodarz und Alfons Schuhbeck das Konzept nach Deutschland.\n\n",
273
+ "# Leonardo da Vinci in Hamburg\n\nNach dem großen Erfolg der immersiven Show Tutanchamun startete am 17.09.2024 die neue Ausstellung \"Leonardo da Vinci – uomo universale\" in\nHamburg.\n\nLeonard da Vinci - uomo universalis\n\nAlegria Exhibition GmbH\n\n## Eine Reise durch das Leben des Genies\n\nMit einer aufwändigen immersiven Inszenierung nimmt die Ausstellung die\nBesucherinnen und Besucher mit auf eine Reise durch das Leben des Genies.\nGezeigt werden unter anderem Nachbildungen seiner zahlreichen Erfindungen und\nalle 16 Bilder, die ihm heute sicher zugeschrieben werden können. Das\nHerzstück der Ausstellung ist die immersive Inszenierung. Mittels Projektoren\nerwachen Werk und Welt des Universalgenies zu neuem Leben.\n\n**Tickets sind hier erhältlich:\n\nTickets buchen*\n\n**\n\n## Erster unter den uomini universali\n\nLeonardo da Vinci (1452 - 1519) hatte durchaus berühmte Zeitgenossen, wie\nMichelangelo, Raffael oder Botticelli, doch niemand war so vielseitig.\n\nLeonardo war ein Universalgenie, war begnadeter Künstler, Wissenschaftler,\nIngenieur und Erfinder. Erster unter den uomini universali (Erster unter den\nUniversalmenschen)wurde Leonardo genannt. In der Renaissance beschrieb man so\ndas Idealbild eines Menschen.\n\n## Berühmte Gemälde und Erfindungen\n\nZu Leonardos berühmtesten Gemälden zählen die _Dame mit Hermelin, Das letzte\nAbendmahl, Mona Lisa_ oder der _Vitruvianische Mensch._ Zu seinen Erfindungen\nzählen Fluggeräte, ein Helikopter, Fallschirm, mechanische Roboter, Panzer und\nvieles mehr.\n\n## Info-Spalte\n\n## Kontakt\n\nGaußhöfe\n\n * Gaußstraße 190 22765 Hamburg \n\nEmpfohlene Besuchsdauer: ca. 65-75 Minuten\n\n * +49 40 391771 \n\n * Webauftritt aufrufen \n\n * Kontakt speichern \n\n * Adresse auf Karte anzeigen \n\n * Anfahrt \n\n### Termine\n\nTermin\n\n 01.09.2025 - 26.12.2024 \n\n### Altersempfehlung:\n\nAltersempfehlung:\n\nAlle sind willkommen! Kinder unter 14 Jahren müssen in Begleitung ihrer\nEltern sein.\n\n### Barrierefreiheit\n\nBarrierefreiheit\n\nDie Ausstellung ist barrierefrei.\n\nDisclaimer\n\n### *Hinweis\n\n*Über die Einbindung dieses mit *Sternchen markierten Angebots erhalten wir beim Kauf möglicherweise eine Provision vom Händler. Die gekauften Produkte werden dadurch für Sie als Nutzerinnen und Nutzer nicht teurer. \n\n",
274
+ "# MJ – Das Michael Jackson Musical\n\nDas Erfolgsmusical über den Ausnahmekünstler Michael Jackson ist seit Dezember\n2024 in Hamburg zu sehen! Tickets und Hotel können Sie hier bequem online\nbuchen.\n\nBenét Monteiro als Michael Jackson in MJ – Das Michael Jackson Musical.\n\nMatthew Murphy / Stage Entertainment\n\n## Inhaltsverzeichnis\n\n * Tickets buchen \n\n * Tickets und Hotel buchen \n\n * MJ – Das Michael Jackson Musical in Hamburg \n\n * Die Entstehung der „Dangerous“-Welttournee \n\n## **Tickets buchen**\n\nErleben Sie das mehrfach ausgezeichnete Musical MJ – Das Michael Jackson\nMusical.\n\nTickets buchen ab 63,99€*\n\n## **Tickets und Hotel buchen**\n\nSie möchten Ihren Musical-Besuch mit einer Reise nach Hamburg verbinden? Hier\nkönnen Sie Ihre Tickets und Hotelübernachtung im Paket buchen:\n\nReisepaket: MJ – Das Michael Jackson Musical ab 134,60€*\n\nAlle Vorteile auf einen Blick:\n\n * Übernachtung im ausgewählten Hotel inkl. Frühstück, Zusatznächte buchbar \n * Musical-Ticket in der gewählten Preiskategorie \n * Hamburg Card (3 Tage) – Ihr Entdeckerticket für freie Fahrt mit Bus und Bahn im Wert von 31,90€ \n\n## MJ – Das Michael Jackson Musical in Hamburg\n\nMit Hits wie \"Billy Jean\", \"Beat it\" oder \"Thriller\" wurde Michael Jackson\nweltberühmt. Sein künstlerisches Talent und Charisma werden in dem Broadway-\nMusical über den Entertainer gewürdigt. Das Stück erhielt vier Tony Awards,\neine Grammy Nominierung für das Cast Album und hat bereits fast eine Million\nZuschauer*innen in New York begeistert.\n\nAm 01.12.2024 feierte MJ – Das Michael Jackson Musical jetzt seine\nDeutschlandpremiere! Das Stage Theater an der Elbe ist die Bühne für das\nMusical-Spektakel über die Musiklegende Michael Jackson.\n\n## Trailer für MJ – Das Michael Jackson Musical\n\n### Youtube Video aktivieren\n\nMit dem Laden des Videos akzeptierst Du die\n\naktuelle Datenschutzerklärung\n\nDieses Video aktivieren\n\nZu den Datenschutz-Bestimmungen\n\n## Die Entstehung der „Dangerous“-Welttournee\n\nRund um die Entstehungsgeschichte der 1992 er „Dangerous“-Welttournee zeigt MJ\n– Das Michael Jackson Musical einige der beliebtesten und meistverkauften\nSongs der Musikgeschichte überhaupt. Während der Proben zur Tournee nimmt\nMichael Jackson das Publikum mit auf eine Reise durch sein Leben. Das Publikum\nkann sich auf die Moves und den unverwechselbaren Sound des Stars freuen –\nerhält aber auch seltene Einblicke in den Teamgeist und die schöpferische\nKraft, die Michael Jackson zur Legende werden ließen.\n\nFür die deutsche Produktion werden die Songs von Michael Jackson im englischen\nOriginal aufgeführt, während die Dialoge ins Deutsche übersetzt sind. Für die\nBesetzung der Hauptrolle in MJ – Das Michael Jackson Musical fanden\numfangreiche Castings statt. Am Ende konnte sich Musicaldarsteller Benét\nMonteiro durchsetzen. Der gebürtige Brasilianer steht als Michael Jackson auf\nder Musicalbühne.\n\n## Termine für MJ – Das Michael Jackson Musical\n\n## Anfahrt\n\nMJ – Das Michael Jackson Musical | Stage Theater an der Elbe \n\n * [Norderelbstraße 8 \n\n20457 Hamburg](#)\n\n * Kontakt speichern \n\n * Adresse auf Karte anzeigen \n\n * Anfahrt \n\n### Termine\n\nTermin\n\nPremiere am 01.12.2024\n\n",
275
+ "# Liedernachmittag\n\nLieder von R. Schubert, R. Franz, A. Webern, H. Wolf\n\n© MAK/LHH\n\nVortrag im Museum August Kestner\n\nMonika Abel, Sopran / Kathrin Isabelle Klein, Klavier\n\nTermine\n\n01.11.2025 ab 16:00\n\nOrt\n\nMuseum August Kestner\n\nPlatz der Menschenrechte 3\n\n30159 Hannover\n\nKonzertkarten: [email protected] und Tageskasse ab 15:00 im Museum\n\nUnter der Schirmherrschaft von Kammersängerin Helen Donath.\n\nBis zu viermal im Jahr laden wir zu einem Liedernachmittag im Museum ein.\n\nIn Zusammenarbeit mit: Lohmann-Stiftung für Liedgesang e.V., Hannover;\nFreundes- und Förderkreis des Museum August Kestner „Antike & Gegenwart e.V.“\n\n",
276
+ "## Die goldene Stadt\n\n**Rom im Spiegel seiner Medaillen**\n\n© MAK/LHH\n\nVortrag im Museum August Kestner\n\nMit Dr. Martin Hersch (München)\n\nIn Kooperation mit dem Freundeskreis Antike und Gegenwart\n\n**Termine**\n\n15.01.2025 ab 18:00\n\n**Ort**\n\nMuseum August Kestner\n\nPlatz der Menschenrechte 3\n\n30159 Hannover\n\n**Eintritt**\n\n5,00 €\n\n**ermäßigter Eintritt**\n\n4,00 €\n\n",
277
+ "###\n\nVeranstaltungen\n\n# Stadtansichten\n\n(Reise-)Geschichten zu Münzen aus Antike und Barock\n\n© Historisches Museum Hannover\n\nPostkarte Gruß aus Hannover, 1954\n\nErleben Sie bei einem Besuch der Sonderausstellung Städtetrip vielfältige und\nspannende Lyrik und Prosa verschiedener Autor*innen, nicht nur zum Thema\nReisen. Ausgewählt und vorgetragen von der Literarischen Komponistin und\nRezitatorin Marie Dettmer.\n\nTermine\n\n01.12.2025 ab 14:00-15:00\n\nOrt\n\nLandeshauptstadt Hannover\n\nTrammplatz 2\n\n30159 Hannover\n\n",
278
+ "# Finissage der Sonderausstellung Bartmann, Bier und Tafelzier. Steinzeug in\nder niederländischen Malerei\n\n© Museum August Kestner\n\nDiverse Krüge aus der Ausstellung\n\nMit Kuratorinnenführung\n\n**Termine**\n\n18.01.2025 ab 11:00-18:00\n\n**Ort**\n\nMuseum August Kestner\n\nPlatz der Menschenrechte 3\n\n30159 Hannover\n\n",
279
+ "# Nikolausmarkt\n\n## Veranstaltungsinformationen\n\n 01.12.2024 – 08.12.2024 \n\n 2. Kulturhof \n\n© Stadt Bottrop\n\nWie in jedem Jahr organisiert das Kulturamt am zweiten Adventswochenende den\nNikolausmarkt im Kulturhof und im Umfeld des Kulturzentrums. Dabei gibt es ein\nbuntes Programm für die ganze Familie. Für die Kleinen gibt's die Möglichkeit,\nWeihnachtsgeschenke zu basteln. Das Spielmobil \"Rollmobs\" des Jugendamtes ist\nauch mit dabei und bietet jede Menge Bastel-, Back- und Nähaktionen an. In der\nLebendigen Bibliothek gibt es ein Lese- und Bastelangebot für Kinder und auch\ndie MEWA Nähstube ist hier zu finden.\n\nPrivate Kunsthandwerkerinnen und Kunsthandwerker geben einen Einblick in ihr\nSchaffen und verkaufen selbstgestalteten Schmuck, Strick- und Filzwaren. Auch\ngemeinnützige Einrichtungen sind mit von der Partie und verkaufen\nselbstgemachte Produkte. Ein Holzschnitzer aus Kirchhellen stellt zahlreiche\nHolzarbeiten aus und zeigt auch live, wie verschiedene Motive hergestellt\nwerden.\n\nIm Kulturhof wird wieder das RWW Weihnachtshaus aufgebaut, wo es heiße und\nkalte Getränke gibt. Außerdem gibt es ein weihnachtlich-gastronomisches\nAngebot auf dem Vorplatz des Kulturzentrums.\n\nDer ELE Winter-Express zieht seine Runden rund um das Kulturzentrum August\nEverding.\n\nIm B12 findet der Kunstmarkt „ARTvent“ statt, bei dem Bottroper KünstlerInnen\nihre Werke ausstellen und verkaufen.\n\nAlle kulturellen und spielerischen Angebote auf dem Nikolausmarkt sind\nkostenlos.\n\n* * *\n\n## Programm\n\n**Der Nikolaus kommt am Freitag um 17:00!**\n\n**Filmforum:** \nWeihnachtskino \n\"Arthur Weihnachtsmann\" \nSa 12.00 und 16:00, So 12:00 \nKindertheater \nSa und So 15:00 \n**B12:** ARTvent \n**Lebendige Bibliothek:** \nLesungen und Basteln \nMEWA Nähstube \n**Kulturhof:** \nKunsthandwerk, Sterne basteln \n**Außerdem:** Der ELE Winter-Express \nDas Spielmobil \nMusikalische Walkacts \nRiesenrad & Karussell\n\n## Informationen\n\n| Beginn | Freitag 16:00-20:00 / Samstag 11:00-20:00 / Sonntag 11:00-18:00 | | --- | --- | \n\n## Veranstaltungsort\n\nKulturhof\n\n## Veranstalter\n\nKulturamt der Stadt Bottrop\n\n",
280
+ "# Verleihung Erlanger Theaterpreis\n\nTheater in der Garage\n\nEinmal jährlich vergibt der Förderverein den Erlanger Theaterpreis für\nherausragende künstlerische Leistungen am Schauspiel Erlangen.\n\n## Daten\n\nSo.\n\n8.12.\n\nkeine Anmeldung nötig\n\nTheater in der Garage\n\n19:00\n\nEintritt frei\n\n## Infos\n\nMehr Infos zum Förderverein Theater Erlangen.\n\n",
281
+ "# 🎉 SILVESTER 2024 IM FOODKLUB 🎉\n\nFeiern Sie mit uns ins neue Jahr mit einem exquisiten levantinischen\nFestbuffet! Was Sie erwartet: • Reichhaltiges levantinisches Buffet • 48€ pro\nPerson • Erlesene Weine aus der Region • Erstklassiger fränkischer Secco •\nOptional: Champagner (auf Vorbestellung) ⚠️ Limitierte Plätze verfügbar!\nSichern Sie sich jetzt Ihren Tisch für einen unvergesslichen Jahresabschluss.\nReservierung unter: 📞 09131/9171462 📧 [email protected] Foodklub Ebrardstr.\n30, 91054 Erlangen\n\n",
282
+ "PIANOKLÄNGE & Herzgeschichten So, 15.12.2024\n===================================================\n\nVeranstalter: Stiftung Gemeinsam für Halle\n\nWo findet diese Veranstaltung statt? Lichthaus Halle \nDreyhauptstraße 3 \n06108 Halle (Saale)\n\nWann findet diese Veranstaltung statt? So, 15.12.2024\n\nBeginn: 15:00\n\nEinlass: 14:30\n\nZum Kalender hinzufügen\n\n### Tickets\n\n#### Ticket (Vorverkauf)\n\nAktuell verfügbar: 21\n\n18,00 € inkl. 7% MwSt.\n\n\\-\n\n\\+\n\nZur Kasse\n\n",
283
+
284
+ ]
285
+
286
+ # texts = TEXTS
287
+
288
+
289
+ date_time_extractor = DateTimeExtractor()
290
+
291
+ for text in texts:
292
+ print("*"*100)
293
+ print(text)
294
+ date_time_extractor.extract(text)
295
+ print("*"*100)
296
+
297
+ # segments = date_time_extractor.text_segmentation(text)
298
+ # # Ausgabe der finalen Gruppen
299
+ # print("*"*100)
300
+ # print(text)
301
+ # print("\n\n")
302
+ # for segment in segments:
303
+ # print("Relation: ", " ".join([element.text for element in segment]))
304
+ # print("*" * 100)
305
+
src/nlp/playground/pipelines/title_extractor.py CHANGED
@@ -4,7 +4,14 @@ from src.utils.markdown_processing.CustomMarkdownAnalyzer.MarkdownAnalyzer impor
4
  from src.utils.markdown_processing.CustomMarkdownAnalyzer.MarkdownElements import Header
5
  import streamlit as st
6
  import pickle
 
 
 
 
7
 
 
 
 
8
 
9
  class TitleExtractor:
10
  def __init__(self):
@@ -50,8 +57,10 @@ class TitleExtractor:
50
  def extract_title_classy_classification(self,event_text):
51
  analyzer = MarkdownAnalyzer(event_text)
52
  identified_headers = analyzer.identify_headers()
53
- f = open("src/nlp/playground/pipelines/classifier.pkl", "rb")
54
- classifier = pickle.load(f)
 
 
55
 
56
  headers = identified_headers["Header"] if identified_headers else analyzer.identify_emphasis()
57
  if headers:
 
4
  from src.utils.markdown_processing.CustomMarkdownAnalyzer.MarkdownElements import Header
5
  import streamlit as st
6
  import pickle
7
+ import joblib
8
+ from huggingface_hub import login, hf_hub_download
9
+ import os
10
+ from dotenv import load_dotenv
11
 
12
+ load_dotenv()
13
+ token = os.getenv("HUGGING_FACE_SPACES_TOKEN")
14
+ login(token=token)
15
 
16
  class TitleExtractor:
17
  def __init__(self):
 
57
  def extract_title_classy_classification(self,event_text):
58
  analyzer = MarkdownAnalyzer(event_text)
59
  identified_headers = analyzer.identify_headers()
60
+
61
+ classifier = joblib.load(
62
+ hf_hub_download(repo_id="adojo/title_classifier", filename="title_classifier" + ".pkl")
63
+ )
64
 
65
  headers = identified_headers["Header"] if identified_headers else analyzer.identify_emphasis()
66
  if headers:
src/nlp/relation_extraction.py CHANGED
@@ -18,19 +18,6 @@ class Token:
18
  def __repr__(self): # Automatische Ausgabe in print() oder Listen
19
  return self.text
20
 
21
- class EntityGroup:
22
- def __init__(self):
23
- self.group = []
24
-
25
- @property
26
- def text(self):
27
- return " ".join([e.text for e in self.group])
28
-
29
-
30
-
31
-
32
-
33
-
34
 
35
  stop_words = ["*","a","aber","ach","acht","achte","achten","achter","achtes","ag","alle","allein","allem","allen","aller","allerdings","alles","allgemeinen","als","also","ander","andere","anderem","anderen","anderer","anderes","anderm","andern","anderer","anders","au","auch","auf","aus","ausser","ausserdem","außer","außerdem","b","bald","bei","beide","beiden","beim","beispiel","bekannt","bereits","besonders","besser","besten","bin","bisher","bist","c","d","d.h","da","dabei","dadurch","dafür","dagegen","daher","dahin","dahinter","damals","damit","daneben","dank","dann","daran","darauf","daraus","darf","darfst","darin","darum","darunter","darüber","das","dasein","daselbst","dass","dasselbe","davon","dazu","dazwischen","daß","dein","deine","deinem","deinen","deiner","deines","dem","dementsprechend","demgegenüber","demgemäss","demgemäß","demselben","demzufolge","den","denen","denn","denselben","der","deren","derer","derjenige","derjenigen","dermassen","dermaßen","derselbe","derselben","des","deshalb","desselben","dessen","deswegen","dich","die","diejenige","diejenigen","dies","diese","dieselbe","dieselben","diesem","diesen","dieser","dieses","dir","doch","dort","drei","drin","dritte","dritten","dritter","drittes","du","durch","durchaus","durfte","durften","dürfen","dürft","e","eben","ebenso","ehrlich","ei","ei,","eigen","eigene","eigenen","eigener","eigenes","ein","einander","eine","einem","einen","einer","eines","einig","einige","einigem","einigen","einiger","einiges","einmal","eins","elf","en","ende","endlich","entweder","er","ernst","erst","erste","ersten","erster","erstes","es","etwa","etwas","euch","euer","eure","eurem","euren","eurer","eures","f","folgende","früher","fünf","fünfte","fünften","fünfter","fünftes","für","g","gab","ganz","ganze","ganzen","ganzer","ganzes","gar","gedurft","gegen","gegenüber","gehabt","gehen","geht","gekannt","gekonnt","gemacht","gemocht","gemusst","genug","gerade","gern","gesagt","geschweige","gewesen","gewollt","geworden","gibt","ging","gleich","gott","gross","grosse","grossen","grosser","grosses","groß","große","großen","großer","großes","gut","gute","guter","gutes","h","hab","habe","haben","habt","hast","hat","hatte","hatten","hattest","hattet","heisst","her","heute","hier","hin","hinter","hoch","hätte","hätten","i","ich","ihm","ihn","ihnen","ihr","ihre","ihrem","ihren","ihrer","ihres","im","immer","in","indem","infolgedessen","ins","irgend","ist","j","ja","jahr","jahre","jahren","je","jede","jedem","jeden","jeder","jedermann","jedermanns","jedes","jedoch","jemand","jemandem","jemanden","jene","jenem","jenen","jener","jenes","jetzt","k","kam","kann","kannst","kaum","kein","keine","keinem","keinen","keiner","keines","kleine","kleinen","kleiner","kleines","kommen","kommt","konnte","konnten","kurz","können","könnt","könnte","l","lang","lange","leicht","leide","lieber","los","m","machen","macht","machte","mag","magst","mahn","mal","man","manche","manchem","manchen","mancher","manches","mann","mehr","mein","meine","meinem","meinen","meiner","meines","mensch","menschen","mich","mir","mit","mittel","mochte","mochten","morgen","muss","musst","musste","mussten","muß","mußt","möchte","mögen","möglich","mögt","müssen","müsst","müßt","n","na","nach","nachdem","nahm","natürlich","neben","nein","neue","neuen","neun","neunte","neunten","neunter","neuntes","nicht","nichts","nie","niemand","niemandem","niemanden","noch","nun","nur","o","ob","oben","oder","offen","oft","ohne","ordnung","p","q","r","recht","rechte","rechten","rechter","rechtes","richtig","rund","s","sa","sache","sagt","sagte","sah","satt","schlecht","schluss","schon","sechs","sechste","sechsten","sechster","sechstes","sehr","sei","seid","seien","sein","seine","seinem","seinen","seiner","seines","seit","seitdem","selbst","sich","sie","sieben","siebente","siebenten","siebenter","siebentes","sind","so","solang","solche","solchem","solchen","solcher","solches","soll","sollen","sollst","sollt","sollte","sollten","sondern","sonst","soweit","sowie","später","startseite","statt","steht","suche","t","tag","tage","tagen","tat","teil","tel","tritt","trotzdem","tun","u","uhr","und","uns","unse","unsem","unsen","unser","unsere","unserer","unses","unter","v","vergangenen","viel","viele","vielem","vielen","vielleicht","vier","vierte","vierten","vierter","viertes","vom","von","vor","w","wahr","wann","war","waren","warst","wart","warum","was","weg","wegen","weil","weit","weiter","weitere","weiteren","weiteres","welche","welchem","welchen","welcher","welches","wem","wen","wenig","wenige","weniger","weniges","wenigstens","wenn","wer","werde","werden","werdet","weshalb","wessen","wie","wieder","wieso","will","willst","wir","wird","wirklich","wirst","wissen","wo","woher","wohin","wohl","wollen","wollt","wollte","wollten","worden","wurde","wurden","während","währenddem","währenddessen","wäre","würde","würden","x","y","z","z.b","zehn","zehnte","zehnten","zehnter","zehntes","zeit","zu","zuerst","zugleich","zum","zunächst","zur","zurück","zusammen","zwanzig","zwar","zwei","zweite","zweiten","zweiter","zweites","zwischen","zwölf","über","überhaupt","übrigens"]
36
 
@@ -187,10 +174,9 @@ for text in texts:
187
  for element in md_elements:
188
  text = element.text
189
  text = normalize_data(text)
190
- text = text.replace(" und ", " + ")
191
- text = text.replace(" bis ", " - ")
192
- text = text.replace(" um ", " ")
193
- text = text.replace(" ab ", " ")
194
  # text = remove_stopwords(text)
195
  doc = nlp(text)
196
  for sent in doc.sents:
 
18
  def __repr__(self): # Automatische Ausgabe in print() oder Listen
19
  return self.text
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  stop_words = ["*","a","aber","ach","acht","achte","achten","achter","achtes","ag","alle","allein","allem","allen","aller","allerdings","alles","allgemeinen","als","also","ander","andere","anderem","anderen","anderer","anderes","anderm","andern","anderer","anders","au","auch","auf","aus","ausser","ausserdem","außer","außerdem","b","bald","bei","beide","beiden","beim","beispiel","bekannt","bereits","besonders","besser","besten","bin","bisher","bist","c","d","d.h","da","dabei","dadurch","dafür","dagegen","daher","dahin","dahinter","damals","damit","daneben","dank","dann","daran","darauf","daraus","darf","darfst","darin","darum","darunter","darüber","das","dasein","daselbst","dass","dasselbe","davon","dazu","dazwischen","daß","dein","deine","deinem","deinen","deiner","deines","dem","dementsprechend","demgegenüber","demgemäss","demgemäß","demselben","demzufolge","den","denen","denn","denselben","der","deren","derer","derjenige","derjenigen","dermassen","dermaßen","derselbe","derselben","des","deshalb","desselben","dessen","deswegen","dich","die","diejenige","diejenigen","dies","diese","dieselbe","dieselben","diesem","diesen","dieser","dieses","dir","doch","dort","drei","drin","dritte","dritten","dritter","drittes","du","durch","durchaus","durfte","durften","dürfen","dürft","e","eben","ebenso","ehrlich","ei","ei,","eigen","eigene","eigenen","eigener","eigenes","ein","einander","eine","einem","einen","einer","eines","einig","einige","einigem","einigen","einiger","einiges","einmal","eins","elf","en","ende","endlich","entweder","er","ernst","erst","erste","ersten","erster","erstes","es","etwa","etwas","euch","euer","eure","eurem","euren","eurer","eures","f","folgende","früher","fünf","fünfte","fünften","fünfter","fünftes","für","g","gab","ganz","ganze","ganzen","ganzer","ganzes","gar","gedurft","gegen","gegenüber","gehabt","gehen","geht","gekannt","gekonnt","gemacht","gemocht","gemusst","genug","gerade","gern","gesagt","geschweige","gewesen","gewollt","geworden","gibt","ging","gleich","gott","gross","grosse","grossen","grosser","grosses","groß","große","großen","großer","großes","gut","gute","guter","gutes","h","hab","habe","haben","habt","hast","hat","hatte","hatten","hattest","hattet","heisst","her","heute","hier","hin","hinter","hoch","hätte","hätten","i","ich","ihm","ihn","ihnen","ihr","ihre","ihrem","ihren","ihrer","ihres","im","immer","in","indem","infolgedessen","ins","irgend","ist","j","ja","jahr","jahre","jahren","je","jede","jedem","jeden","jeder","jedermann","jedermanns","jedes","jedoch","jemand","jemandem","jemanden","jene","jenem","jenen","jener","jenes","jetzt","k","kam","kann","kannst","kaum","kein","keine","keinem","keinen","keiner","keines","kleine","kleinen","kleiner","kleines","kommen","kommt","konnte","konnten","kurz","können","könnt","könnte","l","lang","lange","leicht","leide","lieber","los","m","machen","macht","machte","mag","magst","mahn","mal","man","manche","manchem","manchen","mancher","manches","mann","mehr","mein","meine","meinem","meinen","meiner","meines","mensch","menschen","mich","mir","mit","mittel","mochte","mochten","morgen","muss","musst","musste","mussten","muß","mußt","möchte","mögen","möglich","mögt","müssen","müsst","müßt","n","na","nach","nachdem","nahm","natürlich","neben","nein","neue","neuen","neun","neunte","neunten","neunter","neuntes","nicht","nichts","nie","niemand","niemandem","niemanden","noch","nun","nur","o","ob","oben","oder","offen","oft","ohne","ordnung","p","q","r","recht","rechte","rechten","rechter","rechtes","richtig","rund","s","sa","sache","sagt","sagte","sah","satt","schlecht","schluss","schon","sechs","sechste","sechsten","sechster","sechstes","sehr","sei","seid","seien","sein","seine","seinem","seinen","seiner","seines","seit","seitdem","selbst","sich","sie","sieben","siebente","siebenten","siebenter","siebentes","sind","so","solang","solche","solchem","solchen","solcher","solches","soll","sollen","sollst","sollt","sollte","sollten","sondern","sonst","soweit","sowie","später","startseite","statt","steht","suche","t","tag","tage","tagen","tat","teil","tel","tritt","trotzdem","tun","u","uhr","und","uns","unse","unsem","unsen","unser","unsere","unserer","unses","unter","v","vergangenen","viel","viele","vielem","vielen","vielleicht","vier","vierte","vierten","vierter","viertes","vom","von","vor","w","wahr","wann","war","waren","warst","wart","warum","was","weg","wegen","weil","weit","weiter","weitere","weiteren","weiteres","welche","welchem","welchen","welcher","welches","wem","wen","wenig","wenige","weniger","weniges","wenigstens","wenn","wer","werde","werden","werdet","weshalb","wessen","wie","wieder","wieso","will","willst","wir","wird","wirklich","wirst","wissen","wo","woher","wohin","wohl","wollen","wollt","wollte","wollten","worden","wurde","wurden","während","währenddem","währenddessen","wäre","würde","würden","x","y","z","z.b","zehn","zehnte","zehnten","zehnter","zehntes","zeit","zu","zuerst","zugleich","zum","zunächst","zur","zurück","zusammen","zwanzig","zwar","zwei","zweite","zweiten","zweiter","zweites","zwischen","zwölf","über","überhaupt","übrigens"]
23
 
 
174
  for element in md_elements:
175
  text = element.text
176
  text = normalize_data(text)
177
+ replacements = {" und ": " + ", " bis ": " - ", " um ": " ", " ab ": " "}
178
+ for old, new in replacements.items():
179
+ text = text.replace(old, new)
 
180
  # text = remove_stopwords(text)
181
  doc = nlp(text)
182
  for sent in doc.sents:
src/{nlp/playground → utils}/Event.py RENAMED
File without changes
src/utils/apis/open_router_api.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from openai import OpenAI
2
+
3
+ from src.nlp.data.test_texts import TEXTS
4
+
5
+ class ModelName:
6
+ DEEP_SEEK_R1 = "deepseek/deepseek-r1:free"
7
+ DEEP_SEEK_R1_DISTILL_LLAMA = "deepseek/deepseek-r1-distill-llama-70b:free"
8
+ QWEN_CODER_INSTRUCT = "qwen/qwen-2.5-coder-32b-instruct:free"
9
+
10
+ client = OpenAI(
11
+ base_url="https://openrouter.ai/api/v1",
12
+ api_key="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",
13
+ )
14
+
15
+ def deep_seek_extraction(text, model_name: str):
16
+ return client.chat.completions.create(
17
+ extra_headers={
18
+ # "HTTP-Referer": "<YOUR_SITE_URL>", # Optional. Site URL for rankings on openrouter.ai.
19
+ # "X-Title": "<YOUR_SITE_NAME>", # Optional. Site title for rankings on openrouter.ai.
20
+ },
21
+ extra_body={},
22
+ model=model_name,
23
+ messages=[
24
+ {
25
+ "role": "user",
26
+ "content": """
27
+ Extrahiere die Veranstaltungsdaten (wenn vorhanden) aus dem Text in folgendem JSON Format:
28
+ {
29
+ "title": String,
30
+ "start_date": String,
31
+ "end_date": String | None,
32
+ "start_time": String | None,
33
+ "end_time": String | None,
34
+ "admittance_time": String | None,
35
+ "location_name": String | None,
36
+ "adress": {
37
+ "street": String | None,
38
+ "housenumber": String | None,
39
+ "postal_code": String | None,
40
+ "city": String | None,
41
+ }
42
+ "categories": Array<String> | None,
43
+ "organizers": Array<String> | None,
44
+ }
45
+
46
+ Text:
47
+ """
48
+ + text
49
+
50
+ }
51
+ ]
52
+ )
53
+
54
+ for text in TEXTS:
55
+ print("*"*100)
56
+ print("TEXT")
57
+ print(text)
58
+ completion = deep_seek_extraction(text, ModelName.QWEN_CODER_INSTRUCT)
59
+ print("DATA:")
60
+ print(completion.choices[0].message.content)
61
+ print("*"*100)