1. Refactoring form objects and database objects to use inheritance and abstract base class for consistency and reduced redundancy.\n2. Contact us page button links updated to resolve error of missing link causing page refresh instead of expected functionality.

This commit is contained in:
2024-09-10 12:09:50 +01:00
parent b3e801e1ec
commit 2d55fe6239
709 changed files with 5158 additions and 1512 deletions

54
DEPRECATED - routes.py Normal file
View File

@@ -0,0 +1,54 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Controller - Webpage routing
Description:
Defines the routes and view functions for each page.
Manages the interaction between the frontend and backend.
"""
from flask import render_template, url_for, Blueprint
from app import app
from app.forms import Form_Contact
# from forms import MyForm
# from app import MyForm
from models.model_view_contact import Model_View_Contact
@app.route('/', methods=['GET'])
def home():
return render_template('_home.html', title='Home')
@app.route('/store', methods=['GET'])
def store_home():
return render_template('_store_home.html', title='Store Home')
@app.route('/contact', methods=['GET', 'POST'])
def contact():
form = Form_Contact()
if form.validate_on_submit():
# Handle form submission
email = form.sender_email.data
CC = form.sender_CC.data
name = form.sender_name.data
msg = form.sender_message.data
# return render_template('contact.html', form=form)
# return render_template('_contact.html', title='Contact Us')
return render_template('contact.html', model=Model_View_Contact(form))
"""
@app.route('/about')
def about():
return render_template('about.html')
@app.route('/contact', methods=['GET', 'POST'])
def contact():
form = MyForm()
if form.validate_on_submit():
# Handle form submission
pass
return render_template('contact.html', form=form)
"""

View File

@@ -252,12 +252,12 @@ def basket_add():
print(f'editing basket:')
model.basket_item_edit(permutation_id, quantity, True) # new_basket =
except:
return jsonify({'status': 'failure', 'Message': 'Bad data received by controller'})
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.STATUS_FAILURE, 'Message': 'Bad data received by controller'})
# return jsonify(Success = True, data = { html_block: render_template(), Model_View_Store.key_basket: new_basket })
html_block = render_template('_block_store_basket.html', model = model)
# print(f'html_block:\n{html_block}')
return jsonify(Success = True, data = { 'html_block': html_block, 'basket': {'items': model.basket.to_json_list()}}) # 'items': [b_i.to_json() for b_i in model.basket]}})
return jsonify({'status': 'failure', 'Message': 'Invalid quantities'})
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.STATUS_FAILURE, 'Message': 'Invalid quantities'})
@@ -285,12 +285,12 @@ def basket_edit():
permutation_id, quantity = model.import_JSON_basket_item(data, form)
model.basket_item_edit(permutation_id, quantity, False) # new_basket =
except:
return jsonify({'status': 'failure', 'Message': 'Bad data received by controller'})
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.STATUS_FAILURE, 'Message': 'Bad data received by controller'})
# return jsonify(Success = True, data = { html_block: render_template(), Model_View_Store.key_basket: new_basket })
html_block = render_template('_block_store_basket.html', model = model)
# print(f'html_block:\n{html_block}')
return jsonify(Success = True, data = { 'html_block': html_block, 'basket': {'items': model.basket.to_json_list()}})
return jsonify({'status': 'failure', 'Message': 'Invalid quantities'})
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.STATUS_FAILURE, 'Message': 'Invalid quantities'})
@app.route('/store/basket_delete', methods=['POST'])
def basket_delete():
@@ -307,7 +307,7 @@ def basket_delete():
permutation_id, quantity = model.import_JSON_basket_item(data)
model.basket_item_edit(permutation_id, 0, False) # new_basket =
except:
return jsonify({'status': 'failure', 'Message': 'Bad data received by controller'})
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.STATUS_FAILURE, 'Message': 'Bad data received by controller'})
# return jsonify(Success = True, data = { html_block: render_template(), Model_View_Store.key_basket: new_basket })
html_block = render_template('_block_store_basket.html', model = model)
# print(f'html_block:\n{html_block}')
@@ -320,7 +320,7 @@ def basket_delete():
item_deleted = True
break
if not item_deleted:
return jsonify({'status': 'failure', 'Message': 'Basket item removal failure: product not found in basket.'})
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.STATUS_FAILURE, 'Message': 'Basket item removal failure: product not found in basket.'})
# return jsonify(Success = True, data = { html_block: render_template(), Model_View_Store.key_basket: new_basket })
html_block = render_template('_block_store_basket.html', model = model)
# print(f'html_block:\n{html_block}')
@@ -345,7 +345,7 @@ def store_basket():
print('importing basket')
model.import_JSON_basket(data)
except:
return jsonify({'status': 'failure', 'Message': 'Bad basket data received by controller'})
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.STATUS_FAILURE, 'Message': 'Bad basket data received by controller'})
# return jsonify(Success = True, data = { html_block: render_template(), Model_View_Store.key_basket: new_basket })
html_block = render_template('_page_store_billing.html', model = model)
# print(f'html_block:\n{html_block}')
@@ -390,7 +390,7 @@ def basket_info():
data_info[model.key_info_identical] = form.identical
print(f'identical: {data_info[model.key_info_identical]}')
except:
return jsonify({'status': 'failure', 'Message': 'Bad form data received by controller'})
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.STATUS_FAILURE, 'Message': 'Bad form data received by controller'})
# return jsonify(Success = True, data = { html_block: render_template(), Model_View_Store.key_basket: new_basket })
# html_block = render_template('_block_store_basket.html', model = model)
# print(f'html_block:\n{html_block}')
@@ -398,7 +398,7 @@ def basket_info():
data[model.key_info_type] = model.key_info_billing if (info_type == model.key_info_billing) else model.key_info_delivery
data[info_type] = data_info
return jsonify(Success = True, data = data)
return jsonify({'status': 'failure', 'Message': f'Invalid address information\n{form.errors}'})
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.STATUS_FAILURE, 'Message': f'Invalid address information\n{form.errors}'})
@app.route('/store/product?permutationId=<permutation_id>regionId=&<region_id>&currencyId=<currency_id>&isIncludedVAT=<is_included_VAT>', methods=['GET']) # <product_id>&
@@ -507,7 +507,7 @@ def create_checkout_session():
code_currency = 'GBP' # data[model.key_code_currency]
print(f'currency code: {code_currency}')
except:
return jsonify({'status': 'failure', 'Message': 'Bad form data received by controller'})
return jsonify({Model_View_Base.FLAG_STATUS: Model_View_Base.STATUS_FAILURE, 'Message': 'Bad form data received by controller'})
items = []
for item in model.basket.items:
permutation = item.product.get_permutation_selected()

2
README
View File

@@ -7,4 +7,4 @@ host for machine:
python -m flask run
host for local network:
python -m flask run --host=0.0.0.0
python -m flask run --host=0.0.0.0

Binary file not shown.

5
app.py
View File

@@ -39,6 +39,8 @@ import lib.argument_validation as av
"""
from routing.core import routes_core
from routing.legal import routes_legal
from routing.store.store import routes_store
from routing.store.product import routes_store_product
from routing.store.product_category import routes_store_product_category
from routing.store.product_permutation import routes_store_product_permutation
from routing.store.stock_item import routes_store_stock_item
@@ -100,7 +102,8 @@ with app.app_context():
app.register_blueprint(routes_core)
app.register_blueprint(routes_legal)
# app.register_blueprint(routes_store_product)
app.register_blueprint(routes_store)
app.register_blueprint(routes_store_product)
app.register_blueprint(routes_store_product_category)
app.register_blueprint(routes_store_product_permutation)
app.register_blueprint(routes_store_stock_item)

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,73 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Database Base Business Objects
Description:
Abstract business object for database objects
"""
# internal
# from helpers.DEPRECATED.helper_abc import Interface_ABC
from extensions import db
import lib.argument_validation as av
# external
from typing import ClassVar
from abc import abstractmethod, ABCMeta
from pydantic import BaseModel
from sqlalchemy.ext.declarative import DeclarativeMeta
# from flask_sqlalchemy import SQLAlchemy
class Get_Many_Parameters_Base(BaseModel, metaclass=ABCMeta):
def __init__(self, **kwargs):
super().__init__(**kwargs)
@classmethod
@abstractmethod
def get_default(cls):
pass
@abstractmethod
def to_json(self):
pass
@classmethod
@abstractmethod
def from_json(cls, json):
pass
"""
@classmethod
@abstractmethod
def from_form(cls, form):
pass
"""
# db = SQLAlchemy()
# Base = declarative_base()
class SQLAlchemy_ABCMeta(db.Model.__class__, ABCMeta):
pass
class SQLAlchemy_ABC(db.Model, metaclass=SQLAlchemy_ABCMeta):
__abstract__ = True
# id = db.Column(db.Integer, primary_key=True)
def __init__(self):
pass
def __repr__(self):
pass
def to_json(self):
pass
@classmethod
def from_json(cls, json):
pass
def to_json_option(self):
pass
def to_temporary_record(self):
pass
def to_object_with_missing_attributes(self, excluded_attributes):
return {
column.name: getattr(self, column.name)
for column in self.__table__.columns
if column.name not in excluded_attributes
}

View File

@@ -0,0 +1,95 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Product Category Business Object
Description:
Business object for product
"""
# internal
import lib.argument_validation as av
from business_objects.store.store_base import Store_Base
from extensions import db
# external
from pydantic import BaseModel
from typing import ClassVar
class Access_Level(db.Model, Store_Base):
__tablename__ = 'Shop_Access_Level_Temp'
id_access_level = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
name = db.Column(db.String(255))
description = db.Column(db.String(4000))
priority = db.Column(db.Integer)
display_order = db.Column(db.Integer)
active = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
created_by = db.Column(db.Integer)
def __init__(self):
super().__init__()
Store_Base.__init__(self)
def from_DB_access_level(query_row):
access_level = Access_Level()
access_level.id_access_level = query_row[0]
access_level.code = query_row[1]
access_level.name = query_row[2]
access_level.priority = query_row[3]
access_level.display_order = query_row[4]
access_level.active = query_row[5]
return access_level
def __repr__(self):
return f'''
id: {self.id_access_level[0] if isinstance(self.id_access_level, tuple) else self.id_access_level}
code: {self.code[0] if isinstance(self.code, tuple) else self.code}
name: {self.name[0] if isinstance(self.name, tuple) else self.name}
description: {self.description[0] if isinstance(self.description, tuple) else self.description}
priority: {self.priority[0] if isinstance(self.priority, tuple) else self.priority}
display_order: {self.display_order}
active: {self.active}
'''
def to_json(self):
return {
self.ATTR_ID_ACCESS_LEVEL: self.id_access_level[0] if isinstance(self.id_access_level, tuple) else self.id_access_level,
self.FLAG_CODE: self.code[0] if isinstance(self.code, tuple) else self.code,
self.FLAG_NAME: self.name[0] if isinstance(self.name, tuple) else self.name,
self.FLAG_DESCRIPTION: self.description[0] if isinstance(self.description, tuple) else self.description,
self.FLAG_PRIORITY: self.priority[0] if isinstance(self.priority, tuple) else self.priority,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_ACTIVE: self.active
}
def to_json_option(self):
return {
'value': self.id_access_level,
'text': self.name
}
@classmethod
def from_json(cls, json):
print(f'Access Level.from_json: {json}')
access_level = cls()
access_level.id_access_level = json[cls.ATTR_ID_ACCESS_LEVEL],
access_level.code = json[cls.FLAG_CODE],
access_level.name = json[cls.FLAG_NAME],
access_level.priority = json[cls.FLAG_PRIORITY],
access_level.description = json[cls.FLAG_DESCRIPTION],
access_level.display_order = json[cls.FLAG_DISPLAY_ORDER]
access_level.active = json[cls.FLAG_ACTIVE]
return access_level
class Filters_Access_Level(BaseModel):
get_inactive_access_level: bool
def __init__(self, get_inactive_access_level = False):
super().__init__(get_inactive_access_level = get_inactive_access_level)
def to_json(self):
return {
'a_get_inactive_access_level': self.get_inactive_access_level
}
@classmethod
def from_json(cls, json):
filters = cls()
filters.get_inactive_access_level = json['a_get_inactive_access_level']
return filters

View File

@@ -61,8 +61,8 @@ class Basket_Item():
basket_item.is_included_VAT = is_included_VAT
return basket_item
def add_discount(self, discount):
av.val_instance(discount, 'discount', 'Basket_Item.add_discount', Discount, v_arg_type='class attribute')
def add_product_price_discount(self, discount):
av.val_instance(discount, 'discount', 'Basket_Item.add_product_price_discount', Discount, v_arg_type='class attribute')
self.discounts.append(discount)
def set_delivery_option(self, delivery_option):

View File

@@ -66,8 +66,8 @@ class Currency(db.Model):
currency.display_order = query_row[5]
return currency
"""
def from_DB_product(query_row):
_m = 'Currency.from_DB_product'
def from_DB_get_many_product_catalogue(query_row):
_m = 'Currency.from_DB_get_many_product_catalogue'
v_arg_type = 'class attribute'
currency = Currency()
currency.id_permutation = query_row[0]

View File

@@ -63,7 +63,7 @@ class Delivery_Option(db.Model):
display_order = db.Column(db.Integer)
def __init__(self):
self.delivery_regions = []
def from_DB_product(query_row):
def from_DB_get_many_product_catalogue(query_row):
option = Delivery_Option()
option.id_option = query_row[0]
option.id_product = query_row[1]

View File

@@ -59,7 +59,7 @@ class Delivery_Region(db.Model):
self.code = code
self.display_order = display_order
"""
def from_DB_product(query_row):
def from_DB_get_many_product_catalogue(query_row):
region = Delivery_Region()
region.id_region = query_row[0]
region.name = query_row[1]

View File

@@ -37,7 +37,7 @@ class Discount(db.Model):
def __init__(self):
self.delivery_regions = []
def from_DB_product(query_row):
def from_DB_get_many_product_catalogue(query_row):
discount = Discount()
discount.id_discount = query_row[0]
discount.id_category = query_row[1]

View File

@@ -73,8 +73,8 @@ class Image(db.Model):
self.display_order = display_order
super().__init__()
"""
def from_DB_product(query_row):
_m = 'Image.from_DB_product'
def from_DB_get_many_product_catalogue(query_row):
_m = 'Image.from_DB_get_many_product_catalogue'
# print(f'image: {query_row}')
image = Image()
image.id_image = query_row[0]

View File

@@ -14,6 +14,7 @@ Business object for product
import lib.argument_validation as av
from lib import data_types
from forms.forms import Form_Basket_Add, Form_Basket_Edit, Form_Filters_Permutation
from business_objects.db_base import SQLAlchemy_ABC, Get_Many_Parameters_Base
from business_objects.store.delivery_option import Delivery_Option
from business_objects.store.discount import Discount
from business_objects.store.image import Image
@@ -24,6 +25,7 @@ from business_objects.store.stock_item import Stock_Item
from business_objects.store.product_variation import Product_Variation
from business_objects.store.product_variation_tree import Product_Variation_Tree
from extensions import db
from forms.store.product import Form_Filters_Product
# external
from dataclasses import dataclass
from typing import ClassVar, List
@@ -49,7 +51,7 @@ class Enum_Status_Stock(Enum):
return data_types.get_enum_member_by_text(Enum_Status_Stock, text.upper())
"""
class Product(db.Model, Store_Base):
class Product(SQLAlchemy_ABC, Store_Base):
FLAG_NAME: ClassVar[str] = 'name-product'
FLAG_DISPLAY_ORDER: ClassVar[str] = 'display-order-product'
FLAG_CAN_VIEW: ClassVar[str] = 'can-view-product'
@@ -83,9 +85,10 @@ class Product(db.Model, Store_Base):
Store_Base.__init__(self)
self.form_basket_add = Form_Basket_Add()
self.form_basket_edit = Form_Basket_Edit()
self.name_access_level_required = None
def from_DB_product(query_row):
_m = 'Product.from_DB_product'
def from_DB_get_many_product_catalogue(query_row):
_m = 'Product.from_DB_get_many_product_catalogue'
v_arg_type = 'class attribute'
product = Product()
product.id_product = query_row[0]
@@ -93,11 +96,12 @@ class Product(db.Model, Store_Base):
product.name = query_row[2]
product.has_variations = av.input_bool(query_row[3], "has_variations", _m, v_arg_type=v_arg_type)
product.id_access_level_required = query_row[4]
product.active = av.input_bool(query_row[5], "active", _m, v_arg_type=v_arg_type)
product.display_order = query_row[6]
product.can_view = av.input_bool(query_row[7], "can_view", _m, v_arg_type=v_arg_type)
product.can_edit = av.input_bool(query_row[8], "can_edit", _m, v_arg_type=v_arg_type)
product.can_admin = av.input_bool(query_row[9], "can_admin", _m, v_arg_type=v_arg_type)
product.name_access_level_required = query_row[5]
product.active = av.input_bool(query_row[6], "active", _m, v_arg_type=v_arg_type)
product.display_order = query_row[7]
product.can_view = av.input_bool(query_row[8], "can_view", _m, v_arg_type=v_arg_type)
product.can_edit = av.input_bool(query_row[9], "can_edit", _m, v_arg_type=v_arg_type)
product.can_admin = av.input_bool(query_row[10], "can_admin", _m, v_arg_type=v_arg_type)
return product
"""
def from_permutation(permutation, has_variations = False):
@@ -117,8 +121,8 @@ class Product(db.Model, Store_Base):
# product.get_variation_trees()
return product
"""
def add_permutation(self, permutation):
_m = 'Product.add_permutation'
def add_product_permutation(self, permutation):
_m = 'Product.add_product_permutation'
av.val_instance(permutation, 'permutation', _m, Product_Permutation)
try:
self.permutation_index[permutation.id_permutation]
@@ -159,7 +163,8 @@ class Product(db.Model, Store_Base):
def get_variation_trees(self):
self.variation_trees = []
for index_permutation in range(len(self.permutations)):
variation_tree = Product_Variation_Tree.from_product_permutation(self.permutations[index_permutation])
permutation = self.permutations[index_permutation]
variation_tree = permutation.variation_tree # Product_Variation_Tree.from_product_permutation(permutation)
found_variation_tree_match = False
for index_tree in range(len(self.variation_trees)):
if self.variation_trees[index_tree].is_equal(variation_tree):
@@ -245,27 +250,27 @@ class Product(db.Model, Store_Base):
return index_permutation
raise ValueError(f"{av.error_msg_str(id_permutation, 'id_permutation', 'Product.get_index_permutation_from_id', int)}\nPermutation ID not found.")
"""
def add_variation(self, variation):
av.val_instance(variation, 'variation', 'Product.add_variation', Product_Variation)
def add_product_variation(self, variation):
av.val_instance(variation, 'variation', 'Product.add_product_variation', Product_Variation)
# print(f'variation: {variation}')
index_permutation = self.permutation_index[variation.id_permutation] # self.get_index_permutation_from_id(variation.id_permutation)
self.permutations[index_permutation].add_variation(variation)
def add_price(self, price):
av.val_instance(price, 'price', 'Product.add_price', Product_Price)
self.permutations[index_permutation].add_product_variation(variation)
def add_product_price(self, price):
av.val_instance(price, 'price', 'Product.add_product_price', Product_Price)
index_permutation = self.permutation_index[price.id_permutation] # self.get_index_permutation_from_id(price.id_permutation)
self.permutations[index_permutation].add_price(price)
def add_image(self, image):
av.val_instance(image, 'image', 'Product.add_image', Image)
self.permutations[index_permutation].add_product_price(price)
def add_product_image(self, image):
av.val_instance(image, 'image', 'Product.add_product_image', Image)
index_permutation = self.permutation_index[image.id_permutation] # self.get_index_permutation_from_id(image.id_permutation)
self.permutations[index_permutation].add_image(image)
self.permutations[index_permutation].add_product_image(image)
def add_delivery_option(self, delivery_option):
av.val_instance(delivery_option, 'delivery_option', 'Product.add_delivery_option', Delivery_Option)
index_permutation = self.permutation_index[delivery_option.id_permutation] # self.get_index_permutation_from_id(delivery_option.id_permutation)
self.permutations[index_permutation].add_delivery_option(delivery_option)
def add_discount(self, discount):
av.val_instance(discount, 'discount', 'Product.add_discount', Discount)
def add_product_price_discount(self, discount):
av.val_instance(discount, 'discount', 'Product.add_product_price_discount', Discount)
index_permutation = self.permutation_index[discount.id_permutation] # self.get_index_permutation_from_id(discount.id_permutation)
self.permutations[index_permutation].add_discount(discount)
self.permutations[index_permutation].add_product_price_discount(discount)
def add_stock_item(self, stock_item):
av.val_instance(stock_item, 'stock_item', 'Product.add_stock_item', Stock_Item)
index_permutation = self.permutation_index[stock_item.id_permutation]
@@ -279,7 +284,7 @@ class Product(db.Model, Store_Base):
if permutation.is_available():
return True
return False
def to_list_rows_permutation(self):
def to_permutation_row_list(self):
list_rows = []
for permutation in self.permutations:
list_rows.append(permutation.to_row_permutation())
@@ -317,6 +322,35 @@ class Product(db.Model, Store_Base):
self.ATTR_ID_PRODUCT_PERMUTATION: [permutation.to_json() for permutation in self.permutations],
self.FLAG_VARIATION_TREES: [tree.to_json() for tree in self.variation_trees]
}
def to_json_option(self):
return {
'value': self.id_product,
'text': self.name
}
def get_variation_types_unique(self):
list_types = []
for tree in self.variation_trees:
for type in tree.get_types_unique():
if type not in list_types:
list_types.append(type)
return list_types
"""
def get_json_str_types_variation_trees(self):
json_str = ''
for tree in self.variation_trees:
if json_str != '':
json_str += '\n'
json_str += tree.get_json_str_types()
return json_str
def get_text_input_variation_types(self):
text_input = ''
for tree in self.variation_trees:
if text_input != '':
text_input += '\n'
text_input += tree.get_text_input_types()
return text_input
"""
@dataclass
class Filters_Product():
@@ -386,7 +420,44 @@ class Filters_Product():
}
@staticmethod
def from_form(form):
def from_form_filters_product(form):
# if not (form is Form_Filters_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Filters_Product.from_form', Form_Filters_Product)
has_filter_category = not (form.id_category.data == '0' or form.id_category.data == '')
is_not_empty = av.input_bool(form.is_not_empty.data, "is_not_empty", "Filters_Product.from_form_filters_product")
active = av.input_bool(form.active.data, "active", "Filters_Product.from_form_filters_product")
return Filters_Product(
get_all_product_category = not has_filter_category,
get_inactive_product_category = not active,
# get_first_product_category_only = False,
ids_product_category = form.id_category.data,
get_all_product = True,
get_inactive_product = not active,
# get_first_product_only = False,
ids_product = '',
get_all_permutation = True,
get_inactive_permutation = not active,
# get_first_permutation_only = False,
ids_permutation = '',
get_all_image = False,
get_inactive_image = False,
# get_first_image_only = False,
ids_image = '',
# get_all_region = False,
# get_inactive_region = False,
# get_first_region_only = False,
# ids_region = '',
# get_all_currency = False,
# get_inactive_currency = False,
# get_first_currency_only = False,
# ids_currency = '',
# get_all_discount = False,
# get_inactive_discount = False,
# ids_discount = '',
get_products_quantity_stock_below_min = False
)
@staticmethod
def from_form_filters_product_permutation(form):
# if not (form is Form_Filters_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Filters_Product.from_form', Form_Filters_Permutation)
has_category_filter = not (form.id_category.data == '0' or form.id_category.data == '')
@@ -494,7 +565,7 @@ class Filters_Product():
def from_filters_product_category(cls, filters_category):
return cls(
get_all_product_category = True,
get_inactive_product_category = filters_category.active_only,
get_inactive_product_category = filters_category.active,
ids_product_category = '',
get_all_product = True,
get_inactive_product = False,
@@ -506,4 +577,234 @@ class Filters_Product():
get_inactive_image = False,
ids_image = '',
get_products_quantity_stock_below_min = False
)
class Filters_Product(Get_Many_Parameters_Base):
# id_user: str
get_all_product_category: bool
get_inactive_product_category: bool
# get_first_product_category_only: bool
ids_product_category: str
get_all_product: bool
get_inactive_product: bool
# get_first_product_only: bool
ids_product: str
get_all_permutation: bool
get_inactive_permutation: bool
# get_first_permutation_only: bool
ids_permutation: str
get_all_image: bool
get_inactive_image: bool
# get_first_image_only: bool
ids_image: str
"""
get_all_region: bool
get_inactive_region: bool
get_first_region_only: bool
ids_region: str
get_all_currency: bool
get_inactive_currency: bool
get_first_currency_only: bool
ids_currency: str
get_all_discount: bool
get_inactive_discount: bool
ids_discount: str
"""
get_products_quantity_stock_below_min: bool
def __init__(self, **kwargs):
super().__init__(**kwargs)
def to_json(self):
return {
'a_id_user': None,
'a_get_all_product_category': self.get_all_product_category,
'a_get_inactive_product_category': self.get_inactive_product_category,
# 'a_get_first_product_category_only': self.get_first_product_category_only,
'a_ids_product_category': self.ids_product_category,
'a_get_all_product': self.get_all_product,
'a_get_inactive_product': self.get_inactive_product,
# 'a_get_first_product_only': self.get_first_product_only,
'a_ids_product': self.ids_product,
'a_get_all_permutation': self.get_all_permutation,
'a_get_inactive_permutation': self.get_inactive_permutation,
# 'a_get_first_permutation_only': self.get_first_permutation_only,
'a_ids_permutation': self.ids_permutation,
'a_get_all_image': self.get_all_image,
'a_get_inactive_image': self.get_inactive_image,
# 'a_get_first_image_only': self.get_first_image_only,
'a_ids_image': self.ids_image,
# 'a_get_all_delivery_region': self.get_all_region,
# 'a_get_inactive_delivery_region': self.get_inactive_region,
# 'a_get_first_delivery_region_only': self.get_first_region_only,
# 'a_ids_delivery_region': self.ids_region,
# 'a_get_all_currency': self.get_all_currency,
# 'a_get_inactive_currency': self.get_inactive_currency,
# 'a_get_first_currency_only': self.get_first_currency_only,
# 'a_ids_currency': self.ids_currency,
# 'a_get_all_discount': self.get_all_discount,
# 'a_get_inactive_discount': self.get_inactive_discount,
# 'a_ids_discount': self.ids_discount,
'a_get_products_quantity_stock_below_min': self.get_products_quantity_stock_below_min
}
@staticmethod
def from_form_filters_product(form):
# if not (form is Form_Filters_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Filters_Product.from_form', Form_Filters_Product)
has_filter_category = not (form.id_category.data == '0' or form.id_category.data == '')
is_not_empty = av.input_bool(form.is_not_empty.data, "is_not_empty", "Filters_Product.from_form_filters_product")
active = av.input_bool(form.active.data, "active", "Filters_Product.from_form_filters_product")
return Filters_Product(
get_all_product_category = not has_filter_category,
get_inactive_product_category = not active,
# get_first_product_category_only = False,
ids_product_category = form.id_category.data,
get_all_product = True,
get_inactive_product = not active,
# get_first_product_only = False,
ids_product = '',
get_all_permutation = True,
get_inactive_permutation = not active,
# get_first_permutation_only = False,
ids_permutation = '',
get_all_image = False,
get_inactive_image = False,
# get_first_image_only = False,
ids_image = '',
# get_all_region = False,
# get_inactive_region = False,
# get_first_region_only = False,
# ids_region = '',
# get_all_currency = False,
# get_inactive_currency = False,
# get_first_currency_only = False,
# ids_currency = '',
# get_all_discount = False,
# get_inactive_discount = False,
# ids_discount = '',
get_products_quantity_stock_below_min = False
)
@staticmethod
def from_form_filters_product_permutation(form):
# if not (form is Form_Filters_Permutation): raise ValueError(f'Invalid form type: {type(form)}')
av.val_instance(form, 'form', 'Filters_Product.from_form', Form_Filters_Permutation)
has_category_filter = not (form.id_category.data == '0' or form.id_category.data == '')
has_product_filter = not (form.id_product.data == '0' or form.id_product.data == '')
get_permutations_stock_below_min = av.input_bool(form.is_out_of_stock.data, "is_out_of_stock", "Filters_Product.from_form")
print(f'form question: {type(form.is_out_of_stock)}\nbool interpretted: {get_permutations_stock_below_min}\type form: {type(form)}')
return Filters_Product(
get_all_product_category = not has_category_filter,
get_inactive_product_category = False,
# get_first_product_category_only = False,
ids_product_category = form.id_category.data,
get_all_product = not has_product_filter,
get_inactive_product = False,
# get_first_product_only = False,
ids_product = form.id_product.data,
get_all_permutation = not get_permutations_stock_below_min,
get_inactive_permutation = False,
# get_first_permutation_only = False,
ids_permutation = '',
get_all_image = False,
get_inactive_image = False,
# get_first_image_only = False,
ids_image = '',
# get_all_region = False,
# get_inactive_region = False,
# get_first_region_only = False,
# ids_region = '',
# get_all_currency = False,
# get_inactive_currency = False,
# get_first_currency_only = False,
# ids_currency = '',
# get_all_discount = False,
# get_inactive_discount = False,
# ids_discount = '',
get_products_quantity_stock_below_min = get_permutations_stock_below_min
)
@staticmethod
def get_default():
return Filters_Product(
get_all_product_category = True,
get_inactive_product_category = False,
# get_first_product_category_only = False,
ids_product_category = '',
get_all_product = True,
get_inactive_product = False,
# get_first_product_only = False,
ids_product = '',
get_all_permutation = True,
get_inactive_permutation = False,
# get_first_permutation_only = False,
ids_permutation = '',
get_all_image = True,
get_inactive_image = False,
# get_first_image_only = False,
ids_image = '',
# get_all_region = True,
# et_inactive_region = False,
# get_first_region_only = False,
# ids_region = '',
# get_all_currency = True,
# get_inactive_currency = False,
# get_first_currency_only = False,
# ids_currency = '',
# get_all_discount = True,
# get_inactive_discount = False,
# ids_discount = '',
get_products_quantity_stock_below_min = True
)
@classmethod
def from_json(cls, json):
return cls(
get_all_product_category = json.get('a_get_all_product_category', False),
get_inactive_product_category = json.get('a_get_inactive_product_category', False),
# get_first_product_category_only = json.get('a_get_first_product_category_only', False),
ids_product_category = json.get('a_ids_product_category', ''),
get_all_product = json.get('a_get_all_product', False),
get_inactive_product = json.get('a_get_inactive_product', False),
# get_first_product_only = json.get('a_get_first_product_only', False),
ids_product = json.get('a_ids_product', ''),
get_all_permutation = json.get('a_get_all_permutation', False),
get_inactive_permutation = json.get('a_get_inactive_permutation', False),
# get_first_permutation_only = json.get('a_get_first_permutation_only', False),
ids_permutation = json.get('a_ids_permutation', ''),
get_all_image = json.get('a_get_all_image', False),
get_inactive_image = json.get('a_get_inactive_image', False),
# get_first_image_only = json.get('a_get_first_image_only', False),
ids_image = json.get('a_ids_image', ''),
# get_all_region = json.get('a_get_all_region', False),
# get_inactive_region = json.get('a_get_inactive_region', False),
# get_first_region_only = json.get('a_get_first_region_only', False),
# ids_region = json.get('a_ids_region', ''),
# get_all_currency = json.get('a_get_all_currency', False),
# get_inactive_currency = json.get('a_get_inactive_currency', False),
# get_first_currency_only = json.get('a_get_first_currency_only', False),
# ids_currency = json.get('a_ids_currency', ''),
# get_all_discount = json.get('a_get_all_discount', False),
# get_inactive_discount = json.get('a_get_inactive_discount', False),
# ids_discount = json.get('a_ids_discount', ''),
get_products_quantity_stock_below_min = json.get('a_get_products_quantity_stock_below_min', False)
)
@classmethod
def from_filters_product_category(cls, filters_category):
return cls(
get_all_product_category = True,
get_inactive_product_category = filters_category.active.data,
ids_product_category = '',
get_all_product = True,
get_inactive_product = False,
ids_product = '',
get_all_permutation = True,
get_inactive_permutation = False,
ids_permutation = '',
get_all_image = False,
get_inactive_image = False,
ids_image = '',
get_products_quantity_stock_below_min = False
)

View File

@@ -12,6 +12,7 @@ Business object for product category
# internal
import lib.argument_validation as av
from business_objects.db_base import SQLAlchemy_ABC, Get_Many_Parameters_Base
from business_objects.store.product import Product, Product_Permutation, Product_Price
from business_objects.store.product_variation import Product_Variation
from business_objects.store.image import Image
@@ -24,36 +25,16 @@ from extensions import db
from pydantic import BaseModel
from typing import ClassVar
"""
class Enum_Product_Category(Enum):
ASSISTIVE_DEVICES = 0
HOME_DECOR = 1
MISCELLANEOUS = 99
def text(self):
return Enum_Product_Category.Enum_Product_Category_Text(self)
def Enum_Product_Category_Text(category):
av.val_instance(category, 'category', 'Product_Category_Enum_Text', Enum_Product_Category)
if category == Enum_Product_Category.ASSISTIVE_DEVICES:
return 'Assistive devices'
elif category == Enum_Product_Category.HOME_DECOR:
return 'Home decor'
else:
return 'Other'
def get_member_by_text(text):
av.val_str(text, 'text', 'Enum_Product_Category.get_member_by_text')
return data_types.get_enum_member_by_text(Enum_Product_Category, text.upper())
"""
class Product_Category(db.Model, Store_Base):
class Product_Category(SQLAlchemy_ABC, Store_Base):
FLAG_ACCESS_LEVEL_REQUIRED: ClassVar[str] = 'id_access_level_required'
__tablename__ = 'Shop_Product_Category_Temp'
id_category = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
name = db.Column(db.String(255))
description = db.Column(db.String(4000))
id_access_level_required = db.Column(db.Integer)
name_access_level_required = db.Column(db.String(255))
display_order = db.Column(db.Integer)
active = db.Column(db.Boolean)
can_view = db.Column(db.Boolean)
@@ -61,39 +42,25 @@ class Product_Category(db.Model, Store_Base):
can_admin = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
created_by = db.Column(db.Integer)
"""
def __new__(cls, id, name, description, display_order):
_m = 'Category.__new__'
v_arg_type = 'class attribute'
av.val_int(id, 'id', _m, 0, v_arg_type=v_arg_type)
av.val_str(name, 'name', _m, max_len=256, v_arg_type=v_arg_type)
av.val_str(description, 'description', _m, max_len=4001, v_arg_type=v_arg_type)
av.val_int(display_order, 'display_order', _m, v_arg_type=v_arg_type)
return super(Category, cls).__new__(cls)
"""
def __init__(self): # , id, name, description, display_order):
"""
self.id_category = id
self.name = name
self.description = description
self.display_order = display_order
"""
def __init__(self):
self.products = []
self.product_index = {}
super().__init__()
Store_Base.__init__(self)
def from_DB_product(query_row):
self.name_access_level_required = None
def from_DB_get_many_product_catalogue(query_row):
category = Product_Category()
category.id_category = query_row[0]
category.code = query_row[1]
category.name = query_row[2]
category.description = query_row[3]
category.id_access_level_required = query_row[4]
category.display_order = query_row[5]
category.active = query_row[6]
category.can_view = query_row[7]
category.can_edit = query_row[8]
category.can_admin = query_row[9]
category.name_access_level_required = query_row[5]
category.display_order = query_row[6]
category.active = query_row[7]
category.can_view = query_row[8]
category.can_edit = query_row[9]
category.can_admin = query_row[10]
return category
"""
def key_product_index_from_ids_product_permutation(id_product, id_permutation):
@@ -128,39 +95,39 @@ class Product_Category(db.Model, Store_Base):
except KeyError:
self.product_index[product.id_product] = len(self.products)
self.products.append(product)
def add_permutation(self, permutation):
_m = 'Category.add_permutation'
def add_product_permutation(self, permutation):
_m = 'Category.add_product_permutation'
av.val_instance(permutation, 'permutation', _m, Product_Permutation)
# self.product_index.append(len(self.products))
# self.product_index[Category.key_product_index_from_ids_product_permutation(product.id_product, product.id_permutation)] = len(self.products)
index_product = self.get_index_product_from_id(permutation.id_product)
# index_product = self.product_index[permutation.id_product]
self.products[index_product].add_permutation(permutation)
def add_variation(self, variation):
av.val_instance(variation, 'variation', 'Category.add_variation', Product_Variation)
self.products[index_product].add_product_permutation(permutation)
def add_product_variation(self, variation):
av.val_instance(variation, 'variation', 'Category.add_product_variation', Product_Variation)
index_product = self.get_index_product_from_id(variation.id_product)
self.products[index_product].add_variation(variation)
def add_price(self, price):
av.val_instance(price, 'price', 'Category.add_price', Product_Price)
self.products[index_product].add_product_variation(variation)
def add_product_price(self, price):
av.val_instance(price, 'price', 'Category.add_product_price', Product_Price)
index_product = self.get_index_product_from_id(price.id_product)
self.products[index_product].add_price(price)
def add_image(self, image):
av.val_instance(image, 'image', 'Category.add_image', Image)
self.products[index_product].add_product_price(price)
def add_product_image(self, image):
av.val_instance(image, 'image', 'Category.add_product_image', Image)
index_product = self.get_index_product_from_id(image.id_product)
self.products[index_product].add_image(image)
self.products[index_product].add_product_image(image)
def add_delivery_option(self, delivery_option):
av.val_instance(delivery_option, 'delivery_option', 'Category.add_delivery_option', Delivery_Option)
index_product = self.get_index_product_from_id(delivery_option.id_product)
self.products[index_product].add_delivery_option(delivery_option)
def add_discount(self, discount):
av.val_instance(discount, 'discount', 'Category.add_discount', Discount)
def add_product_price_discount(self, discount):
av.val_instance(discount, 'discount', 'Category.add_product_price_discount', Discount)
index_product = self.get_index_product_from_id(discount.id_product)
self.products[index_product].add_discount(discount)
self.products[index_product].add_product_price_discount(discount)
def add_stock_item(self, stock_item):
av.val_instance(stock_item, 'stock_item', 'Category.add_stock_item', Stock_Item)
index_product = self.get_index_product_from_id(stock_item.id_product)
self.products[index_product].add_stock_item(stock_item)
def get_all_variation_trees(self):
def get_all_product_variation_trees(self):
for product in self.products:
if product.has_variations:
print(f'product with id:{product.id_product} has variations')
@@ -181,14 +148,17 @@ class Product_Category(db.Model, Store_Base):
code: {self.code[0] if isinstance(self.code, tuple) else self.code}
name: {self.name[0] if isinstance(self.name, tuple) else self.name}
description: {self.description[0] if isinstance(self.description, tuple) else self.description}
access_level: {self.name_access_level_required[0] if isinstance(self.name_access_level_required, tuple) else self.name_access_level_required}
display_order: {self.display_order}
active: {self.active}
products: {self.products}
'''
"""
def get_permutation_first(self):
if not (len(self.products) == 0):
print(f'getting first permutation from product')
return None if len(self.products) == 0 else self.products[0].get_permutation_selected()
"""
def is_available(self):
if len(self.products) == 0:
return False
@@ -196,22 +166,25 @@ class Product_Category(db.Model, Store_Base):
if product.is_available():
return True
return False
def to_list_rows_permutation(self):
def to_permutation_row_list(self):
list_rows = []
for product in self.products:
list_rows += product.to_list_rows_permutation()
list_rows += product.to_permutation_row_list()
return list_rows
def to_list_products(self):
def to_product_option_list(self):
list_products = []
for product in self.products:
list_products.append({'value': product.id_product, 'text': product.name})
return list_products
def to_json(self):
return {
self.FLAG_KEY_PRIMARY: self.ATTR_ID_PRODUCT_CATEGORY,
self.ATTR_ID_PRODUCT_CATEGORY: self.id_category[0] if isinstance(self.id_category, tuple) else self.id_category,
self.FLAG_CODE: self.code[0] if isinstance(self.code, tuple) else self.code,
self.FLAG_NAME: self.name[0] if isinstance(self.name, tuple) else self.name,
self.FLAG_DESCRIPTION: self.description[0] if isinstance(self.description, tuple) else self.description,
self.ATTR_ID_ACCESS_LEVEL: self.id_access_level_required[0] if isinstance(self.id_access_level_required, tuple) else self.id_access_level_required,
self.FLAG_ACCESS_LEVEL_REQUIRED: self.name_access_level_required[0] if isinstance(self.name_access_level_required, tuple) else self.name_access_level_required,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_ACTIVE: self.active,
self.FLAG_CAN_VIEW: self.can_view,
@@ -222,31 +195,42 @@ class Product_Category(db.Model, Store_Base):
def from_json(cls, json):
print(f' Category.from_json: {json}')
category = cls()
category.id_category = json.get(cls.ATTR_ID_PRODUCT_CATEGORY),
category.id_category = json[cls.ATTR_ID_PRODUCT_CATEGORY],
category.code = json[cls.FLAG_CODE],
category.name = json[cls.FLAG_NAME],
category.description = json[cls.FLAG_DESCRIPTION],
category.id_access_level_required = json[cls.ATTR_ID_ACCESS_LEVEL],
category.name_access_level_required = json.get(cls.FLAG_ACCESS_LEVEL_REQUIRED, ''),
category.display_order = json[cls.FLAG_DISPLAY_ORDER]
category.active = json[cls.FLAG_ACTIVE]
category.can_view = json.get(cls.FLAG_CAN_VIEW, False)
category.can_edit = json.get(cls.FLAG_CAN_EDIT, False)
category.can_admin = json.get(cls.FLAG_CAN_ADMIN, False)
return category
"""
def to_json_str(self):
return {
self.ATTR_ID_PRODUCT_CATEGORY: self.id_category[0] if isinstance(self.id_category, tuple) else self.id_category,
self.FLAG_CODE: self.code[0] if isinstance(self.code, tuple) else self.code,
self.FLAG_NAME: self.name[0] if isinstance(self.name, tuple) else self.name,
self.FLAG_DESCRIPTION: self.description[0] if isinstance(self.description, tuple) else self.description,
self.ATTR_ID_ACCESS_LEVEL: self.id_access_level_required[0] if isinstance(self.id_access_level_required, tuple) else self.id_access_level_required,
self.FLAG_ACCESS_LEVEL_REQUIRED: self.name_access_level_required[0] if isinstance(self.name_access_level_required, tuple) else self.name_access_level_required,
self.FLAG_DISPLAY_ORDER: self.display_order,
self.FLAG_ACTIVE: self.output_bool(self.active),
self.FLAG_CAN_VIEW: self.output_bool(self.can_view),
self.FLAG_CAN_EDIT: self.output_bool(self.can_edit),
self.FLAG_CAN_ADMIN: self.output_bool(self.can_admin)
}
"""
@staticmethod
def output_bool(value):
return av.input_bool(value, 'Product_Category bool attribute', 'Product_Category.output_bool')
def to_json_option(self):
return {
'value': self.id_category,
'text': self.name
}
"""
class Filters_Product_Category(BaseModel, Store_Base):
ids_product_category: str
@@ -284,42 +268,45 @@ class Filters_Product_Category(BaseModel, Store_Base):
filters = cls()
filters.ids_product_category = json['a_ids_product_category'],
filters.ids_product = json['a_ids_product']
"""
class Filters_Product_Category(BaseModel, Store_Base):
is_not_empty_only: bool
active_only: bool
def __init__(self, is_not_empty_only, active_only):
super().__init__(is_not_empty_only=is_not_empty_only, active_only=active_only)
class Filters_Product_Category(Get_Many_Parameters_Base):
FLAG_IS_NOT_EMPTY: ClassVar[str] = 'is_not_empty'
is_not_empty: bool
active: bool
def __init__(self, is_not_empty, active):
super().__init__(is_not_empty=is_not_empty, active=active)
@classmethod
def get_default(cls):
return cls(
is_not_empty_only = False,
active_only = True
is_not_empty = False,
active = True
)
def to_json(self):
return {
'is_not_empty_only': self.is_not_empty_only,
'active_only': self.active_only
self.FLAG_IS_NOT_EMPTY: self.is_not_empty,
self.FLAG_ACTIVE: self.active
}
@classmethod
def from_json(cls, json):
return cls(
is_not_empty_only = json['is_not_empty_only'],
active_only = json['active_only']
is_not_empty = json['is_not_empty'],
active = json['active']
)
@classmethod
def from_form(cls, form):
return cls(
is_not_empty_only = av.input_bool(form.is_not_empty.data, 'is_not_empty', 'Filters_Product_Category.from_form'),
active_only = av.input_bool(form.active.data, 'active', 'Filters_Product_Category.from_form')
is_not_empty = av.input_bool(form.is_not_empty.data, 'is_not_empty', 'Filters_Product_Category.from_form'),
active = av.input_bool(form.active.data, 'active', 'Filters_Product_Category.from_form')
)
"""
class Container_Product_Category(Store_Base):
class Product_Category_Container(Store_Base):
categories: list
def __init__(self):
self.categories = []
def add_category(self, category):
av.val_instance(category, 'category', 'Container_Product_Categories.add_category', Product_Category)
def add_product_category(self, category):
av.val_instance(category, 'category', 'Container_Product_Categories.add_product_category', Product_Category)
self.categories.append(category)
def get_index_category_from_id(self, id_category):
for index_category in range(len(self.categories)):
@@ -340,73 +327,89 @@ class Container_Product_Category(Store_Base):
av.val_instance(product, 'product', 'Container_Product_Categories.add_product', Product)
index_category = self.get_index_category_from_id(product.id_category)
self.categories[index_category].add_product(product)
def add_permutation(self, permutation):
av.val_instance(permutation, 'permutation', 'Container_Product_Categories.add_permutation', Product_Permutation)
def add_product_permutation(self, permutation):
av.val_instance(permutation, 'permutation', 'Container_Product_Categories.add_product_permutation', Product_Permutation)
index_category = self.get_index_category_from_id(permutation.id_category)
self.categories[index_category].add_permutation(permutation)
def add_variation(self, variation):
av.val_instance(variation, 'variation', 'Container_Product_Categories.add_variation', Product_Variation)
self.categories[index_category].add_product_permutation(permutation)
def add_product_variation(self, variation):
av.val_instance(variation, 'variation', 'Container_Product_Categories.add_product_variation', Product_Variation)
index_category = self.get_index_category_from_id(variation.id_category)
self.categories[index_category].add_variation(variation)
def add_price(self, price):
av.val_instance(price, 'price', 'Container_Product_Categories.add_price', Product_Price)
self.categories[index_category].add_product_variation(variation)
def add_product_price(self, price):
av.val_instance(price, 'price', 'Container_Product_Categories.add_product_price', Product_Price)
index_category = self.get_index_category_from_id(price.id_category)
self.categories[index_category].add_price(price)
def add_image(self, image):
av.val_instance(image, 'image', 'Container_Product_Categories.add_image', Image)
self.categories[index_category].add_product_price(price)
def add_product_image(self, image):
av.val_instance(image, 'image', 'Container_Product_Categories.add_product_image', Image)
index_category = self.get_index_category_from_id(image.id_category)
self.categories[index_category].add_image(image)
self.categories[index_category].add_product_image(image)
def add_delivery_option(self, delivery_option):
av.val_instance(delivery_option, 'delivery_option', 'Container_Product_Categories.add_delivery_option', Delivery_Option)
index_category = self.get_index_category_from_id(delivery_option.id_category)
self.categories[index_category].add_delivery_option(delivery_option)
def add_discount(self, discount):
av.val_instance(discount, 'discount', 'Container_Product_Categories.add_discount', Discount)
def add_product_price_discount(self, discount):
av.val_instance(discount, 'discount', 'Container_Product_Categories.add_product_price_discount', Discount)
index_category = self.get_index_category_from_id(discount.id_category)
self.categories[index_category].add_discount(discount)
self.categories[index_category].add_product_price_discount(discount)
def add_stock_item(self, stock_item):
av.val_instance(stock_item, 'stock_item', 'Container_Product_Categories.add_stock_item', Stock_Item)
index_category = self.get_index_category_from_id(stock_item.id_category)
self.categories[index_category].add_stock_item(stock_item)
def get_all_variation_trees(self):
def get_all_product_variation_trees(self):
for category in self.categories:
category.get_all_variation_trees()
category.get_all_product_variation_trees()
def __repr__(self):
return f'categories: {self.categories}'
"""
def get_permutation_first(self):
print(f'getting first permutation from category list')
if not (len(self.categories) == 0):
print(f'getting first permutation from category')
return None if len(self.categories) == 0 else self.categories[0].get_permutation_first()
def get_count_categories(self):
"""
def get_category_count(self):
return len(self.categories)
def to_list_rows_permutation(self):
def to_permutation_row_list(self):
list_rows = []
for category in self.categories:
list_rows += category.to_list_rows_permutation()
list_rows += category.to_permutation_row_list()
return list_rows
def to_list_category_options(self):
def to_category_option_list(self):
list_categories = []
for category in self.categories:
list_categories.append({'value': category.id_category, 'text': category.name})
return list_categories
def to_list_products(self):
def to_product_option_list(self):
list_products = []
for category in self.categories:
# list_products.append(category.to_list_products())
# list_products.append(category.to_product_option_list())
for product in category.products:
list_products.append({'value': product.id_product, 'text': product.name, Product.ATTR_ID_PRODUCT_CATEGORY: product.id_category})
return list_products
def to_dict_lists_products(self):
def get_product_option_lists_by_category(self):
dict_lists_products = {}
for category in self.categories:
dict_lists_products[category.id_category] = category.to_list_products()
dict_lists_products[category.id_category] = category.to_product_option_list()
return dict_lists_products
def to_json(self):
return {
'categories': [category.to_json() for category in self.categories]
f'{self.FLAG_ROWS}': [category.to_json() for category in self.categories]
}
"""
def to_json_str(self):
return {
'categories': [category.to_json_str() for category in self.categories]
}
f'{self.FLAG_ROWS}': [category.to_json_str() for category in self.categories]
}
"""
@classmethod
def from_json(cls, json):
return None
def to_json_option(self):
return None
def to_temporary_record(self):
excluded_attributes = {
column.name: getattr(self, column.name)
for column in self.__table__.columns
if column.name not in ['created_on', 'created_by']
}
return self.to_object_with_missing_attributes(excluded_attributes)

View File

@@ -21,6 +21,7 @@ from business_objects.store.product_price import Product_Price
from business_objects.store.stock_item import Stock_Item
from business_objects.store.store_base import Store_Base
from business_objects.store.product_variation import Product_Variation
from business_objects.store.product_variation_tree import Product_Variation_Tree
from extensions import db
# external
from datetime import datetime, timedelta
@@ -64,6 +65,7 @@ class Product_Permutation(db.Model, Store_Base):
# form_basket_edit: Form_Basket_Edit
# is_unavailable_in_currency_or_region: bool
# is_available: bool
# variation_tree
def __init__(self):
self.variations = []
@@ -84,9 +86,10 @@ class Product_Permutation(db.Model, Store_Base):
self.form_basket_edit = Form_Basket_Edit()
self.is_unavailable_in_currency_or_region = False
# self.is_available = False
self.variation_tree = None
def from_DB_product(query_row):
_m = 'Product_Permutation.from_DB_product'
def from_DB_get_many_product_catalogue(query_row):
_m = 'Product_Permutation.from_DB_get_many_product_catalogue'
v_arg_type = 'class attribute'
print(f'query_row: {query_row}')
permutation = Product_Permutation()
@@ -155,7 +158,7 @@ class Product_Permutation(db.Model, Store_Base):
if permutation.has_variations:
for jsonProductVariation in json[cls.ATTR_ID_PRODUCT_VARIATION]:
variation = Product_Variation.from_json(jsonProductVariation)
permutation.add_variation(variation)
permutation.add_product_variation(variation)
permutation.quantity_stock = json[cls.FLAG_QUANTITY_STOCK]
permutation.quantity_min = json[cls.FLAG_QUANTITY_MIN]
permutation.quantity_max = json[cls.FLAG_QUANTITY_MAX]
@@ -185,6 +188,13 @@ class Product_Permutation(db.Model, Store_Base):
'delivery_options': {self.delivery_options},
'prices': {self.prices}
}
def to_json_option(self):
return {
'value': self.id_permutation,
'text': self.get_name_variations()
}
def get_name_variations(self):
return self.variation_tree.get_name_variations()
def is_available(self):
return len(self.prices) > 0
def get_price(self):
@@ -270,8 +280,9 @@ class Product_Permutation(db.Model, Store_Base):
price_GBP_min: {self.price_GBP_min}
"""
def add_variation(self, variation):
_m = 'Product_Permutation.add_variation'
def add_product_variation(self, variation):
_m = 'Product_Permutation.add_product_variation'
"""
av.val_instance(variation, 'variation', _m, Product_Variation)
try:
self.variation_index[variation.id_variation]
@@ -279,8 +290,13 @@ class Product_Permutation(db.Model, Store_Base):
except KeyError:
self.variation_index[variation.id_variation] = len(self.variations)
self.variations.append(variation)
def add_price(self, price):
_m = 'Product_Permutation.add_price'
"""
if self.variation_tree is None:
self.variation_tree = Product_Variation_Tree.from_product_variation(variation)
else:
self.variation_tree.add_product_variation(variation)
def add_product_price(self, price):
_m = 'Product_Permutation.add_product_price'
av.val_instance(price, 'price', _m, Product_Price)
try:
self.price_index[price.display_order]
@@ -288,8 +304,8 @@ class Product_Permutation(db.Model, Store_Base):
except KeyError:
self.price_index[price.display_order] = len(self.prices)
self.prices.append(price)
def add_image(self, image):
_m = 'Product_Permutation.add_image'
def add_product_image(self, image):
_m = 'Product_Permutation.add_product_image'
av.val_instance(image, 'image', _m, Image)
try:
self.image_index[image.id_image]
@@ -306,8 +322,8 @@ class Product_Permutation(db.Model, Store_Base):
except KeyError:
self.delivery_option_index[delivery_option.id_option] = len(self.delivery_options)
self.delivery_options.append(delivery_option)
def add_discount(self, discount):
_m = 'Product_Permutation.add_discount'
def add_product_price_discount(self, discount):
_m = 'Product_Permutation.add_product_price_discount'
av.val_instance(discount, 'discount', _m, Discount)
try:
self.discount_index[discount.display_order]
@@ -352,8 +368,8 @@ class Permutation_Product_Variation_Link(db.Model):
id_category = db.Column(db.Integer)
id_variation = db.Column(db.Integer)
def from_DB_product(query_row):
_m = 'Permutation_Product_Variation_Link.from_DB_product'
def from_DB_get_many_product_catalogue(query_row):
_m = 'Permutation_Product_Variation_Link.from_DB_get_many_product_catalogue'
v_arg_type = 'class attribute'
link = Permutation_Product_Variation_Link()
link.id_permutation = query_row[0]

View File

@@ -42,8 +42,8 @@ class Product_Price(db.Model, Store_Base):
super().__init__()
Store_Base.__init__(self)
def from_DB_product(query_row):
# _m = 'Product_Price.from_DB_product'
def from_DB_get_many_product_catalogue(query_row):
# _m = 'Product_Price.from_DB_get_many_product_catalogue'
price = Product_Price()
price.id_price = query_row[0]
price.id_permutation = query_row[1]
@@ -90,6 +90,11 @@ class Product_Price(db.Model, Store_Base):
self.FLAG_VALUE_LOCAL_VAT_EXCL: {self.value_local_VAT_excl},
self.FLAG_DISPLAY_ORDER: {self.display_order}
}
def to_json_option(self):
return {
'value': self.id_price,
'text': f'{self.symbol_currency} {self.value_local_VAT_incl}'
}
@classmethod
def from_json(cls, json):

View File

@@ -56,7 +56,7 @@ class Product_Variation(db.Model, Store_Base):
super().__init__()
Store_Base.__init__(self)
def from_DB_product(query_row):
def from_DB_get_many_product_catalogue(query_row):
variation = Product_Variation.from_DB_variation(query_row)
variation.id_product = query_row[11]
variation.id_permutation = query_row[12]
@@ -122,6 +122,11 @@ class Product_Variation(db.Model, Store_Base):
self.KEY_ACTIVE_VARIATION_TYPE: self.active_variation_type,
self.KEY_ACTIVE_VARIATION: self.active_variation,
}
def to_json_option(self):
return {
'value': self.id_variation,
'text': self.name_variation
}
def to_json_variation_type(self):
return {
@@ -159,7 +164,7 @@ class Product_Variation_Filters():
@staticmethod
def from_form(form):
av.val_instance(form, 'form', 'User_Filters.from_form', Form_Filters_Product_Variation)
get_inactive = av.input_bool(form.active_only.data, "active_only", "User_Filters.from_form")
get_inactive = av.input_bool(form.active.data, "active", "User_Filters.from_form")
id_user = form.id_user.data
return User_Filters(
get_all_user = (id_user is None),
@@ -197,8 +202,8 @@ class Product_Variation_Filters():
class Product_Variation_List(BaseModel):
variations: list = []
def add_variation(self, variation):
av.val_instance(variation, 'variation', 'Product_Variation_List.add_variation', Product_Variation)
def add_product_variation(self, variation):
av.val_instance(variation, 'variation', 'Product_Variation_List.add_product_variation', Product_Variation)
self.variations.append(variation)
def __repr__(self):

View File

@@ -38,24 +38,19 @@ class Product_Variation_Tree_Node():
class Product_Variation_Tree():
node_root: Product_Variation_Tree_Node
def from_node_root(node_root):
tree = Product_Variation_Tree()
@classmethod
def from_node_root(cls, node_root):
tree = cls()
tree.node_root = node_root
return tree
def get_variation_type_list(self):
variation_types = []
node = self.node_root
at_leaf_node = node.is_leaf()
while not at_leaf_node:
variation_types.append(node.variation.name_variation_type)
at_leaf_node = node.is_leaf()
if not at_leaf_node:
node = node.nodes_child[0]
return variation_types
@classmethod
def from_variation_root(cls, variation_root):
node_root = Product_Variation_Tree_Node.from_variation_and_node_parent(variation_root, None)
return cls.from_node_root(node_root)
def is_equal(self, tree):
my_type_list = self.get_variation_type_list()
my_type_list = self.get_product_variations()
sz_me = len(my_type_list)
other_type_list = tree.get_variation_type_list()
other_type_list = tree.get_product_variations()
sz_other = len(other_type_list)
is_equal = (sz_me == sz_other)
if is_equal:
@@ -71,3 +66,44 @@ class Product_Variation_Tree():
for depth in range(depth_max - 1):
node = Product_Variation_Tree_Node.from_variation_and_node_parent(product_permutation.variations[depth + 1], node)
return Product_Variation_Tree.from_node_root(node_root)
def from_product_variation(product_variation):
node_root = Product_Variation_Tree_Node.from_variation_and_node_parent(product_variation, None)
return Product_Variation_Tree.from_node_root(node_root)
def get_name_variations(self):
node = self.node_root
name = node.variation.name_variation_type
at_leaf_node = node.is_leaf()
while not at_leaf_node:
node = node.nodes_child[0]
name += f', {node.variation.name_variation_type}'
at_leaf_node = node.is_leaf()
return name
def get_node_leaf(self):
node = self.node_root
at_leaf_node = node.is_leaf()
while not at_leaf_node:
node = node.nodes_child[0]
at_leaf_node = node.is_leaf()
return node
def add_product_variation(self, variation):
node_leaf = self.get_node_leaf()
node_new = Product_Variation_Tree_Node.from_variation_and_node_parent(variation, node_leaf)
node_leaf.add_child(node_new)
def get_product_variation_types(self):
types = []
node = self.node_root
at_leaf_node = node.is_leaf()
while not at_leaf_node:
types.append(node.variation.name_variation_type)
node = node.nodes_child[0]
at_leaf_node = node.is_leaf()
return types
def get_product_variations(self):
variations = []
node = self.node_root
at_leaf_node = node.is_leaf()
while not at_leaf_node:
variations.append(node.variation)
node = node.nodes_child[0]
at_leaf_node = node.is_leaf()
return variations

View File

@@ -173,6 +173,19 @@ class Stock_Item(db.Model, Store_Base):
permutations: {self.permutations}
variation trees: {self.variation_trees}
'''
def to_json(self):
return {
self.ATTR_ID_PRODUCT: {self.id_product},
self.ATTR_ID_PRODUCT_CATEGORY: {self.id_category},
self.FLAG_NAME: {self.name},
self.FLAG_DISPLAY_ORDER: {self.display_order},
self.FLAG_CAN_VIEW: {self.can_view},
self.FLAG_CAN_EDIT: {self.can_edit},
self.FLAG_CAN_ADMIN: {self.can_admin},
self.FLAG_HAS_VARIATIONS: {self.has_variations},
self.FLAG_PERMUTATIONS: {self.permutations},
self.FLAG_VARIATION_TREES: {self.variation_trees},
}
def has_permutations(self):
return len(self.permutations) > 0
def is_available(self):
@@ -183,12 +196,17 @@ class Stock_Item(db.Model, Store_Base):
return True
return False
"""
def to_list_rows_permutation(self):
def to_permutation_row_list(self):
list_rows = []
for permutation in self.permutations:
list_rows.append(permutation.to_row_permutation())
return list_rows
"""
def to_json_option(self):
return {
'value': self.id_stock_item,
'text': self.id_stock_item
}
@dataclass
class Stock_Item_Filters():

View File

@@ -11,10 +11,52 @@ Abstract business object for store objects
"""
# internal
# from helpers.DEPRECATED.helper_abc import Interface_ABC
from extensions import db
import lib.argument_validation as av
# external
from typing import ClassVar
"""
class I_Store_Base():
@abstractmethod
def __repr__(self):
pass
@classmethod
@abstractmethod
def from_json(cls, json):
pass
@abstractmethod
def to_json(self):
pass
@abstractmethod
def to_json_option(self):
pass
@abstractmethod
def test_69 (self):
pass
""
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
for name, value in vars(Store_Base).items():
if getattr(value, "__isabstractmethod__", False):
if name not in cls.__dict__:
raise TypeError(f"Can't instantiate class {cls.__name__} "
f"without implementation of abstract method {name}")
subclass_value = cls.__dict__[name]
if (isinstance(value, (staticmethod, classmethod)) and
not isinstance(subclass_value, type(value))):
raise TypeError(f"Abstract {type(value).__name__} {name} in {cls.__name__} "
f"must be implemented as a {type(value).__name__}")
def __new__(cls, *args, **kwargs):
if cls is Store_Base:
raise TypeError("Can't instantiate abstract class Store_Base directly")
return super().__new__(cls)
""
"""
class Store_Base():
ATTR_ID_ACCESS_LEVEL: ClassVar[str] = 'id_access_level'
ATTR_ID_CURRENCY: ClassVar[str] = 'id_currency'
# ATTR_ID_CURRENCY_COST: ClassVar[str] = 'id_currency_cost'
ATTR_ID_DELIVERY_REGION: ClassVar[str] = 'id_delivery_region'
@@ -28,6 +70,7 @@ class Store_Base():
ATTR_ID_PRODUCT_VARIATION: ClassVar[str] = 'id_variation'
ATTR_ID_PRODUCT_VARIATION_TYPE: ClassVar[str] = 'id_variation_type'
ATTR_ID_STOCK_ITEM: ClassVar[str] = 'id_stock_item'
FLAG_ACCESS_LEVEL_REQUIRED: ClassVar[str] = 'access_level_required'
FLAG_ACTIVE: ClassVar[str] = 'active'
FLAG_CAN_ADMIN: ClassVar[str] = 'can_admin'
FLAG_CAN_EDIT: ClassVar[str] = 'can_edit'
@@ -35,31 +78,17 @@ class Store_Base():
FLAG_CODE: ClassVar[str] = 'code'
FLAG_DESCRIPTION: ClassVar[str] = 'description'
FLAG_DISPLAY_ORDER: ClassVar[str] = 'display_order'
FLAG_HAS_VARIATIONS: ClassVar[str] = 'has_variations'
FLAG_IS_NOT_EMPTY: ClassVar[str] = 'is_not_empty'
FLAG_KEY_PRIMARY: ClassVar[str] = 'key_primary'
FLAG_NAME: ClassVar[str] = 'name'
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
for name, value in vars(Store_Base).items():
if getattr(value, "__isabstractmethod__", False):
if name not in cls.__dict__:
raise TypeError(f"Can't instantiate class {cls.__name__} "
f"without implementation of abstract method {name}")
subclass_value = cls.__dict__[name]
if (isinstance(value, (staticmethod, classmethod)) and
not isinstance(subclass_value, type(value))):
raise TypeError(f"Abstract {type(value).__name__} {name} in {cls.__name__} "
f"must be implemented as a {type(value).__name__}")
def __new__(cls, *args, **kwargs):
if cls is Store_Base:
raise TypeError("Can't instantiate abstract class Store_Base directly")
return super().__new__(cls)
def __repr__(self):
pass
FLAG_PERMUTATIONS: ClassVar[str] = 'permutations'
FLAG_PRIORITY: ClassVar[str] = 'priority'
FLAG_ROWS: ClassVar[str] = 'rows'
FLAG_VARIATION_TREES: ClassVar[str] = 'variation_trees'
@classmethod
def from_json(cls, json):
pass
def to_json(self):
pass
def output_bool(cls, value):
return av.input_bool(value, f'{cls.__name__} bool attribute', f'{cls.__name__}.output_bool')
@staticmethod
def convert_list_objects_to_list_options(objects):
return [object.to_json_option() for object in objects]

View File

@@ -153,7 +153,7 @@ class Stripe_Product(db.Model):
delivery_options: {self.delivery_options}
'''
def add_discount(self, discount):
def add_product_price_discount(self, discount):
_m = 'Category.add_product'
av.val_instance(discount, 'discount', _m, Discount)
# self.product_index.append(len(self.products))

View File

@@ -160,7 +160,7 @@ class User_Filters():
@staticmethod
def from_form(form):
av.val_instance(form, 'form', 'User_Filters.from_form', Form_Filters_User)
get_inactive = av.input_bool(form.active_only.data, "active_only", "User_Filters.from_form")
get_inactive = av.input_bool(form.active.data, "active", "User_Filters.from_form")
id_user = form.id_user.data
return User_Filters(
get_all_user = (id_user is None),
@@ -212,7 +212,7 @@ class User_Filters():
@staticmethod
def from_form(form):
av.val_instance(form, 'form', 'User_Filters.from_form', Form_Filters_User)
get_inactive = av.input_bool(form.active_only.data, "active_only", "User_Filters.from_form")
get_inactive = av.input_bool(form.active.data, "active", "User_Filters.from_form")
return User_Filters(
ids_user = form.id_user.data,
get_inactive_users = get_inactive,

View File

@@ -13,8 +13,9 @@ Datastore for Store
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.store.access_level import Access_Level, Filters_Access_Level
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Container_Product_Category, Product_Category
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
@@ -26,7 +27,7 @@ from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from helpers.helper_db_mysql import Helper_DB_MySQL
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
# external
@@ -164,17 +165,52 @@ class DataStore_Base(BaseModel):
def get_user_auth0():
return User.from_json_auth0(session.get(current_app.config['ID_TOKEN_USER']))
@staticmethod
def upload_bulk(objects, objectType, batch_size):
def upload_bulk(permanent_table_name, records, batch_size):
_m = 'DataStore_Base.upload_bulk'
print(f'{_m}\nstarting...')
try:
for i in range(0, len(objects), batch_size):
batch = objects[i:i+batch_size]
for i in range(0, len(records), batch_size):
batch = records[i:i+batch_size]
data = [object.to_json() for object in batch]
print(f'batch: {batch}\ndata: {data}')
db.session.bulk_insert_mappings(objectType, data)
db.session.bulk_insert_mappings(permanent_table_name, data)
db.session.commit()
except Exception as e:
print(f'{_m}\n{e}')
db.session.rollback()
raise e
raise e
@classmethod
def get_many_access_level(cls, filters):
_m = 'DataStore_Store_Base.get_many_access_level'
av.val_instance(filters, 'filters', _m, Filters_Access_Level)
argument_dict = filters.to_json()
# user = cls.get_user_session()
# argument_dict['a_id_user'] = 1 # 'auth0|6582b95c895d09a70ba10fef' # id_user
print(f'argument_dict: {argument_dict}')
print('executing p_shop_get_many_access_level')
result = cls.db_procedure_execute('p_shop_get_many_access_level', argument_dict)
cursor = result.cursor
print('data received')
# access_levels
result_set_1 = cursor.fetchall()
print(f'raw access levels: {result_set_1}')
access_levels = []
for row in result_set_1:
new_access_level = Access_Level.from_DB_access_level(row)
access_levels.append(new_access_level)
# Errors
cursor.nextset()
result_set_e = cursor.fetchall()
print(f'raw errors: {result_set_e}')
errors = []
if len(result_set_e) > 0:
errors = [SQL_Error.from_DB_record(row) for row in result_set_e] # (row[0], row[1])
for error in errors:
print(f"Error [{error.code}]: {error.msg}")
DataStore_Base.db_cursor_clear(cursor)
cursor.close()
return access_levels, errors

View File

@@ -13,8 +13,9 @@ Datastore for Store
# internal
# from routes import bp_home
import lib.argument_validation as av
# from business_objects.store.access_level import Access_Level, Filters_Access_Level
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Container_Product_Category, Product_Category
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
@@ -27,7 +28,7 @@ from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from datastores.datastore_base import DataStore_Base
from helpers.helper_db_mysql import Helper_DB_MySQL
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
# external
@@ -69,7 +70,7 @@ class DataStore_Store_Base(DataStore_Base):
print('data received')
category_list = Container_Product_Category()
category_list = Product_Category_Container()
# Categories
result_set_1 = cursor.fetchall()
print(f'raw categories: {result_set_1}')
@@ -77,10 +78,10 @@ class DataStore_Store_Base(DataStore_Base):
# categories = []
# category_index = {}
for row in result_set_1:
new_category = Product_Category.from_DB_product(row) # Product_Category(row[0], row[1], row[2], row[3])
new_category = Product_Category.from_DB_get_many_product_catalogue(row) # Product_Category(row[0], row[1], row[2], row[3])
# category_index[new_category.id_category] = len(categories)
# categories.append(new_category)
category_list.add_category(new_category)
category_list.add_product_category(new_category)
# print(f'categories: {[c.id_category for c in categories]}')
# Products
@@ -90,7 +91,7 @@ class DataStore_Store_Base(DataStore_Base):
# products = [] # [Product(**row) for row in result_set_2]
# product_index = {}
for row in result_set_2:
new_product = Product.from_DB_product(row) # (row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19])
new_product = Product.from_DB_get_many_product_catalogue(row) # (row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19])
index_category = category_list.get_index_category_from_id(new_product.id_category)
category = category_list.categories[index_category]
category_list.add_product(new_product)
@@ -104,10 +105,10 @@ class DataStore_Store_Base(DataStore_Base):
permutations = [] # [Product(**row) for row in result_set_2]
# permutation_index = {}
for row in result_set_3:
new_permutation = Product_Permutation.from_DB_product(row) # (row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19])
new_permutation = Product_Permutation.from_DB_get_many_product_catalogue(row) # (row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7], row[8], row[9], row[10], row[11], row[12], row[13], row[14], row[15], row[16], row[17], row[18], row[19])
index_category = category_list.get_index_category_from_id(new_permutation.id_category)
category = category_list.categories[index_category]
category_list.add_permutation(new_permutation)
category_list.add_product_permutation(new_permutation)
print(f'category_list: {category_list}')
# Product_Variations
@@ -117,13 +118,13 @@ class DataStore_Store_Base(DataStore_Base):
# variations = [Product_Variation(**row) for row in result_set_4]
variations = []
for row in result_set_4:
new_variation = Product_Variation.from_DB_product(row) # (row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7])
new_variation = Product_Variation.from_DB_get_many_product_catalogue(row) # (row[0], row[1], row[2], row[3], row[4], row[5], row[6], row[7])
variations.append(new_variation)
# products[product_index[new_variation.id_product]].variations.append(new_variation)
# index_category = category_index[new_variation.id_category]
# index_product = categories[index_category].index_product_from_ids_product_permutation(new_variation.id_product, new_variation.id_permutation)
# categories[index_category].products[index_product].variations.append(new_variation)
category_list.add_variation(new_variation)
category_list.add_product_variation(new_variation)
# print(f'variations: {variations}')
# print(f'products: {[p.id_product for p in products]}')
@@ -134,7 +135,7 @@ class DataStore_Store_Base(DataStore_Base):
# images = [Image(**row) for row in result_set_5]
images = []
for row in result_set_5:
new_image = Image.from_DB_product(row) # (row[0], row[1], row[2], row[3], row[4])
new_image = Image.from_DB_get_many_product_catalogue(row) # (row[0], row[1], row[2], row[3], row[4])
images.append(new_image)
# products[product_index[new_image.id_product]].images.append(new_image)
"""
@@ -142,7 +143,7 @@ class DataStore_Store_Base(DataStore_Base):
index_product = categories[index_category].index_product_from_ids_product_permutation(new_image.id_product, new_image.id_permutation)
categories[index_category].products[index_product].images.append(new_image)
"""
category_list.add_image(new_image)
category_list.add_product_image(new_image)
# print(f'images: {images}')
# print(f'products: {[p.id_product for p in products]}')
@@ -156,7 +157,7 @@ class DataStore_Store_Base(DataStore_Base):
for error in errors:
print(f"Error [{error.code}]: {error.msg}")
category_list.get_all_variation_trees()
category_list.get_all_product_variation_trees()
"""
for category in category_list.categories:
print(f'category: {category.name}')
@@ -283,7 +284,7 @@ class DataStore_Store_Base(DataStore_Base):
variations = Product_Variation_List()
for row in result_set:
new_variation = Product_Variation.from_DB_variation(row)
variations.add_variation(new_variation)
variations.add_product_variation(new_variation)
errors = []
cursor.nextset()
@@ -298,4 +299,5 @@ class DataStore_Store_Base(DataStore_Base):
cursor.close()
return variations, errors
return variations, errors

View File

@@ -14,7 +14,7 @@ Datastore for Store Baskets
# from routes import bp_home
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Container_Product_Category, Product_Category
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
@@ -27,7 +27,7 @@ from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_db_mysql import Helper_DB_MySQL
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
# external

View File

@@ -0,0 +1,129 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Store Product DataStore
Description:
Datastore for Store Products
"""
# internal
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
from business_objects.store.delivery_region import Delivery_Region
from business_objects.store.discount import Discount
from business_objects.store.order import Order
from business_objects.store.product import Product, Product_Permutation, Product_Price, Filters_Product
from business_objects.sql_error import SQL_Error
from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
# from datastores.datastore_base import Table_Shop_Product_Category, Table_Shop_Product_Category_Temp
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
# external
# from abc import ABC, abstractmethod, abstractproperty
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text
import stripe
import os
from flask import Flask, session, current_app
from pydantic import BaseModel, ConfigDict
from typing import ClassVar
from datetime import datetime
# db = SQLAlchemy()
"""
class Table_Shop_Product_Category(db.Model):
__tablename__ = 'Shop_Product_Category'
id_category: int = db.Column(db.Integer, primary_key=True)
code: str = db.Column(db.String(50))
name: str = db.Column(db.String(255))
description: str = db.Column(db.String(4000))
active: bool = db.Column(db.Boolean)
display_order: int = db.Column(db.Integer)
created_on: datetime = db.Column(db.DateTime)
created_by: int = db.Column(db.Integer)
id_change_set: int = db.Column(db.Integer)
"""
class Row_Shop_Product_Temp(db.Model):
__tablename__ = 'Shop_Product_Temp'
__table_args__ = { 'extend_existing': True }
id_product: int = db.Column(db.Integer, primary_key=True)
id_category: int = db.Column(db.Integer)
name: str = db.Column(db.String(50))
has_variations: str = db.Column(db.String(255))
id_access_level_required: int = db.Column(db.Integer)
active: bool = db.Column(db.Boolean)
display_order: int = db.Column(db.Integer)
guid: str = db.Column(db.BINARY(36))
@classmethod
def from_product(cls, product):
row = cls()
row.id_product = product.id_product[0] if isinstance(product.id_product, tuple) else product.id_product
row.id_category = product.id_category[0] if isinstance(product.id_category, tuple) else product.id_category
row.name = product.name[0] if isinstance(product.name, tuple) else product.name
row.id_access_level_required = product.id_access_level_required[0] if isinstance(product.id_access_level_required, tuple) else product.id_access_level_required
row.active = product.active
row.display_order = product.display_order
return row
def to_json(self):
return {
'id_category': self.id_category,
'name': self.name,
'id_access_level_required': self.id_access_level_required,
'active': self.active,
'display_order': self.display_order,
'guid': self.guid,
}
class DataStore_Store_Product(DataStore_Store_Base):
def __init__(self):
super().__init__()
@classmethod
def save_categories(cls, comment, categories):
_m = 'DataStore_Store_Product_Category.save_categories'
print(f'{_m}\nstarting...')
print(f'comment: {comment}\ncategories: {categories}')
# av.val_str(comment, 'comment', _m)
# av.val_list_instances(categories, 'categories', _m, Product_Category, 1)
guid = Helper_DB_MySQL.create_guid()
user = cls.get_user_session()
rows = []
id_category_new = 0
for category in categories:
row = Row_Shop_Product_Temp.from_product(category)
if row.id_category == '':
id_category_new -= 1
row.id_category = id_category_new
else:
print(f'row.id_category: {row.id_category}')
row.guid = guid
rows.append(row)
print(f'rows: {rows}')
DataStore_Store_Base.upload_bulk(rows, Row_Shop_Product_Temp, 1000)
argument_dict_list = {
'a_id_user': user.id_user,
'a_guid': guid,
'a_comment': comment,
}
save_result = cls.db_procedure_execute('p_shop_save_product', argument_dict_list)
save_result.close()
print('save procedure executed')

View File

@@ -13,7 +13,7 @@ Datastore for Store Product Categories
# internal
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Container_Product_Category, Product_Category
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
@@ -63,11 +63,12 @@ class Row_Shop_Product_Category_Temp(db.Model):
code: str = db.Column(db.String(50))
name: str = db.Column(db.String(255))
description: str = db.Column(db.String(4000))
id_access_level_required: int = db.Column(db.Integer)
active: bool = db.Column(db.Boolean)
display_order: int = db.Column(db.Integer)
guid: str = db.Column(db.BINARY(36))
created_on: datetime = db.Column(db.DateTime)
created_by: int = db.Column(db.Integer)
# created_on: datetime = db.Column(db.DateTime)
# created_by: int = db.Column(db.Integer)
@classmethod
def from_product_category(cls, product_category):
@@ -76,6 +77,7 @@ class Row_Shop_Product_Category_Temp(db.Model):
row.code = product_category.code[0] if isinstance(product_category.code, tuple) else product_category.code
row.name = product_category.name[0] if isinstance(product_category.name, tuple) else product_category.name
row.description = product_category.description[0] if isinstance(product_category.description, tuple) else product_category.description
row.id_access_level_required = product_category.id_access_level_required[0] if isinstance(product_category.id_access_level_required, tuple) else product_category.id_access_level_required
row.active = product_category.active
row.display_order = product_category.display_order
"""
@@ -90,12 +92,15 @@ class Row_Shop_Product_Category_Temp(db.Model):
'code': self.code,
'name': self.name,
'description': self.description,
'id_access_level_required': self.id_access_level_required,
'active': self.active,
'display_order': self.display_order,
'guid': self.guid,
'created_on': self.created_on,
'created_by': self.created_by
}
"""
'created_on': self.created_on,
'created_by': self.created_by
"""
class DataStore_Store_Product_Category(DataStore_Store_Base):
@@ -115,7 +120,8 @@ class DataStore_Store_Product_Category(DataStore_Store_Base):
rows = []
id_category_new = 0
for category in categories:
row = Row_Shop_Product_Category_Temp.from_product_category(category)
# row = Row_Shop_Product_Category_Temp.from_product_category(category)
row = category.to_temporary_record()
# id_tmp =
if row.id_category == '':
id_category_new -= 1
@@ -123,8 +129,8 @@ class DataStore_Store_Product_Category(DataStore_Store_Base):
else:
print(f'row.id_category: {row.id_category}')
row.guid = guid
row.created_on = now
row.created_by = user.id_user
# row.created_on = now
# row.created_by = user.id_user
rows.append(row)
print(f'rows: {rows}')
@@ -141,7 +147,7 @@ class DataStore_Store_Product_Category(DataStore_Store_Base):
cursor.close()
print('cursor closed')
"""
DataStore_Store_Base.upload_bulk(rows, Row_Shop_Product_Category_Temp, 1000)
DataStore_Store_Base.upload_bulk(rows, Product_Category.__tablename__, 1000)
argument_dict_list = {
'a_id_user': user.id_user,

View File

@@ -13,7 +13,7 @@ Datastore for Store Product Permutations
# internal
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Container_Product_Category, Product_Category
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
@@ -26,7 +26,7 @@ from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_db_mysql import Helper_DB_MySQL
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
# external

View File

@@ -14,7 +14,7 @@ Datastore for Store Product Variations
# from routes import bp_home
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Container_Product_Category, Product_Category
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
@@ -27,7 +27,7 @@ from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_db_mysql import Helper_DB_MySQL
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
# external
@@ -83,7 +83,7 @@ class DataStore_Store_Product_Variation(DataStore_Store_Base):
variations = Product_Variation_List()
for row in result_set:
new_variation = Product_Variation.from_DB_variation(row)
variations.add_variation(new_variation)
variations.add_product_variation(new_variation)
errors = []
cursor.nextset()

View File

@@ -14,7 +14,7 @@ Datastore for Store Stock Items
# from routes import bp_home
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Container_Product_Category, Product_Category
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
@@ -27,7 +27,7 @@ from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_db_mysql import Helper_DB_MySQL
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
# external
@@ -73,7 +73,7 @@ class DataStore_Store_Stock_Item(DataStore_Store_Base):
def input_many_stock_item(cursor):
_m = 'DataStore_Store_Stock_Item.input_many_stock_item'
category_list = Container_Product_Category()
category_list = Product_Category_Container()
# Categories
result_set_1 = cursor.fetchall()
print(f'raw categories: {result_set_1}')
@@ -95,12 +95,12 @@ class DataStore_Store_Stock_Item(DataStore_Store_Base):
except KeyError:
permutation = Product_Permutation.from_DB_stock_item(row)
permutation.add_stock_item(new_stock_item)
product.add_permutation(permutation)
product.add_product_permutation(permutation)
except KeyError:
product = Product.from_DB_stock_item(row)
permutation = Product_Permutation.from_DB_stock_item(row)
permutation.add_stock_item(new_stock_item)
product.add_permutation(permutation)
product.add_product_permutation(permutation)
category_list.add_product(product)
"""
except KeyError:
@@ -109,10 +109,10 @@ class DataStore_Store_Stock_Item(DataStore_Store_Base):
product = Product.from_DB_stock_item(row)
permutation = Product_Permutation.from_DB_stock_item(row)
permutation.add_stock_item(new_stock_item)
product.add_permutation(permutation)
product.add_product_permutation(permutation)
new_category.add_product(product)
"""
category_list.add_category(new_category)
category_list.add_product_category(new_category)
try:
index_product = category.get_index_product_from_id(new_stock_item.id_product)
product = category.products[index_product]
@@ -125,7 +125,7 @@ class DataStore_Store_Stock_Item(DataStore_Store_Base):
permutation.add_stock_item(new_stock_item)
except KeyError:
new_permutation = Product_Permutation.from_DB_stock_item(row)
product.add_permutation(new_permutation)
product.add_product_permutation(new_permutation)
category_list.add_stock_item(new_stock_item)
# Product_Variations
@@ -133,9 +133,9 @@ class DataStore_Store_Stock_Item(DataStore_Store_Base):
result_set_3 = cursor.fetchall()
variations = []
for row in result_set_3:
new_variation = Product_Variation.from_DB_product(row)
new_variation = Product_Variation.from_DB_get_many_product_catalogue(row)
variations.append(new_variation)
category_list.add_variation(new_variation)
category_list.add_product_variation(new_variation)
# Errors
cursor.nextset()
@@ -147,7 +147,7 @@ class DataStore_Store_Stock_Item(DataStore_Store_Base):
for error in errors:
print(f"Error [{error.code}]: {error.msg}")
category_list.get_all_variation_trees()
category_list.get_all_product_variation_trees()
"""
for category in category_list.categories:
print(f'category: {category.name}')

View File

@@ -14,7 +14,7 @@ Datastore for Store Stripe service
# from routes import bp_home
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Container_Product_Category, Product_Category
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
@@ -27,7 +27,7 @@ from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from datastores.datastore_store_base import DataStore_Store_Base
from helpers.helper_db_mysql import Helper_DB_MySQL
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
# external

View File

@@ -14,7 +14,7 @@ Datastore for Users
# from routes import bp_home
import lib.argument_validation as av
from business_objects.store.basket import Basket, Basket_Item
from business_objects.store.product_category import Container_Product_Category, Product_Category
from business_objects.store.product_category import Product_Category_Container, Product_Category
from business_objects.store.currency import Currency
from business_objects.store.image import Image
from business_objects.store.delivery_option import Delivery_Option
@@ -27,7 +27,7 @@ from business_objects.store.stock_item import Stock_Item, Stock_Item_Filters
from business_objects.user import User, User_Filters, User_Permission_Evaluation
from business_objects.store.product_variation import Product_Variation, Product_Variation_Filters, Product_Variation_List
from datastores.datastore_base import DataStore_Base
from helpers.helper_db_mysql import Helper_DB_MySQL
# from helpers.helper_db_mysql import Helper_DB_MySQL
# from models.model_view_store_checkout import Model_View_Store_Checkout # circular!
from extensions import db
# external

View File

@@ -0,0 +1 @@
pip

View File

@@ -0,0 +1,20 @@
Copyright (c) 2008-present The pip developers (see AUTHORS.txt file)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,88 @@
Metadata-Version: 2.1
Name: pip
Version: 23.0.1
Summary: The PyPA recommended tool for installing Python packages.
Home-page: https://pip.pypa.io/
Author: The pip developers
Author-email: distutils-sig@python.org
License: MIT
Project-URL: Documentation, https://pip.pypa.io
Project-URL: Source, https://github.com/pypa/pip
Project-URL: Changelog, https://pip.pypa.io/en/stable/news/
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Topic :: Software Development :: Build Tools
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.7
License-File: LICENSE.txt
pip - The Python Package Installer
==================================
.. image:: https://img.shields.io/pypi/v/pip.svg
:target: https://pypi.org/project/pip/
.. image:: https://readthedocs.org/projects/pip/badge/?version=latest
:target: https://pip.pypa.io/en/latest
pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes.
Please take a look at our documentation for how to install and use pip:
* `Installation`_
* `Usage`_
We release updates regularly, with a new version every 3 months. Find more details in our documentation:
* `Release notes`_
* `Release process`_
In pip 20.3, we've `made a big improvement to the heart of pip`_; `learn more`_. We want your input, so `sign up for our user experience research studies`_ to help us do it right.
**Note**: pip 21.0, in January 2021, removed Python 2 support, per pip's `Python 2 support policy`_. Please migrate to Python 3.
If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms:
* `Issue tracking`_
* `Discourse channel`_
* `User IRC`_
If you want to get involved head over to GitHub to get the source code, look at our development documentation and feel free to jump on the developer mailing lists and chat rooms:
* `GitHub page`_
* `Development documentation`_
* `Development IRC`_
Code of Conduct
---------------
Everyone interacting in the pip project's codebases, issue trackers, chat
rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
.. _package installer: https://packaging.python.org/guides/tool-recommendations/
.. _Python Package Index: https://pypi.org
.. _Installation: https://pip.pypa.io/en/stable/installation/
.. _Usage: https://pip.pypa.io/en/stable/
.. _Release notes: https://pip.pypa.io/en/stable/news.html
.. _Release process: https://pip.pypa.io/en/latest/development/release-process/
.. _GitHub page: https://github.com/pypa/pip
.. _Development documentation: https://pip.pypa.io/en/latest/development
.. _made a big improvement to the heart of pip: https://pyfound.blogspot.com/2020/11/pip-20-3-new-resolver.html
.. _learn more: https://pip.pypa.io/en/latest/user_guide/#changes-to-the-pip-dependency-resolver-in-20-3-2020
.. _sign up for our user experience research studies: https://pyfound.blogspot.com/2020/03/new-pip-resolver-to-roll-out-this-year.html
.. _Python 2 support policy: https://pip.pypa.io/en/latest/development/release-process/#python-2-support
.. _Issue tracking: https://github.com/pypa/pip/issues
.. _Discourse channel: https://discuss.python.org/c/packaging
.. _User IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa
.. _Development IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa-dev
.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.38.4)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -0,0 +1,4 @@
[console_scripts]
pip = pip._internal.cli.main:main
pip3 = pip._internal.cli.main:main
pip3.9 = pip._internal.cli.main:main

View File

@@ -0,0 +1 @@
pip

Some files were not shown because too many files have changed in this diff Show More