-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate_data.py
More file actions
348 lines (276 loc) · 18.5 KB
/
generate_data.py
File metadata and controls
348 lines (276 loc) · 18.5 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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# -*- coding: utf-8 -*-
"""
Скрипт для создания тестовых данных продавца Ozon за 6 месяцев.
Создаёт два CSV файла: products.csv и sales.csv
ВАЖНО для импорта в PostgreSQL через DBeaver:
1. Все числа БЕЗ кавычек и пробелов
2. Даты в формате 'YYYY-MM-DD' (например: 2025-01-15)
3. Первая строка CSV содержит названия колонок
4. Разделитель - запятая (,)
5. Кодировка UTF-8 с BOM для корректного отображения кириллицы
"""
import csv
import os
import random
import sys
from datetime import datetime, timedelta
# Настройка кодировки для консоли Windows (чтобы русский текст отображался правильно)
if sys.platform == 'win32':
sys.stdout.reconfigure(encoding='utf-8')
# ==================== НАСТРОЙКИ ====================
# Папка для сохранения CSV файлов
OUTPUT_FOLDER = r'C:\Users\Nimos\Desktop\OZON_Analytycs'
# Список категорий товаров
CATEGORIES = ['Электроника', 'Одежда', 'Книги', 'Дом', 'Красота']
# Бренды для каждой категории (соответствуют порядку CATEGORIES)
BRANDS = ['Xiaomi', 'Nike', 'Манн Иванов Фербер', 'IKEA', 'Loreal']
# Примеры названий товаров по категориям
PRODUCT_NAMES = {
'Электроника': ['Смартфон Xiaomi Redmi Note 12', 'Наушники Bluetooth', 'Портативное зарядное устройство',
'Умные часы', 'Беспроводная колонка', 'Веб-камера', 'Клавиатура механическая',
'Мышь беспроводная', 'Планшет', 'Зарядка для телефона'],
'Одежда': ['Футболка', 'Джинсы', 'Куртка', 'Свитшот', 'Кроссовки', 'Шорты', 'Платье',
'Толстовка', 'Брюки', 'Кепка'],
'Книги': ['Книга по программированию', 'Роман', 'Учебник', 'Детская книга', 'Энциклопедия',
'Словарь', 'Художественная литература', 'Бизнес-книга', 'Комикс', 'Справочник'],
'Дом': ['Набор посуды', 'Постельное бельё', 'Ваза', 'Светильник', 'Полотенце',
'Держатель для телефона', 'Органайзер', 'Коврик', 'Крышки для банок', 'Контейнер'],
'Красота': ['Шампунь', 'Крем для лица', 'Помада', 'Тушь для ресниц', 'Лосьон',
'Маска для волос', 'Сыворотка', 'Дезодорант', 'Парфюм', 'Гель для душа'],
}
# 15 российских городов
CITIES = ['Москва', 'Санкт-Петербург', 'Новосибирск', 'Екатеринбург', 'Казань',
'Нижний Новгород', 'Челябинск', 'Самара', 'Ростов-на-Дону', 'Уфа',
'Красноярск', 'Воронеж', 'Пермь', 'Волгоград', 'Краснодар']
# Статусы заказов (85% доставлен, 10% отменен, 5% возврат)
# Используем список для взвешенной случайности
STATUSES = ['Доставлен'] * 85 + ['Отменен'] * 10 + ['Возврат'] * 5
def generate_products():
"""
Создаёт список из 100 товаров.
ВОЗМОЖНЫЕ ПРОБЛЕМЫ:
- Если названия товаров содержат запятые, они будут заключены в кавычки автоматически
- Убедитесь, что все числа целые (без десятичных знаков для INTEGER полей)
"""
products = []
product_id = 1
# Создаём по 20 товаров в каждой категории (5 категорий × 20 = 100 товаров)
for i, category in enumerate(CATEGORIES):
brand = BRANDS[i]
base_names = PRODUCT_NAMES[category]
for j in range(20):
# Берём название из списка (циклически, если товаров больше чем названий)
name = base_names[j % len(base_names)]
# Генерируем цены: закупочная от 500 до 5000 рублей
# ВАЖНО: используем целые числа, без копеек (для простоты)
purchase_price = random.randint(500, 5000)
# Текущая цена = закупочная + 20% наценка
# int() убирает дробную часть, оставляя только целое число
current_price = int(purchase_price * 1.2)
# Количество на складе от 0 до 100
stock = random.randint(0, 100)
# Создаём словарь с данными товара
# ВАЖНО: все значения должны быть простыми типами (числа, строки)
# Не используем объекты или сложные структуры
products.append({
'product_id': product_id, # Целое число
'product_name': name.strip(), # Убираем лишние пробелы
'category': category.strip(), # Убираем лишние пробелы
'brand': brand.strip(), # Убираем лишние пробелы
'purchase_price': purchase_price, # Целое число
'current_price': current_price, # Целое число
'stock': stock # Целое число
})
product_id += 1
return products
def get_product_id_by_season(month):
"""
Выбирает product_id с учётом сезона для более реалистичных данных.
Логика:
- Лето (6,7,8): чаще одежда (товары 21-40)
- Зима (11,12,1): чаще электроника (товары 1-20)
- Остальное: случайный товар из всех 100
ВОЗМОЖНЫЕ ПРОБЛЕМЫ:
- Если изменить количество товаров, нужно обновить диапазоны (21-40, 1-20)
"""
if month in [6, 7, 8]:
# Летом 50% шанс — одежда (товары с ID 21-40)
if random.random() < 0.5:
return random.randint(21, 40)
elif month in [11, 12, 1]:
# Зимой 50% шанс — электроника (товары с ID 1-20)
if random.random() < 0.5:
return random.randint(1, 20)
# В остальных случаях — случайный товар из всех 100
return random.randint(1, 100)
def generate_sales(products):
"""
Создаёт 5000 продаж за последние 6 месяцев (180 дней).
ВОЗМОЖНЫЕ ПРОБЛЕМЫ:
1. Даты должны быть в формате 'YYYY-MM-DD' (например: '2025-01-15')
- НЕ используйте формат 'DD.MM.YYYY' или 'MM/DD/YYYY'
- PostgreSQL строго требует формат ISO 8601
2. product_id должен существовать в таблице products
- Если товара с таким ID нет, будет ошибка внешнего ключа
3. Все числа должны быть целыми (без десятичных знаков)
- quantity и customer_id - целые числа
- price должен соответствовать current_price из products
4. Статусы должны быть из списка STATUSES
- Если добавить новый статус, убедитесь, что он допустим в БД
"""
sales = []
# Создаём словарь для быстрого доступа к данным товара по ID
# Это нужно, чтобы получить цену товара при создании продажи
products_dict = {p['product_id']: p for p in products}
# Период: последние 180 дней (6 месяцев)
end_date = datetime.now()
start_date = end_date - timedelta(days=180)
# Создаём список всех возможных дат за этот период
all_dates = []
current_date = start_date
while current_date <= end_date:
all_dates.append(current_date)
current_date += timedelta(days=1)
# Ноябрь (Чёрная пятница): добавляем даты ноября по 3 раза
# Это создаст больше продаж в ноябре (реалистично для распродаж)
november_dates = [d for d in all_dates if d.month == 11]
date_pool = all_dates + november_dates + november_dates
# Генерируем 5000 продаж
for sale_id in range(1, 5001):
# Выбираем случайную дату (ноябрь выпадает чаще)
date = random.choice(date_pool)
# ВАЖНО: формат даты 'YYYY-MM-DD' обязателен для PostgreSQL
# strftime('%Y-%m-%d') создаёт строку в формате '2025-01-15'
date_str = date.strftime('%Y-%m-%d')
# Выбираем товар с учётом сезона
product_id = get_product_id_by_season(date.month)
product = products_dict[product_id]
# Количество товара в продаже (от 1 до 3 штук)
quantity = random.randint(1, 3)
# Цена берётся из текущей цены товара
# ВАЖНО: используем целое число, без копеек
price = product['current_price']
# Статус заказа (85% доставлен, 10% отменен, 5% возврат)
status = random.choice(STATUSES)
# Случайный город из списка
city = random.choice(CITIES)
# ID покупателя (от 1000 до 9999)
customer_id = random.randint(1000, 9999)
# Создаём словарь с данными продажи
# ВАЖНО: используем 'sale_date' вместо 'date', так как в SQL таблице колонка называется sale_date
sales.append({
'sale_id': sale_id, # Целое число
'sale_date': date_str, # Строка в формате 'YYYY-MM-DD'
'product_id': product_id, # Целое число (должен существовать в products)
'quantity': quantity, # Целое число (1-3)
'price': price, # Целое число (цена товара)
'status': status.strip(), # Строка (убираем пробелы)
'city': city.strip(), # Строка (убираем пробелы)
'customer_id': customer_id # Целое число
})
return sales
def save_products_to_csv(products, filename='products.csv'):
"""
Сохраняет товары в CSV файл.
ВАЖНО для PostgreSQL:
1. Разделитель - запятая (,)
2. Кодировка UTF-8 с BOM (utf-8-sig) для корректного отображения кириллицы
3. Первая строка содержит названия колонок
4. csv.QUOTE_MINIMAL - кавычки добавляются только если нужно (если есть запятая в тексте)
5. quoting=csv.QUOTE_MINIMAL - убирает лишние кавычки вокруг чисел
ВОЗМОЖНЫЕ ПРОБЛЕМЫ:
- Если названия колонок не совпадают с SQL таблицей, будет ошибка при импорте
- Если файл уже открыт в другой программе, запись может не удаться
"""
filepath = os.path.join(OUTPUT_FOLDER, filename)
# Определяем порядок и названия колонок
# ВАЖНО: названия должны точно совпадать с названиями колонок в SQL таблице
fieldnames = ['product_id', 'product_name', 'category',
'brand', 'purchase_price', 'current_price', 'stock']
# Открываем файл для записи
# 'w' - режим записи (перезаписывает файл, если он существует)
# newline='' - убирает лишние пустые строки между записями
# encoding='utf-8-sig' - UTF-8 с BOM (для Excel и DBeaver)
with open(filepath, 'w', newline='', encoding='utf-8-sig') as f:
# Создаём writer для CSV
# delimiter=',' - разделитель запятая
# quoting=csv.QUOTE_MINIMAL - кавычки только когда нужно
writer = csv.DictWriter(f, fieldnames=fieldnames, delimiter=',', quoting=csv.QUOTE_MINIMAL)
# Записываем заголовок (первая строка с названиями колонок)
writer.writeheader()
# Записываем все товары
writer.writerows(products)
print(f'[OK] Создан файл {filename} ({len(products)} товаров)')
def save_sales_to_csv(sales, filename='sales.csv'):
"""
Сохраняет продажи в CSV файл.
ВАЖНО для PostgreSQL:
1. Разделитель - запятая (,)
2. Кодировка UTF-8 с BOM (utf-8-sig)
3. Первая строка содержит названия колонок
4. Название колонки 'sale_date' должно совпадать с SQL таблицей
ВОЗМОЖНЫЕ ПРОБЛЕМЫ:
1. Колонка называется 'sale_date', а не 'date' (как в SQL таблице)
2. Если product_id не существует в products, будет ошибка внешнего ключа
3. Если формат даты неправильный, PostgreSQL выдаст ошибку
4. Если файл очень большой (>100MB), импорт может занять много времени
"""
filepath = os.path.join(OUTPUT_FOLDER, filename)
# Определяем порядок и названия колонок
# ВАЖНО: 'sale_date' должно совпадать с названием колонки в SQL таблице
fieldnames = ['sale_id', 'sale_date', 'product_id', 'quantity',
'price', 'status', 'city', 'customer_id']
# Открываем файл для записи
with open(filepath, 'w', newline='', encoding='utf-8-sig') as f:
# Создаём writer для CSV
writer = csv.DictWriter(f, fieldnames=fieldnames, delimiter=',', quoting=csv.QUOTE_MINIMAL)
# Записываем заголовок
writer.writeheader()
# Записываем все продажи
writer.writerows(sales)
print(f'[OK] Создан файл {filename} ({len(sales)} продаж)')
# ==================== ОСНОВНАЯ ПРОГРАММА ====================
if __name__ == '__main__':
print('=' * 60)
print('Генерация данных Ozon для PostgreSQL')
print('=' * 60)
try:
# Шаг 1: Создаём 100 товаров
print('\n[1/2] Генерация товаров...')
products = generate_products()
# Проверка: должно быть ровно 100 товаров
if len(products) != 100:
print(f'[ОШИБКА] Создано {len(products)} товаров вместо 100!')
sys.exit(1)
# Сохраняем товары в CSV
save_products_to_csv(products, 'products.csv')
# Шаг 2: Создаём 5000 продаж
print('\n[2/2] Генерация продаж...')
sales = generate_sales(products)
# Проверка: должно быть ровно 5000 продаж
if len(sales) != 5000:
print(f'[ОШИБКА] Создано {len(sales)} продаж вместо 5000!')
sys.exit(1)
# Сохраняем продажи в CSV
save_sales_to_csv(sales, 'sales.csv')
# Итоговая информация
print('\n' + '=' * 60)
print('Готово! Файлы созданы:')
print(f' 📁 Папка: {OUTPUT_FOLDER}')
print(f' 📄 products.csv - {len(products)} товаров')
print(f' 📄 sales.csv - {len(sales)} продаж')
print('\nИнструкция по импорту в DBeaver:')
print(' 1. Откройте таблицу products')
print(' 2. Правой кнопкой → Import Data')
print(' 3. Выберите файл products.csv')
print(' 4. Убедитесь, что разделитель = запятая')
print(' 5. Повторите для таблицы sales')
print('=' * 60)
except Exception as e:
# Если что-то пошло не так, выводим ошибку
print(f'\n[ОШИБКА] Что-то пошло не так: {e}')
print('Проверьте, что:')
print(' - Папка OUTPUT_FOLDER существует')
print(' - У вас есть права на запись в эту папку')
print(' - Файлы не открыты в другой программе')
sys.exit(1)