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

Commit 9248cd2

Browse files
committed
add csvs for plans and tiers and django command for adding them
1 parent e06c5d8 commit 9248cd2

File tree

4 files changed

+166
-0
lines changed

4 files changed

+166
-0
lines changed
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,12,"{""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: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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+
# Create a dictionary of field names and values, converting '' to None
37+
model_data = {
38+
field: self.convert_value(value)
39+
for field, value in row.items()
40+
if field in [f.name for f in Model._meta.fields]
41+
}
42+
43+
# Handle ForeignKey for tier
44+
if "tier_id" in row and model_choice == "plans":
45+
try:
46+
model_data["tier"] = Tier.objects.get(id=row["tier_id"])
47+
except Tier.DoesNotExist:
48+
self.stdout.write(
49+
self.style.ERROR(
50+
f"Tier with id {row['tier_id']} does not exist. Skipping row."
51+
)
52+
)
53+
continue
54+
55+
# Create the model instance
56+
Model.objects.create(**model_data)
57+
self.stdout.write(self.style.SUCCESS(f"Inserted row: {row}"))
58+
59+
self.stdout.write(
60+
self.style.SUCCESS(
61+
f"Successfully inserted all data into {model_choice} from CSV"
62+
)
63+
)
64+
65+
def convert_value(self, value):
66+
"""Convert CSV string values to appropriate Python types."""
67+
if value == "":
68+
return None
69+
if value.lower() == "true":
70+
return True
71+
if value.lower() == "false":
72+
return False
73+
return value

core/tests/test_management_commands.py

Lines changed: 72 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,71 @@ 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():
132+
# Create a temporary CSV file
133+
with tempfile.NamedTemporaryFile(mode="w", delete=False, newline="") as temp_csv:
134+
writer = csv.writer(temp_csv)
135+
writer.writerow(
136+
["name", "marketing_name", "base_unit_price", "tier_id", "is_active"]
137+
)
138+
writer.writerow(["Plan A", "Marketing A", "100", "1", "true"])
139+
writer.writerow(["Plan B", "Marketing B", "200", "2", "false"])
140+
csv_path = temp_csv.name
141+
142+
# Create Tier instances to reference
143+
tier1 = Tier.objects.create(tier_name="Tier 1")
144+
tier2 = Tier.objects.create(tier_name="Tier 2")
145+
146+
# Run the management command
147+
out = StringIO()
148+
call_command("insert_data_to_db_from_csv", csv_path, "--model", "plans", stdout=out)
149+
150+
# Check the output
151+
assert "Successfully inserted all data into plans from CSV" in out.getvalue()
152+
153+
# Verify the data was inserted correctly
154+
assert Plan.objects.filter(
155+
name="Plan A",
156+
marketing_name="Marketing A",
157+
base_unit_price=100,
158+
tier=tier1,
159+
is_active=True,
160+
).exists()
161+
assert Plan.objects.filter(
162+
name="Plan B",
163+
marketing_name="Marketing B",
164+
base_unit_price=200,
165+
tier=tier2,
166+
is_active=False,
167+
).exists()
168+
169+
# Clean up the temporary file
170+
os.remove(csv_path)
171+
172+
173+
@pytest.mark.django_db
174+
def test_insert_data_to_db_from_csv_for_tiers():
175+
# Create a temporary CSV file
176+
with tempfile.NamedTemporaryFile(mode="w", delete=False, newline="") as temp_csv:
177+
writer = csv.writer(temp_csv)
178+
writer.writerow(["tier_name"])
179+
writer.writerow(["Tier 1"])
180+
writer.writerow(["Tier 2"])
181+
csv_path = temp_csv.name
182+
183+
# Run the management command
184+
out = StringIO()
185+
call_command("insert_data_to_db_from_csv", csv_path, "--model", "tiers", stdout=out)
186+
187+
# Check the output
188+
assert "Successfully inserted all data into tiers from CSV" in out.getvalue()
189+
190+
# Verify the data was inserted correctly
191+
assert Tier.objects.filter(tier_name="Tier 1").exists()
192+
assert Tier.objects.filter(tier_name="Tier 2").exists()
193+
194+
# Clean up the temporary file
195+
os.remove(csv_path)

0 commit comments

Comments
 (0)