Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.

Commit 3f7fe6f

Browse files
committed
Merge branch 'main' into Ajay/milestone-3-migration
2 parents 34b1677 + ae83c2f commit 3f7fe6f

File tree

5 files changed

+162
-0
lines changed

5 files changed

+162
-0
lines changed

core/apps.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,40 @@
1+
import logging
2+
13
from django.apps import AppConfig
4+
from django.core.management import call_command
25
from shared.helpers.cache import RedisBackend
36

47
from services.redis_configuration import get_redis_connection
58
from utils.cache import cache
69
from utils.config import RUN_ENV
710

11+
logger = logging.getLogger(__name__)
12+
813

914
class CoreConfig(AppConfig):
1015
name = "core"
1116

1217
def ready(self):
1318
import core.signals # noqa: F401
1419

20+
if RUN_ENV == "DEV":
21+
try:
22+
# Call your management command here
23+
call_command(
24+
"insert_data_to_db_from_csv",
25+
"core/management/commands/codecovTiers-Jan25.csv",
26+
"--model",
27+
"tiers",
28+
)
29+
call_command(
30+
"insert_data_to_db_from_csv",
31+
"core/management/commands/codecovPlans-Jan25.csv",
32+
"--model",
33+
"plans",
34+
)
35+
except Exception as e:
36+
logger.error(f"Failed to run startup command: {e}")
37+
1538
if RUN_ENV not in ["DEV", "TESTING"]:
1639
cache_backend = RedisBackend(get_redis_connection())
1740
cache.configure(cache_backend)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"id","created_at","updated_at","base_unit_price","benefits","billing_rate","is_active","marketing_name","max_seats","monthly_uploads_limit","paid_plan","name","tier_id","stripe_id"
2+
10,2025-01-16 04:40:55.162 -0800,2025-01-24 11:33:46.043 -0800,0,"{""Configurable # of users"",""Unlimited public repositories"",""Unlimited private repositories""}",,true,Developer,,,false,users-trial,6,
3+
9,2025-01-16 04:39:59.759 -0800,2025-01-24 11:34:10.038 -0800,10,"{""Configurable # of users"",""Unlimited public repositories"",""Unlimited private repositories"",""Priority Support""}",annually,true,Enterprise Cloud,,,true,users-enterprisey,4,price_1LmjzwGlVGuVgOrkIwlM46EU
4+
8,2025-01-16 04:39:15.877 -0800,2025-01-24 11:34:31.904 -0800,12,"{""Configurable # of users"",""Unlimited public repositories"",""Unlimited private repositories"",""Priority Support""}",monthly,true,Enterprise Cloud,,,true,users-enterprisem,4,price_1LmjypGlVGuVgOrkzKtNqhwW
5+
7,2025-01-16 04:38:12.544 -0800,2025-01-24 11:34:53.935 -0800,4,"{""Up to 10 users"",""Unlimited repositories"",""2500 private repo uploads"",""Patch coverage analysis""}",annually,true,Team,10,2500,true,users-teamy,2,price_1NrlXiGlVGuVgOrkgMTw5yno
6+
6,2025-01-16 04:37:08.918 -0800,2025-01-24 11:35:15.346 -0800,5,"{""Up to 10 users"",""Unlimited repositories"",""2500 private repo uploads"",""Patch coverage analysis""}",monthly,true,Team,10,2500,true,users-teamm,2,price_1NqPKdGlVGuVgOrkm9OFvtz8
7+
5,2025-01-16 04:35:34.152 -0800,2025-01-24 11:35:42.724 -0800,10,"{""Includes 5 seats"",""$10 per additional seat"",""Unlimited public repositories"",""Unlimited private repositories"",""Priority Support""}",annually,true,Sentry Pro,5,,true,users-sentryy,5,price_1MlYAYGlVGuVgOrke9SdbBUn
8+
4,2025-01-16 04:34:33.867 -0800,2025-01-24 11:35:48.218 -0800,12,"{""Includes 5 seats"",""$10 per additional seat"",""Unlimited public repositories"",""Unlimited private repositories"",""Priority Support""}",monthly,true,Sentry Pro,5,,true,users-sentrym,5,price_1MlY9yGlVGuVgOrkHluurBtJ
9+
3,2025-01-16 04:32:44.655 -0800,2025-01-24 11:36:09.660 -0800,10,"{""Configurable # of users"",""Unlimited public repositories"",""Unlimited private repositories"",""Priority Support""}",annually,true,Pro,,,true,users-pr-inappy,3,price_1Gv2COGlVGuVgOrkuOYVLIj7
10+
2,2025-01-16 04:30:42.897 -0800,2025-01-24 11:36:14.651 -0800,12,"{""Configurable # of users"",""Unlimited public repositories"",""Unlimited private repositories"",""Priority Support""}",monthly,true,Pro,,,true,users-pr-inappm,3,price_1Gv2B8GlVGuVgOrkFnLunCgc
11+
13,2025-01-23 14:25:04.793 -0800,2025-01-23 14:25:04.793 -0800,12,"{""Configurable # of users"",""Unlimited public repositories"",""Unlimited private repositories""}",,true,Github Marketplace,,,false,users,3,
12+
12,2025-01-16 04:44:51.064 -0800,2025-01-24 11:33:14.405 -0800,0,"{""Up to 1 user"",""Unlimited public repositories"",""Unlimited private repositories""}",,true,Developer,1,250,false,users-developer,2,
13+
11,2025-01-16 04:44:01.249 -0800,2025-01-24 11:33:28.532 -0800,0,"{""Up to 1 user"",""Unlimited public repositories"",""Unlimited private repositories""}",,true,Developer,1,250,false,users-basic,1,
14+
1,2025-01-16 04:26:44.776 -0800,2025-01-24 11:36:32.387 -0800,0,"{""Up to 1 user"",""Unlimited public repositories"",""Unlimited private repositories""}",,false,Developer,1,,false,users-free,1,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"id","created_at","updated_at","tier_name","bundle_analysis","test_analytics","flaky_test_detection","project_coverage","private_repo_support"
2+
2,2025-01-16 04:22:06.838 -0800,2025-01-16 04:22:06.838 -0800,team,true,true,false,false,true
3+
3,2025-01-16 04:22:47.221 -0800,2025-01-16 04:22:47.221 -0800,pro,true,true,true,true,true
4+
4,2025-01-16 04:22:59.652 -0800,2025-01-16 04:22:59.652 -0800,enterprise,true,true,true,true,true
5+
5,2025-01-16 04:23:08.585 -0800,2025-01-16 04:23:08.585 -0800,sentry,true,true,true,true,true
6+
1,2025-01-16 04:21:40.374 -0800,2025-01-16 07:34:49.139 -0800,basic,true,true,false,true,true
7+
6,2025-01-23 14:23:21.504 -0800,2025-01-23 14:23:21.504 -0800,trial,true,true,true,true,true
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import csv
2+
3+
from django.core.management.base import BaseCommand
4+
from shared.django_apps.codecov_auth.models import Plan, Tier
5+
6+
7+
class Command(BaseCommand):
8+
help = "Insert data from a CSV file into the database for either plans or tiers"
9+
10+
def add_arguments(self, parser):
11+
parser.add_argument("csv_file", type=str, help="The path to the CSV file")
12+
parser.add_argument(
13+
"--model",
14+
type=str,
15+
choices=["plans", "tiers"],
16+
required=True,
17+
help="Specify the model to insert data into: plans or tiers",
18+
)
19+
20+
def handle(self, *args, **kwargs):
21+
csv_file_path = kwargs["csv_file"]
22+
model_choice = kwargs["model"]
23+
24+
# Determine which model to use
25+
if model_choice == "plans":
26+
Model = Plan
27+
elif model_choice == "tiers":
28+
Model = Tier
29+
else:
30+
self.stdout.write(self.style.ERROR("Invalid model choice"))
31+
return
32+
33+
with open(csv_file_path, newline="") as csvfile:
34+
reader = csv.DictReader(csvfile)
35+
for row in reader:
36+
model_data = {
37+
field: self.convert_value(value)
38+
for field, value in row.items()
39+
if field in [f.name for f in Model._meta.fields]
40+
}
41+
42+
# Handle ForeignKey for tier
43+
if "tier_id" in row and model_choice == "plans":
44+
try:
45+
model_data["tier"] = Tier.objects.get(id=row["tier_id"])
46+
except Tier.DoesNotExist:
47+
self.stdout.write(
48+
self.style.ERROR(
49+
f"Tier with id {row['tier_id']} does not exist. Skipping row."
50+
)
51+
)
52+
continue
53+
54+
Model.objects.create(**model_data)
55+
self.stdout.write(self.style.SUCCESS(f"Inserted row: {row}"))
56+
57+
self.stdout.write(
58+
self.style.SUCCESS(
59+
f"Successfully inserted all data into {model_choice} from CSV"
60+
)
61+
)
62+
63+
def convert_value(self, value):
64+
"""Convert CSV string values to appropriate Python types."""
65+
if value == "":
66+
return None
67+
if value.lower() == "true":
68+
return True
69+
if value.lower() == "false":
70+
return False
71+
return value

core/tests/test_management_commands.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
import csv
2+
import os
3+
import tempfile
14
import unittest.mock as mock
25
from io import StringIO
36

47
import pytest
58
from django.core.management import call_command
69
from shared.config import ConfigHelper
10+
from shared.django_apps.codecov_auth.models import Plan, Tier
711
from shared.django_apps.core.tests.factories import OwnerFactory, RepositoryFactory
812

913
from services.redis_configuration import get_redis_connection
@@ -121,3 +125,46 @@ def test_delete_rate_limit_keys_ip_option():
121125
# Get rid of lingering keys
122126
redis.delete("rl-user:1")
123127
redis.delete("rl-ip:2")
128+
129+
130+
@pytest.mark.django_db
131+
def test_insert_data_to_db_from_csv_for_plans_and_tiers():
132+
with tempfile.NamedTemporaryFile(mode="w", delete=False, newline="") as temp_csv:
133+
writer = csv.writer(temp_csv)
134+
writer.writerow(["id", "tier_name"])
135+
writer.writerow([1, "Tier 1"])
136+
writer.writerow([2, "Tier 2"])
137+
csv_path = temp_csv.name
138+
139+
out = StringIO()
140+
call_command("insert_data_to_db_from_csv", csv_path, "--model", "tiers", stdout=out)
141+
142+
# Check the output
143+
assert "Successfully inserted all data into tiers from CSV" in out.getvalue()
144+
145+
# Verify the data was inserted correctly
146+
assert Tier.objects.filter(tier_name="Tier 1").exists()
147+
assert Tier.objects.filter(tier_name="Tier 2").exists()
148+
149+
# Create a temporary CSV file
150+
with tempfile.NamedTemporaryFile(mode="w", delete=False, newline="") as temp_csv:
151+
writer = csv.writer(temp_csv)
152+
writer.writerow(
153+
["name", "marketing_name", "base_unit_price", "tier_id", "is_active"]
154+
)
155+
writer.writerow(["Plan A", "Marketing A", 100, 1, "true"])
156+
writer.writerow(["Plan B", "Marketing B", 200, 2, "false"])
157+
csv_path = temp_csv.name
158+
159+
out = StringIO()
160+
call_command("insert_data_to_db_from_csv", csv_path, "--model", "plans", stdout=out)
161+
162+
# Check the output
163+
assert "Successfully inserted all data into plans from CSV" in out.getvalue()
164+
165+
# Verify the data was inserted correctly
166+
assert Plan.objects.filter(name="Plan A").exists()
167+
assert Plan.objects.filter(name="Plan B").exists()
168+
169+
# Clean up the temporary file
170+
os.remove(csv_path)

0 commit comments

Comments
 (0)