flpolprojects commited on
Commit
4051dff
·
verified ·
1 Parent(s): da6d436

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +76 -173
app.py CHANGED
@@ -1,4 +1,4 @@
1
- from flask import Flask, render_template_string, request, redirect, url_for, abort
2
  import json
3
  import os
4
  import logging
@@ -11,7 +11,6 @@ from werkzeug.utils import secure_filename
11
 
12
  app = Flask(__name__)
13
  DATA_FILE = 'products.json'
14
- MAX_PHOTOS = 5 # Максимальное количество фото на товар
15
 
16
  # Настройки Hugging Face
17
  REPO_ID = "flpolprojects/Clients"
@@ -101,7 +100,6 @@ def catalog():
101
  padding: 0;
102
  box-sizing: border-box;
103
  }
104
-
105
  body {
106
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
107
  background-color: #f5f5f5;
@@ -109,12 +107,10 @@ def catalog():
109
  line-height: 1.6;
110
  padding: 20px;
111
  }
112
-
113
  .container {
114
  max-width: 1200px;
115
  margin: 0 auto;
116
  }
117
-
118
  h1 {
119
  text-align: center;
120
  color: #2c3e50;
@@ -122,14 +118,12 @@ def catalog():
122
  font-size: 2.5em;
123
  font-weight: 700;
124
  }
125
-
126
  .products-grid {
127
  display: grid;
128
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
129
  gap: 20px;
130
  padding: 0 15px;
131
  }
132
-
133
  .product {
134
  background: #ffffff;
135
  border-radius: 12px;
@@ -139,12 +133,10 @@ def catalog():
139
  flex-direction: column;
140
  box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
141
  }
142
-
143
  .product:hover {
144
  transform: translateY(-5px);
145
  box-shadow: 0 5px 25px rgba(0, 0, 0, 0.15);
146
  }
147
-
148
  .product-image {
149
  width: 100%;
150
  height: 200px;
@@ -152,39 +144,33 @@ def catalog():
152
  border-radius: 8px;
153
  margin-bottom: 15px;
154
  }
155
-
156
  .product-image img {
157
  width: 100%;
158
  height: 100%;
159
  object-fit: cover;
160
  transition: transform 0.3s ease;
161
  }
162
-
163
  .product-image img:hover {
164
  transform: scale(1.05);
165
  }
166
-
167
  .product h2 {
168
  font-size: 1.2em;
169
  color: #2c3e50;
170
  margin-bottom: 10px;
171
  font-weight: 600;
172
  }
173
-
174
  .product-price {
175
  font-size: 1.3em;
176
  color: #e74c3c;
177
  font-weight: 700;
178
  margin: 10px 0;
179
  }
180
-
181
  .product-description {
182
  color: #7f8c8d;
183
  font-size: 0.9em;
184
  flex-grow: 1;
185
  margin-bottom: 15px;
186
  }
187
-
188
  .product-button {
189
  background-color: #3498db;
190
  color: white;
@@ -198,52 +184,41 @@ def catalog():
198
  display: inline-block;
199
  font-weight: 500;
200
  }
201
-
202
  .product-button:hover {
203
  background-color: #2980b9;
204
  }
205
-
206
  @media (max-width: 768px) {
207
  .products-grid {
208
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
209
  gap: 10px;
210
  }
211
-
212
  body {
213
  padding: 10px;
214
  }
215
-
216
  h1 {
217
  font-size: 1.8em;
218
  margin-bottom: 20px;
219
  }
220
-
221
  .product {
222
  padding: 10px;
223
  }
224
-
225
  .product-image {
226
  height: 120px;
227
  }
228
-
229
  .product h2 {
230
  font-size: 1em;
231
  }
232
-
233
  .product-price {
234
  font-size: 1.1em;
235
  }
236
-
237
  .product-description {
238
  font-size: 0.8em;
239
  }
240
-
241
  .product-button {
242
  padding: 8px 15px;
243
  font-size: 0.9em;
244
  }
245
  }
246
-
247
  @keyframes fadeIn {
248
  from {
249
  opacity: 0;
@@ -254,24 +229,19 @@ def catalog():
254
  transform: translateY(0);
255
  }
256
  }
257
-
258
  .product {
259
  animation: fadeIn 0.5s ease-out forwards;
260
  }
261
-
262
  ::-webkit-scrollbar {
263
  width: 8px;
264
  }
265
-
266
  ::-webkit-scrollbar-track {
267
  background: #f1f1f1;
268
  }
269
-
270
  ::-webkit-scrollbar-thumb {
271
  background: #888;
272
  border-radius: 4px;
273
  }
274
-
275
  ::-webkit-scrollbar-thumb:hover {
276
  background: #555;
277
  }
@@ -282,19 +252,21 @@ def catalog():
282
  <h1>Каталог товаров</h1>
283
  <div class="products-grid">
284
  {% for product in products %}
285
- <div class="product">
286
- {% if product.get('photos') and product['photos'] %}
287
- <div class="product-image">
288
- <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ product['photos'][0] }}"
289
- alt="{{ product['name'] }}"
290
- loading="lazy">
 
 
 
 
 
 
 
291
  </div>
292
- {% endif %}
293
- <h2>{{ product['name'] }}</h2>
294
- <div class="product-price">{{ product['price'] }} ₽</div>
295
- <p class="product-description">{{ product['description'] }}</p>
296
- <a href="{{ url_for('product', product_id=loop.index0) }}" class="product-button">Подробнее</a>
297
- </div>
298
  {% endfor %}
299
  </div>
300
  </div>
@@ -303,15 +275,14 @@ def catalog():
303
  '''
304
  return render_template_string(catalog_html, products=products, repo_id=REPO_ID)
305
 
306
- @app.route('/product/<int:product_id>')
307
- def product(product_id):
308
  products = load_data()
309
  try:
310
- product = products[product_id]
311
  except IndexError:
312
- abort(404)
313
-
314
- product_html = '''
315
  <!DOCTYPE html>
316
  <html lang="ru">
317
  <head>
@@ -319,126 +290,90 @@ def product(product_id):
319
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
320
  <title>{{ product['name'] }}</title>
321
  <style>
322
- * {
323
- margin: 0;
324
- padding: 0;
325
- box-sizing: border-box;
326
- }
327
-
328
  body {
329
- font-family: Arial, sans-serif;
330
- line-height: 1.6;
331
- background-color: #f4f4f4;
332
  color: #333;
333
  padding: 20px;
334
  }
335
-
336
  .container {
337
- max-width: 900px;
338
  margin: 0 auto;
 
 
 
 
339
  }
340
-
341
  h1 {
342
- text-align: center;
343
- margin-bottom: 30px;
344
- color: #3498db;
345
  }
346
-
347
- .product-gallery {
348
  display: flex;
349
  flex-wrap: wrap;
350
- justify-content: center;
351
  margin-bottom: 20px;
352
  }
353
-
354
- .product-gallery img {
355
- width: 200px;
356
- height: 200px;
357
- object-fit: cover;
358
- margin: 10px;
359
- border-radius: 8px;
360
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
361
- }
362
-
363
- .product-description {
364
- background-color: #fff;
365
- padding: 20px;
366
- border-radius: 8px;
367
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
368
- }
369
-
370
- .product-description h2 {
371
- color: #3498db;
372
- margin-bottom: 15px;
373
  }
374
-
375
- .product-description p {
376
- font-size: 1.1em;
377
- line-height: 1.7;
378
  }
379
-
380
  .back-button {
381
  display: inline-block;
382
- padding: 10px 20px;
 
383
  background-color: #3498db;
384
  color: white;
385
  text-decoration: none;
386
  border-radius: 5px;
387
- transition: background-color 0.3s;
388
- margin-top: 20px;
389
- }
390
-
391
- .back-button:hover {
392
- background-color: #2980b9;
393
  }
394
  </style>
395
  </head>
396
  <body>
397
  <div class="container">
398
  <h1>{{ product['name'] }}</h1>
399
- <div class="product-gallery">
400
- {% if product.get('photos') and product['photos'] %}
401
  {% for photo in product['photos'] %}
402
- <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ photo }}" alt="{{ product['name'] }}">
403
  {% endfor %}
404
- {% else %}
405
- <p>Нет доступных фотографий.</p>
406
  {% endif %}
407
  </div>
408
- <div class="product-description">
409
- <h2>Описание товара</h2>
410
- <p>{{ product['description'] }}</p>
411
- <p><strong>Цена:</strong> {{ product['price'] }} ₽</p>
412
- </div>
413
- <a href="{{ url_for('catalog') }}" class="back-button">Вернуться в каталог</a>
414
  </div>
415
  </body>
416
  </html>
417
  '''
418
- return render_template_string(product_html, product=product, repo_id=REPO_ID)
419
 
420
  @app.route('/admin', methods=['GET', 'POST'])
421
  def admin():
422
  products = load_data()
423
  if request.method == 'POST':
424
  action = request.form.get('action')
425
-
426
  if action == 'add':
427
  name = request.form.get('name')
428
  price = request.form.get('price')
429
  description = request.form.get('description')
430
- photos = request.files.getlist('photos')
431
-
432
- photo_filenames = []
433
- if photos:
434
- for photo in photos[:MAX_PHOTOS]:
435
- if photo.filename:
 
436
  photo_filename = secure_filename(photo.filename)
437
  uploads_dir = 'uploads'
438
  os.makedirs(uploads_dir, exist_ok=True)
439
  temp_path = os.path.join(uploads_dir, photo_filename)
440
  photo.save(temp_path)
441
-
442
  try:
443
  api = HfApi()
444
  api.upload_file(
@@ -449,47 +384,44 @@ def admin():
449
  token=HF_TOKEN_WRITE,
450
  commit_message=f"Добавлено фото для товара {name}"
451
  )
452
- photo_filenames.append(photo_filename)
453
  except Exception as e:
454
  logging.error(f"Ошибка при загрузке фото: {e}")
 
455
  finally:
456
  os.remove(temp_path)
457
-
458
  if name and price and description:
459
  try:
460
  price = float(price.replace(',', '.'))
461
  except ValueError:
462
  return "Ошибка: Цена должна быть числом.", 400
463
-
464
  product = {
465
  'name': name,
466
  'price': price,
467
  'description': description,
468
- 'photos': photo_filenames
469
  }
470
-
471
  products.append(product)
472
  save_data(products)
473
  return redirect(url_for('admin'))
474
-
475
  elif action == 'edit':
476
  index = int(request.form.get('index'))
477
  name = request.form.get('name')
478
  price = request.form.get('price')
479
  description = request.form.get('description')
480
- photos = request.files.getlist('photos')
481
-
482
- photo_filenames = products[index].get('photos', []) # Сохраняем текущие фото
483
-
484
- if photos:
485
- for photo in photos[:MAX_PHOTOS]:
486
- if photo.filename:
 
487
  photo_filename = secure_filename(photo.filename)
488
  uploads_dir = 'uploads'
489
  os.makedirs(uploads_dir, exist_ok=True)
490
  temp_path = os.path.join(uploads_dir, photo_filename)
491
  photo.save(temp_path)
492
-
493
  try:
494
  api = HfApi()
495
  api.upload_file(
@@ -500,12 +432,13 @@ def admin():
500
  token=HF_TOKEN_WRITE,
501
  commit_message=f"Обновлено фото для товара {name}"
502
  )
503
- photo_filenames.append(photo_filename)
504
  except Exception as e:
505
  logging.error(f"Ошибка при загрузке фото: {e}")
 
506
  finally:
507
  os.remove(temp_path)
508
-
509
  products[index]['name'] = name
510
  try:
511
  price = float(price.replace(',', '.'))
@@ -513,17 +446,13 @@ def admin():
513
  return "Ошибка: Цена должна быть числом.", 400
514
  products[index]['price'] = price
515
  products[index]['description'] = description
516
- products[index]['photos'] = photo_filenames[:MAX_PHOTOS] # Обновляем список фото, ограничивая MAX_PHOTOS
517
-
518
  save_data(products)
519
  return redirect(url_for('admin'))
520
-
521
  elif action == 'delete':
522
  index = int(request.form.get('index'))
523
  del products[index]
524
  save_data(products)
525
  return redirect(url_for('admin'))
526
-
527
  admin_html = '''
528
  <!DOCTYPE html>
529
  <html lang="ru">
@@ -545,7 +474,7 @@ def admin():
545
  padding: 20px;
546
  border: 1px solid #ddd;
547
  border-radius: 5px;
548
- max-width: 100%; /* Make form responsive */
549
  margin-bottom: 20px;
550
  }
551
  label {
@@ -559,7 +488,7 @@ def admin():
559
  margin-top: 5px;
560
  border: 1px solid #ddd;
561
  border-radius: 4px;
562
- box-sizing: border-box; /* Include padding and border in the element's total width and height */
563
  }
564
  button {
565
  margin-top: 15px;
@@ -590,8 +519,6 @@ def admin():
590
  border-radius: 5px;
591
  background-color: #f9f9f9;
592
  }
593
-
594
- /* Media query for mobile devices */
595
  @media (max-width: 600px) {
596
  body {
597
  margin: 10px;
@@ -609,20 +536,6 @@ def admin():
609
  margin-bottom: 10px;
610
  }
611
  }
612
-
613
- .product-photos {
614
- display: flex;
615
- flex-wrap: wrap;
616
- margin-top: 10px;
617
- }
618
-
619
- .product-photos img {
620
- max-width: 50px;
621
- max-height: 50px;
622
- margin-right: 5px;
623
- border: 1px solid #ddd;
624
- padding: 2px;
625
- }
626
  </style>
627
  </head>
628
  <body>
@@ -631,16 +544,12 @@ def admin():
631
  <input type="hidden" name="action" value="add">
632
  <label for="name">Название товара:</label>
633
  <input type="text" id="name" name="name" required>
634
-
635
  <label for="price">Цена:</label>
636
  <input type="number" id="price" name="price" step="0.01" required>
637
-
638
  <label for="description">Описание:</label>
639
  <textarea id="description" name="description" rows="4" required></textarea>
640
-
641
  <label for="photos">Фотографии товара (до 5):</label>
642
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
643
-
644
  <button type="submit">Добавить товар</button>
645
  </form>
646
 
@@ -660,13 +569,11 @@ def admin():
660
  <h3>{{ product['name'] }}</h3>
661
  <p><strong>Цена:</strong> {{ product['price'] }} руб.</p>
662
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
663
- <div class="product-photos">
664
- {% if product.get('photos') and product['photos'] %}
665
- {% for photo in product['photos'] %}
666
- <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ photo }}" alt="{{ product['name'] }}">
667
- {% endfor %}
668
- {% endif %}
669
- </div>
670
 
671
  <details>
672
  <summary>Редактировать</summary>
@@ -675,16 +582,12 @@ def admin():
675
  <input type="hidden" name="index" value="{{ loop.index0 }}">
676
  <label for="name">Название товара:</label>
677
  <input type="text" id="name" name="name" value="{{ product['name'] }}" required>
678
-
679
  <label for="price">Цена:</label>
680
  <input type="number" id="price" name="price" step="0.01" value="{{ product['price'] }}" required>
681
-
682
  <label for="description">Описание:</label>
683
  <textarea id="description" name="description" rows="4" required>{{ product['description'] }}</textarea>
684
-
685
  <label for="photos">Фотографии товара (до 5):</label>
686
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
687
-
688
  <button type="submit">Сохранить изменения</button>
689
  </form>
690
  </details>
@@ -721,4 +624,4 @@ if __name__ == '__main__':
721
  except Exception as e:
722
  logging.error(f"Не удалось загрузить базу данных при запуске: {e}")
723
 
724
- app.run(debug=True, host='0.0.0.0', port=7860)
 
1
+ from flask import Flask, render_template_string, request, redirect, url_for
2
  import json
3
  import os
4
  import logging
 
11
 
12
  app = Flask(__name__)
13
  DATA_FILE = 'products.json'
 
14
 
15
  # Настройки Hugging Face
16
  REPO_ID = "flpolprojects/Clients"
 
100
  padding: 0;
101
  box-sizing: border-box;
102
  }
 
103
  body {
104
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
105
  background-color: #f5f5f5;
 
107
  line-height: 1.6;
108
  padding: 20px;
109
  }
 
110
  .container {
111
  max-width: 1200px;
112
  margin: 0 auto;
113
  }
 
114
  h1 {
115
  text-align: center;
116
  color: #2c3e50;
 
118
  font-size: 2.5em;
119
  font-weight: 700;
120
  }
 
121
  .products-grid {
122
  display: grid;
123
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
124
  gap: 20px;
125
  padding: 0 15px;
126
  }
 
127
  .product {
128
  background: #ffffff;
129
  border-radius: 12px;
 
133
  flex-direction: column;
134
  box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
135
  }
 
136
  .product:hover {
137
  transform: translateY(-5px);
138
  box-shadow: 0 5px 25px rgba(0, 0, 0, 0.15);
139
  }
 
140
  .product-image {
141
  width: 100%;
142
  height: 200px;
 
144
  border-radius: 8px;
145
  margin-bottom: 15px;
146
  }
 
147
  .product-image img {
148
  width: 100%;
149
  height: 100%;
150
  object-fit: cover;
151
  transition: transform 0.3s ease;
152
  }
 
153
  .product-image img:hover {
154
  transform: scale(1.05);
155
  }
 
156
  .product h2 {
157
  font-size: 1.2em;
158
  color: #2c3e50;
159
  margin-bottom: 10px;
160
  font-weight: 600;
161
  }
 
162
  .product-price {
163
  font-size: 1.3em;
164
  color: #e74c3c;
165
  font-weight: 700;
166
  margin: 10px 0;
167
  }
 
168
  .product-description {
169
  color: #7f8c8d;
170
  font-size: 0.9em;
171
  flex-grow: 1;
172
  margin-bottom: 15px;
173
  }
 
174
  .product-button {
175
  background-color: #3498db;
176
  color: white;
 
184
  display: inline-block;
185
  font-weight: 500;
186
  }
 
187
  .product-button:hover {
188
  background-color: #2980b9;
189
  }
 
190
  @media (max-width: 768px) {
191
  .products-grid {
192
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
193
  gap: 10px;
194
  }
 
195
  body {
196
  padding: 10px;
197
  }
 
198
  h1 {
199
  font-size: 1.8em;
200
  margin-bottom: 20px;
201
  }
 
202
  .product {
203
  padding: 10px;
204
  }
 
205
  .product-image {
206
  height: 120px;
207
  }
 
208
  .product h2 {
209
  font-size: 1em;
210
  }
 
211
  .product-price {
212
  font-size: 1.1em;
213
  }
 
214
  .product-description {
215
  font-size: 0.8em;
216
  }
 
217
  .product-button {
218
  padding: 8px 15px;
219
  font-size: 0.9em;
220
  }
221
  }
 
222
  @keyframes fadeIn {
223
  from {
224
  opacity: 0;
 
229
  transform: translateY(0);
230
  }
231
  }
 
232
  .product {
233
  animation: fadeIn 0.5s ease-out forwards;
234
  }
 
235
  ::-webkit-scrollbar {
236
  width: 8px;
237
  }
 
238
  ::-webkit-scrollbar-track {
239
  background: #f1f1f1;
240
  }
 
241
  ::-webkit-scrollbar-thumb {
242
  background: #888;
243
  border-radius: 4px;
244
  }
 
245
  ::-webkit-scrollbar-thumb:hover {
246
  background: #555;
247
  }
 
252
  <h1>Каталог товаров</h1>
253
  <div class="products-grid">
254
  {% for product in products %}
255
+ <a href="{{ url_for('product_detail', index=loop.index0) }}" style="text-decoration: none; color: inherit;">
256
+ <div class="product">
257
+ {% if product.get('photos') and product['photos']|length > 0 %}
258
+ <div class="product-image">
259
+ <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ product['photos'][0] }}"
260
+ alt="{{ product['name'] }}"
261
+ loading="lazy">
262
+ </div>
263
+ {% endif %}
264
+ <h2>{{ product['name'] }}</h2>
265
+ <div class="product-price">{{ product['price'] }} ₽</div>
266
+ <p class="product-description">{{ product['description'][:100] }}{% if product['description']|length > 100 %}...{% endif %}</p>
267
+ <div class="product-button">Подробнее</div>
268
  </div>
269
+ </a>
 
 
 
 
 
270
  {% endfor %}
271
  </div>
272
  </div>
 
275
  '''
276
  return render_template_string(catalog_html, products=products, repo_id=REPO_ID)
277
 
278
+ @app.route('/product/<int:index>')
279
+ def product_detail(index):
280
  products = load_data()
281
  try:
282
+ product = products[index]
283
  except IndexError:
284
+ return "Продукт не найден", 404
285
+ detail_html = '''
 
286
  <!DOCTYPE html>
287
  <html lang="ru">
288
  <head>
 
290
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
291
  <title>{{ product['name'] }}</title>
292
  <style>
 
 
 
 
 
 
293
  body {
294
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
295
+ background-color: #f5f5f5;
 
296
  color: #333;
297
  padding: 20px;
298
  }
 
299
  .container {
300
+ max-width: 800px;
301
  margin: 0 auto;
302
+ background: #fff;
303
+ padding: 20px;
304
+ border-radius: 8px;
305
+ box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
306
  }
 
307
  h1 {
308
+ margin-bottom: 20px;
 
 
309
  }
310
+ .photos {
 
311
  display: flex;
312
  flex-wrap: wrap;
313
+ gap: 10px;
314
  margin-bottom: 20px;
315
  }
316
+ .photos img {
317
+ width: calc(50% - 10px);
318
+ height: auto;
319
+ border-radius: 5px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320
  }
321
+ @media (max-width: 600px) {
322
+ .photos img {
323
+ width: 100%;
324
+ }
325
  }
 
326
  .back-button {
327
  display: inline-block;
328
+ margin-top: 20px;
329
+ padding: 10px 15px;
330
  background-color: #3498db;
331
  color: white;
332
  text-decoration: none;
333
  border-radius: 5px;
 
 
 
 
 
 
334
  }
335
  </style>
336
  </head>
337
  <body>
338
  <div class="container">
339
  <h1>{{ product['name'] }}</h1>
340
+ <div class="photos">
341
+ {% if product.get('photos') %}
342
  {% for photo in product['photos'] %}
343
+ <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ photo }}" alt="{{ product['name'] }}">
344
  {% endfor %}
 
 
345
  {% endif %}
346
  </div>
347
+ <p><strong>Цена:</strong> {{ product['price'] }} ₽</p>
348
+ <p><strong>Описание:</strong> {{ product['description'] }}</p>
349
+ <a href="{{ url_for('catalog') }}" class="back-button">Назад к каталогу</a>
 
 
 
350
  </div>
351
  </body>
352
  </html>
353
  '''
354
+ return render_template_string(detail_html, product=product, repo_id=REPO_ID)
355
 
356
  @app.route('/admin', methods=['GET', 'POST'])
357
  def admin():
358
  products = load_data()
359
  if request.method == 'POST':
360
  action = request.form.get('action')
 
361
  if action == 'add':
362
  name = request.form.get('name')
363
  price = request.form.get('price')
364
  description = request.form.get('description')
365
+ photos_files = request.files.getlist('photos')
366
+ photos_list = []
367
+ if photos_files:
368
+ if len(photos_files) > 5:
369
+ photos_files = photos_files[:5]
370
+ for photo in photos_files:
371
+ if photo and photo.filename:
372
  photo_filename = secure_filename(photo.filename)
373
  uploads_dir = 'uploads'
374
  os.makedirs(uploads_dir, exist_ok=True)
375
  temp_path = os.path.join(uploads_dir, photo_filename)
376
  photo.save(temp_path)
 
377
  try:
378
  api = HfApi()
379
  api.upload_file(
 
384
  token=HF_TOKEN_WRITE,
385
  commit_message=f"Добавлено фото для товара {name}"
386
  )
387
+ photos_list.append(photo_filename)
388
  except Exception as e:
389
  logging.error(f"Ошибка при загрузке фото: {e}")
390
+ return f"Ошибка при загрузке фото: {e}", 500
391
  finally:
392
  os.remove(temp_path)
 
393
  if name and price and description:
394
  try:
395
  price = float(price.replace(',', '.'))
396
  except ValueError:
397
  return "Ошибка: Цена должна быть числом.", 400
 
398
  product = {
399
  'name': name,
400
  'price': price,
401
  'description': description,
402
+ 'photos': photos_list
403
  }
 
404
  products.append(product)
405
  save_data(products)
406
  return redirect(url_for('admin'))
 
407
  elif action == 'edit':
408
  index = int(request.form.get('index'))
409
  name = request.form.get('name')
410
  price = request.form.get('price')
411
  description = request.form.get('description')
412
+ photos_files = request.files.getlist('photos')
413
+ # Если загружены новые фото, обновляем список фотографий товара
414
+ if photos_files and any(photo.filename for photo in photos_files):
415
+ new_photos_list = []
416
+ if len(photos_files) > 5:
417
+ photos_files = photos_files[:5]
418
+ for photo in photos_files:
419
+ if photo and photo.filename:
420
  photo_filename = secure_filename(photo.filename)
421
  uploads_dir = 'uploads'
422
  os.makedirs(uploads_dir, exist_ok=True)
423
  temp_path = os.path.join(uploads_dir, photo_filename)
424
  photo.save(temp_path)
 
425
  try:
426
  api = HfApi()
427
  api.upload_file(
 
432
  token=HF_TOKEN_WRITE,
433
  commit_message=f"Обновлено фото для товара {name}"
434
  )
435
+ new_photos_list.append(photo_filename)
436
  except Exception as e:
437
  logging.error(f"Ошибка при загрузке фото: {e}")
438
+ return f"Ошибка при загрузке фото: {e}", 500
439
  finally:
440
  os.remove(temp_path)
441
+ products[index]['photos'] = new_photos_list
442
  products[index]['name'] = name
443
  try:
444
  price = float(price.replace(',', '.'))
 
446
  return "Ошибка: Цена должна быть числом.", 400
447
  products[index]['price'] = price
448
  products[index]['description'] = description
 
 
449
  save_data(products)
450
  return redirect(url_for('admin'))
 
451
  elif action == 'delete':
452
  index = int(request.form.get('index'))
453
  del products[index]
454
  save_data(products)
455
  return redirect(url_for('admin'))
 
456
  admin_html = '''
457
  <!DOCTYPE html>
458
  <html lang="ru">
 
474
  padding: 20px;
475
  border: 1px solid #ddd;
476
  border-radius: 5px;
477
+ max-width: 100%;
478
  margin-bottom: 20px;
479
  }
480
  label {
 
488
  margin-top: 5px;
489
  border: 1px solid #ddd;
490
  border-radius: 4px;
491
+ box-sizing: border-box;
492
  }
493
  button {
494
  margin-top: 15px;
 
519
  border-radius: 5px;
520
  background-color: #f9f9f9;
521
  }
 
 
522
  @media (max-width: 600px) {
523
  body {
524
  margin: 10px;
 
536
  margin-bottom: 10px;
537
  }
538
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
  </style>
540
  </head>
541
  <body>
 
544
  <input type="hidden" name="action" value="add">
545
  <label for="name">Название товара:</label>
546
  <input type="text" id="name" name="name" required>
 
547
  <label for="price">Цена:</label>
548
  <input type="number" id="price" name="price" step="0.01" required>
 
549
  <label for="description">Описание:</label>
550
  <textarea id="description" name="description" rows="4" required></textarea>
 
551
  <label for="photos">Фотографии товара (до 5):</label>
552
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
 
553
  <button type="submit">Добавить товар</button>
554
  </form>
555
 
 
569
  <h3>{{ product['name'] }}</h3>
570
  <p><strong>Цена:</strong> {{ product['price'] }} руб.</p>
571
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
572
+ {% if product.get('photos') and product['photos']|length > 0 %}
573
+ <img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/{{ product['photos'][0] }}"
574
+ alt="{{ product['name'] }}"
575
+ style="max-width: 100px;">
576
+ {% endif %}
 
 
577
 
578
  <details>
579
  <summary>Редактировать</summary>
 
582
  <input type="hidden" name="index" value="{{ loop.index0 }}">
583
  <label for="name">Название товара:</label>
584
  <input type="text" id="name" name="name" value="{{ product['name'] }}" required>
 
585
  <label for="price">Цена:</label>
586
  <input type="number" id="price" name="price" step="0.01" value="{{ product['price'] }}" required>
 
587
  <label for="description">Описание:</label>
588
  <textarea id="description" name="description" rows="4" required>{{ product['description'] }}</textarea>
 
589
  <label for="photos">Фотографии товара (до 5):</label>
590
  <input type="file" id="photos" name="photos" accept="image/*" multiple>
 
591
  <button type="submit">Сохранить изменения</button>
592
  </form>
593
  </details>
 
624
  except Exception as e:
625
  logging.error(f"Не удалось загрузить базу данных при запуске: {e}")
626
 
627
+ app.run(debug=True, host='0.0.0.0', port=7860)