Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions tests/test_daily_report_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from pathlib import Path

import pandas as pd
import pytest
import streamlit as st

from ui import daily_report
Expand Down Expand Up @@ -112,6 +113,77 @@ def metric(self, label, value, delta=None):
assert any(call[0] == "Increased" and call[1] == "1" for call in metric_calls)


def _setup_fallback_diff_db(tmp_path: Path, manager_pk: str = "manager_id") -> str:
db_path = tmp_path / f"daily_report_fallback_{manager_pk}.db"
conn = sqlite3.connect(db_path)
conn.execute(f"CREATE TABLE managers ({manager_pk} INTEGER PRIMARY KEY, name TEXT NOT NULL)")
conn.execute(
"CREATE TABLE daily_diffs ("
"manager_id INTEGER NOT NULL, report_date TEXT NOT NULL, cusip TEXT NOT NULL, "
"name_of_issuer TEXT, delta_type TEXT NOT NULL, shares_prev INTEGER, shares_curr INTEGER, "
"value_prev REAL, value_curr REAL)"
)
conn.execute(
"CREATE TABLE news_items ("
"manager_id INTEGER, headline TEXT, url TEXT, published_at DATETIME, "
"source TEXT, topics TEXT, confidence REAL)"
)
conn.execute(
"CREATE TABLE activism_filings ("
"filing_id INTEGER PRIMARY KEY, manager_id INTEGER, filing_type TEXT, "
"subject_company TEXT, filed_date DATE)"
)
conn.execute(
"CREATE TABLE activism_events ("
"event_id INTEGER PRIMARY KEY, manager_id INTEGER, filing_id INTEGER, event_type TEXT, "
"subject_company TEXT, ownership_pct REAL, previous_pct REAL, delta_pct REAL, "
"detected_at DATETIME)"
)
conn.execute(f"INSERT INTO managers({manager_pk}, name) VALUES (1, 'Fallback Manager')")
conn.execute(
"INSERT INTO daily_diffs(manager_id, report_date, cusip, name_of_issuer, delta_type, "
"shares_prev, shares_curr, value_prev, value_curr) "
"VALUES (1, '2024-05-01', 'AAA', 'Issuer Alpha', 'INCREASE', 1000, 2500, 10000, 16000)"
)
conn.execute(
"INSERT INTO news_items(manager_id, headline, url, published_at, source, topics, confidence) "
"VALUES (1, 'Headline', 'https://example.com', '2024-05-01 10:00:00', 'Wire', 'topic', 0.9)"
)
conn.execute(
"INSERT INTO activism_filings(filing_id, manager_id, filing_type, subject_company, filed_date) "
"VALUES (10, 1, 'SC 13D', 'Target Co', '2024-05-01')"
)
conn.execute(
"INSERT INTO activism_events(event_id, manager_id, filing_id, event_type, subject_company, "
"ownership_pct, previous_pct, delta_pct, detected_at) "
"VALUES (20, 1, 10, 'initial_stake', 'Target Co', 5.0, 0.0, 5.0, '2024-05-01 12:00:00')"
)
conn.commit()
conn.close()
return str(db_path)


@pytest.mark.parametrize("manager_pk", ["manager_id", "id"])
def test_daily_report_fallback_queries_resolve_manager_primary_key(
tmp_path, monkeypatch, manager_pk: str
):
db_path = _setup_fallback_diff_db(tmp_path, manager_pk=manager_pk)
monkeypatch.setenv("DB_PATH", db_path)
st.cache_data.clear()

diffs = daily_report.load_diffs("2024-05-01")
assert len(diffs) == 1
assert diffs.iloc[0]["manager_name"] == "Fallback Manager"

news = daily_report.load_news("2024-05-01")
assert len(news) == 1
assert news.iloc[0]["manager_name"] == "Fallback Manager"

activism = daily_report.load_activism_events("2024-05-01")
assert len(activism) == 1
assert activism.iloc[0]["manager_name"] == "Fallback Manager"


def test_daily_report_page_renders_under_500ms_with_ten_managers(monkeypatch):
manager_rows = [
{
Expand Down
25 changes: 23 additions & 2 deletions tests/test_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from time import perf_counter

import pandas as pd
import pytest
import streamlit as st

sys.path.append(str(Path(__file__).resolve().parents[1]))
Expand Down Expand Up @@ -41,10 +42,10 @@
)


def setup_db(tmp_path: Path) -> str:
def setup_db(tmp_path: Path, manager_pk: str = "manager_id") -> str:
db_path = tmp_path / "dev.db"
conn = sqlite3.connect(db_path)
conn.execute("CREATE TABLE managers (manager_id INTEGER PRIMARY KEY, name TEXT)")
conn.execute(f"CREATE TABLE managers ({manager_pk} INTEGER PRIMARY KEY, name TEXT)")
conn.execute(
# No `filed` column: schema.sql keeps filing dates on filings.filed_date.
"CREATE TABLE holdings (cik TEXT, accession TEXT, nameOfIssuer TEXT, "
Expand Down Expand Up @@ -787,6 +788,26 @@ def test_load_managers_sorted(tmp_path: Path, monkeypatch):
assert list(df["manager_id"]) == [1, 2]


@pytest.mark.parametrize("manager_pk", ["manager_id", "id"])
def test_load_managers_and_qc_summary_resolve_manager_primary_key(
tmp_path: Path, monkeypatch, manager_pk: str
):
db_path = setup_db(tmp_path, manager_pk=manager_pk)
monkeypatch.setenv("DB_PATH", db_path)
st.cache_data.clear()

managers = load_managers()
assert list(managers["manager_id"]) == [1, 2]
assert list(managers["name"]) == ["Alpha Partners", "Zulu Capital"]

summary = load_all_managers_summary()
assert summary["total_managers"] == 2
stale = summary["stale_managers"]
assert not stale.empty
assert set(stale["manager_id"]) == {1, 2}
assert set(stale["name"]) == {"Alpha Partners", "Zulu Capital"}


class FakeStreamlit:
def __init__(self):
self.session_state = {}
Expand Down
11 changes: 7 additions & 4 deletions ui/daily_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pandas as pd
import streamlit as st

from adapters.base import connect_db
from adapters.base import connect_db, resolve_manager_id_column
from api.signals import query_contrarian_signals, query_crowded_trades

from . import require_login
Expand All @@ -26,10 +26,11 @@ def load_diffs(date: str) -> pd.DataFrame:
FROM mv_daily_report
WHERE report_date = {placeholder}
ORDER BY manager_name, delta_type"""
id_column = resolve_manager_id_column(conn)
fallback_query = f"""SELECT m.name AS manager_name, d.cusip, d.name_of_issuer, d.delta_type,
d.shares_prev, d.shares_curr, d.value_prev, d.value_curr
FROM daily_diffs d
JOIN managers m ON m.manager_id = d.manager_id
JOIN managers m ON m.{id_column} = d.manager_id
WHERE d.report_date = {placeholder}
ORDER BY manager_name, d.delta_type"""
try:
Expand All @@ -51,11 +52,12 @@ def load_news(date: str) -> pd.DataFrame:
conn = connect_db()
try:
if isinstance(conn, sqlite3.Connection):
id_column = resolve_manager_id_column(conn)
query = (
"SELECT n.headline, n.url, n.published_at, n.source, n.topics, n.confidence, "
"m.name AS manager_name "
"FROM news_items n "
"LEFT JOIN managers m ON m.manager_id = n.manager_id "
f"LEFT JOIN managers m ON m.{id_column} = n.manager_id "
"WHERE date(n.published_at) = ? "
"ORDER BY n.published_at DESC "
"LIMIT 50"
Expand Down Expand Up @@ -94,12 +96,13 @@ def load_activism_events(date: str) -> pd.DataFrame:
is_sqlite = isinstance(conn, sqlite3.Connection)
placeholder = "?" if is_sqlite else "%s"
try:
id_column = resolve_manager_id_column(conn)
query = (
"SELECT m.name AS manager_name, ae.event_type, ae.subject_company, "
"ae.ownership_pct, ae.previous_pct, ae.delta_pct, af.filed_date "
"FROM activism_events ae "
"JOIN activism_filings af ON af.filing_id = ae.filing_id "
"LEFT JOIN managers m ON m.manager_id = ae.manager_id "
f"LEFT JOIN managers m ON m.{id_column} = ae.manager_id "
f"WHERE af.filed_date = {placeholder} "
"ORDER BY af.filed_date DESC, ae.detected_at DESC"
)
Expand Down
9 changes: 7 additions & 2 deletions ui/dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,9 @@ def load_qc_flags(manager_id: int) -> dict[str, Any]:
def load_managers() -> pd.DataFrame:
conn = connect_db()
try:
id_column = resolve_manager_id_column(conn)
df = pd.read_sql_query(
"SELECT manager_id, name FROM managers ORDER BY name",
f"SELECT {id_column} AS manager_id, name FROM managers ORDER BY name",
conn,
)
except Exception:
Expand Down Expand Up @@ -1020,7 +1021,11 @@ def load_all_managers_summary() -> dict[str, Any]:
)
summary["recent_activity"] = activity_df.sort_values("activity_date")

managers_df = pd.read_sql_query("SELECT manager_id, name FROM managers", conn)
id_column = resolve_manager_id_column(conn)
managers_df = pd.read_sql_query(
f"SELECT {id_column} AS manager_id, name FROM managers",
conn,
)
filings_df = pd.read_sql_query(
"SELECT manager_id, filing_id, type, filed_date FROM filings", conn
)
Expand Down
Loading