Skip to content

Commit 761d6b1

Browse files
FEAT: add script that deletes a specified support from the DB (#108)
Co-authored-by: Yoom Lam <yoom@navapbc.com>
1 parent cc6603f commit 761d6b1

4 files changed

Lines changed: 172 additions & 2 deletions

File tree

app/Makefile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,13 +241,15 @@ add-crawl-jobs:
241241
make manage-crawl-job ACTION="upsert" PROMPT_NAME="crawl_indeed" DOMAIN="indeed.com" INTERVAL=6
242242
make manage-crawl-job ACTION="upsert" PROMPT_NAME="crawl_gcta" DOMAIN="gctatraining.org" INTERVAL=6
243243

244+
# Usage: make delete-support NAME="Support or SupportListing Name"
245+
delete-support:
246+
$(PY_RUN_CMD) delete-support "$(NAME)"
247+
244248
process-crawl-jobs:
245249
$(PY_RUN_CMD) process-crawl-jobs
246250

247251
copy-prompts:
248252
$(PY_RUN_CMD) copy-prompts
249253

250-
251-
252254
run-experiment:
253255
$(PY_RUN_CMD) run-experiment "$(DATASET)" "$(PIPELINE)" "$(ACTION)" $(if $(PROMPT_VERSION),--prompt_version "$(PROMPT_VERSION)")

app/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ copy-prompts= "src.common.phoenix_utils:copy_deployed_prompts"
7272
run-experiment= "src.experiments:main"
7373
manage-crawl-job= "src.db.manage_crawl_job:main"
7474
process-crawl-jobs= "src.ingestion.process_crawl_jobs:main"
75+
delete-support= "src.db.delete_support:main"
7576

7677
[tool.black]
7778
line-length = 100

app/src/db/delete_support.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
"""Delete Support entries and their related SupportListing from the database."""
2+
3+
import argparse
4+
import logging
5+
import sys
6+
7+
from sqlalchemy.orm import Session
8+
9+
from src.app_config import config
10+
from src.db.models.support_listing import Support
11+
12+
logger = logging.getLogger(__name__)
13+
14+
15+
def delete_support_by_name(db_session: Session, support_name: str) -> bool:
16+
"""Delete a Support entry by name and its related SupportListing if no other supports remain.
17+
18+
Args:
19+
db_session: Database session
20+
support_name: Name of the support to delete
21+
22+
Returns:
23+
True if the support was found and deleted, False otherwise
24+
"""
25+
# Find the support by name
26+
support = db_session.query(Support).where(Support.name == support_name).one_or_none()
27+
28+
if not support:
29+
logger.error("Support with name '%s' not found", support_name)
30+
return False
31+
32+
# Get the associated SupportListing before deleting the support
33+
support_listing = support.support_listing
34+
35+
logger.info("Found Support (id=%s, name='%s')", support.id, support.name)
36+
logger.info(
37+
"Associated with SupportListing (id=%s, name='%s')",
38+
support_listing.id,
39+
support_listing.name,
40+
)
41+
42+
# Delete the support
43+
db_session.delete(support)
44+
db_session.flush()
45+
logger.info("Deleted Support '%s'", support_name)
46+
47+
# Check if the SupportListing has any remaining supports
48+
remaining_supports_count = (
49+
db_session.query(Support).where(Support.support_listing_id == support_listing.id).count()
50+
)
51+
52+
if remaining_supports_count == 0:
53+
logger.info(
54+
"No remaining supports for SupportListing '%s', deleting it as well",
55+
support_listing.name,
56+
)
57+
db_session.delete(support_listing)
58+
logger.info("Deleted SupportListing '%s'", support_listing.name)
59+
else:
60+
logger.info(
61+
"SupportListing '%s' has %d remaining support(s), keeping it",
62+
support_listing.name,
63+
remaining_supports_count,
64+
)
65+
66+
return True
67+
68+
69+
def main() -> None: # pragma: no cover
70+
logging.basicConfig(format="%(levelname)s - %(name)s - %(message)s", level=logging.INFO)
71+
72+
parser = argparse.ArgumentParser(description="Delete a Support or SupportListing entry by name")
73+
parser.add_argument("name", help="Name of the support or support listing to delete")
74+
75+
args = parser.parse_args()
76+
77+
with config.db_session() as db_session, db_session.begin():
78+
success = delete_support_by_name(db_session, args.name)
79+
80+
if not success:
81+
sys.exit(1)
82+
83+
logger.info("Done")
84+
85+
86+
if __name__ == "__main__":
87+
main()
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"""Tests for delete_support module."""
2+
import logging
3+
4+
import pytest
5+
6+
from src.db.delete_support import delete_support_by_name
7+
from src.db.models.support_listing import Support, SupportListing
8+
from tests.src.db.models import factories
9+
10+
11+
@pytest.fixture
12+
def caplog_info(caplog):
13+
"""Configure caplog to capture INFO level logs."""
14+
caplog.set_level(logging.INFO)
15+
return caplog
16+
17+
18+
"""Tests for delete_support_by_name function."""
19+
20+
21+
def test_delete_single_support_deletes_listing(enable_factory_create, db_session, caplog_info):
22+
"""Test that deleting the only support also deletes the listing."""
23+
# Create a support listing with a single support
24+
support_listing = factories.SupportListingFactory.create(name="Test Listing")
25+
support = factories.SupportFactory.create(name="Test Support", support_listing=support_listing)
26+
db_session.flush()
27+
28+
support_id = support.id
29+
listing_id = support_listing.id
30+
31+
# Delete the support
32+
result = delete_support_by_name(db_session, "Test Support")
33+
34+
assert result is True
35+
assert "Deleted Support 'Test Support'" in caplog_info.text
36+
assert "No remaining supports for SupportListing" in caplog_info.text
37+
assert "Deleted SupportListing 'Test Listing'" in caplog_info.text
38+
39+
# Verify support and listing are deleted
40+
assert db_session.query(Support).filter_by(id=support_id).one_or_none() is None
41+
assert db_session.query(SupportListing).filter_by(id=listing_id).one_or_none() is None
42+
43+
44+
def test_delete_support_keeps_listing_with_remaining_supports(
45+
enable_factory_create, db_session, caplog_info
46+
):
47+
"""Test that deleting a support keeps the listing if other supports remain."""
48+
# Create a support listing with multiple supports
49+
support_listing = factories.SupportListingFactory.create(name="Test Listing 1")
50+
support1 = factories.SupportFactory.create(
51+
name="Test Support 1", support_listing=support_listing
52+
)
53+
support2 = factories.SupportFactory.create(
54+
name="Test Support 2", support_listing=support_listing
55+
)
56+
db_session.flush()
57+
58+
support1_id = support1.id
59+
support2_id = support2.id
60+
listing_id = support_listing.id
61+
62+
# Delete one support
63+
result = delete_support_by_name(db_session, "Test Support 1")
64+
65+
assert result is True
66+
assert "Deleted Support 'Test Support 1'" in caplog_info.text
67+
assert "has 1 remaining support(s), keeping it" in caplog_info.text
68+
69+
# Verify first support is deleted but listing and second support remain
70+
assert db_session.query(Support).filter_by(id=support1_id).one_or_none() is None
71+
assert db_session.query(Support).filter_by(id=support2_id).one_or_none() is not None
72+
assert db_session.query(SupportListing).filter_by(id=listing_id).one_or_none() is not None
73+
74+
75+
def test_delete_nonexistent_support(enable_factory_create, db_session, caplog_info):
76+
"""Test that deleting a non-existent support returns False."""
77+
result = delete_support_by_name(db_session, "Nonexistent Support")
78+
79+
assert result is False
80+
assert "Support with name 'Nonexistent Support' not found" in caplog_info.text

0 commit comments

Comments
 (0)