Kgshop commited on
Commit
8240a5c
·
verified ·
1 Parent(s): 1a6690b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -22
app.py CHANGED
@@ -4,15 +4,16 @@ import os
4
  import logging
5
  import threading
6
  import time
 
7
  from datetime import datetime
8
  from huggingface_hub import HfApi, hf_hub_download
9
  from huggingface_hub.utils import RepositoryNotFoundError
10
  from werkzeug.utils import secure_filename
11
 
12
  app = Flask(__name__)
13
- app.secret_key = 'your_unique_secret_key_12345' # Установите уникальный и секретный ключ
14
  DATA_FILE = 'data_detobuv.json'
15
- USERS_FILE = 'users.json'
16
 
17
  # Настройки Hugging Face
18
  REPO_ID = "Kgshop/clients"
@@ -22,9 +23,51 @@ HF_TOKEN_READ = os.getenv("HF_TOKEN_READ")
22
  # Адрес магазина
23
  STORE_ADDRESS = "Бишкек, Рынок Дордой, 9 ряд, 06 контейнер"
24
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  # Настройка логирования
26
  logging.basicConfig(level=logging.DEBUG)
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  def load_data():
29
  try:
30
  download_db_from_hf()
@@ -114,7 +157,9 @@ def catalog():
114
  products = data['products']
115
  categories = data['categories']
116
  is_authenticated = 'user' in session
117
-
 
 
118
  catalog_html = '''
119
  <!DOCTYPE html>
120
  <html lang="ru">
@@ -428,6 +473,12 @@ def catalog():
428
  font-style: italic;
429
  color: #666;
430
  }
 
 
 
 
 
 
431
  </style>
432
  </head>
433
  <body>
@@ -437,6 +488,13 @@ def catalog():
437
  <div class="auth-links">
438
  {% if is_authenticated %}
439
  <span>Добро пожаловать, {{ session['user'] }}!</span>
 
 
 
 
 
 
 
440
  <a href="{{ url_for('logout') }}">Выйти</a>
441
  {% else %}
442
  <a href="{{ url_for('login') }}">Войти</a>
@@ -472,7 +530,7 @@ def catalog():
472
  {% endif %}
473
  <h2>{{ product['name'] }}</h2>
474
  {% if is_authenticated %}
475
- <div class="product-price">{{ product['price'] }} с</div>
476
  {% else %}
477
  <div class="product-price">Цена доступна после входа</div>
478
  {% endif %}
@@ -512,7 +570,7 @@ def catalog():
512
  <h2>Корзина</h2>
513
  <div id="cartContent"></div>
514
  <div style="margin-top: 20px; text-align: right;">
515
- <strong>Итого: <span id="cartTotal">0</span> с</strong>
516
  <button class="product-button clear-cart" onclick="clearCart()">Очистить</button>
517
  <button class="product-button order-button" onclick="orderViaWhatsApp()">Заказать</button>
518
  </div>
@@ -527,6 +585,7 @@ def catalog():
527
  <script>
528
  const products = {{ products|tojson }};
529
  let selectedProductIndex = null;
 
530
 
531
  function toggleTheme() {
532
  document.body.classList.toggle('dark-mode');
@@ -648,7 +707,7 @@ def catalog():
648
  let total = 0;
649
 
650
  cartContent.innerHTML = cart.length === 0 ? '<p>Корзина пуста</p>' : cart.map(item => {
651
- const itemTotal = item.price * item.quantity;
652
  total += itemTotal;
653
  return `
654
  <div class="cart-item">
@@ -656,15 +715,15 @@ def catalog():
656
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/${item.photo}" alt="${item.name}">` : ''}
657
  <div>
658
  <strong>${item.name}</strong>
659
- <p>${item.price} с × ${item.quantity} (Цвет: ${item.color})</p>
660
  </div>
661
  </div>
662
- <span>${itemTotal} с</span>
663
  </div>
664
  `;
665
  }).join('');
666
 
667
- document.getElementById('cartTotal').textContent = total;
668
  document.getElementById('cartModal').style.display = 'block';
669
  }
670
 
@@ -677,11 +736,11 @@ def catalog():
677
  let total = 0;
678
  let orderText = "Заказ:%0A";
679
  cart.forEach((item, index) => {
680
- const itemTotal = item.price * item.quantity;
681
  total += itemTotal;
682
- orderText += `${index + 1}. ${item.name} - ${item.price} с × ${item.quantity} (Цвет: ${item.color})%0A`;
683
  });
684
- orderText += `Итого: ${total} с%0A`;
685
  orderText += `Страна: {{ session.get('country', 'Не указана') }}%0A`;
686
  orderText += `Город: {{ session.get('city', 'Не указан') }}`;
687
  window.open(`https://api.whatsapp.com/send?phone=996555360556&text=${orderText}`, '_blank');
@@ -726,13 +785,17 @@ def catalog():
726
  '''
727
  return render_template_string(catalog_html, products=products, categories=categories,
728
  repo_id=REPO_ID, is_authenticated=is_authenticated,
729
- store_address=STORE_ADDRESS, session=session)
 
 
730
 
731
  @app.route('/product/<int:index>')
732
  def product_detail(index):
733
  data = load_data()
734
  products = data['products']
735
  is_authenticated = 'user' in session
 
 
736
  try:
737
  product = products[index]
738
  except IndexError:
@@ -764,7 +827,7 @@ def product_detail(index):
764
  </div>
765
  <p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
766
  {% if is_authenticated %}
767
- <p><strong>Цена:</strong> {{ product['price'] }} с</p>
768
  {% else %}
769
  <p><strong>Цена:</strong> Доступна после входа</p>
770
  {% endif %}
@@ -772,7 +835,17 @@ def product_detail(index):
772
  <p><strong>Доступные цвета:</strong> {{ product.get('colors', ['Нет цветов'])|join(', ') }}</p>
773
  </div>
774
  '''
775
- return render_template_string(detail_html, product=product, repo_id=REPO_ID, is_authenticated=is_authenticated)
 
 
 
 
 
 
 
 
 
 
776
 
777
  @app.route('/register', methods=['GET', 'POST'])
778
  def register():
@@ -794,7 +867,7 @@ def register():
794
  return "Пользователь с таким логином уже существует", 400
795
 
796
  users[login] = {
797
- 'password': password, # Сохранение пароля без хеширования
798
  'country': country,
799
  'city': city,
800
  'purchase_type': purchase_type
@@ -803,6 +876,7 @@ def register():
803
  session['user'] = login
804
  session['country'] = country
805
  session['city'] = city
 
806
  return redirect(url_for('catalog'))
807
 
808
  return render_template_string('''
@@ -900,6 +974,7 @@ def login():
900
  session['user'] = login
901
  session['country'] = users[login]['country']
902
  session['city'] = users[login]['city']
 
903
  return redirect(url_for('catalog'))
904
  return "Неверный логин или пароль", 401
905
 
@@ -987,6 +1062,7 @@ def auto_login():
987
  session['user'] = login
988
  session['country'] = users[login]['country']
989
  session['city'] = users[login]['city']
 
990
  return "OK", 200
991
  return "Ошибка авторизации", 401
992
 
@@ -995,6 +1071,7 @@ def logout():
995
  session.pop('user', None)
996
  session.pop('country', None)
997
  session.pop('city', None)
 
998
  return redirect(url_for('catalog'))
999
 
1000
  @app.route('/admin', methods=['GET', 'POST'])
@@ -1056,10 +1133,10 @@ def admin():
1056
  if not name or not price or not description:
1057
  return "Ошибка: Заполните все обязательные поля", 400
1058
 
1059
- price = float(price.replace(',', '.'))
1060
  new_product = {
1061
  'name': name,
1062
- 'price': price,
1063
  'description': description,
1064
  'category': category if category in categories else 'Без категории',
1065
  'photos': photos_list,
@@ -1102,7 +1179,7 @@ def admin():
1102
  products[index]['photos'] = new_photos_list
1103
 
1104
  products[index]['name'] = name
1105
- products[index]['price'] = float(price.replace(',', '.'))
1106
  products[index]['description'] = description
1107
  products[index]['category'] = category if category in categories else 'Без категории'
1108
  products[index]['colors'] = colors if colors else []
@@ -1230,7 +1307,7 @@ def admin():
1230
  <input type="hidden" name="action" value="add">
1231
  <label>Название товара:</label>
1232
  <input type="text" name="name" required>
1233
- <label>Цена:</label>
1234
  <input type="number" name="price" step="0.01" required>
1235
  <label>Описание:</label>
1236
  <textarea name="description" rows="4" required></textarea>
@@ -1289,7 +1366,7 @@ def admin():
1289
  <div class="product-item">
1290
  <h3>{{ product['name'] }}</h3>
1291
  <p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
1292
- <p><strong>Цена:</strong> {{ product['price'] }} с</p>
1293
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
1294
  <p><strong>Цвета:</strong> {{ product.get('colors', ['Нет цветов'])|join(', ') }}</p>
1295
  {% if product.get('photos') and product['photos']|length > 0 %}
@@ -1308,7 +1385,7 @@ def admin():
1308
  <input type="hidden" name="index" value="{{ loop.index0 }}">
1309
  <label>Название:</label>
1310
  <input type="text" name="name" value="{{ product['name'] }}" required>
1311
- <label>Цена:</label>
1312
  <input type="number" name="price" step="0.01" value="{{ product['price'] }}" required>
1313
  <label>Описание:</label>
1314
  <textarea name="description" rows="4" required>{{ product['description'] }}</textarea>
 
4
  import logging
5
  import threading
6
  import time
7
+ import requests
8
  from datetime import datetime
9
  from huggingface_hub import HfApi, hf_hub_download
10
  from huggingface_hub.utils import RepositoryNotFoundError
11
  from werkzeug.utils import secure_filename
12
 
13
  app = Flask(__name__)
14
+ app.secret_key = 'your_unique_secret_key_12345' # Уникальный секретный ключ
15
  DATA_FILE = 'data_detobuv.json'
16
+ USERS_FILE = 'users_detobuv.json'
17
 
18
  # Настройки Hugging Face
19
  REPO_ID = "Kgshop/clients"
 
23
  # Адрес магазина
24
  STORE_ADDRESS = "Бишкек, Рынок Дордой, 9 ряд, 06 контейнер"
25
 
26
+ # API для получения актуальных курсов валют
27
+ EXCHANGE_RATE_API_KEY = "a1bbe02a37d9584ab12ffc1f" # Замените на ваш ключ от ExchangeRate-API
28
+ EXCHANGE_RATE_API_URL = f"https://v6.exchangerate-api.com/v6/{EXCHANGE_RATE_API_KEY}/latest/USD"
29
+
30
+ # Поддерживаемые валюты
31
+ CURRENCIES = {
32
+ 'USD': 'Доллар США ($)',
33
+ 'KGS': 'Кыргызский сом (с)',
34
+ 'RUB': 'Российский рубль (₽)',
35
+ 'KZT': 'Казахский тенге (₸)'
36
+ }
37
+
38
  # Настройка логирования
39
  logging.basicConfig(level=logging.DEBUG)
40
 
41
+ def get_exchange_rates():
42
+ """Получение актуальных курсов валют из API."""
43
+ try:
44
+ response = requests.get(EXCHANGE_RATE_API_URL)
45
+ data = response.json()
46
+ if data['result'] == 'success':
47
+ rates = {
48
+ 'USD': 1.0,
49
+ 'KGS': data['conversion_rates']['KGS'],
50
+ 'RUB': data['conversion_rates']['RUB'],
51
+ 'KZT': data['conversion_rates']['KZT']
52
+ }
53
+ logging.info("Курсы валют успешно обновлены из API")
54
+ return rates
55
+ else:
56
+ raise Exception("Ошибка API: " + data['error-type'])
57
+ except Exception as e:
58
+ logging.error(f"Не удалось получить курсы валют: {e}")
59
+ # Резервные фиксированные курсы в случае сбоя API
60
+ return {
61
+ 'USD': 1.0,
62
+ 'KGS': 89.0,
63
+ 'RUB': 97.0,
64
+ 'KZT': 480.0
65
+ }
66
+
67
+ def convert_price(price_usd, currency, rates):
68
+ """Конвертация цены из USD в указанную валюту."""
69
+ return round(price_usd * rates[currency], 2)
70
+
71
  def load_data():
72
  try:
73
  download_db_from_hf()
 
157
  products = data['products']
158
  categories = data['categories']
159
  is_authenticated = 'user' in session
160
+ rates = get_exchange_rates()
161
+ selected_currency = session.get('currency', 'USD') if is_authenticated else 'USD'
162
+
163
  catalog_html = '''
164
  <!DOCTYPE html>
165
  <html lang="ru">
 
473
  font-style: italic;
474
  color: #666;
475
  }
476
+ .currency-select {
477
+ padding: 8px;
478
+ border-radius: 8px;
479
+ border: 1px solid #e2e8f0;
480
+ font-size: 1rem;
481
+ }
482
  </style>
483
  </head>
484
  <body>
 
488
  <div class="auth-links">
489
  {% if is_authenticated %}
490
  <span>Добро пожаловать, {{ session['user'] }}!</span>
491
+ <form method="POST" action="{{ url_for('set_currency') }}" style="display: inline;">
492
+ <select name="currency" class="currency-select" onchange="this.form.submit()">
493
+ {% for code, name in currencies.items() %}
494
+ <option value="{{ code }}" {% if code == selected_currency %}selected{% endif %}>{{ name }}</option>
495
+ {% endfor %}
496
+ </select>
497
+ </form>
498
  <a href="{{ url_for('logout') }}">Выйти</a>
499
  {% else %}
500
  <a href="{{ url_for('login') }}">Войти</a>
 
530
  {% endif %}
531
  <h2>{{ product['name'] }}</h2>
532
  {% if is_authenticated %}
533
+ <div class="product-price">{{ convert_price(product['price'], selected_currency, rates) }} {{ selected_currency }}</div>
534
  {% else %}
535
  <div class="product-price">Цена доступна после входа</div>
536
  {% endif %}
 
570
  <h2>Корзина</h2>
571
  <div id="cartContent"></div>
572
  <div style="margin-top: 20px; text-align: right;">
573
+ <strong>Итого: <span id="cartTotal">0</span> {{ selected_currency }}</strong>
574
  <button class="product-button clear-cart" onclick="clearCart()">Очистить</button>
575
  <button class="product-button order-button" onclick="orderViaWhatsApp()">Заказать</button>
576
  </div>
 
585
  <script>
586
  const products = {{ products|tojson }};
587
  let selectedProductIndex = null;
588
+ const selectedCurrency = '{{ selected_currency }}';
589
 
590
  function toggleTheme() {
591
  document.body.classList.toggle('dark-mode');
 
707
  let total = 0;
708
 
709
  cartContent.innerHTML = cart.length === 0 ? '<p>Корзина пуста</p>' : cart.map(item => {
710
+ const itemTotal = item.price * item.quantity * {{ rates[selected_currency]|tojson }};
711
  total += itemTotal;
712
  return `
713
  <div class="cart-item">
 
715
  ${item.photo ? `<img src="https://huggingface.co/datasets/{{ repo_id }}/resolve/main/photos/${item.photo}" alt="${item.name}">` : ''}
716
  <div>
717
  <strong>${item.name}</strong>
718
+ <p>${(item.price * {{ rates[selected_currency]|tojson }}).toFixed(2)} ${selectedCurrency} × ${item.quantity} (Цвет: ${item.color})</p>
719
  </div>
720
  </div>
721
+ <span>${itemTotal.toFixed(2)} ${selectedCurrency}</span>
722
  </div>
723
  `;
724
  }).join('');
725
 
726
+ document.getElementById('cartTotal').textContent = total.toFixed(2);
727
  document.getElementById('cartModal').style.display = 'block';
728
  }
729
 
 
736
  let total = 0;
737
  let orderText = "Заказ:%0A";
738
  cart.forEach((item, index) => {
739
+ const itemTotal = item.price * item.quantity * {{ rates[selected_currency]|tojson }};
740
  total += itemTotal;
741
+ orderText += `${index + 1}. ${item.name} - ${(item.price * {{ rates[selected_currency]|tojson }}).toFixed(2)} ${selectedCurrency} × ${item.quantity} (Цвет: ${item.color})%0A`;
742
  });
743
+ orderText += `Итого: ${total.toFixed(2)} ${selectedCurrency}%0A`;
744
  orderText += `Страна: {{ session.get('country', 'Не указана') }}%0A`;
745
  orderText += `Город: {{ session.get('city', 'Не указан') }}`;
746
  window.open(`https://api.whatsapp.com/send?phone=996555360556&text=${orderText}`, '_blank');
 
785
  '''
786
  return render_template_string(catalog_html, products=products, categories=categories,
787
  repo_id=REPO_ID, is_authenticated=is_authenticated,
788
+ store_address=STORE_ADDRESS, session=session,
789
+ convert_price=convert_price, rates=rates,
790
+ selected_currency=selected_currency, currencies=CURRENCIES)
791
 
792
  @app.route('/product/<int:index>')
793
  def product_detail(index):
794
  data = load_data()
795
  products = data['products']
796
  is_authenticated = 'user' in session
797
+ rates = get_exchange_rates()
798
+ selected_currency = session.get('currency', 'USD') if is_authenticated else 'USD'
799
  try:
800
  product = products[index]
801
  except IndexError:
 
827
  </div>
828
  <p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
829
  {% if is_authenticated %}
830
+ <p><strong>Цена:</strong> {{ convert_price(product['price'], selected_currency, rates) }} {{ selected_currency }}</p>
831
  {% else %}
832
  <p><strong>Цена:</strong> Доступна после входа</p>
833
  {% endif %}
 
835
  <p><strong>Доступные цвета:</strong> {{ product.get('colors', ['Нет цветов'])|join(', ') }}</p>
836
  </div>
837
  '''
838
+ return render_template_string(detail_html, product=product, repo_id=REPO_ID,
839
+ is_authenticated=is_authenticated, convert_price=convert_price,
840
+ rates=rates, selected_currency=selected_currency)
841
+
842
+ @app.route('/set_currency', methods=['POST'])
843
+ def set_currency():
844
+ if 'user' in session:
845
+ currency = request.form.get('currency')
846
+ if currency in CURRENCIES:
847
+ session['currency'] = currency
848
+ return redirect(url_for('catalog'))
849
 
850
  @app.route('/register', methods=['GET', 'POST'])
851
  def register():
 
867
  return "Пользователь с таким логином уже существует", 400
868
 
869
  users[login] = {
870
+ 'password': password,
871
  'country': country,
872
  'city': city,
873
  'purchase_type': purchase_type
 
876
  session['user'] = login
877
  session['country'] = country
878
  session['city'] = city
879
+ session['currency'] = 'USD' # Устанавливаем валюту по умолчанию
880
  return redirect(url_for('catalog'))
881
 
882
  return render_template_string('''
 
974
  session['user'] = login
975
  session['country'] = users[login]['country']
976
  session['city'] = users[login]['city']
977
+ session['currency'] = 'USD' # Устанавливаем валюту по умолчанию
978
  return redirect(url_for('catalog'))
979
  return "Неверный логин или пароль", 401
980
 
 
1062
  session['user'] = login
1063
  session['country'] = users[login]['country']
1064
  session['city'] = users[login]['city']
1065
+ session['currency'] = 'USD' # Устанавливаем валюту по умолчанию
1066
  return "OK", 200
1067
  return "Ошибка авторизации", 401
1068
 
 
1071
  session.pop('user', None)
1072
  session.pop('country', None)
1073
  session.pop('city', None)
1074
+ session.pop('currency', None)
1075
  return redirect(url_for('catalog'))
1076
 
1077
  @app.route('/admin', methods=['GET', 'POST'])
 
1133
  if not name or not price or not description:
1134
  return "Ошибка: Заполните все обязательные поля", 400
1135
 
1136
+ price = float(price.replace(',', '.')) # Цена в USD
1137
  new_product = {
1138
  'name': name,
1139
+ 'price': price, # Цена в долларах
1140
  'description': description,
1141
  'category': category if category in categories else 'Без категории',
1142
  'photos': photos_list,
 
1179
  products[index]['photos'] = new_photos_list
1180
 
1181
  products[index]['name'] = name
1182
+ products[index]['price'] = float(price.replace(',', '.')) # Цена в USD
1183
  products[index]['description'] = description
1184
  products[index]['category'] = category if category in categories else 'Без категории'
1185
  products[index]['colors'] = colors if colors else []
 
1307
  <input type="hidden" name="action" value="add">
1308
  <label>Название товара:</label>
1309
  <input type="text" name="name" required>
1310
+ <label>Цена (USD):</label>
1311
  <input type="number" name="price" step="0.01" required>
1312
  <label>Описание:</label>
1313
  <textarea name="description" rows="4" required></textarea>
 
1366
  <div class="product-item">
1367
  <h3>{{ product['name'] }}</h3>
1368
  <p><strong>Категория:</strong> {{ product.get('category', 'Без категории') }}</p>
1369
+ <p><strong>Цена:</strong> {{ product['price'] }} USD</p>
1370
  <p><strong>Описание:</strong> {{ product['description'] }}</p>
1371
  <p><strong>Цвета:</strong> {{ product.get('colors', ['Нет цветов'])|join(', ') }}</p>
1372
  {% if product.get('photos') and product['photos']|length > 0 %}
 
1385
  <input type="hidden" name="index" value="{{ loop.index0 }}">
1386
  <label>Название:</label>
1387
  <input type="text" name="name" value="{{ product['name'] }}" required>
1388
+ <label>Цена (USD):</label>
1389
  <input type="number" name="price" step="0.01" value="{{ product['price'] }}" required>
1390
  <label>Описание:</label>
1391
  <textarea name="description" rows="4" required>{{ product['description'] }}</textarea>