feat(categories): Hierarchiczna struktura kategorii
- Dodanie parent_id do tabeli categories - Model Category z relacją parent/subcategories - 4 główne grupy: Usługi, Budownictwo, Handel, Produkcja - Skrypt assign_category_parents.py do przypisania podkategorii - Migracja 030_add_category_hierarchy.sql Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
1fb938feb3
commit
4b38f8953c
13
database.py
13
database.py
@ -233,7 +233,7 @@ class User(Base, UserMixin):
|
|||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
class Category(Base):
|
class Category(Base):
|
||||||
"""Company categories"""
|
"""Company categories with hierarchical structure"""
|
||||||
__tablename__ = 'categories'
|
__tablename__ = 'categories'
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
@ -244,7 +244,18 @@ class Category(Base):
|
|||||||
sort_order = Column(Integer, default=0)
|
sort_order = Column(Integer, default=0)
|
||||||
created_at = Column(DateTime, default=datetime.now)
|
created_at = Column(DateTime, default=datetime.now)
|
||||||
|
|
||||||
|
# Hierarchical structure
|
||||||
|
parent_id = Column(Integer, ForeignKey('categories.id'), nullable=True)
|
||||||
|
display_order = Column(Integer, default=0)
|
||||||
|
|
||||||
|
# Relationships
|
||||||
companies = relationship('Company', back_populates='category')
|
companies = relationship('Company', back_populates='category')
|
||||||
|
parent = relationship('Category', remote_side=[id], backref='subcategories')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_main_category(self):
|
||||||
|
"""Check if this is a main (parent) category"""
|
||||||
|
return self.parent_id is None
|
||||||
|
|
||||||
|
|
||||||
class Company(Base):
|
class Company(Base):
|
||||||
|
|||||||
30
database/migrations/030_add_category_hierarchy.sql
Normal file
30
database/migrations/030_add_category_hierarchy.sql
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
-- Migration: Add category hierarchy (parent-child structure)
|
||||||
|
-- Date: 2026-01-28
|
||||||
|
-- Description: Adds parent_id to categories for 4 main groups + subcategories
|
||||||
|
|
||||||
|
-- Add parent_id column
|
||||||
|
ALTER TABLE categories ADD COLUMN IF NOT EXISTS parent_id INTEGER REFERENCES categories(id);
|
||||||
|
ALTER TABLE categories ADD COLUMN IF NOT EXISTS display_order INTEGER DEFAULT 0;
|
||||||
|
|
||||||
|
-- Create index for parent lookup
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_categories_parent_id ON categories(parent_id);
|
||||||
|
|
||||||
|
-- Insert 4 main categories (parents)
|
||||||
|
INSERT INTO categories (name, slug, description, parent_id, display_order)
|
||||||
|
VALUES
|
||||||
|
('Usługi', 'uslugi', 'Firmy usługowe - IT, doradztwo, marketing, prawo, finanse', NULL, 1),
|
||||||
|
('Budownictwo', 'budownictwo-grupa', 'Budownictwo, instalacje, architektura, nieruchomości', NULL, 2),
|
||||||
|
('Handel', 'handel', 'Handel, hurtownie, motoryzacja, hotelarstwo', NULL, 3),
|
||||||
|
('Produkcja', 'produkcja-grupa', 'Produkcja, wytwarzanie, rolnictwo', NULL, 4)
|
||||||
|
ON CONFLICT (slug) DO NOTHING;
|
||||||
|
|
||||||
|
-- Get IDs of main categories (will be used in next migration step)
|
||||||
|
-- Note: The actual parent assignment will be done via Python script
|
||||||
|
-- because we need to know the exact IDs
|
||||||
|
|
||||||
|
-- Comments
|
||||||
|
COMMENT ON COLUMN categories.parent_id IS 'Parent category ID for hierarchical structure (NULL = main category)';
|
||||||
|
COMMENT ON COLUMN categories.display_order IS 'Order for display in UI';
|
||||||
|
|
||||||
|
-- Grant permissions
|
||||||
|
GRANT ALL ON TABLE categories TO nordabiz_app;
|
||||||
112
scripts/assign_category_parents.py
Normal file
112
scripts/assign_category_parents.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Skrypt do przypisania istniejących kategorii do głównych grup.
|
||||||
|
Uruchom po migracji 030_add_category_hierarchy.sql
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# Add parent directory to path
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from database import SessionLocal, Category
|
||||||
|
|
||||||
|
# Mapowanie kategorii do głównych grup
|
||||||
|
# Klucz: slug głównej grupy, wartość: lista slugów podkategorii
|
||||||
|
CATEGORY_MAPPING = {
|
||||||
|
'uslugi': [
|
||||||
|
'it-technologie',
|
||||||
|
'it-telekomunikacja',
|
||||||
|
'marketing',
|
||||||
|
'uslugi-prawne',
|
||||||
|
'ksiegowosc-finanse',
|
||||||
|
'uslugi-biznesowe',
|
||||||
|
'bezpieczenstwo-ochrona',
|
||||||
|
'media',
|
||||||
|
],
|
||||||
|
'budownictwo-grupa': [
|
||||||
|
'budownictwo',
|
||||||
|
'hvac-instalacje',
|
||||||
|
'architektura-projektowanie',
|
||||||
|
'energia-oze',
|
||||||
|
'nieruchomosci',
|
||||||
|
],
|
||||||
|
'handel': [
|
||||||
|
'handel-hurtownie',
|
||||||
|
'motoryzacja',
|
||||||
|
'hotelarstwo',
|
||||||
|
],
|
||||||
|
'produkcja-grupa': [
|
||||||
|
'produkcja',
|
||||||
|
'rolnictwo',
|
||||||
|
'inne',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
# Znajdź lub utwórz główne kategorie
|
||||||
|
main_categories = {}
|
||||||
|
|
||||||
|
for main_slug, subcategory_slugs in CATEGORY_MAPPING.items():
|
||||||
|
main_cat = db.query(Category).filter(Category.slug == main_slug).first()
|
||||||
|
|
||||||
|
if not main_cat:
|
||||||
|
print(f"[!] Główna kategoria '{main_slug}' nie istnieje - tworzę...")
|
||||||
|
# Utwórz główną kategorię
|
||||||
|
name_map = {
|
||||||
|
'uslugi': 'Usługi',
|
||||||
|
'budownictwo-grupa': 'Budownictwo',
|
||||||
|
'handel': 'Handel',
|
||||||
|
'produkcja-grupa': 'Produkcja',
|
||||||
|
}
|
||||||
|
main_cat = Category(
|
||||||
|
name=name_map.get(main_slug, main_slug),
|
||||||
|
slug=main_slug,
|
||||||
|
description=f'Główna kategoria: {name_map.get(main_slug, main_slug)}',
|
||||||
|
parent_id=None,
|
||||||
|
display_order=list(CATEGORY_MAPPING.keys()).index(main_slug) + 1
|
||||||
|
)
|
||||||
|
db.add(main_cat)
|
||||||
|
db.flush() # Get ID
|
||||||
|
|
||||||
|
main_categories[main_slug] = main_cat
|
||||||
|
print(f"[✓] Główna kategoria: {main_cat.name} (ID: {main_cat.id})")
|
||||||
|
|
||||||
|
# Przypisz podkategorie
|
||||||
|
for sub_slug in subcategory_slugs:
|
||||||
|
sub_cat = db.query(Category).filter(Category.slug == sub_slug).first()
|
||||||
|
if sub_cat:
|
||||||
|
if sub_cat.parent_id != main_cat.id:
|
||||||
|
sub_cat.parent_id = main_cat.id
|
||||||
|
print(f" → {sub_cat.name} przypisana do {main_cat.name}")
|
||||||
|
else:
|
||||||
|
print(f" ✓ {sub_cat.name} już przypisana")
|
||||||
|
else:
|
||||||
|
print(f" [!] Kategoria '{sub_slug}' nie istnieje - pomijam")
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
print("\n[✓] Przypisanie kategorii zakończone!")
|
||||||
|
|
||||||
|
# Pokaż podsumowanie
|
||||||
|
print("\n=== PODSUMOWANIE ===")
|
||||||
|
for main_slug, main_cat in main_categories.items():
|
||||||
|
subcats = db.query(Category).filter(Category.parent_id == main_cat.id).all()
|
||||||
|
print(f"\n{main_cat.name} ({len(subcats)} podkategorii):")
|
||||||
|
for sub in subcats:
|
||||||
|
company_count = len(sub.companies) if sub.companies else 0
|
||||||
|
print(f" - {sub.name}: {company_count} firm")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[!] Błąd: {e}")
|
||||||
|
db.rollback()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Loading…
Reference in New Issue
Block a user