initial commit
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
venv/
|
||||||
|
migrations/
|
||||||
|
__pycache__/
|
||||||
|
.idea/
|
||||||
|
.git/
|
||||||
|
.env
|
||||||
|
test.*
|
||||||
33
app.py
Normal file
33
app.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from flask import Flask
|
||||||
|
from flask_cors import CORS
|
||||||
|
from flask_jwt_extended import JWTManager
|
||||||
|
from flask_migrate import Migrate
|
||||||
|
from flask_session import Session
|
||||||
|
|
||||||
|
import config
|
||||||
|
import database
|
||||||
|
import routes
|
||||||
|
|
||||||
|
# Flask config
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.config.from_object(config.FlaskConfig)
|
||||||
|
|
||||||
|
# Database config
|
||||||
|
database.db.init_app(app)
|
||||||
|
migrate = Migrate(app, database.db)
|
||||||
|
|
||||||
|
# Session config
|
||||||
|
server_session = Session(app)
|
||||||
|
|
||||||
|
# CORS config
|
||||||
|
CORS(app, supports_credentials=True)
|
||||||
|
jwt = JWTManager(app)
|
||||||
|
blueprints = [
|
||||||
|
(routes.auth_blueprint, '/auth')
|
||||||
|
]
|
||||||
|
|
||||||
|
for blueprint, url_prefix in blueprints:
|
||||||
|
app.register_blueprint(blueprint, url_prefix=url_prefix)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run()
|
||||||
21
config.py
Normal file
21
config.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import redis
|
||||||
|
|
||||||
|
import settings
|
||||||
|
|
||||||
|
|
||||||
|
class FlaskConfig:
|
||||||
|
# Flask settings
|
||||||
|
SECRET_KEY = settings.SECRET_KEY
|
||||||
|
TEMPLATES_AUTO_RELOAD = True
|
||||||
|
|
||||||
|
# Database setting
|
||||||
|
SQLALCHEMY_TRACK_MODIFICATIONS = True
|
||||||
|
SQLALCHEMY_DATABASE_URI = f'postgresql://{settings.PG_LOGIN}:{settings.PG_PASSWORD}@{settings.PG_HOST}:{settings.PG_PORT}/{settings.PG_DATABASE}'
|
||||||
|
|
||||||
|
# Session settings
|
||||||
|
SESSION_TYPE = 'redis'
|
||||||
|
SESSION_PERMANENT = False
|
||||||
|
SESSION_USE_SIGNER = True
|
||||||
|
SESSION_REDIS = redis.from_url('redis://127.0.0.1:6379')
|
||||||
|
|
||||||
|
# SQLALCHEMY_ECHO = True
|
||||||
1
database/__init__.py
Normal file
1
database/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from database.models import *
|
||||||
40
database/mariadb.py
Normal file
40
database/mariadb.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import mariadb
|
||||||
|
|
||||||
|
import settings
|
||||||
|
|
||||||
|
|
||||||
|
class MariadbConnector:
|
||||||
|
def __init__(self):
|
||||||
|
# Settings
|
||||||
|
self.user = settings.MY_LOGIN
|
||||||
|
self.password = settings.MY_PASSWORD
|
||||||
|
self.host = settings.MY_HOST
|
||||||
|
self.port = int(settings.MY_PORT)
|
||||||
|
self.database = settings.MY_DATABASE
|
||||||
|
|
||||||
|
self.conn = self._get_connection()
|
||||||
|
self.cursor = self._get_cursor()
|
||||||
|
|
||||||
|
def _get_connection(self) -> mariadb.connections.Connection:
|
||||||
|
return mariadb.connect(user=self.user,
|
||||||
|
password=self.password,
|
||||||
|
host=self.host,
|
||||||
|
port=self.port,
|
||||||
|
database=self.database)
|
||||||
|
|
||||||
|
def _get_cursor(self) -> mariadb.cursors.Cursor:
|
||||||
|
return self.conn.cursor()
|
||||||
|
|
||||||
|
def select(self, query_string: str, as_list=True):
|
||||||
|
cursor = self.cursor
|
||||||
|
|
||||||
|
cursor.execute(query_string)
|
||||||
|
if not as_list:
|
||||||
|
return cursor.fetchone()
|
||||||
|
return cursor.fetchall()
|
||||||
|
|
||||||
|
def insert(self, query_string) -> int:
|
||||||
|
cursor = self.cursor
|
||||||
|
|
||||||
|
cursor.execute(query_string)
|
||||||
|
return cursor.insert_id
|
||||||
20
database/models.py
Normal file
20
database/models.py
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
|
db = SQLAlchemy()
|
||||||
|
|
||||||
|
|
||||||
|
class User(db.Model):
|
||||||
|
__tablename__ = 'users'
|
||||||
|
id = db.Column(db.Integer, primary_key=True, comment='ID пользователя')
|
||||||
|
|
||||||
|
login = db.Column(db.String, nullable=False, comment='Логин')
|
||||||
|
password_hash = db.Column(db.String, nullable=False, comment='Пароль')
|
||||||
|
|
||||||
|
sipro_id = db.Column(db.Integer, nullable=True, comment='ID пользователя в SIPRO')
|
||||||
|
|
||||||
|
|
||||||
|
class Barcode(db.Model):
|
||||||
|
__tablename__ = 'barcodes'
|
||||||
|
id = db.Column(db.Integer, primary_key=True, comment='ID пользователя')
|
||||||
|
denco_article = db.Column(db.Integer, nullable=False, comment='Артикул', index=True)
|
||||||
|
barcode = db.Column(db.String, nullable=False, comment='Баркод', index=True)
|
||||||
16
requirements.txt
Normal file
16
requirements.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
flask
|
||||||
|
flask-sqlalchemy
|
||||||
|
flask-session
|
||||||
|
flask-cors
|
||||||
|
flask-migrate
|
||||||
|
Flask-JWT-Extended
|
||||||
|
|
||||||
|
# Working with database
|
||||||
|
redis
|
||||||
|
psycopg2
|
||||||
|
mariadb
|
||||||
|
|
||||||
|
|
||||||
|
# Other stuff
|
||||||
|
requests
|
||||||
|
python-dotenv
|
||||||
1
routes/__init__.py
Normal file
1
routes/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from routes.auth import auth_blueprint
|
||||||
44
routes/auth.py
Normal file
44
routes/auth.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
from flask import Blueprint, request, jsonify
|
||||||
|
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
|
||||||
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
|
||||||
|
from database import User, db
|
||||||
|
|
||||||
|
auth_blueprint = Blueprint('auth', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@auth_blueprint.post('/register')
|
||||||
|
def register_endpoint():
|
||||||
|
data = request.json
|
||||||
|
login = data.get('login')
|
||||||
|
password = data.get('password')
|
||||||
|
password_hash = generate_password_hash(password, method='sha256')
|
||||||
|
|
||||||
|
new_user = User(login=login,
|
||||||
|
password_hash=password_hash)
|
||||||
|
db.session.add(new_user)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return jsonify(ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
@auth_blueprint.post('/login')
|
||||||
|
def login_endpoint():
|
||||||
|
data = request.json
|
||||||
|
print(data)
|
||||||
|
login = data.get('login')
|
||||||
|
user = User.query.filter_by(login=login).first()
|
||||||
|
if not user:
|
||||||
|
return jsonify(ok=False), 401
|
||||||
|
password = data.get('password')
|
||||||
|
if not check_password_hash(user.password_hash, password):
|
||||||
|
return jsonify(ok=False), 401
|
||||||
|
access_token = create_access_token(identity=user.id)
|
||||||
|
return jsonify(access_token=access_token)
|
||||||
|
|
||||||
|
|
||||||
|
@auth_blueprint.get('/protected')
|
||||||
|
@jwt_required()
|
||||||
|
def protected_endpoint():
|
||||||
|
print(type(get_jwt_identity()))
|
||||||
|
return 'test'
|
||||||
0
scanner/__init__.py
Normal file
0
scanner/__init__.py
Normal file
13
scanner/enums.py
Normal file
13
scanner/enums.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from enum import unique, IntEnum
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class CodeType(IntEnum):
|
||||||
|
BARCODE = 0
|
||||||
|
QRCODE = 1
|
||||||
|
INVALID = 2
|
||||||
|
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class SearchType(IntEnum):
|
||||||
|
PRODUCT = 0
|
||||||
11
scanner/search.py
Normal file
11
scanner/search.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from scanner.enums import CodeType
|
||||||
|
from scanner.utils import guess_code_type
|
||||||
|
|
||||||
|
|
||||||
|
class ScannerSearch:
|
||||||
|
def __init__(self, string_value: str):
|
||||||
|
self.string_value = string_value
|
||||||
|
self._code_type = guess_code_type(self.string_value)
|
||||||
|
|
||||||
|
def get_code_type(self) -> CodeType:
|
||||||
|
return self._code_type
|
||||||
9
scanner/utils.py
Normal file
9
scanner/utils.py
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
from scanner.enums import CodeType
|
||||||
|
|
||||||
|
|
||||||
|
def guess_code_type(string_value: str) -> CodeType:
|
||||||
|
if string_value.isdigit():
|
||||||
|
return CodeType.BARCODE
|
||||||
|
if string_value:
|
||||||
|
return CodeType.QRCODE
|
||||||
|
return CodeType.INVALID
|
||||||
21
settings.py
Normal file
21
settings.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import os
|
||||||
|
from dotenv import load_dotenv, find_dotenv
|
||||||
|
|
||||||
|
load_dotenv(find_dotenv())
|
||||||
|
|
||||||
|
# PostgreSQL database settings
|
||||||
|
PG_LOGIN = os.environ.get('PG_LOGIN')
|
||||||
|
PG_PASSWORD = os.environ.get('PG_PASSWORD')
|
||||||
|
PG_PORT = os.environ.get('PG_PORT')
|
||||||
|
PG_HOST = os.environ.get('PG_HOST')
|
||||||
|
PG_DATABASE = os.environ.get('PG_DATABASE')
|
||||||
|
|
||||||
|
# MySQL database settings
|
||||||
|
MY_LOGIN = os.environ.get('MY_LOGIN')
|
||||||
|
MY_PASSWORD = os.environ.get('MY_PASSWORD')
|
||||||
|
MY_PORT = os.environ.get('MY_PORT')
|
||||||
|
MY_DATABASE = os.environ.get('MY_DATABASE')
|
||||||
|
MY_HOST = os.environ.get('MY_HOST')
|
||||||
|
|
||||||
|
# Flask settings
|
||||||
|
SECRET_KEY = os.environ.get('SECRET_KEY')
|
||||||
0
utils/__init__.py
Normal file
0
utils/__init__.py
Normal file
17
utils/barcodes_synchronizer.py
Normal file
17
utils/barcodes_synchronizer.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import database
|
||||||
|
from database.mariadb import MariadbConnector
|
||||||
|
|
||||||
|
|
||||||
|
class BarcodesSynchronizer:
|
||||||
|
def __init__(self):
|
||||||
|
self.mariadb_connector = MariadbConnector()
|
||||||
|
|
||||||
|
def synchronize(self):
|
||||||
|
existing_barcodes = database.Barcode.query.all()
|
||||||
|
denco_articles = list(set([barcode.denco_article for barcode in existing_barcodes]))
|
||||||
|
|
||||||
|
|
||||||
|
query_string = 'SELECT product, barcode FROM modx_0_connections WHERE barcode != "" LIMIT 100'
|
||||||
|
for denco_article, barcodes_string in self.mariadb_connector.select(query_string):
|
||||||
|
barcodes = barcodes_string.split(',')
|
||||||
|
print(denco_article, barcodes)
|
||||||
Reference in New Issue
Block a user