From 626ac21b41b1f323f29d044b70b82b1584448831 Mon Sep 17 00:00:00 2001 From: Vladlav064 Date: Thu, 12 Nov 2026 08:02:52 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BD=D0=B0=20=D0=B4=D0=B0=D0=BD=D0=BD=D0=BE=D0=BC=20?= =?UTF-8?q?=D1=8D=D1=82=D0=B0=D0=BF=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 + data/raw_orders.csv | 1501 +++++++++++++++++++++++++++++++ docs/ai_work_log.md | 37 + docs/implementation_plan.md | 51 ++ src/extract.py | 50 + src/generate_data.py | 72 ++ src/transform.py | 131 +++ tests/test_extract_transform.py | 118 +++ tests/test_generate_data.py | 20 + 9 files changed, 1986 insertions(+) create mode 100644 .gitignore create mode 100644 data/raw_orders.csv create mode 100644 docs/ai_work_log.md create mode 100644 docs/implementation_plan.md create mode 100644 src/extract.py create mode 100644 src/generate_data.py create mode 100644 src/transform.py create mode 100644 tests/test_extract_transform.py create mode 100644 tests/test_generate_data.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..45981e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.venv/ +__pycache__/ +.pytest_cache/ +.ruff_cache/ +*.pyc +.env \ No newline at end of file diff --git a/data/raw_orders.csv b/data/raw_orders.csv new file mode 100644 index 0000000..6c0c798 --- /dev/null +++ b/data/raw_orders.csv @@ -0,0 +1,1501 @@ +order_id,order_date,category,quantity,unit_price +718,2024-01-30,Электроника,8,1108.44 +1300,2024-01-03,Книги,3,1277.54 +883,2024-02-09,Спорт,4,3663.08 +1270,2024-01-03,Электроника,1,4454.29 +97,2024-01-01,Товары для дома,5,1748.80 +682,2024-03-02,Одежда,5,1095.19 +1290,2024-02-18,Товары для дома,7,4756.57 +1006,2024-02-24,Одежда,8,459.43 +1353,2024-03-11,Электроника,6,1491.57 +1235,2024-02-22,Спорт,7,2034.41 +598,2024-03-12,Одежда,3,3649.95 +403,2024-02-20,Одежда,6,4564.06 +1101,2024-01-03,Книги,4,3548.11 +481,2024-02-28,Одежда,4,193.91 +1267,2024-03-23,Книги,5,3180.48 +1032,2024-03-13,Электроника,2,3082.47 +1341,2024-01-19,Электроника,7,2472.91 +699,2024-03-08,Книги,1,177.34 +1224,2024-03-06,Электроника,8,4209.08 +169,2024-02-23,Книги,8,1322.63 +934,2024-02-03,Товары для дома,1,4299.64 +1362,2024-03-28,Электроника,1,2415.51 +53,2024-01-01,Книги,4,372.21 +1158,2024-02-27,Книги,8,1657.08 +1196,2024-01-14,Электроника,4,3230.19 +1407,2024-02-27,Книги,7,4664.24 +1388,2024-02-04,Электроника,6,2471.21 +286,2024-01-22,Одежда,1,3638.57 +260,2024-01-15,Книги,3,2419.32 +398,2024-01-15,Книги,6,2123.54 +871,2024-03-08,Книги,6,4659.48 +66,2024-03-11,Книги,1,513.33 +362,2024-01-12,Книги,2,4360.89 +798,2024-02-10,Товары для дома,5,617.94 +999,2024-03-31,Спорт,3,162.50 +387,2024-03-08,Одежда,1,3497.70 +1361,2024-02-01,Одежда,8,3971.35 +179,2024-01-26,Электроника,4,398.45 +997,2024-02-18,Книги,2,843.61 +1231,2024-03-28,Книги,8,806.12 +470,2024-01-19,Спорт,1,1527.11 +931,2024-01-28,Одежда,3,4382.66 +444,2024-03-27,Товары для дома,8,3772.71 +209,2024-01-13,Товары для дома,5,4365.38 +366,2024-03-26,Спорт,1,4934.24 +1438,2024-01-25,Электроника,7,2158.69 +1046,2024-01-08,Товары для дома,2,2701.50 +745,2024-01-22,Одежда,5,727.93 +1251,2024-01-21,Товары для дома,5,964.66 +750,2024-03-17,Товары для дома,8,3233.13 +225,2024-03-19,Книги,4,3880.20 +330,2024-01-13,Одежда,6,3594.94 +1266,2024-03-03,Книги,3,2892.35 +658,2024-01-25,Книги,1,4914.74 +1188,2024-02-15,Спорт,7,2947.97 +454,2024-02-27,Электроника,3,4746.42 +301,2024-03-25,Одежда,1,2075.17 +489,2024-02-13,Спорт,8,979.47 +1492,2024-01-14,Товары для дома,2,2544.74 +372,2024-02-24,Электроника,1,4431.05 +276,2024-01-13,Спорт,2,4583.28 +1444,2024-01-18,Книги,8,1518.32 +972,2024-01-17,Товары для дома,6,1786.56 +1414,2024-02-04,Спорт,6,4654.07 +967,2024-03-13,Товары для дома,8,4972.19 +383,2024-03-25,Спорт,8,2223.06 +1023,2024-01-16,Спорт,3,4976.83 +1100,2024-01-02,Одежда,4,4676.69 +2,01.02.2024,Товары для дома,2,1170.27 +3,2024-03-27,Электроника,2,3883.24 +1187,2024-03-05,Одежда,2,2844.22 +235,2024-01-10,Товары для дома,5,312.68 +508,2024-03-24,Товары для дома,2,1461.14 +191,2024-02-29,Электроника,7,2987.66 +91,2024-03-25,Товары для дома,5,543.10 +1313,2024-01-03,Электроника,7,1060.89 +1387,2024-01-24,Одежда,4,2876.06 +802,2024-03-03,Электроника,3,3331.86 +1131,2024-02-07,Одежда,7,1888.57 +1427,2024-03-11,Товары для дома,3,2332.27 +422,2024-01-11,Товары для дома,1,2645.29 +459,2024-01-29,Одежда,2,147.79 +432,2024-03-17,Книги,5,3716.34 +255,2024-01-07,Одежда,3,1067.24 +167,2024-02-18,Товары для дома,8,1716.28 +98,2024-01-21,Спорт,7,3886.19 +741,2024-03-11,Электроника,6,487.93 +410,2024-03-13,Электроника,8,3983.95 +23,2024-01-05,Одежда,7,4220.76 +405,2024-03-06,Книги,4,3319.93 +1103,2024-01-17,Спорт,5,538.49 +220,2024-01-11,Книги,7,1301.24 +227,2024-01-21,Товары для дома,2,1257.59 +309,2024-02-23,Товары для дома,3,920.86 +857,2024-03-23,Товары для дома,2,3550.15 +258,2024-03-31,Одежда,1,830.36 +774,2024-03-27,Электроника,3,3085.91 +861,2024-01-12,Книги,3,4700.73 +42,2024-01-15,Одежда,5,4871.16 +613,2024-02-12,Спорт,8,1491.93 +517,2024-02-08,Товары для дома,3,4463.59 +287,2024-01-06,Книги,5,1700.66 +1400,2024-01-28,Одежда,8,4266.76 +696,2024-01-22,Одежда,3,3841.84 +764,2024-01-31,Товары для дома,7,1324.78 +909,2024-02-29,Электроника,8,1930.82 +849,2024-02-24,Книги,2,3436.64 +1274,2024-02-20,Электроника,5,1170.77 +401,2024-02-25,Книги,4,4427.54 +335,2024-01-17,Электроника,4,4126.04 +1209,2024-01-06,Одежда,1,805.85 +701,2024-01-09,Товары для дома,1,3707.61 +417,2024-02-06,Книги,7,402.30 +413,2024-01-09,Спорт,5,2458.50 +1203,2024-01-25,Спорт,3,1542.34 +1156,2024-01-31,Спорт,8,413.30 +1206,2024-02-24,Электроника,5,1463.36 +76,2024-01-08,Книги,3,2662.49 +107,2024-01-21,Товары для дома,7,4193.72 +1280,2024-03-02,Одежда,1,4923.99 +157,2024-03-14,Спорт,1,1994.30 +59,2024-01-13,Спорт,6,508.20 +430,2024-02-12,Электроника,3,427.31 +102,2024-02-25,Книги,5,668.19 +968,2024-03-10,Товары для дома,8,1620.33 +407,2024-01-14,Одежда,4,767.79 +154,2024-02-23,Одежда,8,2137.00 +485,2024-03-23,Одежда,6,1001.94 +969,2024-01-05,Книги,4,473.21 +1016,2024-03-30,Книги,2,3499.46 +984,2024-03-04,Одежда,2,737.38 +1009,2024-03-01,Книги,5,4423.79 +1202,2024-03-22,Одежда,4,3694.29 +431,2024-02-14,Спорт,3,1626.51 +575,2024-01-03,Одежда,2,1931.26 +315,2024-01-02,Электроника,2,1386.53 +832,2024-02-21,Одежда,6,2703.30 +673,2024-02-08,Товары для дома,8,1973.74 +614,2024-01-18,Электроника,8,4462.01 +636,2024-01-08,Электроника,4,4345.17 +1289,2024-03-16,Книги,5,3425.37 +1005,2024-01-20,Одежда,1,285.77 +952,2024-01-26,Электроника,4,3369.36 +386,2024-03-27,Электроника,8,3557.23 +1043,2024-02-11,Спорт,2,1412.54 +1396,2024-01-02,Спорт,8,925.94 +1357,2024-02-22,Книги,2,2654.15 +1436,2024-02-08,Спорт,5,47.89 +1319,2024-02-17,Книги,7,4586.60 +995,2024-02-07,Одежда,5,105.27 +421,2024-01-08,Книги,6,2362.04 +516,2024-03-31,Электроника,5,340.47 +980,2024-02-18,Одежда,3,1608.64 +894,2024-01-08,Электроника,3,2619.40 +388,2024-01-29,Электроника,1,2072.32 +1212,2024-03-31,Электроника,4,1666.50 +733,2024-02-01,Книги,2,3738.75 +210,2024-01-30,Товары для дома,5,1892.06 +1052,2024-02-17,Спорт,7,4317.55 +544,2024-01-27,Электроника,3,1783.59 +763,2024-03-27,Товары для дома,5,2617.79 +293,2024-03-22,Спорт,5,1331.58 +991,2024-01-22,Спорт,8,4637.60 +395,2024-01-03,Книги,6,2410.44 +1155,2024-03-11,Спорт,7,3372.84 +1261,2024-02-24,Книги,6,884.16 +867,2024-03-07,Электроника,4,3182.05 +905,2024-02-21,Спорт,5,361.38 +679,2024-02-17,Электроника,4,484.78 +174,2024-01-17,Книги,4,2482.83 +1291,2024-02-15,Одежда,8,4284.68 +1098,2024-02-24,Спорт,1,2502.96 +433,2024-03-12,Одежда,3,1974.89 +1406,2024-02-06,Одежда,8,1861.80 +253,2024-02-22,Товары для дома,6,284.19 +842,2024-01-16,Товары для дома,8,4872.91 +1378,2024-03-03,Электроника,5,958.78 +49,2024-02-09,Одежда,8,2091.87 +441,2024-03-17,Товары для дома,3,1975.10 +958,2024-03-17,Книги,5,1148.30 +11,2024-01-13, тОВАРЫ для ДОМА ,5,1882.09 +1199,2024-02-18,Спорт,6,2031.91 +518,2024-02-16,Товары для дома,2,2667.35 +681,2024-01-10,Электроника,4,986.52 +649,2024-03-23,Спорт,6,3823.58 +346,2024-03-06,Книги,8,3112.52 +595,2024-02-07,Одежда,2,220.55 +21,2024-02-29,Одежда,4,1989.44 +88,2024-02-26,Электроника,5,4371.23 +957,2024-02-04,Книги,6,942.76 +94,2024-02-13,Одежда,8,1067.44 +785,2024-01-30,Спорт,3,4182.54 +1191,2024-01-04,Товары для дома,1,1996.86 +1459,2024-02-28,Книги,4,3855.21 +15,2024-03-31,Книги,три,364.67 +445,2024-03-19,Спорт,4,1493.85 +919,2024-03-22,Спорт,2,4298.97 +1173,2024-01-14,Спорт,1,3969.65 +988,2024-02-01,Книги,6,3634.24 +981,2024-01-23,Книги,7,3821.18 +1278,2024-03-06,Спорт,3,735.95 +559,2024-02-25,Одежда,6,702.21 +450,2024-01-26,Электроника,3,4200.91 +321,2024-03-30,Спорт,2,1584.35 +606,2024-03-24,Одежда,6,1045.73 +364,2024-03-12,Электроника,6,314.48 +813,2024-03-14,Одежда,6,598.78 +824,2024-03-31,Одежда,5,1534.25 +1434,2024-02-14,Спорт,1,1503.47 +1049,2024-01-23,Товары для дома,1,1010.64 +572,2024-01-21,Электроника,5,2060.22 +262,2024-02-23,Спорт,8,4376.36 +1485,2024-03-20,Одежда,2,1251.93 +672,2024-03-30,Спорт,3,2182.75 +800,2024-03-06,Спорт,3,2036.67 +1349,2024-01-31,Книги,3,3434.19 +1096,2024-03-03,Книги,3,4116.77 +123,2024-02-02,Спорт,1,232.72 +515,2024-03-28,Одежда,6,4432.97 +720,2024-03-10,Книги,3,2954.42 +1024,2024-01-22,Товары для дома,3,1110.92 +697,2024-01-18,Электроника,8,3776.52 +933,2024-02-20,Книги,5,2692.01 +1467,2024-01-06,Спорт,3,3046.52 +1399,2024-02-20,Товары для дома,1,518.92 +1358,2024-03-05,Одежда,2,1542.73 +1338,2024-01-28,Спорт,6,2665.19 +236,2024-03-17,Электроника,5,3926.70 +69,2024-02-21,Книги,3,4731.40 +690,2024-01-06,Товары для дома,3,2949.25 +778,2024-02-28,Товары для дома,8,4111.84 +10,03/15/2024,Книги,7,535.88 +1122,2024-03-07,Одежда,7,1057.35 +164,2024-02-20,Электроника,1,3103.23 +168,2024-02-11,Спорт,5,1769.48 +1186,2024-03-28,Товары для дома,3,1810.34 +128,2024-02-21,Одежда,3,3655.39 +1113,2024-01-07,Книги,7,3616.28 +1204,2024-03-28,Товары для дома,3,390.44 +771,2024-02-25,Книги,2,555.46 +390,2024-01-18,Одежда,5,4006.41 +910,2024-01-26,Электроника,4,3840.22 +1055,2024-01-23,Спорт,6,4563.83 +1440,2024-02-18,Одежда,2,1156.54 +246,2024-03-01,Спорт,6,1824.32 +503,2024-03-23,Одежда,7,1920.62 +1001,2024-01-07,Одежда,4,530.89 +3,2024-03-27,Электроника,3,3883.24 +490,2024-02-17,Электроника,5,4846.05 +674,2024-01-31,Товары для дома,5,4453.00 +114,2024-01-04,Товары для дома,7,3460.48 +1054,2024-01-31,Спорт,8,1952.80 +1254,2024-02-07,Книги,5,3521.57 +165,2024-01-11,Спорт,3,3369.86 +532,2024-01-11,Товары для дома,5,1313.90 +911,2024-01-20,Спорт,1,33.44 +1238,2024-03-23,Электроника,2,3776.38 +737,2024-02-16,Одежда,2,3991.65 +678,2024-02-12,Книги,8,3390.61 +264,2024-02-19,Электроника,3,999.35 +912,2024-01-30,Одежда,1,2839.26 +202,2024-03-11,Одежда,7,2783.56 +103,2024-02-16,Книги,6,4713.26 +252,2024-03-25,Электроника,8,4557.99 +36,2024-03-23,Книги,5,1783.50 +730,2024-03-03,Одежда,7,1196.11 +612,2024-02-08,Электроника,2,2977.76 +1011,2024-01-12,Электроника,7,3365.23 +1087,2024-01-16,Книги,2,2466.97 +1183,2024-01-18,Спорт,5,724.47 +171,2024-02-14,Книги,1,1175.57 +839,2024-03-13,Спорт,6,4469.15 +1197,2024-03-23,Спорт,4,2942.68 +873,2024-02-16,Товары для дома,8,2864.49 +1008,2024-01-22,Одежда,2,3345.96 +746,2024-02-04,Товары для дома,2,2585.39 +83,2024-02-28,Книги,1,1657.68 +449,2024-03-05,Спорт,3,2765.41 +1130,2024-01-07,Товары для дома,1,523.29 +1425,2024-01-04,Одежда,6,184.32 +142,2024-02-01,Книги,2,773.70 +541,2024-03-02,Спорт,5,1283.87 +1435,2024-03-29,Спорт,8,4834.40 +240,2024-02-25,Одежда,5,3851.08 +1012,2024-01-24,Спорт,6,1945.22 +1115,2024-03-01,Товары для дома,4,3679.82 +973,2024-01-08,Товары для дома,3,114.89 +1258,2024-01-25,Спорт,8,1666.12 +767,2024-03-10,Одежда,2,192.76 +536,2024-03-13,Книги,2,215.98 +1287,2024-03-15,Электроника,3,2853.97 +1381,2024-03-10,Электроника,6,170.73 +62,2024-02-21,Одежда,2,3817.94 +1013,2024-01-24,Книги,5,2337.15 +795,2024-03-16,Одежда,6,598.91 +683,2024-01-02,Товары для дома,2,1110.49 +732,2024-01-11,Электроника,5,4048.39 +13,2024-02-18,Электроника,0,413.14 +492,2024-02-25,Товары для дома,5,3158.60 +99,2024-03-12,Книги,2,50.70 +1260,2024-03-13,Одежда,6,2971.99 +146,2024-02-26,Электроника,7,3212.07 +551,2024-02-11,Электроника,6,4895.55 +1255,2024-01-13,Электроника,8,1035.04 +584,2024-03-05,Электроника,8,4063.09 +1141,2024-03-13,Спорт,3,3556.74 +269,2024-02-14,Электроника,7,1742.95 +573,2024-03-16,Электроника,8,1763.57 +1311,2024-03-17,Книги,4,3096.43 +1445,2024-03-20,Одежда,8,2373.81 +35,2024-03-28,Электроника,5,4638.84 +740,2024-01-14,Книги,6,137.69 +560,2024-02-21,Книги,7,1918.57 +966,2024-03-31,Одежда,3,3672.96 +1375,2024-03-19,Электроника,1,34.73 +1225,2024-03-03,Одежда,4,229.91 +173,2024-01-03,Товары для дома,4,3257.59 +267,2024-02-08,Электроника,8,4390.79 +591,2024-03-31,Спорт,6,1086.74 +1021,2024-03-01,Книги,7,1566.85 +1249,2024-01-27,Одежда,4,3923.23 +237,2024-02-26,Спорт,5,651.98 +888,2024-01-21,Одежда,5,2602.64 +939,2024-03-28,Книги,5,3304.58 +601,2024-03-22,Книги,2,1220.74 +1334,2024-01-23,Электроника,8,1940.99 +1242,2024-02-18,Книги,8,2071.54 +1165,2024-03-25,Спорт,2,1904.71 +172,2024-01-04,Товары для дома,4,4978.71 +522,2024-03-19,Книги,2,4636.00 +468,2024-01-09,Спорт,1,581.22 +775,2024-02-14,Спорт,2,128.78 +1022,2024-01-05,Электроника,3,2632.46 +552,2024-01-25,Товары для дома,8,4000.96 +352,2024-03-18,Книги,3,777.70 +1463,2024-02-16,Книги,4,4653.16 +22,2024-03-28,Книги,4,1700.18 +345,2024-01-09,Спорт,6,2496.91 +710,2024-02-15,Спорт,2,1401.56 +1466,2024-02-20,Товары для дома,1,4137.39 +1160,2024-02-22,Электроника,1,187.87 +1395,2024-01-19,Товары для дома,8,4194.51 +850,2024-01-01,Спорт,2,1118.95 +880,2024-02-05,Одежда,2,438.29 +272,2024-01-30,Спорт,1,4487.98 +1133,2024-01-18,Товары для дома,6,3477.90 +660,2024-01-31,Книги,2,1080.47 +423,2024-02-01,Спорт,8,3709.98 +200,2024-02-21,Товары для дома,5,3626.96 +760,2024-03-16,Товары для дома,1,2860.67 +1482,2024-02-07,Одежда,6,4296.12 +273,2024-02-10,Спорт,7,3904.40 +1385,2024-03-27,Спорт,2,1104.95 +155,2024-02-23,Товары для дома,7,326.46 +779,2024-02-14,Книги,8,2496.47 +205,2024-01-16,Товары для дома,6,3781.61 +652,2024-03-24,Одежда,2,4510.38 +1039,2024-01-07,Спорт,5,3909.88 +1437,2024-03-17,Книги,6,1212.24 +1066,2024-02-02,Спорт,5,1144.05 +1301,2024-03-14,Товары для дома,3,3105.61 +1449,2024-02-26,Книги,4,3812.14 +715,2024-01-08,Книги,3,3338.40 +177,2024-01-21,Книги,1,1630.74 +791,2024-03-09,Одежда,6,980.21 +108,2024-01-04,Одежда,7,940.37 +514,2024-02-27,Спорт,2,905.22 +698,2024-01-24,Электроника,1,4807.20 +226,2024-03-21,Одежда,2,1106.38 +998,2024-02-13,Электроника,8,3114.44 +501,2024-01-19,Электроника,8,4449.48 +197,2024-03-12,Товары для дома,8,3974.97 +962,2024-03-09,Книги,4,4279.69 +1056,2024-02-14,Одежда,2,857.29 +1265,2024-02-29,Одежда,5,2334.96 +305,2024-03-27,Товары для дома,2,3961.41 +75,2024-03-05,Электроника,3,4820.24 +44,2024-01-11,Спорт,2,3837.31 +451,2024-02-02,Спорт,6,273.58 +526,2024-02-29,Электроника,6,3565.73 +1367,2024-02-06,Спорт,6,487.64 +580,2024-01-19,Товары для дома,1,4307.88 +617,2024-01-24,Одежда,5,73.85 +1431,2024-01-12,Одежда,6,2316.63 +1195,2024-03-10,Одежда,8,169.38 +765,2024-02-18,Спорт,8,387.80 +1397,2024-01-18,Электроника,6,4683.31 +992,2024-02-18,Электроника,7,1841.47 +229,2024-03-29,Спорт,5,3113.29 +1423,2024-01-30,Одежда,8,1297.08 +1405,2024-03-14,Электроника,3,3019.35 +555,2024-03-05,Товары для дома,4,2397.21 +1276,2024-02-20,Электроника,1,1907.71 +735,2024-02-19,Одежда,2,1660.31 +661,2024-03-17,Спорт,1,176.04 +834,2024-03-15,Книги,8,12.97 +944,2024-02-20,Книги,7,2601.14 +1326,2024-03-14,Книги,1,2739.62 +132,2024-02-06,Спорт,6,1101.97 +520,2024-03-04,Одежда,6,137.81 +1457,2024-01-16,Товары для дома,7,4027.30 +1088,2024-03-01,Товары для дома,2,4663.16 +608,2024-03-24,Товары для дома,1,3010.12 +130,2024-03-19,Одежда,7,2983.76 +878,2024-02-24,Спорт,2,3679.51 +897,2024-03-06,Книги,1,13.47 +460,2024-03-24,Товары для дома,3,1757.89 +259,2024-03-11,Книги,4,2139.05 +65,2024-02-29,Книги,8,1309.71 +43,2024-01-31,Товары для дома,2,303.70 +249,2024-03-25,Спорт,1,4955.54 +753,2024-02-12,Одежда,7,2308.04 +1420,2024-01-04,Спорт,2,4041.92 +667,2024-02-08,Товары для дома,4,4594.12 +949,2024-03-23,Спорт,7,1547.21 +1175,2024-02-27,Электроника,3,2398.03 +150,2024-02-26,Одежда,4,406.24 +337,2024-03-16,Товары для дома,6,1132.21 +27,2024-01-18,Электроника,5,1293.04 +1091,2024-01-26,Спорт,8,1796.58 +1104,2024-01-22,Книги,5,342.30 +686,2024-02-06,Спорт,6,1995.77 +1162,2024-03-06,Товары для дома,8,2948.25 +705,2024-03-09,Спорт,1,2892.21 +1452,2024-03-04,Спорт,1,1947.20 +983,2024-03-19,Электроника,2,1158.13 +825,2024-03-06,Книги,3,4719.41 +1448,2024-01-14,Одежда,3,1479.10 +1230,2024-03-09,Спорт,2,4345.03 +156,2024-02-19,Электроника,1,4743.96 +789,2024-01-01,Товары для дома,4,3694.85 +979,2024-01-29,Электроника,3,3489.31 +356,2024-01-13,Книги,4,4158.11 +19,2024-03-26,Книги,3,12.340 +1108,2024-02-21,Товары для дома,4,100.27 +986,2024-03-22,Книги,6,4641.63 +1135,2024-01-12,Одежда,3,1953.62 +469,2024-03-21,Электроника,4,2702.06 +629,2024-03-19,Товары для дома,3,1157.89 +216,2024-01-24,Одежда,8,269.37 +1241,2024-03-18,Одежда,8,453.54 +376,2024-03-13,Спорт,2,3446.38 +719,2024-02-04,Книги,5,244.12 +447,2024-01-25,Электроника,8,1928.55 +263,2024-02-01,Электроника,3,2394.88 +233,2024-02-24,Электроника,4,601.76 +797,2024-03-19,Книги,3,3094.19 +1019,2024-02-19,Электроника,7,4290.81 +758,2024-02-11,Одежда,7,1971.62 +274,2024-02-19,Товары для дома,8,3479.65 +1455,2024-03-10,Товары для дома,4,2464.93 +28,2024-03-15,Электроника,7,2246.23 +1350,2024-01-12,Спорт,6,1446.71 +892,2024-02-03,Электроника,2,3734.63 +1040,2024-02-17,Одежда,5,106.08 +1284,2024-02-19,Спорт,1,2462.45 +1317,2024-03-02,Товары для дома,1,3457.12 +567,2024-01-26,Электроника,7,3158.65 +602,2024-02-22,Спорт,2,1731.69 +1237,2024-03-27,Электроника,1,620.27 +343,2024-01-03,Одежда,1,913.56 +1069,2024-03-12,Электроника,5,3393.94 +380,2024-02-09,Одежда,2,3783.25 +1471,2024-03-23,Электроника,2,806.98 +1236,2024-02-25,Книги,7,1658.87 +126,2024-03-20,Книги,5,1645.83 +1239,2024-03-15,Книги,6,238.64 +1185,2024-01-26,Электроника,5,1099.50 +1275,2024-01-18,Электроника,2,2436.38 +1324,2024-03-17,Электроника,7,4596.79 +1081,2024-02-07,Спорт,3,511.73 +895,2024-02-17,Одежда,2,824.76 +889,2024-01-22,Товары для дома,5,1949.67 +144,2024-01-07,Товары для дома,2,2922.42 +1201,2024-01-26,Спорт,4,3253.54 +799,2024-01-15,Одежда,3,959.69 +1417,2024-01-31,Одежда,2,3470.91 +1030,2024-03-30,Электроника,7,3266.62 +502,2024-01-05,Книги,4,661.81 +374,2024-01-24,Одежда,6,1432.73 +1163,2024-02-13,Книги,4,1904.97 +41,2024-03-17,Спорт,1,1699.52 +354,2024-02-18,Электроника,5,3132.06 +1369,2024-02-10,Товары для дома,5,1853.56 +887,2024-01-02,Товары для дома,1,3711.44 +1322,2024-02-24,Одежда,8,701.94 +1393,2024-01-13,Одежда,4,3779.15 +361,2024-01-20,Спорт,2,3600.93 +248,2024-02-12,Спорт,5,2158.21 +1429,2024-03-08,Электроника,5,3354.33 +858,2024-03-08,Книги,3,2953.27 +1243,2024-01-21,Товары для дома,3,2796.91 +671,2024-01-07,Спорт,6,1805.73 +893,2024-03-04,Электроника,6,2386.55 +935,2024-02-21,Спорт,4,4480.58 +1340,2024-02-19,Книги,2,3333.83 +568,2024-03-05,Книги,1,211.06 +757,2024-02-07,Спорт,4,3852.86 +257,2024-01-04,Электроника,4,3303.14 +668,2024-02-22,Одежда,1,1575.50 +884,2024-01-08,Книги,7,1281.78 +375,2024-02-14,Товары для дома,3,32.02 +1495,2024-01-07,Товары для дома,2,1920.76 +256,2024-02-06,Спорт,2,2297.00 +531,2024-02-14,Электроника,5,4967.55 +,2024-03-16,Книги,1,2212.09 +1344,2024-02-19,Спорт,5,4879.47 +865,2024-03-11,Электроника,5,3150.97 +659,2024-03-25,Электроника,1,2198.03 +637,2024-03-14,Товары для дома,3,2813.65 +960,2024-03-15,Товары для дома,2,1220.35 +896,2024-03-14,Книги,4,3534.34 +461,2024-01-05,Спорт,3,1504.08 +435,2024-03-23,Спорт,6,3515.13 +1189,2024-03-27,Электроника,5,3920.99 +891,2024-02-24,Электроника,2,1425.97 +436,2024-01-03,Одежда,3,1901.06 +863,2024-02-24,Товары для дома,4,1648.91 +426,2024-02-22,Одежда,1,4060.43 +141,2024-03-30,Спорт,7,2013.16 +1097,2024-03-19,Спорт,7,2273.90 +1176,2024-03-04,Электроника,1,22.75 +448,2024-02-29,Одежда,7,4034.08 +596,2024-02-14,Спорт,5,4844.48 +650,2024-02-18,Спорт,7,2351.63 +17,2024-02-18,Спорт,6,-12.50 +74,2024-02-10,Книги,8,299.72 +942,2024-02-12,Книги,8,642.40 +937,2024-01-16,Книги,4,62.69 +129,2024-01-25,Спорт,3,2204.35 +296,2024-02-26,Одежда,5,4955.76 +820,2024-03-31,Спорт,3,3199.53 +428,2024-02-12,Электроника,7,495.49 +956,2024-03-13,Одежда,7,2633.99 +1410,2024-03-21,Электроника,7,3210.27 +193,2024-01-24,Товары для дома,6,2556.17 +578,2024-01-07,Спорт,3,3059.35 +915,2024-02-23,Книги,3,2579.65 +599,2024-01-14,Электроника,4,3227.93 +1470,2024-03-21,Книги,6,314.85 +853,2024-02-24,Товары для дома,8,3638.73 +1364,2024-02-18,Товары для дома,4,3322.22 +963,2024-03-14,Спорт,6,4438.13 +392,2024-02-25,Товары для дома,7,921.15 +326,2024-03-16,Товары для дома,7,3891.63 +404,2024-01-29,Товары для дома,2,1746.24 +1488,2024-03-19,Одежда,7,1484.42 +1031,2024-01-15,Товары для дома,3,2172.88 +801,2024-03-14,Спорт,3,2011.75 +1037,2024-01-19,Электроника,7,3241.39 +113,2024-02-09,Товары для дома,4,4301.98 +368,2024-01-14,Электроника,4,3029.96 +899,2024-02-19,Товары для дома,3,4824.64 +55,2024-01-10,Товары для дома,5,2695.66 +1147,2024-03-26,Одежда,2,4577.79 +64,2024-01-18,Товары для дома,5,2211.88 +302,2024-03-13,Книги,8,4535.45 +836,2024-03-09,Товары для дома,7,2085.18 +1062,2024-03-23,Электроника,1,908.08 +523,2024-02-09,Спорт,7,2087.51 +9,2024-02-30,Товары для дома,4,1456.85 +1286,2024-03-31,Одежда,5,2733.50 +353,2024-02-05,Электроника,6,3273.23 +604,2024-03-11,Товары для дома,7,4871.16 +1151,2024-01-02,Товары для дома,8,3373.27 +833,2024-02-28,Спорт,6,4982.07 +907,2024-01-02,Товары для дома,5,1934.20 +1053,2024-03-22,Книги,1,4839.57 +201,2024-03-25,Одежда,8,3049.03 +458,2024-01-31,Книги,6,2024.63 +400,2024-03-29,Одежда,2,4822.25 +1159,2024-03-24,Книги,7,93.17 +1494,2024-03-08,Электроника,5,3271.41 +1126,2024-01-15,Электроника,8,4379.03 +1210,2024-03-21,Одежда,8,3227.57 +151,2024-02-04,Одежда,2,1760.81 +1428,2024-03-16,Спорт,7,3133.07 +143,2024-02-24,Товары для дома,8,1147.36 +1014,2024-02-26,Одежда,4,2438.25 +93,2024-02-04,Электроника,4,1477.22 +63,2024-02-01,Товары для дома,8,1004.49 +1218,2024-03-12,Книги,2,4318.50 +754,2024-03-24,Товары для дома,5,3116.34 +947,2024-02-05,Товары для дома,1,2338.39 +923,2024-03-30,Одежда,6,4361.56 +72,2024-01-20,Одежда,4,995.62 +1325,2024-02-02,Книги,1,991.02 +788,2024-02-27,Спорт,7,3104.86 +1226,2024-02-11,Книги,2,2694.29 +1314,2024-01-20,Книги,3,3174.63 +1298,2024-03-02,Спорт,8,3540.15 +812,2024-02-28,Спорт,2,2965.64 +1018,2024-03-16,Электроника,1,1192.83 +475,2024-03-22,Одежда,5,2028.38 +708,2024-01-14,Книги,3,4813.22 +499,2024-03-26,Спорт,6,3342.41 +694,2024-03-04,Одежда,5,2886.96 +80,2024-02-23,Электроника,6,3446.53 +178,2024-02-09,Спорт,7,3018.84 +242,2024-03-27,Одежда,1,4306.62 +558,2024-02-06,Книги,2,2813.81 +351,2024-02-26,Книги,6,385.31 +581,2024-03-14,Книги,4,4971.33 +1389,2024-02-16,Спорт,7,964.24 +442,2024-01-24,Электроника,3,4358.65 +809,2024-03-03,Книги,1,876.89 +759,2024-02-16,Электроника,4,597.11 +804,2024-01-18,Одежда,8,980.94 +394,2024-01-10,Книги,7,2081.80 +829,2024-03-27,Спорт,3,2058.89 +1139,2024-01-17,Книги,3,3649.94 +562,2024-02-16,Одежда,2,2900.49 +250,2024-02-28,Одежда,5,461.69 +245,2024-03-22,Спорт,2,4268.99 +1376,2024-03-02,Электроника,6,1946.14 +605,2024-03-24,Спорт,8,4902.27 +411,2024-03-28,Электроника,6,4845.23 +1035,2024-02-03,Спорт,8,2008.26 +1269,2024-02-03,Книги,2,3845.89 +773,2024-02-27,Спорт,8,1961.47 +691,2024-01-03,Электроника,7,1367.21 +570,2024-03-07,Товары для дома,3,2395.94 +334,2024-01-07,Одежда,4,4125.07 +110,2024-01-21,Книги,7,4128.12 +1136,2024-01-15,Спорт,8,2101.23 +815,2024-03-10,Спорт,2,1145.79 +1433,2024-01-02,Электроника,3,0.49 +131,2024-03-11,Книги,5,4371.13 +1002,2024-01-25,Электроника,5,2145.81 +1421,2024-01-14,Электроника,3,1376.01 +357,2024-02-25,Товары для дома,7,2367.45 +1257,2024-01-13,Электроника,2,1494.94 +1051,2024-02-16,Товары для дома,5,3694.32 +1164,2024-02-28,Электроника,8,1740.50 +835,2024-03-22,Одежда,2,3482.03 +360,2024-02-24,Одежда,6,1638.53 +116,2024-02-14,Электроника,7,3363.22 +487,2024-01-16,Одежда,7,4190.86 +1025,2024-01-06,Книги,7,2219.71 +1331,2024-03-13,Электроника,5,1917.03 +500,2024-03-06,Товары для дома,1,4200.06 +1010,2024-02-20,Спорт,5,2469.11 +1041,2024-02-09,Спорт,2,1469.83 +838,2024-02-06,Электроника,3,1770.81 +188,2024-02-29,Электроника,5,2283.67 +1403,2024-02-14,Спорт,2,104.00 +373,2024-03-18,Одежда,1,4871.08 +265,2024-01-09,Спорт,6,1448.34 +1386,2024-02-11,Электроника,5,1895.84 +148,2024-02-27,Товары для дома,5,1358.92 +230,2024-01-05,Одежда,5,1213.61 +238,2024-03-30,Одежда,8,2110.31 +482,2024-01-06,Товары для дома,1,4147.70 +831,2024-02-18,Книги,2,2369.74 +755,2024-02-11,Товары для дома,8,3162.57 +959,2024-01-08,Спорт,6,4239.68 +100,2024-03-29,Товары для дома,1,4739.19 +480,2024-03-21,Товары для дома,2,955.67 +1244,2024-02-20,Электроника,5,4177.38 +135,2024-02-06,Электроника,6,2702.46 +1248,2024-03-09,Книги,5,2870.62 +1442,2024-02-06,Электроника,5,3399.79 +1093,2024-03-03,Товары для дома,6,1743.94 +1145,2024-01-06,Одежда,6,332.07 +203,2024-03-11,Одежда,8,1734.30 +1082,2024-02-03,Электроника,3,3802.07 +725,2024-02-19,Товары для дома,2,3961.57 +365,2024-03-26,Спорт,6,640.85 +632,2024-03-09,Товары для дома,4,4204.77 +457,2024-01-07,Электроника,6,2078.57 +948,2024-03-19,Электроника,4,3246.27 +607,2024-02-06,Книги,2,3386.37 +611,2024-02-07,Книги,1,4596.66 +1305,2024-03-25,Одежда,2,4306.00 +1380,2024-01-07,Спорт,8,2756.37 +437,2024-01-28,Спорт,4,1791.67 +781,2024-02-08,Книги,1,234.27 +56,2024-03-26,Товары для дома,3,2544.97 +393,2024-03-09,Товары для дома,6,3689.27 +1245,2024-03-22,Одежда,6,755.38 +1373,2024-03-20,Товары для дома,2,1866.61 +1112,2024-03-17,Книги,1,251.46 +841,2024-01-13,Спорт,1,3282.99 +134,2024-03-06,Товары для дома,2,2480.86 +1065,2024-02-18,Одежда,8,4464.77 +1412,2024-01-30,Одежда,5,1643.55 +554,2024-03-04,Книги,7,121.52 +1259,2024-02-12,Одежда,3,3231.41 +628,2024-03-29,Одежда,6,615.60 +739,2024-02-27,Электроника,7,2920.21 +73,2024-01-08,Электроника,1,3036.58 +1475,2024-01-18,Книги,8,1314.38 +1003,2024-03-25,Одежда,4,1476.00 +588,2024-03-10,Одежда,8,1857.49 +291,2024-03-20,Товары для дома,8,3581.60 +1491,2024-01-17,Книги,4,936.59 +1070,2024-02-08,Книги,8,3588.30 +716,2024-01-11,Спорт,6,2620.70 +563,2024-02-19,Спорт,5,2643.76 +877,2024-03-21,Электроника,7,3736.23 +1240,2024-03-05,Одежда,5,556.46 +212,2024-02-05,Книги,5,238.64 +105,2024-02-15,Электроника,7,4090.06 +527,2024-01-17,Электроника,3,4603.04 +1302,2024-03-21,Книги,3,2687.73 +638,2024-02-11,Электроника,8,4479.97 +16,2024-02-07,Товары для дома,2,0 +118,2024-01-04,Одежда,3,604.73 +281,2024-02-12,Электроника,7,4468.73 +162,2024-03-26,Спорт,3,3560.66 +822,2024-01-08,Одежда,5,2988.84 +280,2024-03-20,Спорт,2,3631.77 +879,2024-03-22,Электроника,3,2813.15 +688,2024-02-29,Спорт,6,517.00 +452,2024-03-11,Книги,2,4898.39 +327,2024-03-24,Спорт,7,519.16 +1309,2024-03-20,Электроника,5,3458.46 +1177,2024-03-11,Спорт,8,4903.71 +294,2024-03-26,Спорт,5,49.27 +198,2024-03-23,Спорт,8,3732.11 +206,2024-01-16,Электроника,3,3894.84 +329,2024-02-26,Товары для дома,6,2331.49 +1075,2024-01-04,Спорт,5,3436.19 +655,2024-02-08,Электроника,7,2427.48 +1208,2024-01-01,Спорт,3,1733.11 +1454,2024-02-04,Спорт,2,4066.66 +1365,2024-02-10,Одежда,1,2162.30 +1282,2024-01-09,Одежда,4,1386.00 +1182,2024-02-20,Одежда,8,1797.57 +1330,2024-02-03,Электроника,7,4482.55 +1038,2024-01-05,Одежда,4,2075.10 +48,2024-01-28,Электроника,4,4870.19 +1285,2024-01-06,Одежда,2,3400.14 +1068,2024-03-15,Книги,6,536.42 +918,2024-01-11,Книги,3,3044.92 +609,2024-01-23,Товары для дома,1,2190.93 +1273,2024-03-24,Товары для дома,6,4064.27 +46,2024-03-25,Электроника,3,2491.85 +319,2024-03-20,Электроника,6,4635.94 +5,,Товары для дома,1,1146.30 +119,2024-03-15,Книги,2,1391.81 +535,2024-01-22,Электроника,6,3453.66 +731,2024-02-25,Электроника,7,973.81 +397,2024-03-31,Спорт,5,3356.19 +471,2024-03-19,Товары для дома,7,1849.78 +987,2024-02-27,Одежда,4,59.75 +844,2024-03-29,Товары для дома,5,4767.92 +211,2024-01-02,Электроника,3,3712.06 +1117,2024-02-23,Книги,5,3469.03 +693,2024-01-30,Электроника,4,1991.46 +529,2024-01-16,Товары для дома,5,2716.35 +1090,2024-02-22,Товары для дома,3,283.45 +1310,2024-01-22,Одежда,8,3796.83 +1057,2024-02-06,Спорт,1,143.92 +318,2024-03-27,Спорт,1,1225.91 +537,2024-01-06,Электроника,5,4973.12 +751,2024-01-14,Одежда,1,710.36 +1430,2024-02-14,Электроника,4,2525.25 +1250,2024-02-22,Электроника,3,4748.10 +195,2024-03-12,Электроника,4,53.23 +158,2024-02-15,Спорт,7,1565.60 +1479,2024-02-11,Спорт,5,3839.90 +495,2024-03-18,Спорт,3,3762.63 +1172,2024-02-28,Товары для дома,6,4323.04 +1370,2024-01-24,Товары для дома,8,4965.63 +1342,2024-01-12,Одежда,4,335.97 +95,2024-02-02,Книги,2,4746.58 +862,2024-01-16,Спорт,6,3091.07 +39,2024-03-21,Электроника,4,1564.72 +384,2024-02-23,Товары для дома,2,1432.19 +1374,2024-01-16,Книги,8,4824.44 +946,2024-01-28,Товары для дома,5,4762.14 +1047,2024-02-09,Электроника,7,3720.19 +929,2024-02-09,Одежда,2,2429.28 +222,2024-03-07,Спорт,8,1994.28 +310,2024-03-10,Одежда,5,4595.18 +463,2024-01-11,Одежда,4,99.64 +381,2024-01-07,Товары для дома,1,815.69 +1015,2024-02-04,Книги,3,2958.28 +898,2024-02-01,Спорт,6,241.08 +89,2024-03-19,Электроника,1,4231.53 +821,2024-03-19,Товары для дома,4,730.00 +279,2024-03-01,Одежда,6,4097.45 +33,2024-03-17,Электроника,5,2453.93 +727,2024-01-13,Товары для дома,2,2291.61 +323,2024-03-04,Книги,4,608.81 +723,2024-01-21,Товары для дома,7,2956.77 +856,2024-02-08,Спорт,3,718.96 +770,2024-03-13,Спорт,6,3381.83 +1496,2024-01-02,Товары для дома,8,4876.99 +964,2024-01-23,Товары для дома,6,923.28 +192,2024-02-13,Спорт,6,150.48 +399,2024-01-22,Электроника,8,4201.24 +621,2024-03-26,Товары для дома,3,500.10 +585,2024-01-08,Товары для дома,4,3700.30 +1335,2024-02-02,Электроника,6,1607.75 +548,2024-02-04,Товары для дома,3,2357.18 +642,2024-02-28,Книги,8,2956.52 +1315,2024-03-01,Электроника,1,4727.03 +348,2024-03-25,Одежда,8,2733.91 +32,2024-03-17,Спорт,7,333.07 +288,2024-02-15,Спорт,3,1965.25 +153,2024-03-29,Товары для дома,2,801.14 +68,2024-02-22,Спорт,4,2546.16 +902,2024-02-04,Книги,6,4330.61 +978,2024-03-24,Товары для дома,6,1663.48 +702,2024-01-28,Спорт,6,2651.50 +982,2024-03-13,Товары для дома,6,169.78 +1461,2024-01-21,Книги,2,3298.64 +20,2024-03-09,Товары для дома,3,"12,50" +549,2024-01-13,Одежда,7,265.32 +569,2024-01-18,Одежда,8,3741.28 +1167,2024-02-13,Одежда,6,1861.96 +695,2024-03-03,Спорт,8,3793.53 +743,2024-02-19,Книги,4,4085.10 +579,2024-03-08,Электроника,7,3297.91 +951,2024-02-05,Товары для дома,7,2008.11 +534,2024-03-05,Товары для дома,3,3360.63 +58,2024-03-01,Спорт,4,4233.61 +761,2024-02-29,Товары для дома,8,4943.91 +872,2024-02-10,Спорт,7,691.98 +803,2024-03-12,Спорт,5,874.86 +1366,2024-03-30,Одежда,4,4363.01 +344,2024-01-17,Спорт,2,3888.74 +550,2024-03-19,Товары для дома,3,2192.24 +472,2024-01-24,Книги,6,3483.22 +643,2024-03-27,Одежда,1,4230.57 +1489,2024-03-23,Книги,2,1688.34 +494,2024-03-25,Одежда,8,1550.01 +140,2024-02-23,Электроника,4,4645.91 +662,2024-02-01,Книги,7,3882.57 +955,2024-03-10,Спорт,6,4096.98 +1327,2024-02-18,Товары для дома,6,3431.71 +486,2024-01-07,Одежда,5,4539.28 +491,2024-01-11,Спорт,2,3812.01 +970,2024-03-18,Книги,8,1147.19 +1293,2024-03-05,Товары для дома,8,4624.40 +339,2024-02-05,Товары для дома,3,4500.53 +886,2024-02-09,Товары для дома,2,3492.18 +1192,2024-03-26,Товары для дома,5,296.78 +30,2024-01-12,Книги,2,3962.48 +1184,2024-03-30,Одежда,4,2872.09 +1426,2024-03-01,Электроника,5,2449.44 +916,2024-02-05,Книги,4,487.73 +1320,2024-01-30,Спорт,7,4920.04 +1044,2024-01-25,Товары для дома,7,2315.87 +870,2024-03-04,Электроника,2,3345.39 +703,2024-01-08,Электроника,8,3239.24 +117,2024-03-27,Электроника,6,4423.22 +1114,2024-02-26,Книги,5,1993.63 +786,2024-01-24,Спорт,7,3357.96 +332,2024-01-16,Спорт,2,4975.55 +941,2024-03-23,Товары для дома,2,2324.86 +1205,2024-03-06,Спорт,1,625.76 +1232,2024-02-12,Книги,2,198.64 +316,2024-02-28,Одежда,7,1932.46 +1083,2024-03-28,Одежда,2,4716.45 +1318,2024-03-27,Спорт,7,3403.85 +261,2024-03-04,Одежда,5,4889.79 +289,2024-02-01,Спорт,3,2784.85 +707,2024-01-03,Спорт,3,2633.06 +207,2024-01-25,Спорт,5,1134.48 +656,2024-03-09,Товары для дома,4,1988.51 +12,2024-01-06,Спорт,1.5,3825.90 +218,2024-03-03,Электроника,1,388.41 +1476,2024-03-31,Электроника,5,2293.77 +826,2024-02-22,Книги,5,4817.00 +1198,2024-03-06,Одежда,1,4112.03 +359,2024-02-23,Книги,6,3825.28 +1460,2024-03-01,Одежда,6,2040.13 +1268,2024-01-18,Электроника,4,4672.97 +1095,2024-02-05,Товары для дома,3,1097.77 +425,2024-02-16,Одежда,7,1954.82 +1036,2024-01-05,Одежда,7,902.83 +1391,2024-02-21,Спорт,3,1466.07 +994,2024-01-21,Книги,1,2366.05 +1153,2024-03-03,Товары для дома,6,1681.05 +1,2024-03-22,Книги,5,583.70 +121,2024-02-25,Электроника,2,3178.29 +1086,2024-02-03,Одежда,8,3070.08 +1356,2024-03-14,Одежда,3,24.39 +477,2024-01-31,Книги,6,3842.73 +25,2024-01-28,Спорт,7,3436.39 +122,2024-02-19,Электроника,4,4716.00 +2,2024-02-01,Товары для дома,2,1170.27 +1486,2024-03-25,Спорт,4,2825.69 +1392,2024-03-14,Электроника,4,687.55 +726,2024-03-05,Одежда,1,3908.34 +239,2024-02-26,Электроника,1,421.75 +1216,2024-03-25,Одежда,6,1740.03 +1060,2024-02-04,Спорт,1,3416.20 +828,2024-01-19,Одежда,4,3729.76 +1312,2024-02-18,Товары для дома,2,2020.94 +1477,2024-03-10,Спорт,4,3654.49 +648,2024-02-06,Товары для дома,3,2143.65 +747,2024-01-22,Одежда,7,2278.96 +533,2024-01-17,Одежда,2,3276.92 +509,2024-01-04,Товары для дома,8,3857.06 +577,2024-03-17,Одежда,4,2488.22 +630,2024-03-02,Спорт,6,803.14 +133,2024-02-29,Спорт,4,2316.24 +1116,2024-03-29,Книги,5,3595.26 +639,2024-01-15,Товары для дома,8,3566.71 +589,2024-01-25,Одежда,5,1292.48 +539,2024-03-20,Книги,8,3350.16 +266,2024-03-05,Книги,5,1400.76 +325,2024-02-17,Электроника,7,3515.28 +869,2024-03-04,Книги,4,2023.16 +1296,2024-02-16,Книги,4,3156.33 +1138,2024-02-23,Товары для дома,2,3401.83 +295,2024-03-27,Товары для дома,2,2865.75 +724,2024-03-27,Электроника,6,4700.38 +1105,2024-02-07,Электроника,6,2390.37 +1207,2024-01-17,Электроника,5,843.74 +1443,2024-02-22,Книги,3,985.67 +742,2024-02-19,Одежда,7,64.71 +496,2024-03-29,Одежда,8,2327.36 +976,2024-02-13,Одежда,3,1582.12 +542,2024-02-28,Книги,3,382.59 +1416,2024-01-22,Книги,3,964.10 +748,2024-02-08,Книги,6,2538.77 +1493,2024-01-05,Спорт,8,2993.75 +974,2024-01-15,Электроника,6,4438.00 +60,2024-02-24,Спорт,1,2155.36 +627,2024-02-09,Спорт,5,837.38 +928,2024-03-09,Товары для дома,8,1878.46 +1372,2024-01-27,Товары для дома,4,1201.71 +215,2024-03-01,Спорт,6,2509.85 +1329,2024-02-24,Электроника,5,3416.14 +1178,2024-03-06,Товары для дома,3,4095.12 +540,2024-03-21,Одежда,5,2860.40 +338,2024-01-19,Электроника,1,4134.77 +1129,2024-02-28,Одежда,8,1552.01 +1170,2024-02-02,Товары для дома,4,3202.63 +712,2024-03-30,Одежда,2,1184.15 +125,2024-02-16,Книги,6,2261.25 +138,2024-01-04,Товары для дома,8,242.30 +483,2024-02-10,Одежда,7,4893.56 +300,2024-03-14,Одежда,1,1551.23 +1125,2024-01-31,Книги,2,14.36 +564,2024-03-20,Электроника,2,3567.25 +864,2024-02-05,Книги,4,3379.67 +438,2024-01-29,Товары для дома,2,721.24 +777,2024-01-19,Одежда,6,1826.25 +70,2024-02-18,Спорт,5,11.31 +996,2024-01-14,Одежда,3,389.03 +1227,2024-01-22,Электроника,1,4054.42 +1377,2024-03-04,Электроника,5,4708.75 +1161,2024-01-12,Электроника,5,4151.23 +331,2024-01-08,Одежда,4,2087.40 +1354,2024-02-14,Одежда,4,4275.65 +1050,2024-02-01,Спорт,1,173.39 +96,2024-03-22,Одежда,1,2220.76 +37,2024-02-25,Спорт,1,829.21 +954,2024-02-18,Одежда,8,4099.50 +1071,2024-01-09,Одежда,6,1763.10 +593,2024-02-06,Электроника,7,638.98 +768,2024-01-14,Товары для дома,6,4031.54 +467,2024-02-23,Электроника,8,3349.95 +182,2024-03-09,Одежда,2,2245.94 +576,2024-02-23,Одежда,1,3060.29 +807,2024-03-12,Спорт,7,2649.96 +587,2024-02-10,Спорт,6,907.98 +610,2024-02-20,Спорт,3,4131.19 +1292,2024-03-02,Электроника,1,4651.34 +709,2024-01-31,Электроника,5,1006.28 +677,2024-02-19,Одежда,4,1273.28 +626,2024-01-08,Книги,8,3501.13 +1077,2024-02-19,Книги,4,3862.60 +1336,2024-03-31,Товары для дома,2,2087.35 +166,2024-02-29,Книги,5,952.79 +183,2024-03-05,Одежда,1,3395.00 +427,2024-03-21,Одежда,2,3393.75 +1253,2024-03-11,Электроника,2,2418.36 +1497,2024-01-11,Книги,6,2453.98 +1045,2024-03-03,Электроника,6,3981.94 +1404,2024-02-14,Электроника,4,4547.51 +340,2024-03-10,Товары для дома,2,1314.15 +306,2024-03-21,Книги,5,3388.69 +618,2024-03-13,Товары для дома,3,3535.81 +859,2024-02-26,Товары для дома,3,1841.24 +1033,2024-01-14,Книги,5,2950.09 +1215,2024-01-12,Товары для дома,4,1831.88 +1424,2024-03-21,Товары для дома,3,662.77 +1462,2024-02-02,Одежда,4,2523.03 +149,2024-03-07,Товары для дома,5,2540.69 +1211,2024-03-14,Одежда,2,2884.43 +358,2024-02-13,Спорт,7,4338.06 +669,2024-03-12,Электроника,3,4972.98 +57,2024-03-14,Спорт,4,3021.01 +152,2024-01-18,Товары для дома,7,790.79 +190,2024-02-05,Спорт,4,4622.29 +51,2024-01-29,Одежда,1,335.69 +213,2024-03-30,Товары для дома,8,4949.75 +1121,2024-01-26,Товары для дома,5,3192.93 +840,2024-03-28,Книги,1,3996.41 +571,2024-02-11,Электроника,6,4883.06 +644,2024-02-20,Книги,4,1334.09 +635,2024-01-10,Электроника,2,4809.01 +285,2024-01-13,Книги,8,3986.53 +1152,2024-03-04,Товары для дома,2,4558.25 +1017,2024-03-26,Электроника,7,1802.46 +251,2024-02-11,Спорт,1,607.77 +7,2024-01-29,Электроника,,2355.15 +615,2024-02-04,Книги,6,1010.89 +1379,2024-02-20,Электроника,3,1164.41 +1478,2024-03-13,Электроника,6,978.47 +1351,2024-03-26,Спорт,4,2071.69 +420,2024-02-11,Спорт,3,3171.98 +1368,2024-01-10,Товары для дома,8,2907.45 +488,2024-02-21,Спорт,7,3897.08 +676,2024-03-12,Спорт,3,2163.62 +1080,2024-01-22,Электроника,3,4407.37 +223,2024-02-08,Электроника,7,4509.76 +244,2024-02-05,Электроника,7,951.53 +1306,2024-01-28,Электроника,8,2355.12 +904,2024-01-29,Электроника,1,4217.05 +1490,2024-03-07,Книги,1,3454.49 +6,2024-03-12,,7,1042.49 +827,2024-03-24,Товары для дома,4,665.26 +557,2024-02-06,Электроника,8,2594.68 +511,2024-02-03,Одежда,8,4063.05 +1110,2024-01-16,Книги,7,1922.00 +1020,2024-03-09,Спорт,4,3602.22 +1465,2024-01-02,Одежда,2,877.46 +26,2024-03-23,Товары для дома,5,2405.71 +519,2024-01-26,Товары для дома,4,4152.71 +8,2024-01-01,Товары для дома,7, +1119,2024-01-14,Электроника,2,2682.18 +1148,2024-02-25,Товары для дома,8,3469.29 +241,2024-01-04,Товары для дома,1,478.82 +371,2024-01-15,Одежда,4,3998.25 +592,2024-03-02,Электроника,5,1828.74 +914,2024-01-14,Электроника,4,1075.81 +634,2024-03-08,Электроника,6,2342.25 +631,2024-02-23,Электроника,8,3680.20 +860,2024-02-22,Спорт,4,3230.02 +106,2024-03-20,Товары для дома,4,3929.43 +1487,2024-03-08,Спорт,3,3582.07 +297,2024-03-22,Одежда,8,2224.61 +333,2024-03-25,Электроника,1,1112.05 +308,2024-01-17,Одежда,6,472.56 +687,2024-02-09,Одежда,8,1371.34 +350,2024-02-21,Книги,8,2234.91 +1252,2024-01-25,Товары для дома,1,600.96 +1107,2024-03-03,Товары для дома,3,1883.51 +776,2024-02-07,Книги,2,2180.91 +328,2024-02-05,Одежда,4,171.53 +1411,2024-01-10,Товары для дома,6,414.78 +787,2024-01-04,Электроника,4,3866.04 +1328,2024-02-22,Книги,3,1899.34 +744,2024-03-14,Товары для дома,7,2732.09 +921,2024-03-25,Товары для дома,5,685.29 +234,2024-03-23,Одежда,3,781.14 +187,2024-03-19,Электроника,8,4237.30 +254,2024-03-20,Спорт,8,3965.95 +1128,2024-01-13,Одежда,4,4743.65 +664,2024-01-08,Электроника,5,734.72 +513,2024-01-26,Товары для дома,2,642.98 +1343,2024-02-17,Электроника,1,419.64 +932,2024-01-12,Товары для дома,6,3728.52 +414,2024-02-05,Книги,6,3100.75 +930,2024-01-12,Одежда,1,826.36 +1474,2024-01-25,Спорт,6,111.96 +52,2024-03-16,Товары для дома,4,2904.15 +924,2024-02-27,Товары для дома,2,1425.84 +1307,2024-03-02,Товары для дома,7,894.27 +847,2024-02-17,Товары для дома,1,3483.94 +1213,2024-01-24,Книги,3,2729.48 +278,2024-01-19,Товары для дома,2,2149.47 +369,2024-01-20,Спорт,4,3443.19 +232,2024-03-28,Одежда,4,1223.82 +1228,2024-03-21,Товары для дома,4,343.27 +186,2024-02-25,Электроника,5,923.47 +926,2024-01-20,Товары для дома,8,553.96 +950,2024-03-11,Книги,2,2511.51 +292,2024-03-15,Товары для дома,8,750.35 +1059,2024-01-29,Товары для дома,4,3394.14 +1279,2024-01-23,Электроника,3,2969.35 +603,2024-03-28,Товары для дома,1,4041.04 +866,2024-03-29,Одежда,4,140.64 +341,2024-03-25,Книги,3,4542.69 +945,2024-03-28,Товары для дома,6,2236.95 +586,2024-03-12,Спорт,7,1704.20 +624,2024-02-04,Одежда,1,1728.19 +816,2024-03-24,Товары для дома,5,3939.22 +901,2024-02-11,Спорт,6,1301.62 +597,2024-03-02,Товары для дома,5,1121.91 +1481,2024-01-09,Товары для дома,8,2784.69 +1332,2024-03-28,Товары для дома,7,2083.81 +1058,2024-01-21,Электроника,4,4476.49 +1132,2024-01-15,Книги,1,498.07 +81,2024-02-03,Одежда,4,1070.91 +811,2024-01-29,Одежда,3,2818.62 +1063,2024-01-29,Электроника,5,1323.26 +504,2024-03-13,Электроника,3,169.80 +87,2024-02-01,Одежда,3,1937.39 +936,2024-01-13,Книги,3,2422.44 +925,2024-01-26,Электроника,2,713.62 +224,2024-02-09,Электроника,1,2981.26 +1190,2024-02-24,Книги,2,4237.85 +79,2024-03-15,Книги,2,3117.00 +1076,2024-02-03,Спорт,7,4754.83 +1099,2024-01-05,Товары для дома,6,2798.56 +1339,2024-03-11,Книги,3,3219.27 +641,2024-01-08,Товары для дома,7,2377.30 +1233,2024-01-07,Одежда,2,933.20 +86,2024-01-17,Одежда,2,4892.97 +830,2024-03-14,Одежда,7,3315.61 +876,2024-01-08,Спорт,5,4973.82 +506,2024-02-26,Книги,3,4000.77 +1222,2024-01-15,Одежда,5,2023.11 +666,2024-03-14,Одежда,4,3134.82 +1214,2024-02-23,Электроника,7,2847.31 +900,2024-01-05,Спорт,4,2898.61 +363,2024-02-25,Одежда,3,506.28 +136,2024-01-12,Товары для дома,5,4290.90 +419,2024-02-27,Электроника,4,4766.04 +416,2024-02-27,Книги,6,197.18 +456,2024-02-14,Книги,7,4878.36 +846,2024-03-24,Одежда,2,1889.72 +553,2024-03-06,Спорт,5,2447.46 +31,2024-01-20,Товары для дома,7,3289.63 +837,2024-01-28,Одежда,6,2593.74 +67,2024-01-12,Товары для дома,3,4856.84 +464,2024-01-20,Электроника,7,2877.32 +1480,2024-03-17,Электроника,3,4794.48 +336,2024-01-09,Электроника,4,4350.64 +1398,2024-03-03,Одежда,1,1573.05 +971,2024-01-01,Электроника,4,1739.25 +1007,2024-01-26,Книги,4,508.50 +794,2024-03-17,Электроника,7,2037.59 +377,2024-01-19,Книги,2,3884.82 +1283,2024-01-06,Товары для дома,6,3505.32 +961,2024-02-01,Товары для дома,5,3443.87 +1352,2024-03-07,Книги,2,80.57 +275,2024-01-05,Электроника,6,661.90 +977,2024-01-21,Спорт,8,3839.64 +163,2024-02-29,Товары для дома,1,4821.05 +736,2024-01-12,Одежда,8,27.24 +1321,2024-02-14,Электроника,2,3088.31 +1472,2024-01-17,Товары для дома,6,2557.71 +476,2024-01-03,Книги,6,1872.21 +473,2024-01-09,Электроника,1,4998.59 +547,2024-02-10,Спорт,2,1267.41 +54,2024-01-09,Книги,6,4747.01 +379,2024-02-28,Товары для дома,6,1787.28 +1345,2024-03-17,Товары для дома,8,301.28 +219,2024-01-20,Электроника,5,782.23 +385,2024-02-14,Книги,5,2253.99 +312,2024-02-13,Книги,8,4216.50 +561,2024-01-07,Электроника,4,2989.92 +1450,2024-01-12,Книги,8,790.02 +622,2024-03-02,Товары для дома,1,1696.61 +843,2024-02-28,Спорт,4,3.55 +1303,2024-01-08,Одежда,3,872.11 +653,2024-01-23,Спорт,3,2816.79 +1200,2024-03-11,Книги,5,282.08 +1288,2024-02-18,Спорт,6,3427.15 +1390,2024-01-27,Одежда,6,2197.26 +378,2024-03-08,Спорт,7,1127.92 +243,2024-01-23,Электроника,8,2466.78 +1092,2024-01-19,Товары для дома,2,1658.77 +1142,2024-03-19,Одежда,1,1990.99 +139,2024-03-19,Книги,8,4455.08 +651,2024-01-11,Электроника,3,3579.48 +1453,2024-02-12,Товары для дома,1,420.61 +1355,2024-03-18,Спорт,7,1019.88 +298,2024-02-08,Спорт,8,1044.48 +1469,2024-01-21,Книги,6,2621.02 +1323,2024-01-30,Книги,5,4771.47 +1073,2024-01-20,Товары для дома,1,2712.11 +633,2024-03-28,Электроника,2,3957.01 +283,2024-03-20,Товары для дома,5,358.73 +415,2024-03-05,Одежда,8,388.98 +1109,2024-01-17,Книги,3,1107.25 +1401,2024-03-21,Спорт,6,504.90 +424,2024-03-08,Электроника,3,2740.71 +270,2024-02-28,Одежда,4,4890.84 +1111,2024-03-22,Электроника,1,1341.08 +920,2024-03-16,Книги,7,2977.02 +675,2024-03-31,Спорт,1,756.82 +640,2024-01-12,Электроника,8,4712.77 +874,2024-01-10,Электроника,2,87.51 +313,2024-01-10,Товары для дома,7,737.98 +1174,2024-03-22,Электроника,2,4390.74 +1464,2024-01-15,Спорт,6,1237.15 +855,2024-01-14,Одежда,6,541.08 +466,2024-02-08,Книги,4,638.84 +1067,2024-03-27,Электроника,7,2729.65 +783,2024-02-01,Товары для дома,3,2705.30 +180,2024-01-14,Одежда,2,3655.30 +282,2024-02-10,Спорт,1,3286.24 +1348,2024-03-19,Книги,6,888.58 +700,2024-01-01,Товары для дома,4,2162.13 +311,2024-01-22,Спорт,5,1347.27 +462,2024-03-31,Спорт,1,2459.49 +574,2024-03-31,Одежда,8,2951.35 +1137,2024-02-04,Спорт,8,4381.52 +61,2024-03-27,Книги,1,3425.99 +793,2024-03-26,Спорт,6,4522.70 +528,2024-01-17,Электроника,1,2268.17 +127,2024-03-05,Спорт,6,1621.55 +565,2024-01-17,Спорт,6,506.89 +1027,2024-02-10,Книги,2,4775.24 +484,2024-03-10,Одежда,1,2481.98 +620,2024-03-06,Книги,7,1719.29 +728,2024-02-13,Электроника,7,3172.83 +721,2024-02-10,Товары для дома,3,82.10 +711,2024-02-17,Спорт,4,2128.76 +1256,2024-02-12,Товары для дома,4,1872.44 +322,2024-01-18,Книги,5,237.75 +806,2024-03-03,Товары для дома,7,715.84 +1004,2024-01-13,Спорт,8,260.24 +684,2024-03-02,Товары для дома,4,4794.31 +1271,2024-03-15,Спорт,4,3035.11 +1085,2024-02-23,Одежда,6,4131.87 +507,2024-03-08,Спорт,6,1923.88 +82,2024-02-03,Товары для дома,5,2075.06 +1415,2024-03-16,Спорт,7,2731.21 +1149,2024-02-14,Спорт,3,3479.76 +938,2024-01-17,Спорт,6,1038.79 +1026,2024-01-26,Товары для дома,5,4312.61 +408,2024-01-23,Товары для дома,2,3158.31 +382,2024-03-27,Одежда,8,427.73 +181,2024-03-13,Книги,6,4103.27 +1483,2024-02-28,Электроника,7,3.78 +1223,2024-01-04,Электроника,6,4778.03 +993,2024-01-13,Спорт,4,4345.34 +647,2024-01-09,Спорт,1,4895.68 +204,2024-02-04,Одежда,4,1607.59 +1042,2024-01-30,Одежда,8,704.57 +780,2024-03-30,Одежда,2,2372.13 +1484,2024-01-24,Книги,6,4629.11 +792,2024-01-26,Книги,5,2398.89 +600,2024-02-01,Электроника,4,266.16 +40,2024-01-20,Товары для дома,1,1960.39 +221,2024-03-18,Электроника,4,3125.43 +1432,2024-02-22,Товары для дома,5,1362.14 +1246,2024-01-02,Электроника,3,2902.07 +796,2024-02-28,Товары для дома,5,3236.76 +137,2024-01-29,Товары для дома,3,4228.72 +18,2024-01-21,Одежда,4,бесплатно +953,2024-03-21,Электроника,1,4205.68 +109,2024-03-26,Товары для дома,5,4530.19 +556,2024-03-15,Книги,1,1850.19 +412,2024-03-21,Товары для дома,8,1657.48 +582,2024-01-03,Одежда,7,2312.32 +124,2024-03-07,Электроника,4,4852.68 +349,2024-03-23,Книги,8,163.32 +524,2024-02-22,Электроника,2,4316.67 +90,2024-03-26,Электроника,5,4283.98 +1079,2024-03-26,Электроника,5,1879.56 +1194,2024-01-08,Одежда,4,3620.71 +772,2024-02-03,Электроника,4,1163.93 +38,2024-02-03,Товары для дома,2,2624.52 +290,2024-01-22,Книги,7,917.87 +990,2024-01-07,Спорт,7,2023.43 +1124,2024-01-27,Спорт,6,3608.18 +530,2024-01-22,Одежда,4,849.18 +1072,2024-02-05,Одежда,4,1369.17 +1120,2024-02-06,Книги,8,597.76 +418,2024-02-29,Электроника,1,3042.60 +434,2024-03-26,Электроника,2,3684.86 +1102,2024-01-29,Одежда,5,3636.07 +429,2024-02-06,Электроника,3,1321.19 +680,2024-01-07,Спорт,1,1407.52 +47,2024-02-03,Электроника,7,2766.54 +965,2024-03-30,Электроника,1,1881.06 +922,2024-02-03,Одежда,7,4461.24 +1294,2024-01-22,Книги,5,1205.82 +228,2024-01-21,Спорт,8,14.03 +704,2024-03-25,Книги,1,2557.77 +29,2024-02-16,Товары для дома,8,1149.88 +590,2024-01-29,Одежда,5,4936.10 +303,2024-02-06,Товары для дома,6,4067.12 +1272,2024-01-05,Одежда,8,2891.95 +498,2024-02-05,Книги,2,3352.67 +112,2024-01-26,Спорт,6,4281.27 +1304,2024-03-07,Электроника,1,2125.76 +176,2024-01-22,Электроника,2,3176.63 +1402,2024-02-03,Одежда,5,1892.49 +766,2024-03-15,Электроника,7,2101.38 +409,2024-01-23,Спорт,8,4987.52 +479,2024-03-23,Спорт,8,917.98 +92,2024-01-15,Книги,3,4664.66 +903,2024-01-16,Книги,3,2632.15 +299,2024-01-14,Спорт,6,1243.65 +1277,2024-03-25,Электроника,3,1252.43 +906,2024-02-09,Книги,8,1716.00 +1263,2024-02-05,Одежда,3,4404.60 +717,2024-03-13,Книги,1,4498.19 +347,2024-03-05,Электроника,1,1161.56 +1360,2024-03-29,Спорт,8,2321.39 +50,2024-03-07,Книги,4,2367.09 +1217,2024-02-07,Электроника,8,1145.29 +706,2024-01-03,Одежда,5,2775.68 +657,2024-02-28,Товары для дома,5,1814.88 +391,2024-02-11,Книги,8,629.07 +714,2024-02-21,Спорт,8,1988.44 +749,2024-02-02,Товары для дома,8,4950.45 +868,2024-02-20,Книги,4,1549.50 +848,2024-02-20,Одежда,4,3279.97 +231,2024-03-30,Спорт,2,4506.82 +184,2024-02-23,Спорт,2,4311.39 +1180,2024-03-05,Спорт,7,4621.03 +1441,2024-02-24,Товары для дома,5,3278.50 +881,2024-03-06,Товары для дома,3,4828.30 +546,2024-02-21,Одежда,2,685.60 +147,2024-03-11,Товары для дома,8,2337.88 +497,2024-01-06,Одежда,7,3810.61 +505,2024-03-27,Одежда,6,2358.90 +1359,2024-03-06,Электроника,5,1351.06 +1262,2024-02-01,Товары для дома,7,4119.13 +1150,2024-03-12,Электроника,1,4676.98 +402,2024-02-25,Спорт,2,3087.39 +1333,2024-03-11,Электроника,8,4109.31 +810,2024-01-29,Книги,5,1531.40 +1384,2024-02-10,Книги,1,3613.45 +406,2024-02-14,Товары для дома,4,1840.78 +1146,2024-01-02,Электроника,4,3693.97 +985,2024-01-31,Одежда,3,1818.29 +1422,2024-01-11,Спорт,5,3433.21 +268,2024-01-20,Электроника,8,2341.20 +1371,2024-01-26,Товары для дома,5,1231.35 +1028,2024-02-19,Спорт,6,2916.96 +817,2024-01-05,Одежда,6,2357.90 +175,2024-02-29,Одежда,6,3667.22 +538,2024-03-24,Электроника,7,1105.25 +818,2024-01-12,Книги,4,2302.00 +1123,2024-02-07,Книги,8,840.82 +101,2024-02-17,Электроника,3,3054.03 +1408,2024-03-19,Спорт,3,1502.21 +975,2024-01-10,Спорт,2,1958.70 +170,2024-01-03,Электроника,1,3927.05 +689,2024-01-27,Одежда,7,1943.57 +271,2024-03-30,Электроника,7,1252.03 +304,2024-01-29,Товары для дома,5,3337.72 +808,2024-03-28,Спорт,8,2578.06 +594,2024-02-20,Одежда,3,4290.66 +885,2024-01-15,Электроника,1,2376.98 +1418,2024-01-27,Спорт,2,875.10 +185,2024-02-25,Спорт,3,1898.91 +1456,2024-02-02,Спорт,1,4284.75 +1179,2024-01-17,Спорт,6,542.93 +1219,2024-03-26,Спорт,4,1814.08 +913,2024-02-12,Спорт,5,7.67 +805,2024-03-20,Одежда,1,281.08 +104,2024-01-27,Товары для дома,2,3576.00 +314,2024-03-12,Книги,7,1917.72 +453,2024-02-06,Товары для дома,5,439.75 +1029,2024-02-05,Спорт,1,2724.67 +670,2024-03-21,Спорт,8,3558.61 +307,2024-02-26,Электроника,6,174.93 +1157,2024-03-14,Книги,8,2349.33 +1106,2024-02-25,Товары для дома,6,4655.55 +654,2024-01-29,Книги,1,4376.87 +85,2024-01-10,Товары для дома,5,2818.76 +1074,2024-03-17,Одежда,3,2118.64 +625,2024-02-03,Одежда,4,3427.19 +111,2024-01-05,Спорт,4,4501.51 +784,2024-02-12,Спорт,8,2935.23 +1295,2024-01-28,Товары для дома,3,787.70 +1299,2024-01-27,Товары для дома,7,3097.45 +24,2024-02-04,Товары для дома,6,347.02 +1234,2024-03-14,Товары для дома,5,4307.66 +1337,2024-01-30,Товары для дома,2,2341.45 +465,2024-01-15,Одежда,4,4069.38 +247,2024-02-11,Книги,3,3513.43 +854,2024-01-10,Спорт,2,1200.35 +890,2024-01-16,Книги,6,4044.52 +713,2024-03-24,Книги,2,4499.55 +685,2024-02-20,Товары для дома,6,4797.90 +115,2024-02-12,Книги,5,1460.69 +161,2024-03-03,Спорт,6,152.18 +782,2024-02-13,Книги,3,3397.23 +1383,2024-01-16,Электроника,8,3660.82 +77,2024-01-09,Книги,4,3119.70 +1394,2024-03-12,Спорт,4,2266.08 +1316,2024-03-09,Книги,2,2638.90 +1220,2024-03-23,Электроника,4,3217.57 +196,2024-01-11,Спорт,8,1265.41 +1468,2024-02-17,Книги,4,4041.20 +1000,2024-03-18,Спорт,3,4664.87 +355,2024-02-28,Электроника,7,2650.55 +989,2024-02-02,Книги,1,4126.26 +1446,2024-01-25,Книги,3,1681.10 +159,2024-03-09,Электроника,4,3919.14 +446,2024-03-09,Одежда,8,1253.94 +1166,2024-01-13,Товары для дома,1,3981.03 +819,2024-01-27,Электроника,6,4250.42 +1409,2024-01-22,Одежда,7,3358.49 +189,2024-02-11,Товары для дома,2,4465.43 +1419,2024-01-13,Спорт,1,334.46 +525,2024-01-17,Книги,8,1662.14 +478,2024-01-18,Одежда,6,232.45 +512,2024-01-28,Одежда,8,3208.00 +1193,2024-01-21,Одежда,3,1502.70 +752,2024-02-20,Электроника,7,1741.78 +543,2024-02-26,Спорт,8,2179.06 +284,2024-01-30,Книги,7,3915.21 +320,2024-03-19,Книги,8,1160.61 +729,2024-02-11,Одежда,8,155.33 +943,2024-01-07,Электроника,7,880.75 +78,2024-02-21,Электроника,4,628.56 +194,2024-02-03,Одежда,5,1784.36 +317,2024-03-22,Одежда,2,4929.84 +1171,2024-01-16,Спорт,4,4877.02 +1169,2024-03-04,Спорт,2,736.36 +1140,2024-01-14,Электроника,8,2181.19 +1048,2024-01-18,Электроника,1,4092.16 +1134,2024-02-15,Спорт,5,4689.91 +566,2024-02-13,Одежда,3,2924.65 +1413,2024-03-31,Электроника,7,1402.33 +45,2024-03-09,Товары для дома,3,4015.18 +645,2024-03-15,Книги,7,382.89 +440,2024-03-25,Электроника,3,1765.62 +455,2024-02-25,Товары для дома,8,480.85 +616,2024-01-21,Спорт,5,3833.18 +14,2024-03-21,Одежда,-2,3242.83 +619,2024-03-19,Спорт,7,4488.68 +277,2024-03-08,Спорт,1,4773.34 +1061,2024-03-28,Спорт,3,46.66 +34,2024-03-11,Книги,2,4512.97 +1221,2024-01-06,Одежда,7,3578.54 +583,2024-01-20,Товары для дома,7,2165.11 +71,2024-02-28,Спорт,8,1495.53 +1,2024-03-22,Книги,5,583.70 +814,2024-03-10,Книги,8,4312.86 +917,2024-03-07,Одежда,8,2165.61 +927,2024-02-29,Спорт,2,1646.44 +738,2024-01-17,Товары для дома,7,457.69 +521,2024-02-29,Электроника,3,4213.51 +1447,2024-01-07,Книги,3,2804.79 +769,2024-01-22,Электроника,1,3392.15 +439,2024-02-07,Книги,1,4427.24 +1118,2024-03-20,Товары для дома,7,3175.27 +1154,2024-02-05,Электроника,4,263.99 +493,2024-02-11,Книги,6,537.91 +1247,2024-01-16,Книги,7,219.02 +663,2024-02-26,Электроника,4,1228.62 +882,2024-03-09,Спорт,2,1713.12 +1297,2024-02-23,Одежда,2,4252.89 +845,2024-01-21,Одежда,2,4584.50 +762,2024-02-07,Книги,7,4358.32 +665,2024-01-30,Электроника,6,4277.07 +1458,2024-02-26,Книги,4,1295.85 +646,2024-02-14,Книги,1,3672.04 +722,2024-02-20,Книги,5,4869.98 +908,2024-03-13,Одежда,4,4113.08 +1229,2024-03-28,Книги,4,2224.08 +1094,2024-03-22,Книги,8,304.16 +324,2024-03-09,Спорт,8,711.28 +1347,2024-01-25,Электроника,4,176.21 +692,2024-02-07,Товары для дома,6,799.08 +1034,2024-03-09,Одежда,7,4614.45 +823,2024-03-17,Товары для дома,6,2824.70 +1127,2024-01-20,Книги,2,4977.84 +370,2024-01-14,Электроника,6,1835.61 +342,2024-01-02,Товары для дома,6,1878.23 +214,2024-01-14,Книги,5,4575.57 +875,2024-01-02,Товары для дома,1,1374.61 +1363,2024-03-02,Одежда,2,609.59 +1439,2024-02-20,Товары для дома,7,1310.18 +940,2024-01-10,Книги,4,114.30 +1382,2024-02-20,Товары для дома,8,4510.98 +734,2024-02-04,Спорт,3,808.57 +1181,2024-03-18,Товары для дома,5,4802.01 +1308,2024-03-17,Электроника,3,748.23 +217,2024-01-15,Книги,7,4310.26 +851,2024-01-17,Товары для дома,8,3112.60 +1473,2024-03-12,Одежда,8,4134.34 +1144,2024-03-03,Спорт,8,592.12 +367,2024-02-06,Одежда,6,3147.88 +1281,2024-01-29,Спорт,4,3992.31 +1168,2024-03-30,Книги,3,557.44 +510,2024-03-07,Электроника,2,2028.93 +1346,2024-02-26,Товары для дома,5,56.16 +1078,2024-02-13,Товары для дома,8,3809.22 +396,2024-03-14,Электроника,7,2250.69 +389,2024-01-01,Одежда,4,1071.77 +790,2024-02-05,Книги,2,3944.36 +1089,2024-03-28,Электроника,5,2001.05 +474,2024-02-19,Спорт,1,4578.78 +208,2024-03-16,Электроника,5,3985.68 +199,2024-01-03,Одежда,4,487.87 +1064,2024-03-31,Одежда,7,3369.82 +443,2024-02-26,Спорт,6,229.08 +160,2024-03-03,Одежда,7,1150.41 +623,2024-02-03,Товары для дома,8,2011.04 +145,2024-02-28,Спорт,6,699.09 +84,2024-02-28,Электроника,2,3256.67 +1084,2024-01-29,Товары для дома,5,1862.71 +545,2024-02-10,Одежда,6,4516.67 +1264,2024-03-27,Одежда,8,3842.89 +120,2024-03-17,Одежда,6,2278.37 +1451,2024-01-28,Спорт,5,1792.61 +756,2024-02-10,Спорт,6,931.34 +852,2024-01-02,Одежда,2,42.28 +1143,2024-03-28,Товары для дома,8,4613.48 diff --git a/docs/ai_work_log.md b/docs/ai_work_log.md new file mode 100644 index 0000000..201ba1f --- /dev/null +++ b/docs/ai_work_log.md @@ -0,0 +1,37 @@ +# Журнал работы с ИИ + +## 12 ноября 2026 — осмотр проекта + +**Запрос пользователя.** Подготовить по этапам ETL-пайплайн для очистки CSV и агрегации выручки по дням и категориям в SQLite. Сохранять исходный CSV, учитывать причины отбраковки, проверить идемпотентность, подготовить три аналитических запроса с результатами, README и готовую базу. Вести журнал по фактически выполненной работе. Проверки писать на pytest. Оставить `main.py` в корне для объединения этапов, функции разместить в `src/`, пути вычислять от корня проекта. + +**Проверки.** Осмотр корня проекта через PowerShell показал `.git/` и `.gitignore`. Прочитан `.gitignore`, проверено состояние Git. Исходный CSV, код и тесты отсутствовали. Файл `AGENTS.md` в корне проекта и родительских каталогах не обнаружен. Дополнительная команда `rg --files` завершилась ошибкой канала запуска. Список содержимого корня получен через `Get-ChildItem -Force`. + +**Решение.** Первый этап реализации пользователь пока не указал. Работа ограничена осмотром и созданием журнала. Схема CSV и правила расчёта выручки пока не определены. Тесты не запускались. + +## 12 ноября 2026 — план реализации + +**Запрос пользователя.** Изучить проект и подготовить краткий план без кода пайплайна. Зафиксировать заданную модель заказа, денежные ограничения, порядок обработки дублей, нумерацию записей CSV, интерфейсы модулей, полную транзакционную перезагрузку, критерии готовности и неоднозначности. + +**Принятые решения.** В [плане реализации](implementation_plan.md) описаны пять полей заказа, точный разбор цены через Decimal, хранение денег в копейках и проверка диапазона INTEGER. Точные дубли определяются по исходным полям до валидации. Конфликты ID проверяются по всей группе валидных заказов до выбора представителя. Для отчёта у каждой отброшенной записи одна причина и логический номер после заголовка, начиная с единицы. + +**Разделение работы.** Чтение, очистка и загрузка распределены между тремя модулями в src/. Дневные агрегаты строятся в SQLite при полной перезагрузке двух таблиц в одной транзакции. В плане отдельно отмечены предлагаемые параметры CSV и определения аналитических запросов. + +**Фактические проверки и изменения.** Повторный осмотр через rg и Git показал только .gitignore и ранее созданный журнал. Прочитан журнал, проверено отсутствие AGENTS.md в каталогах проекта и документации и по пути к корню диска. Добавлен план и обновлён журнал. Код пайплайна не создавался, pytest не запускался. + +## 12 ноября 2026 — генератор синтетических заказов + +**Запрос пользователя.** Создать воспроизводимый CSV на 1500 записей с seed=42, заданными дублями и ошибками. Запустить генератор и тесты количества записей и воспроизводимости на временных файлах. + +**Решения.** В `src/generate_data.py` используется локальный `Random(42)`. Корректные даты выбираются из диапазона 01.01.2024–31.03.2024 включительно. Цены формируются из целых копеек, категории выбираются из фиксированного списка. Для трёх заказов добавляется по одной копии — точной, с другой записью той же даты и с изменённым количеством. Для остальных заданных случаев изменено по одному полю в отдельных заказах. Записи перемешиваются тем же генератором. CSV синтетический, без персональных данных. Кодировка UTF-8 и перевод строки LF заданы явно. + +**Окружение.** В доступном Python 3.14.4 отсутствовал pytest. Создана локальная .venv. Первая попытка установки не нашла дистрибутив, затем из PyPI установлен pytest 9.2.0. + +**Проверки.** Команда `python -m src.generate_data` выполнена дважды. SHA-256 файла `data/raw_orders.csv` после обоих запусков совпал — `c4b9ffd2eb24c6c79b6a3e2436f3bd348ba86696a1aeb9b7e3083af65c799c67`. Чтение через csv.DictReader подтвердило пять заданных колонок, 1500 записей, один точный дубль и по одному пустому значению в каждом поле. ID 1, 2 и 3 встретились по два раза. Запуск `.\.venv\Scripts\python.exe -m pytest -q` завершился результатом `2 passed in 0.08s`. Оба теста используют tmp_path. + +## 12 ноября 2026 — чтение и очистка + +**Запрос пользователя.** Реализовать extract.py и transform.py, сохранить номер и исходные значения отклонённой записи, вернуть все обнаруженные ошибки полей, проверить дубли, конфликты, точность расчётов и сохранность CSV. + +**Решения и изменения.** csv.reader в строгом режиме читает UTF-8 с поддержкой BOM и нумерует логические записи после заголовка. Нечитаемый файл, ошибка разбора CSV или неверный заголовок прерывают обработку. Для читаемой записи неверной ширины сохраняются все исходные поля. Точный дубль получает только причину повтора. Для остальных структурно корректных записей собираются ошибки независимых полей. Выручка проверяется при пригодных для расчёта цене и количестве. Среди валидных заказов сначала определяется наличие конфликта во всей группе ID. Номер источника сохраняется и у принятого заказа, но исключён из сравнения нормализованных значений. План обновлён согласно уточнению о нескольких причинах. + +**Фактические проверки.** Полный запуск pytest дал 32 passed. На синтетическом raw_orders.csv обработаны 1500 записей, приняты 1482, отброшены 18. Из них 14 имели ошибки значений, одна оказалась точным дублем, одна — нормализованным, две отклонены из-за конфликта ID. Проверено, что номера от 1 до 1500 встречаются в совокупном результате ровно по одному разу. Байты CSV до и после обработки совпали, SHA-256 — `c4b9ffd2eb24c6c79b6a3e2436f3bd348ba86696a1aeb9b7e3083af65c799c67`. diff --git a/docs/implementation_plan.md b/docs/implementation_plan.md new file mode 100644 index 0000000..8b9ef7f --- /dev/null +++ b/docs/implementation_plan.md @@ -0,0 +1,51 @@ +# План реализации ETL + +## Модель данных + +Одна запись CSV описывает один завершённый заказ с одним товаром. Возвраты, скидки, бесплатные товары и несколько валют вне модели. Все пять колонок обязательны в заголовке. Пустое значение допустимо только в `category`. + +| Поле | Принятое решение | +|---|---| +| `order_id` | Положительное целое число. После разбора ведущие нули не влияют на идентичность заказа. | +| `order_date` | Только календарно корректные даты строго вида `YYYY-MM-DD` или `DD.MM.YYYY`, включая ведущие нули. В базе хранить текст `YYYY-MM-DD`. | +| `category` | Удалить внешние пробельные символы, каждую внутреннюю последовательность пробельных символов заменить одним пробелом. Первую букву строки привести к верхнему регистру, остальные к нижнему. Пустое значение заменить на «Не указана». | +| `quantity` | Строго положительное целое количество единиц товара. | +| `unit_price` | Строго положительная цена единицы в рублях. Принимать целую часть из цифр и необязательную дробную часть из одной или двух цифр после точки или запятой. Разбирать через `Decimal`, переводить в целые копейки точно, без округления. | + +Для числовых полей принимаем десятичные цифры ASCII, для всех полей при нормализации убираем внешние пробелы. Знаки, экспоненциальная запись, разделители тысяч и неконечные значения, включая NaN и Infinity, отклоняются. Цена `1,230` отклоняется даже при нуле в третьем разряде. Выручка заказа в копейках равна количеству, умноженному на цену единицы в копейках. ID, количество, цена в копейках и произведение должны умещаться в знаковый INTEGER SQLite с максимумом 9 223 372 036 854 775 807. Переполнение отдельного заказа является причиной отбраковки записи. + +## Порядок обработки + +1. Читать CSV стандартным модулем `csv`, открывая файл с `newline=''`. Считать логические записи после заголовка с единицы, включая отклонённые записи. Поле в кавычках с переводом строки входит в одну запись. Сохранять номер у принятого заказа и в отчёте для каждой отброшенной записи. +2. До очистки сравнить кортежи исходных значений полей, полученных от CSV-парсера. Для каждого кортежа оставить первое вхождение, последующие отметить как точные дубли. Это правило действует и для повторов невалидного заказа. +3. Проверить число полей. Для записи правильной ширины собрать все ошибки независимых полей в порядке таблицы. Проверить произведение, только если количество и цена прошли проверку. Сохранять один итоговый статус и список фактически установленных причин на запись. +4. Сгруппировать только валидные записи по нормализованному ID. Сравнивать дату, категорию, количество и цену в копейках. Если значения совпадают у всей группы, принять запись с наименьшим номером, остальные отметить как нормализованные дубли. Если есть хотя бы два варианта значений, отклонить всю валидную группу как конфликт ID, включая повторившиеся варианты. Уже отброшенные точные дубли сохраняют исходную причину. +5. Вывести количество прочитанных, принятых и отброшенных записей, разбивку по причинам и номера отклонённых записей. Число прочитанных должно равняться сумме принятых и отброшенных. Сумма счётчиков причин может быть больше числа отброшенных записей. + +## Модули и загрузка + +| Модуль | Предлагаемый интерфейс и ответственность | +|---|---| +| `src/extract.py` | `read_records(path) -> Iterator[RawRecord]`. Передаёт номер записи и исходные значения, проверяет заголовок и ширину записи. | +| `src/transform.py` | `clean_orders(records) -> tuple[list[Order], list[RejectedRecord]]`. Рассчитывает выручку одного заказа и возвращает принятые заказы и отбраковку с причинами. | +| `src/storage.py` | `reload_database(path, orders) -> None`. Обновляет обе таблицы атомарно и рассчитывает дневные агрегаты в SQLite. | +| `main.py` | Определяет корень по расположению самого файла, собирает пути к ресурсам, вызывает этапы и выводит отчёт. | + +В `orders` хранить одну принятую запись на `order_id` (первичный ключ), включая номер записи, дату, категорию, количество, цену и выручку в копейках. В `daily_revenue` хранить сумму выручки в копейках с составным первичным ключом из даты и категории. + +В одной явной транзакции создать таблицы при первом запуске, очистить обе, загрузить принятые заказы и заполнить `daily_revenue` запросом с `SUM` и `GROUP BY order_date, category` из `orders`. Это единственное место построения дневной витрины. Зафиксировать транзакцию после успешного завершения всех операций. Ошибка загрузки или переполнение суммы группы отменяет транзакцию целиком, сохраняя предыдущие данные. + +## Неоднозначности и рабочие допущения + +- Исходного CSV пока нет. Предлагаемый формат — UTF-8 с поддержкой BOM, запятая между полями, двойные кавычки для значений с запятой или переводом строки. Для синтетического набора явно указать происхождение в README. Исходный файл открывать только на чтение. +- Колонки сопоставлять по именам. Отсутствующее, повторное или лишнее имя в заголовке считать ошибкой файла. Неверное число полей в читаемой записи считать ошибкой записи. Если из-за повреждения CSV границы записей определить невозможно, остановить запуск до загрузки. +- Файл с корректным заголовком и нулём принятых заказов считать пустым снимком. При полной перезагрузке обе таблицы станут пустыми. +- Для аналитики предлагаются три запроса к витрине. Топ — пять категорий по суммарной выручке с сортировкой по имени при равенстве. Динамика — сумма по дате и изменение в копейках к предыдущей доступной дате. Аномалия — дневная выручка категории более чем вдвое выше предыдущего наблюдения той же категории. Первое наблюдение пропускается, пропущенные даты нулями не заполняются. Порог — рабочее допущение для тестового задания. + +## Критерии готовности реализации + +- На pytest проверить оба формата дат, невозможные даты, пробелы, пустые поля, дробное количество, варианты цены и переполнение на границе INTEGER. Проверить нумерацию на CSV с многострочным полем. +- На размеченном примере сверить статус каждой исходной записи, причины и баланс счётчиков. Включить повтор невалидной записи, одинаковые нормализованные заказы, конфликт из трёх записей с двумя совпадающими вариантами и валидную запись рядом с невалидной того же ID. +- Сверить копейки по известному примеру. Две единицы по 10,25 рубля и одна за 0,50 рубля в тот же день и в той же категории должны дать одну строку витрины на 2100 копеек. +- Запустить пайплайн дважды с тем же CSV, в том числе из другой рабочей папки. Сравнить содержимое обеих таблиц, отчёты и контрольную сумму исходного файла. На изменённом снимке проверить удаление устаревших заказов и агрегатов. На переполнении суммы группы проверить откат обеих таблиц. +- Перед сдачей подготовить готовую SQLite-базу, три SQL-запроса с фактическими результатами, README с командами запуска и тестов и журнал фактически выполненных проверок. diff --git a/src/extract.py b/src/extract.py new file mode 100644 index 0000000..9278782 --- /dev/null +++ b/src/extract.py @@ -0,0 +1,50 @@ +"""Чтение логических записей CSV с сохранением исходных значений.""" + +import csv +from collections.abc import Iterator +from dataclasses import dataclass +from pathlib import Path + +FIELDS = ("order_id", "order_date", "category", "quantity", "unit_price") +DEFAULT_INPUT = Path(__file__).resolve().parents[1] / "data" / "raw_orders.csv" + + +class CSVFileError(ValueError): + """Файл целиком нельзя использовать как источник заказов.""" + + +@dataclass(frozen=True) +class RawRecord: + record_number: int + values: tuple[str, ...] + header: tuple[str, ...] + structure_error: str | None = None + + +def read_records(path: Path = DEFAULT_INPUT) -> Iterator[RawRecord]: + """Нумерация начинается с 1 после заголовка, включая пустые записи.""" + try: + with path.open(encoding="utf-8-sig", newline="") as source: + reader = csv.reader(source, strict=True) + header = tuple(next(reader, ())) + if sorted(header) != sorted(FIELDS): + raise CSVFileError( + f"{path}: неверный заголовок. Ожидались колонки {FIELDS}, получены {header}" + ) + for number, row in enumerate(reader, start=1): + error = None + if not row: + error = "Пустая запись CSV" + elif len(row) != len(header): + error = f"Число полей: ожидалось {len(header)}, получено {len(row)}" + yield RawRecord(number, tuple(row), header, error) + except csv.Error as exc: + raise CSVFileError( + f"{path}: ошибка структуры CSV около физической строки {reader.line_num}: {exc}" + ) from exc + except UnicodeDecodeError as exc: + raise CSVFileError(f"{path}: файл не удалось прочитать как UTF-8") from exc + except FileNotFoundError as exc: + raise CSVFileError(f"{path}: файл не найден") from exc + except OSError as exc: + raise CSVFileError(f"{path}: ошибка чтения: {exc}") from exc diff --git a/src/generate_data.py b/src/generate_data.py new file mode 100644 index 0000000..9952b48 --- /dev/null +++ b/src/generate_data.py @@ -0,0 +1,72 @@ +"""Генерация синтетических заказов для тестового ETL.""" + +import csv +from datetime import date, timedelta +from pathlib import Path +from random import Random + +OUTPUT_PATH = Path(__file__).resolve().parents[1] / "data" / "raw_orders.csv" +FIELDS = ("order_id", "order_date", "category", "quantity", "unit_price") +CATEGORIES = ("Книги", "Товары для дома", "Одежда", "Спорт", "Электроника") +START_DATE = date(2024, 1, 1) +END_DATE = date(2024, 3, 31) +RECORD_COUNT = 1500 + +# Каждая подстановка применяется к отдельному заказу. +FIELD_CASES = ( + ("order_id", ""), + ("order_date", ""), + ("category", ""), + ("quantity", ""), + ("unit_price", ""), + ("order_date", "2024-02-30"), + ("order_date", "03/15/2024"), + ("category", " тОВАРЫ для ДОМА "), + ("quantity", "1.5"), + ("quantity", "0"), + ("quantity", "-2"), + ("quantity", "три"), + ("unit_price", "0"), + ("unit_price", "-12.50"), + ("unit_price", "бесплатно"), + ("unit_price", "12.340"), + ("unit_price", "12,50"), +) + + +def generate_data(path: Path = OUTPUT_PATH) -> None: + rng = Random(42) + days = (END_DATE - START_DATE).days + orders = [] + # Три места зарезервированы для копий первых трёх заказов. + for order_id in range(1, RECORD_COUNT - 3 + 1): + order_date = START_DATE + timedelta(days=rng.randint(0, days)) + price_kopecks = rng.randint(1, 500_000) + orders.append({ + "order_id": str(order_id), + "order_date": order_date.isoformat(), + "category": rng.choice(CATEGORIES), + "quantity": str(rng.randint(1, 8)), + "unit_price": f"{price_kopecks // 100}.{price_kopecks % 100:02d}", + }) + + for index, (field, value) in enumerate(FIELD_CASES, start=3): + orders[index][field] = value + + same_date = date.fromisoformat(orders[1]["order_date"]) + orders.extend(( + orders[0].copy(), + dict(orders[1], order_date=same_date.strftime("%d.%m.%Y")), + dict(orders[2], quantity=str(int(orders[2]["quantity"]) + 1)), + )) + rng.shuffle(orders) + + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("w", encoding="utf-8", newline="") as output: + writer = csv.DictWriter(output, fieldnames=FIELDS, lineterminator="\n") + writer.writeheader() + writer.writerows(orders) + + +if __name__ == "__main__": + generate_data() diff --git a/src/transform.py b/src/transform.py new file mode 100644 index 0000000..bc19bd8 --- /dev/null +++ b/src/transform.py @@ -0,0 +1,131 @@ +"""Проверка заказов и обработка дублей до загрузки в SQLite.""" + +import re +from collections import defaultdict +from collections.abc import Iterable +from dataclasses import dataclass, field +from datetime import date +from decimal import Decimal + +from .extract import RawRecord + +MAX_INTEGER = 2**63 - 1 + + +@dataclass(frozen=True) +class Order: + order_id: int + order_date: str + category: str + quantity: int + unit_price_kopecks: int + revenue_kopecks: int + record_number: int = field(compare=False) + + +@dataclass +class RejectedRecord: + source: RawRecord + reasons: list[str] + + +def _positive_integer(value: str) -> int: + if not re.fullmatch(r"[0-9]+", value): + raise ValueError("ожидается положительное целое число") + digits = value.lstrip("0") + if not digits: + raise ValueError("значение должно быть больше нуля") + if len(digits) > 19 or int(digits) > MAX_INTEGER: + raise ValueError("превышен предел INTEGER SQLite") + return int(digits) + + +def _price_kopecks(value: str) -> int: + match = re.fullmatch(r"([0-9]+)(?:[.,]([0-9]+))?", value) + if not match: + raise ValueError("ожидается положительная десятичная цена в рублях") + whole, fraction = match.groups(default="") + if len(fraction) > 2: + raise ValueError("больше двух дробных знаков") + if len(whole.lstrip("0")) > 17: + raise ValueError("цена в копейках превышает предел INTEGER SQLite") + numerator, denominator = Decimal(value.replace(",", ".")).as_integer_ratio() + kopecks = numerator * 100 // denominator + if kopecks == 0: + raise ValueError("цена должна быть больше нуля") + if kopecks > MAX_INTEGER: + raise ValueError("цена в копейках превышает предел INTEGER SQLite") + return kopecks + + +def _order_date(value: str) -> str: + if re.fullmatch(r"[0-9]{2}\.[0-9]{2}\.[0-9]{4}", value): + day, month, year = value.split(".") + value = f"{year}-{month}-{day}" + elif not re.fullmatch(r"[0-9]{4}-[0-9]{2}-[0-9]{2}", value): + raise ValueError("ожидается YYYY-MM-DD или DD.MM.YYYY") + try: + return date.fromisoformat(value).isoformat() + except ValueError as exc: + raise ValueError("несуществующая календарная дата") from exc + + +def _validate(record: RawRecord) -> Order | RejectedRecord: + values = dict(zip(record.header, record.values, strict=True)) + reasons = [] + + def parse(name, parser): + value = values[name].strip() + if not value: + reasons.append(f"{name}: пропущено обязательное значение") + return None + try: + return parser(value) + except ValueError as exc: + reasons.append(f"{name}: {exc}") + return None + + order_id = parse("order_id", _positive_integer) + order_date = parse("order_date", _order_date) + category = " ".join(values["category"].split()).capitalize() or "Не указана" + quantity = parse("quantity", _positive_integer) + price = parse("unit_price", _price_kopecks) + if quantity is not None and price is not None: + revenue = quantity * price + if revenue > MAX_INTEGER: + reasons.append("revenue_kopecks: превышен предел INTEGER SQLite") + + if reasons: + return RejectedRecord(record, reasons) + return Order(order_id, order_date, category, quantity, price, revenue, record.record_number) + + +def clean_orders(records: Iterable[RawRecord]) -> tuple[list[Order], list[RejectedRecord]]: + """Вернуть принятые и отброшенные записи в исходном порядке внутри списков.""" + seen = set() + groups = defaultdict(list) + rejected = [] + for record in records: + if record.values in seen: + rejected.append(RejectedRecord(record, ["Точный дубль"])) + continue + seen.add(record.values) + if record.structure_error: + rejected.append(RejectedRecord(record, [record.structure_error])) + continue + result = _validate(record) + if isinstance(result, RejectedRecord): + rejected.append(result) + else: + groups[result.order_id].append((record, result)) + + accepted = [] + for group in groups.values(): + first = group[0][1] + if any(order != first for _, order in group): + rejected.extend(RejectedRecord(raw, ["Конфликт order_id"]) for raw, _ in group) + else: + accepted.append(first) + rejected.extend(RejectedRecord(raw, ["Нормализованный дубль"]) for raw, _ in group[1:]) + rejected.sort(key=lambda item: item.source.record_number) + return accepted, rejected diff --git a/tests/test_extract_transform.py b/tests/test_extract_transform.py new file mode 100644 index 0000000..90d2857 --- /dev/null +++ b/tests/test_extract_transform.py @@ -0,0 +1,118 @@ +import csv + +import pytest + +from src.extract import CSVFileError, FIELDS, read_records +from src.transform import MAX_INTEGER, clean_orders + + +def order(**changes): + return dict(order_id="1", order_date="2024-02-29", category="Книги", + quantity="3", unit_price="0,07") | changes + + +def process(tmp_path, *rows, header=FIELDS): + path = tmp_path / "orders.csv" + with path.open("w", encoding="utf-8-sig", newline="") as output: + writer = csv.writer(output) + writer.writerow(header) + for row in rows: + writer.writerow([row[name] for name in header] if isinstance(row, dict) else row) + before = path.read_bytes() + accepted, rejected = clean_orders(read_records(path)) + assert path.read_bytes() == before + numbers = [item.record_number for item in accepted] + numbers.extend(item.source.record_number for item in rejected) + assert sorted(numbers) == list(range(1, len(rows) + 1)) + return accepted, rejected + + +@pytest.mark.parametrize("value", ["2024-02-29", " 29.02.2024 "]) +def test_normalization_and_exact_money(tmp_path, value): + accepted, rejected = process(tmp_path, order(order_date=value, category=" "), header=FIELDS[::-1]) + assert not rejected + item, = accepted + assert (item.order_date, item.category) == ("2024-02-29", "Не указана") + assert (item.unit_price_kopecks, item.revenue_kopecks) == (7, 21) + + +@pytest.mark.parametrize(("name", "value"), [ + ("order_date", "2023-02-29"), ("order_date", "1.2.2024"), ("order_date", "20240229"), + ("quantity", "1.2"), ("quantity", "0"), ("quantity", "-2"), ("order_id", "0"), + ("unit_price", "NaN"), ("unit_price", "Infinity"), ("unit_price", "-Infinity"), + ("unit_price", "0"), ("unit_price", "-1"), ("unit_price", "1,230"), + ("unit_price", "1 000.20"), ("unit_price", "1e2"), + ("unit_price", "92233720368547758.08"), ("quantity", str(MAX_INTEGER + 1)), + ("order_id", "9" * 5000), +]) +def test_invalid_field(tmp_path, name, value): + accepted, rejected = process(tmp_path, order(**{name: value})) + assert not accepted + reason, = rejected[0].reasons + assert reason.startswith(name + ":") + + +def test_sqlite_boundary_and_product_overflow(tmp_path): + price = "92233720368547758.07" + accepted, rejected = process(tmp_path, order(quantity="1", unit_price=price), + order(quantity="2", unit_price=price)) + assert accepted[0].revenue_kopecks == MAX_INTEGER + assert rejected[0].reasons == ["revenue_kopecks: превышен предел INTEGER SQLite"] + + +def test_multiple_missing_fields_and_exact_repeat(tmp_path): + bad = order(order_id=" ", order_date="", quantity="", unit_price="") + _, rejected = process(tmp_path, bad, bad) + assert rejected[0].source.values == tuple(bad.values()) + assert [s.split(":")[0] for s in rejected[0].reasons] == [ + "order_id", "order_date", "quantity", "unit_price" + ] + assert rejected[1].reasons == ["Точный дубль"] + + +def test_duplicate_stages_and_conflict(tmp_path): + first = order(category=" ТоВаРы\n ДЛЯ ДоМа ") + equivalent = first | {"order_date": "29.02.2024", "order_id": "001", + "category": "товары для дома", "unit_price": "0.07"} + conflict = order(order_id="2") + accepted, rejected = process( + tmp_path, first, first, equivalent, conflict, conflict | {"quantity": "03"}, + conflict | {"quantity": "4"}, order(quantity="много") + ) + assert [(item.record_number, item.category) for item in accepted] == [(1, "Товары для дома")] + assert [(r.source.record_number, r.reasons) for r in rejected] == [ + (2, ["Точный дубль"]), (3, ["Нормализованный дубль"]), + (4, ["Конфликт order_id"]), (5, ["Конфликт order_id"]), (6, ["Конфликт order_id"]), + (7, ["quantity: ожидается положительное целое число"]), + ] + + +def test_empty_short_and_long_records(tmp_path): + accepted, rejected = process(tmp_path, [], ["1"], list(order().values()) + ["лишнее"], order()) + assert accepted[0].record_number == 4 + assert [r.reasons for r in rejected] == [ + ["Пустая запись CSV"], ["Число полей: ожидалось 5, получено 1"], + ["Число полей: ожидалось 5, получено 6"], + ] + + +def test_header_only(tmp_path): + assert process(tmp_path) == ([], []) + + +@pytest.mark.parametrize("header", ["", "order_id,quantity", ",".join((*FIELDS, "extra")), + ",".join((*FIELDS[:-1], "quantity"))]) +def test_invalid_header(tmp_path, header): + path = tmp_path / "orders.csv" + path.write_text(header, encoding="utf-8") + with pytest.raises(CSVFileError, match="неверный заголовок"): + clean_orders(read_records(path)) + + +def test_missing_or_damaged_file(tmp_path): + path = tmp_path / "orders.csv" + with pytest.raises(CSVFileError, match="файл не найден"): + clean_orders(read_records(path)) + path.write_text(",".join(FIELDS) + '\n1,2024-02-29,"незакрытое поле', encoding="utf-8") + with pytest.raises(CSVFileError, match="ошибка структуры CSV"): + clean_orders(read_records(path)) diff --git a/tests/test_generate_data.py b/tests/test_generate_data.py new file mode 100644 index 0000000..1a11228 --- /dev/null +++ b/tests/test_generate_data.py @@ -0,0 +1,20 @@ +import csv + +from src.generate_data import generate_data + + +def test_record_count_and_header(tmp_path): + path = tmp_path / "orders.csv" + generate_data(path) + with path.open(encoding="utf-8", newline="") as source: + records = csv.reader(source) + assert next(records) == ["order_id", "order_date", "category", "quantity", "unit_price"] + assert sum(1 for _ in records) == 1500 + + +def test_repeated_generation_is_byte_identical(tmp_path): + path = tmp_path / "orders.csv" + generate_data(path) + first_run = path.read_bytes() + generate_data(path) + assert path.read_bytes() == first_run