mirror of
https://github.com/fhswf/aki_prj23_transparenzregister.git
synced 2025-06-21 22:03:55 +02:00
Visualize company sentiment (#422)
Updates the tab and widget "Stimmung"
This commit is contained in:
@ -10,6 +10,7 @@ from sqlalchemy.orm import Session, sessionmaker
|
||||
from aki_prj23_transparenzregister.config.config_template import SQLiteConnectionString
|
||||
from aki_prj23_transparenzregister.models.company import CapitalTypeEnum
|
||||
from aki_prj23_transparenzregister.utils import data_transfer
|
||||
from aki_prj23_transparenzregister.utils.enum_types import SentimentLabel
|
||||
from aki_prj23_transparenzregister.utils.sql import entities
|
||||
from aki_prj23_transparenzregister.utils.sql.connector import (
|
||||
get_engine,
|
||||
@ -189,3 +190,277 @@ def full_db(empty_db: Session, finance_statements: list[dict[str, Any]]) -> Sess
|
||||
empty_db.commit()
|
||||
# print(pd.read_sql_table("company", empty_db.bind).to_string())
|
||||
return empty_db
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def news_db(full_db: Session) -> Session:
|
||||
news_example = entities.News(
|
||||
title="AI Revolution in Tech",
|
||||
timestamp=datetime.datetime(2023, 11, 1, 15, 30),
|
||||
text="The latest advancements in AI are transforming the tech industry.",
|
||||
source_url="http://example-news.com/ai-revolution",
|
||||
source_domain="example-news.com",
|
||||
overall_sentiment_label=SentimentLabel.POSITIVE, # type: ignore
|
||||
overall_sentiment_certainty=0.95,
|
||||
number_of_companies=2,
|
||||
sum_of_times_named=5,
|
||||
)
|
||||
full_db.add(news_example)
|
||||
full_db.commit()
|
||||
# Print to see the object representation
|
||||
|
||||
# Example instances of Sentiment for different company IDs
|
||||
sentiment_examples = [
|
||||
entities.Sentiment(
|
||||
company_id=1,
|
||||
article_id=news_example.id, # This should be the actual ID after insertion into the database
|
||||
times_named=3,
|
||||
specific_sentiment_label=SentimentLabel.NEUTRAL, # type: ignore
|
||||
specific_sentiment_score=0.5,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=2,
|
||||
article_id=news_example.id, # This should be the actual ID after insertion into the database
|
||||
times_named=2,
|
||||
specific_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
specific_sentiment_score=0.2,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=3,
|
||||
article_id=news_example.id, # This should be the actual ID after insertion into the database
|
||||
times_named=1,
|
||||
specific_sentiment_label=SentimentLabel.POSITIVE, # type: ignore
|
||||
specific_sentiment_score=0.8,
|
||||
),
|
||||
]
|
||||
full_db.add_all(sentiment_examples)
|
||||
add_news_examples = [
|
||||
entities.News(
|
||||
title="Breakthrough in Renewable Energy",
|
||||
timestamp=datetime.datetime(2023, 11, 1, 15, 30),
|
||||
text="Innovative solar panels are expected to revolutionize the energy sector.",
|
||||
source_url="http://example-news.com/renewable-breakthrough",
|
||||
source_domain="eco-news.com",
|
||||
overall_sentiment_label=SentimentLabel.POSITIVE, # type: ignore
|
||||
overall_sentiment_certainty=0.9,
|
||||
number_of_companies=1,
|
||||
sum_of_times_named=3,
|
||||
),
|
||||
entities.News(
|
||||
title="Global Economic Outlook",
|
||||
timestamp=datetime.datetime(2023, 11, 2, 10, 0),
|
||||
text="Economists predict a challenging year ahead for global markets.",
|
||||
source_url="http://example-news.com/economic-outlook",
|
||||
source_domain="finance-world.com",
|
||||
overall_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
overall_sentiment_certainty=0.95,
|
||||
number_of_companies=3,
|
||||
sum_of_times_named=7,
|
||||
),
|
||||
entities.News(
|
||||
title="Tech Giants Merge",
|
||||
timestamp=datetime.datetime(2023, 11, 3, 12, 45),
|
||||
text="Two leading tech companies have announced a merger, sparking industry-wide discussions.",
|
||||
source_url="http://example-news.com/tech-merger",
|
||||
source_domain="tech-news.com",
|
||||
overall_sentiment_label=SentimentLabel.NEUTRAL, # type: ignore
|
||||
overall_sentiment_certainty=0.91,
|
||||
number_of_companies=2,
|
||||
sum_of_times_named=10,
|
||||
),
|
||||
entities.News(
|
||||
title="Deutsche Bank announces huge job loss plan",
|
||||
timestamp=datetime.datetime(2023, 11, 5, 12, 45),
|
||||
text="Deutsche Bank has announced plans for major job losses and will also scale down massively its operations worldwide, as part of a major shake-up of.",
|
||||
source_url="http://example-news.com/deutsche-bank",
|
||||
source_domain="tech-news.com",
|
||||
overall_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
overall_sentiment_certainty=0.9,
|
||||
number_of_companies=1,
|
||||
sum_of_times_named=8,
|
||||
),
|
||||
entities.News(
|
||||
title="Germany approves reforms to help its tech industry compete with Silicon Valley",
|
||||
timestamp=datetime.datetime(2023, 11, 6, 9, 45),
|
||||
text="Germany on Friday approved a litany of changes to its rules for stock-based compensation at tech startups, listing of companies and taxation.",
|
||||
source_url="http://example-news.com/german-tech-industry",
|
||||
source_domain="tech-news.com",
|
||||
overall_sentiment_label=SentimentLabel.POSITIVE, # type: ignore
|
||||
overall_sentiment_certainty=0.97,
|
||||
number_of_companies=3,
|
||||
sum_of_times_named=5,
|
||||
),
|
||||
entities.News(
|
||||
title="German housebuilding is collapsing ",
|
||||
timestamp=datetime.datetime(2023, 11, 8, 12, 30),
|
||||
text="German housebuilding is on the brink of collapse as construction projects are being canceled and orders are slowing.",
|
||||
source_url="http://example-news.com/german-housing",
|
||||
source_domain="tech-news.com",
|
||||
overall_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
overall_sentiment_certainty=0.96,
|
||||
number_of_companies=3,
|
||||
sum_of_times_named=2,
|
||||
),
|
||||
entities.News(
|
||||
title="The Berlin start-ups tearing up capitalism and putting workers first",
|
||||
timestamp=datetime.datetime(2023, 11, 10, 12, 45),
|
||||
text="Berlin is one of Europe's hottest start-up hubs. But companies setting up there are doing things differently and it starts with where the power lies.",
|
||||
source_url="http://example-news.com/tech-merger",
|
||||
source_domain="tech-news.com",
|
||||
overall_sentiment_label=SentimentLabel.POSITIVE, # type: ignore
|
||||
overall_sentiment_certainty=0.91,
|
||||
number_of_companies=2,
|
||||
sum_of_times_named=10,
|
||||
),
|
||||
entities.News(
|
||||
title="Air traffic across Germany disrupted due to mass walkouts",
|
||||
timestamp=datetime.datetime(2023, 11, 3, 12, 45),
|
||||
text="Security workers are staging a full-day walkout at airports across Germany over pay disputes.",
|
||||
source_url="http://example-news.com/tech-merger",
|
||||
source_domain="tech-news.com",
|
||||
overall_sentiment_label=SentimentLabel.NEUTRAL, # type: ignore
|
||||
overall_sentiment_certainty=0.95,
|
||||
number_of_companies=3,
|
||||
sum_of_times_named=17,
|
||||
),
|
||||
entities.News(
|
||||
title="Car makers face fraud claims",
|
||||
timestamp=datetime.datetime(2023, 11, 12, 12, 45),
|
||||
text="The German economy grew 0.6 percent in Q1, quarter on quarter, helped by strong exports, booming construction and higher household and state spending.",
|
||||
source_url="http://example-news.com/tech-merger",
|
||||
source_domain="tech-news.com",
|
||||
overall_sentiment_label=SentimentLabel.NEUTRAL, # type: ignore
|
||||
overall_sentiment_certainty=0.9,
|
||||
number_of_companies=2,
|
||||
sum_of_times_named=10,
|
||||
),
|
||||
entities.News(
|
||||
title="Inflation rises, unemployment falls",
|
||||
timestamp=datetime.datetime(2023, 11, 15, 12, 45),
|
||||
text="German inflation rose in February, reaching its highest level in four-and-a-half years, while unemployment fell more than expected.",
|
||||
source_url="http://example-news.com/inflation",
|
||||
source_domain="tech-news.com",
|
||||
overall_sentiment_label=SentimentLabel.POSITIVE, # type: ignore
|
||||
overall_sentiment_certainty=0.98,
|
||||
number_of_companies=2,
|
||||
sum_of_times_named=10,
|
||||
),
|
||||
entities.News(
|
||||
title="Exports struggle on slow factory output",
|
||||
timestamp=datetime.datetime(2023, 11, 16, 12, 45),
|
||||
text="Exports rebounded by less than expected in October, according to latest official figures.",
|
||||
source_url="http://example-news.com/exports",
|
||||
source_domain="tech-news.com",
|
||||
overall_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
overall_sentiment_certainty=0.98,
|
||||
number_of_companies=3,
|
||||
sum_of_times_named=4,
|
||||
),
|
||||
]
|
||||
full_db.add_all(add_news_examples)
|
||||
full_db.commit()
|
||||
# Additional examples for the Sentiment entity
|
||||
sentiment_more_examples = [
|
||||
entities.Sentiment(
|
||||
company_id=1,
|
||||
article_id=add_news_examples[
|
||||
2
|
||||
].id, # Placeholder ID, replace with actual after insertion
|
||||
times_named=1,
|
||||
specific_sentiment_label=SentimentLabel.NEUTRAL, # type: ignore
|
||||
specific_sentiment_score=0.5,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=3,
|
||||
article_id=add_news_examples[
|
||||
1
|
||||
].id, # Placeholder ID, replace with actual after insertion
|
||||
times_named=4,
|
||||
specific_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
specific_sentiment_score=0.3,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=1,
|
||||
article_id=add_news_examples[
|
||||
1
|
||||
].id, # Placeholder ID, replace with actual after insertion
|
||||
times_named=4,
|
||||
specific_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
specific_sentiment_score=0.3,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=1,
|
||||
article_id=add_news_examples[
|
||||
3
|
||||
].id, # Placeholder ID, replace with actual after insertion
|
||||
times_named=4,
|
||||
specific_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
specific_sentiment_score=0.3,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=1,
|
||||
article_id=add_news_examples[
|
||||
4
|
||||
].id, # Placeholder ID, replace with actual after insertion
|
||||
times_named=4,
|
||||
specific_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
specific_sentiment_score=0.3,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=1,
|
||||
article_id=add_news_examples[
|
||||
5
|
||||
].id, # Placeholder ID, replace with actual after insertion
|
||||
times_named=4,
|
||||
specific_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
specific_sentiment_score=0.3,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=1,
|
||||
article_id=add_news_examples[
|
||||
6
|
||||
].id, # Placeholder ID, replace with actual after insertion
|
||||
times_named=4,
|
||||
specific_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
specific_sentiment_score=0.3,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=1,
|
||||
article_id=add_news_examples[
|
||||
7
|
||||
].id, # Placeholder ID, replace with actual after insertion
|
||||
times_named=4,
|
||||
specific_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
specific_sentiment_score=0.3,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=1,
|
||||
article_id=add_news_examples[
|
||||
8
|
||||
].id, # Placeholder ID, replace with actual after insertion
|
||||
times_named=4,
|
||||
specific_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
specific_sentiment_score=0.3,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=1,
|
||||
article_id=add_news_examples[
|
||||
9
|
||||
].id, # Placeholder ID, replace with actual after insertion
|
||||
times_named=4,
|
||||
specific_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
specific_sentiment_score=0.3,
|
||||
),
|
||||
entities.Sentiment(
|
||||
company_id=1,
|
||||
article_id=add_news_examples[
|
||||
10
|
||||
].id, # Placeholder ID, replace with actual after insertion
|
||||
times_named=4,
|
||||
specific_sentiment_label=SentimentLabel.NEGATIVE, # type: ignore
|
||||
specific_sentiment_score=0.3,
|
||||
),
|
||||
]
|
||||
full_db.add_all(sentiment_more_examples)
|
||||
full_db.commit()
|
||||
return full_db
|
||||
|
@ -4,6 +4,7 @@ import pandas as pd
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from aki_prj23_transparenzregister.ui import data_elements
|
||||
from aki_prj23_transparenzregister.utils.enum_types import SentimentLabel
|
||||
|
||||
|
||||
def test_import() -> None:
|
||||
@ -86,3 +87,27 @@ def test_get_finance_data(full_db: Session) -> None:
|
||||
}
|
||||
)
|
||||
pd.testing.assert_frame_equal(finance_df, test_data)
|
||||
|
||||
|
||||
def test_get_news_of_one_company0(news_db: Session) -> None:
|
||||
"""Tests what happens if no sentiment can be found."""
|
||||
selected_company_id = 100
|
||||
assert data_elements.get_news_of_one_company(selected_company_id, news_db).empty
|
||||
|
||||
|
||||
def test_get_news_of_one_company1(news_db: Session) -> None:
|
||||
"""Tests if the sentiments can be collected for a company specified."""
|
||||
selected_company_id = 2
|
||||
sentiment_df = data_elements.get_news_of_one_company(selected_company_id, news_db)
|
||||
assert sentiment_df is not None
|
||||
assert sentiment_df.to_dict(orient="records") == [
|
||||
{
|
||||
"overall_sentiment_certainty": 0.95,
|
||||
"overall_sentiment_label": SentimentLabel.POSITIVE,
|
||||
"source_domain": "example-news.com",
|
||||
"source_url": "http://example-news.com/ai-revolution",
|
||||
"times_named": 2,
|
||||
"timestamp": pd.Timestamp("2023-11-01 15:30:00"),
|
||||
"title": "AI Revolution in Tech",
|
||||
},
|
||||
]
|
||||
|
32
tests/ui/sentiment_elements_test.py
Normal file
32
tests/ui/sentiment_elements_test.py
Normal file
@ -0,0 +1,32 @@
|
||||
"""Tests for sentiment ui elements."""
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from aki_prj23_transparenzregister.ui import data_elements, sentiment_elements
|
||||
|
||||
|
||||
def test_sentiment_layout(news_db: Session) -> None:
|
||||
"""Checks if the sentiment tab layout of the company page can be created."""
|
||||
selected_company_id = 1
|
||||
sentiment_elements.sentiment_layout(selected_company_id, news_db)
|
||||
|
||||
|
||||
def test_sentiment_trend_figure(news_db: Session) -> None:
|
||||
"""Checks if the sentiment trend graph can be created."""
|
||||
n = 10
|
||||
company_id = 1
|
||||
articles = data_elements.get_news_of_one_company(company_id, news_db)
|
||||
|
||||
articles["overall_sentiment_label"] = articles["overall_sentiment_label"].apply(
|
||||
lambda _: _.value
|
||||
)
|
||||
|
||||
articles = articles.sort_values(by=["timestamp"], ascending=True)
|
||||
articles["rolling_mean"] = articles["overall_sentiment_label"].rolling(n).mean()
|
||||
sentiment_elements.sentiment_trend_figure(articles, n)
|
||||
|
||||
|
||||
def test_sentiment_gauge_figure() -> None:
|
||||
"""Checks if the sentiment gauge graph can be created."""
|
||||
sentiment_score = 0.2
|
||||
sentiment_elements.sentiment_gauge_figure(sentiment_score)
|
Reference in New Issue
Block a user