-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmigrate_existing_data.py
More file actions
209 lines (164 loc) · 7.92 KB
/
Copy pathmigrate_existing_data.py
File metadata and controls
209 lines (164 loc) · 7.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
"""
Data Migration Script: Convert existing products to product-variant model
Run this after applying migrations 003-006
"""
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
from app.core.database import get_db_session
from app.models.tenant_database_config import TenantDatabaseConfig
from app.core.central_database import get_central_db
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def generate_category_paths():
"""Generate path field for existing categories"""
logger.info("Starting category path generation...")
# Get all tenant databases
central_db = next(get_central_db())
tenant_configs = central_db.query(TenantDatabaseConfig).all()
for config in tenant_configs:
try:
logger.info(f"Processing tenant: {config.tenant_id}")
tenant_db = next(get_db_session(config.tenant_id))
# Get all categories
categories = tenant_db.execute(text("SELECT id, name, parent_id FROM categories ORDER BY id")).fetchall()
# Build category hierarchy
category_map = {cat.id: cat for cat in categories}
def build_path(category_id, visited=None):
if visited is None:
visited = set()
if category_id in visited:
logger.warning(f"Circular reference detected for category {category_id}")
return None
visited.add(category_id)
cat = category_map.get(category_id)
if not cat:
return None
if cat.parent_id is None:
return f"/{cat.name}"
parent_path = build_path(cat.parent_id, visited.copy())
if parent_path:
return f"{parent_path}/{cat.name}"
else:
return f"/{cat.name}"
# Update paths
for category in categories:
path = build_path(category.id)
if path:
tenant_db.execute(
text("UPDATE categories SET path = :path WHERE id = :id"),
{"path": path, "id": category.id}
)
tenant_db.commit()
tenant_db.close()
logger.info(f"Completed category path generation for tenant: {config.tenant_id}")
except Exception as e:
logger.error(f"Error processing tenant {config.tenant_id}: {str(e)}")
if 'tenant_db' in locals():
tenant_db.rollback()
tenant_db.close()
def migrate_products_to_variants():
"""Convert existing products to product-variant model"""
logger.info("Starting product to variant migration...")
# Get all tenant databases
central_db = next(get_central_db())
tenant_configs = central_db.query(TenantDatabaseConfig).all()
for config in tenant_configs:
try:
logger.info(f"Processing tenant: {config.tenant_id}")
tenant_db = next(get_db_session(config.tenant_id))
# Get all existing products with variant data
products = tenant_db.execute(text("""
SELECT id, sku, price, compare_at_price, cost_price,
inventory_quantity, track_inventory, continue_selling_when_out_of_stock,
weight, weight_unit, created_at, updated_at
FROM products
WHERE sku IS NOT NULL
""")).fetchall()
logger.info(f"Found {len(products)} products to migrate")
for product in products:
# Create a default variant for each product
variant_data = {
'product_id': product.id,
'sku': product.sku,
'price': product.price,
'compare_at_price': product.compare_at_price,
'cost_price': product.cost_price,
'inventory_quantity': product.inventory_quantity or 0,
'track_inventory': product.track_inventory if product.track_inventory is not None else True,
'continue_selling_when_out_of_stock': product.continue_selling_when_out_of_stock if product.continue_selling_when_out_of_stock is not None else False,
'weight': product.weight,
'weight_unit': product.weight_unit or 'kg',
'attributes': '{}',
'created_at': product.created_at,
'updated_at': product.updated_at
}
# Insert variant
tenant_db.execute(text("""
INSERT INTO product_variants
(product_id, sku, price, compare_at_price, cost_price,
inventory_quantity, track_inventory, continue_selling_when_out_of_stock,
weight, weight_unit, attributes, created_at, updated_at, metadata)
VALUES
(:product_id, :sku, :price, :compare_at_price, :cost_price,
:inventory_quantity, :track_inventory, :continue_selling_when_out_of_stock,
:weight, :weight_unit, :attributes, :created_at, :updated_at, '{}')
"""), variant_data)
logger.info(f"Created variant for product {product.id} with SKU {product.sku}")
tenant_db.commit()
tenant_db.close()
logger.info(f"Completed product variant migration for tenant: {config.tenant_id}")
except Exception as e:
logger.error(f"Error processing tenant {config.tenant_id}: {str(e)}")
if 'tenant_db' in locals():
tenant_db.rollback()
tenant_db.close()
def remove_variant_fields_from_products():
"""Remove variant-specific fields from products table after migration"""
logger.info("Removing variant fields from products table...")
# Get all tenant databases
central_db = next(get_central_db())
tenant_configs = central_db.query(TenantDatabaseConfig).all()
for config in tenant_configs:
try:
logger.info(f"Processing tenant: {config.tenant_id}")
tenant_db = next(get_db_session(config.tenant_id))
# Drop variant-specific columns from products table
variant_columns = [
'sku', 'price', 'compare_at_price', 'cost_price',
'inventory_quantity', 'track_inventory', 'continue_selling_when_out_of_stock',
'weight', 'weight_unit'
]
for column in variant_columns:
try:
tenant_db.execute(text(f"ALTER TABLE products DROP COLUMN IF EXISTS {column}"))
logger.info(f"Dropped column {column} from products table")
except Exception as e:
logger.warning(f"Could not drop column {column}: {str(e)}")
tenant_db.commit()
tenant_db.close()
logger.info(f"Completed removing variant fields for tenant: {config.tenant_id}")
except Exception as e:
logger.error(f"Error processing tenant {config.tenant_id}: {str(e)}")
if 'tenant_db' in locals():
tenant_db.rollback()
tenant_db.close()
def main():
"""Run all migration steps"""
logger.info("Starting data migration process...")
try:
# Step 1: Generate category paths
generate_category_paths()
# Step 2: Migrate products to variants
migrate_products_to_variants()
# Step 3: Remove variant fields from products (optional - can be done manually)
# remove_variant_fields_from_products()
logger.info("Data migration completed successfully!")
except Exception as e:
logger.error(f"Migration failed: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
main()