manaviel85370
commited on
Commit
·
83f1514
1
Parent(s):
9b0e5e0
extract dates and times
Browse files- .gitattributes +0 -2
- .gitignore +2 -1
- pages/6_Pipeline.py +126 -95
- src/nlp/experimental/custom_dependecy_parser.py +92 -0
- src/nlp/experimental/ner/create_spacy_annotations_date_and_time.py +0 -0
- src/nlp/experimental/textclassification/classy_classifier.py +1 -1
- src/nlp/experimental/textclassification/classy_classifier_date.py +138 -31
- src/nlp/experimental/textclassification/classy_classifier_time.py +154 -0
- src/nlp/playground/pipelines/date_extractor.py +282 -0
- src/nlp/playground/pipelines/date_extractor_v2.py +305 -0
- src/nlp/playground/pipelines/title_extractor.py +11 -2
- src/nlp/relation_extraction.py +3 -17
- src/{nlp/playground → utils}/Event.py +0 -0
- src/utils/apis/open_router_api.py +61 -0
.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.
|
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,
|
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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
|
38 |
|
39 |
-
st.subheader("Bereinigtes HTML")
|
40 |
-
st.write(
|
41 |
-
st.components.v1.html(
|
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 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
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(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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("
|
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 |
-
|
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
|
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
|
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(
|
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 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
#
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
54 |
-
classifier =
|
|
|
|
|
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 |
-
|
191 |
-
|
192 |
-
|
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)
|