<.icon name="hero-check-circle" class="w-5 h-5" />
Successfully imported {@import_result.imported} business(es)
0} class="alert alert-info">
+ <.icon name="hero-information-circle" class="w-5 h-5" />
+ Skipped {@import_result.skipped} duplicate(s)
+
0} class="alert alert-warning">
<.icon name="hero-exclamation-triangle" class="w-5 h-5" />
diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs
index eca707a..25525bc 100644
--- a/priv/repo/seeds.exs
+++ b/priv/repo/seeds.exs
@@ -11,192 +11,75 @@
# and so on) as they will fail if something goes wrong.
alias Localspot.Repo
-alias Localspot.Businesses.{Category, Business, BusinessHour, BusinessPhoto}
+alias Localspot.Businesses.Category
-# Clear existing data
-Repo.delete_all(BusinessPhoto)
-Repo.delete_all(BusinessHour)
-Repo.delete_all(Business)
-Repo.delete_all(Category)
-
-# Create categories
-categories =
- [
- %{
- name: "Restaurants",
- slug: "restaurants",
- description: "Local dining establishments",
- icon: "hero-cake"
- },
- %{
- name: "Coffee Shops",
- slug: "coffee-shops",
- description: "Cafes and coffee houses",
- icon: "hero-cup-soda"
- },
- %{name: "Retail", slug: "retail", description: "Shops and stores", icon: "hero-shopping-bag"},
- %{
- name: "Services",
- slug: "services",
- description: "Professional services",
- icon: "hero-wrench-screwdriver"
- },
- %{
- name: "Arts & Entertainment",
- slug: "arts-entertainment",
- description: "Galleries, theaters, and venues",
- icon: "hero-paint-brush"
- }
- ]
- |> Enum.map(fn attrs ->
- %Category{}
- |> Category.changeset(attrs)
- |> Repo.insert!()
- end)
-
-[restaurants, coffee, retail, services, arts] = categories
-
-# Sample businesses - using Columbus, OH area coordinates
-businesses_data = [
+# Create categories (only if they don't exist)
+categories_data = [
%{
- name: "The Cozy Bean",
- slug: "the-cozy-bean",
- description:
- "A family-owned coffee shop serving locally roasted beans and homemade pastries since 1998.",
- street_address: "123 High Street",
- city: "Columbus",
- state: "OH",
- zip_code: "43215",
- latitude: Decimal.new("39.9612"),
- longitude: Decimal.new("-82.9988"),
- phone: "6145551234",
- email: "hello@cozybean.example",
- website: "https://cozybean.example",
- locally_owned: true,
- category_id: coffee.id
+ name: "Restaurants",
+ slug: "restaurants",
+ description: "Local dining establishments",
+ icon: "hero-cake"
},
%{
- name: "Mama Rosa's Kitchen",
- slug: "mama-rosas-kitchen",
- description:
- "Authentic Italian cuisine made with recipes passed down through four generations.",
- street_address: "456 Main Street",
- city: "Columbus",
- state: "OH",
- zip_code: "43215",
- latitude: Decimal.new("39.9650"),
- longitude: Decimal.new("-83.0020"),
- phone: "6145555678",
- email: "reservations@mamarosas.example",
- website: "https://mamarosas.example",
- locally_owned: true,
- category_id: restaurants.id
+ name: "Coffee Shops",
+ slug: "coffee-shops",
+ description: "Cafes and coffee houses",
+ icon: "hero-cup-soda"
+ },
+ %{name: "Retail", slug: "retail", description: "Shops and stores", icon: "hero-shopping-bag"},
+ %{
+ name: "Services",
+ slug: "services",
+ description: "Professional services",
+ icon: "hero-wrench-screwdriver"
},
%{
- name: "Buckeye Books",
- slug: "buckeye-books",
- description: "Independent bookstore specializing in local authors and rare finds.",
- street_address: "789 Oak Avenue",
- city: "Columbus",
- state: "OH",
- zip_code: "43215",
- latitude: Decimal.new("39.9580"),
- longitude: Decimal.new("-82.9950"),
- phone: "6145559012",
- locally_owned: true,
- category_id: retail.id
+ name: "Arts & Entertainment",
+ slug: "arts-entertainment",
+ description: "Galleries, theaters, and venues",
+ icon: "hero-paint-brush"
},
%{
- name: "Short North Gallery",
- slug: "short-north-gallery",
- description: "Contemporary art gallery featuring works by Ohio artists.",
- street_address: "321 Short North Ave",
- city: "Columbus",
- state: "OH",
- zip_code: "43201",
- latitude: Decimal.new("39.9750"),
- longitude: Decimal.new("-83.0030"),
- locally_owned: true,
- category_id: arts.id
+ name: "Breweries",
+ slug: "breweries",
+ description: "Craft breweries and taprooms",
+ icon: "hero-beaker"
},
%{
- name: "Fix-It Fred's",
- slug: "fix-it-freds",
- description:
- "Family-owned repair shop for electronics, appliances, and more. If it's broken, Fred can fix it!",
- street_address: "555 Repair Lane",
- city: "Columbus",
- state: "OH",
- zip_code: "43215",
- latitude: Decimal.new("39.9520"),
- longitude: Decimal.new("-83.0100"),
- phone: "6145553456",
- email: "fred@fixitfreds.example",
- locally_owned: true,
- category_id: services.id
+ name: "Wineries",
+ slug: "wineries",
+ description: "Wineries and vineyards",
+ icon: "hero-sparkles"
},
%{
- name: "German Village Bakery",
- slug: "german-village-bakery",
- description: "Traditional German pastries and breads baked fresh daily.",
- street_address: "888 Schiller Park",
- city: "Columbus",
- state: "OH",
- zip_code: "43206",
- latitude: Decimal.new("39.9430"),
- longitude: Decimal.new("-82.9920"),
- phone: "6145557890",
- locally_owned: true,
- category_id: restaurants.id
+ name: "Outdoor Recreation",
+ slug: "outdoor-recreation",
+ description: "Outdoor gear, guides, and adventure",
+ icon: "hero-sun"
+ },
+ %{
+ name: "Farm Markets",
+ slug: "farm-markets",
+ description: "Farm stands, orchards, and local produce",
+ icon: "hero-shopping-cart"
}
]
-businesses =
- businesses_data
- |> Enum.map(fn attrs ->
- %Business{}
- |> Business.changeset(attrs)
- |> Repo.insert!()
+created_count =
+ categories_data
+ |> Enum.reduce(0, fn attrs, count ->
+ case Repo.get_by(Category, slug: attrs.slug) do
+ nil ->
+ %Category{}
+ |> Category.changeset(attrs)
+ |> Repo.insert!()
+
+ count + 1
+
+ _existing ->
+ count
+ end
end)
-# Add hours for each business (most open 9-5 or similar)
-for business <- businesses do
- # Monday through Friday: 9 AM - 5 PM (or restaurant hours)
- is_restaurant = business.category_id == restaurants.id
-
- for day <- 1..5 do
- %BusinessHour{}
- |> BusinessHour.changeset(%{
- business_id: business.id,
- day_of_week: day,
- opens_at: if(is_restaurant, do: ~T[11:00:00], else: ~T[09:00:00]),
- closes_at: if(is_restaurant, do: ~T[21:00:00], else: ~T[17:00:00]),
- closed: false
- })
- |> Repo.insert!()
- end
-
- # Saturday: shorter hours
- %BusinessHour{}
- |> BusinessHour.changeset(%{
- business_id: business.id,
- day_of_week: 6,
- opens_at: ~T[10:00:00],
- closes_at: ~T[15:00:00],
- closed: false
- })
- |> Repo.insert!()
-
- # Sunday: closed (except restaurants)
- %BusinessHour{}
- |> BusinessHour.changeset(%{
- business_id: business.id,
- day_of_week: 0,
- opens_at: if(is_restaurant, do: ~T[12:00:00], else: nil),
- closes_at: if(is_restaurant, do: ~T[20:00:00], else: nil),
- closed: !is_restaurant
- })
- |> Repo.insert!()
-end
-
-IO.puts("Seeded #{length(categories)} categories and #{length(businesses)} businesses")
+IO.puts("Seeded #{created_count} new categories (#{length(categories_data)} total defined)")
diff --git a/test/localspot/businesses/import_test.exs b/test/localspot/businesses/import_test.exs
index 9ba49e4..00b1f6c 100644
--- a/test/localspot/businesses/import_test.exs
+++ b/test/localspot/businesses/import_test.exs
@@ -37,13 +37,40 @@ defmodule Localspot.Businesses.ImportTest do
}
"""
- assert {:ok, %{imported: 1, errors: []}} = Import.from_json(json)
+ assert {:ok, %{imported: 1, skipped: 0, errors: []}} = Import.from_json(json)
business = Businesses.get_business_by_slug("test-business")
assert business.name == "Test Business"
assert business.category_id == category.id
end
+ test "skips duplicate businesses", %{category: _category} do
+ json = """
+ {
+ "businesses": [
+ {
+ "name": "Duplicate Test",
+ "category": "test-category",
+ "street_address": "123 Test St",
+ "city": "Columbus",
+ "state": "OH",
+ "zip_code": "43215"
+ }
+ ]
+ }
+ """
+
+ # First import should succeed
+ assert {:ok, %{imported: 1, skipped: 0, errors: []}} = Import.from_json(json)
+
+ # Second import should skip the duplicate
+ assert {:ok, %{imported: 0, skipped: 1, errors: []}} = Import.from_json(json)
+
+ # Should still only have one business
+ businesses = Businesses.list_businesses(%{query: "Duplicate Test"})
+ assert length(businesses) == 1
+ end
+
test "imports business with hours", %{category: _category} do
json = """
{
@@ -64,7 +91,7 @@ defmodule Localspot.Businesses.ImportTest do
}
"""
- assert {:ok, %{imported: 1, errors: []}} = Import.from_json(json)
+ assert {:ok, %{imported: 1, skipped: 0, errors: []}} = Import.from_json(json)
business = Businesses.get_business_by_slug("business-with-hours")
assert length(business.hours) == 2
@@ -96,7 +123,7 @@ defmodule Localspot.Businesses.ImportTest do
}
"""
- assert {:ok, %{imported: 1, errors: []}} = Import.from_json(json)
+ assert {:ok, %{imported: 1, skipped: 0, errors: []}} = Import.from_json(json)
business = Businesses.get_business_by_slug("business-with-photos")
assert length(business.photos) == 1
@@ -128,7 +155,7 @@ defmodule Localspot.Businesses.ImportTest do
}
"""
- assert {:ok, %{imported: 2, errors: []}} = Import.from_json(json)
+ assert {:ok, %{imported: 2, skipped: 0, errors: []}} = Import.from_json(json)
end
test "reports errors for invalid businesses", %{category: _category} do
@@ -150,7 +177,7 @@ defmodule Localspot.Businesses.ImportTest do
}
"""
- assert {:ok, %{imported: 1, errors: errors}} = Import.from_json(json)
+ assert {:ok, %{imported: 1, skipped: 0, errors: errors}} = Import.from_json(json)
assert length(errors) == 1
end
@@ -179,8 +206,11 @@ defmodule Localspot.Businesses.ImportTest do
"""
assert {:ok,
- %{imported: 0, errors: [{:error, 1, {:unknown_category, "nonexistent-category"}}]}} =
- Import.from_json(json)
+ %{
+ imported: 0,
+ skipped: 0,
+ errors: [{:error, 1, {:unknown_category, "nonexistent-category"}}]
+ }} = Import.from_json(json)
end
end