Initial commit.

This commit is contained in:
2025-06-21 17:51:07 +01:00
commit 6fd3a23aa7
296 changed files with 29154 additions and 0 deletions

4
.babelrc Normal file
View File

@@ -0,0 +1,4 @@
{
"presets": ["@babel/preset-env"],
"plugins": ["@babel/plugin-syntax-dynamic-import"]
}

77
.gitignore vendored Normal file
View File

@@ -0,0 +1,77 @@
# Ignore Git files
git-filter-repo/
# Ignore compiled files
*.class
*.o
*.pyc
.VSCodeCounter/
__pycache__/
# Ignore logs and databases
*.log
*.log.*
# Ignore logs and databases
# *.sql
# *.sqlite
# Ignore OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Ignore IDE and editor files
.vscode/
.idea/
*.swp
*.swo
*.sublime-workspace
# Ignore package management directories
node_modules/
jspm_packages/
bower_components/
# Ignore build directories
build/
dist/
out/
# Ignore environment and config files
.env
config.js
secrets.yaml
# Ignore dependency lock files (uncomment if you want to ignore)
# package-lock.json
# yarn.lock
# Ignore personal TODO lists
TODO.md
# Ignore all files in a directory
tmp/
# Ignore all .txt files in the doc/ directory
doc/*.txt
# But don't ignore doc/important.txt, even though we're ignoring .txt files above
!doc/important.txt
# Ignore all .pdf files in the doc/ directory and any of its subdirectories
doc/**/*.pdf
# Ignore deprecated files
DEPRECATED/
# Ignore virtual environments
bot_env/
bot_web/
env_test/
env_web/

7
.htaccess Normal file
View File

@@ -0,0 +1,7 @@
PassengerEnabled On
PassengerAppRoot parts_website
PassengerAppType wsgi
PassengerStartupFile passenger_wsgi.py
# Optional: Set the Python interpreter if required
PassengerPython /home/partsltd/virtualenv/public_html/parts_website/3.11/bin/python

41
README.md Normal file
View File

@@ -0,0 +1,41 @@
Precision and Research Technology Systems Limited
Website with online store
Powered by flask
enter virtual environment:
python -m venv VIRTUAL_ENVIRONMENT_NAME
run module bundler:
npm run build
host for machine:
python -m flask run
host for local network:
python -m flask run --host=0.0.0.0
files dedicated to each page:
CSS
page
HTML
page
row
JavaScript
page
api
router
base - navigation buttons
MySQL
get
save
table
staging table
audit table
Python
business object
controller
datastore
form
model

20
__init__.py Normal file
View File

@@ -0,0 +1,20 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Initialisation
Description:
Initializes the Flask application.
Initializes any extensions used in the project.
"""
from flask import Flask
app = Flask(__name__)
app.config.from_object('config')
# from app import routes
# import business_objects, lib, models

129
app.py Normal file
View File

@@ -0,0 +1,129 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App General
Feature: App
Description:
Initializes the Flask application, sets the configuration based on the environment, and defines two routes (/ and /about) that render templates with the specified titles.
"""
# IMPORTS
# VARIABLE INSTANTIATION
# METHODS
# IMPORTS
# internal
from config import app_config, Config
from controllers.core.contact import routes_core_contact
from controllers.core.home import routes_core_home
from controllers.legal.legal import routes_legal
from extensions import db, csrf, mail, oauth
from helpers.helper_app import Helper_App
# external
from flask import Flask, render_template, jsonify, request, render_template_string, send_from_directory, redirect, url_for, session
# from flask_appconfig import AppConfig
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from flask_mail import Mail, Message
from flask_wtf.csrf import CSRFProtect
from werkzeug.exceptions import HTTPException
from authlib.integrations.flask_client import OAuth
import os
import sys
from logging.handlers import RotatingFileHandler
import traceback
import logging
sys.path.insert(0, os.path.dirname(__file__)) # Todo: why?
app = Flask(__name__)
app.secret_key = os.getenv('KEY_SECRET_FLASK')
# AppConfig(app)
app.config.from_object(app_config) # for db init with required keys
app.app_config = app_config
# app.config["config"] = app_config()
# logging
handler = RotatingFileHandler('app.log', maxBytes=10000, backupCount=3)
handler.setLevel(logging.DEBUG)
app.logger.addHandler(handler)
@app.errorhandler(Exception)
def internal_server_error(error):
if isinstance(error, HTTPException) and error.code == 404:
return "Not Found", 404
app.logger.error('Server Error: %s', (error))
app.logger.error('Request: %s %s %s %s %s',
request.remote_addr,
request.method,
request.scheme,
request.full_path,
request.headers)
app.logger.error('Request data: %s', request.get_data())
app.logger.error('Traceback: %s', traceback.format_exc())
return "Internal Server Error", 500
@app.before_request
def make_session_permanent():
session.permanent = True
csrf = CSRFProtect()
cors = CORS(app, resources={
r"/static/*": {
"origins": [app.config["URL_HOST"]],
"methods": ["GET"],
"max_age": 3600
}
})
csrf.init_app(app)
cors.init_app(app)
db.init_app(app)
mail.init_app(app)
oauth.init_app(app)
with app.app_context():
# config = app.config["config"]
db.create_all()
db.engine.url = app.config['SQLALCHEMY_DATABASE_URI']
oauth.register(
"auth0",
client_id = app.config['ID_AUTH0_CLIENT'],
client_secret = app.config['ID_AUTH0_CLIENT_SECRET'],
client_kwargs={
"scope": "openid profile email",
},
server_metadata_url=f'https://{app.config["DOMAIN_AUTH0"]}/.well-known/openid-configuration',
api_base_url = f'https://{app.config["DOMAIN_AUTH0"]}',
authorize_url = f'https://{app.config["DOMAIN_AUTH0"]}/authorize',
access_token_url = f'https://{app.config["DOMAIN_AUTH0"]}/oauth/token',
)
app.register_blueprint(routes_core_home)
app.register_blueprint(routes_core_contact)
app.register_blueprint(routes_legal)
@app.template_filter('console_log')
def console_log(value):
Helper_App.console_log(value)
return value
@app.after_request
def add_cache_headers(response):
if request.path.startswith('/static/'):
# Cache static assets
response.headers['Cache-Control'] = 'public, max-age=31536000'
else:
# No caching for dynamic content
response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'
return response

View File

@@ -0,0 +1,11 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Module Initialisation
Feature: Business Objects
Description:
Initialises business objects module.
"""

32
business_objects/api.py Normal file
View File

@@ -0,0 +1,32 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Base Business Object
Description:
Abstract business object
"""
# internal
from extensions import db
import lib.argument_validation as av
# external
from typing import ClassVar
from flask import jsonify
class API():
@staticmethod
def get_standard_response(success: bool, status_code: int, message: str, data: any, errors: list, meta: dict):
return jsonify({
"success": success,
"status_code": status_code,
"message": message,
"data": data,
"errors": errors,
"meta": meta
})

90
business_objects/base.py Normal file
View File

@@ -0,0 +1,90 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Base Business Object
Description:
Abstract base class for all business objects in app
"""
# internal
from extensions import db
import lib.argument_validation as av
# external
from typing import ClassVar
class Base():
ATTR_ID_ACCESS_LEVEL: ClassVar[str] = 'id_access_level'
ATTR_ID_ADDRESS: ClassVar[str] = 'id_address'
ATTR_ID_CURRENCY: ClassVar[str] = 'id_currency'
ATTR_ID_MSG_ERROR_TYPE: ClassVar[str] = 'id_type'
ATTR_ID_REGION: ClassVar[str] = 'id_region'
ATTR_ID_USER: ClassVar[str] = 'id_user'
ATTR_ID_USER_MANAGER: ClassVar[str] = 'id_user_manager'
FLAG_ACCESS_LEVEL_REQUIRED: ClassVar[str] = 'access_level_required'
FLAG_ACTIVE: ClassVar[str] = 'active'
FLAG_ADDRESS: ClassVar[str] = 'address'
FLAG_ADDRESS_LINE_1: ClassVar[str] = 'address_line_1'
FLAG_ADDRESS_LINE_2: ClassVar[str] = 'address_line_2'
FLAG_CAN_ADMIN: ClassVar[str] = 'can_admin'
FLAG_CAN_EDIT: ClassVar[str] = 'can_edit'
FLAG_CAN_VIEW: ClassVar[str] = 'can_view'
FLAG_CITY: ClassVar[str] = 'city'
FLAG_CODE: ClassVar[str] = 'code'
FLAG_COUNTY: ClassVar[str] = 'county'
FLAG_CREATED_BY: ClassVar[str] = 'created_by'
FLAG_CREATED_ON: ClassVar[str] = 'created_on'
FLAG_CURRENCY: ClassVar[str] = 'currency'
FLAG_CURRENCY_COST: ClassVar[str] = 'currency_cost'
FLAG_DATE_FROM: ClassVar[str] = 'date_from'
FLAG_DATE_TO: ClassVar[str] = 'date_to'
FLAG_DESCRIPTION: ClassVar[str] = 'description'
FLAG_DISPLAY_ORDER: ClassVar[str] = 'display_order'
FLAG_EDIT: ClassVar[str] = 'edit'
FLAG_EMAIL: ClassVar[str] = 'email'
FLAG_FAX: ClassVar[str] = 'fax'
FLAG_GUID: ClassVar[str] = 'guid'
FLAG_IS_NOT_EMPTY: ClassVar[str] = 'is_not_empty'
# FLAG_KEY_PRIMARY: ClassVar[str] = 'key_primary'
FLAG_MESSAGE: ClassVar[str] = 'message'
FLAG_NAME: ClassVar[str] = 'name'
FLAG_NAME_ATTR_OPTION_TEXT: ClassVar[str] = 'NAME_ATTR_OPTION_TEXT'
FLAG_NAME_ATTR_OPTION_VALUE: ClassVar[str] = 'NAME_ATTR_OPTION_VALUE'
FLAG_NAME_SINGULAR: ClassVar[str] = 'name_singular'
FLAG_NAME_PLURAL: ClassVar[str] = 'name_plural'
FLAG_PHONE_NUMBER: ClassVar[str] = 'phone_number'
FLAG_POSTCODE: ClassVar[str] = 'postcode'
FLAG_PRIORITY: ClassVar[str] = 'priority'
FLAG_REGION: ClassVar[str] = 'region'
FLAG_ROWS: ClassVar[str] = 'rows'
FLAG_SYMBOL: ClassVar[str] = 'symbol'
FLAG_URL: ClassVar[str] = 'url'
FLAG_USER: ClassVar[str] = 'authorisedUser' # 'user' already used
FLAG_VALUE_LOCAL_VAT_EXCL: ClassVar[str] = 'value_local_vat_excl'
FLAG_VALUE_LOCAL_VAT_INCL: ClassVar[str] = 'value_local_vat_incl'
FLAG_WEBSITE: ClassVar[str] = 'website'
ID_USER_GUEST: ClassVar[int] = 3
"""
NAME_ATTR_OPTION_TEXT: ClassVar[str] = 'name-attribute-option-text'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = 'name-attribute-option-value'
"""
def __repr__(self):
attrs = '\n'.join(f'{k}={v!r}' for k, v in self.__dict__.items())
return f'<{self.__class__.__name__}(\n{attrs}\n)>'
@classmethod
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]
@classmethod
def get_shared_json_attributes(cls, object):
return {
cls.FLAG_NAME_ATTR_OPTION_TEXT: object.NAME_ATTR_OPTION_TEXT,
cls.FLAG_NAME_ATTR_OPTION_VALUE: object.NAME_ATTR_OPTION_VALUE
}

View File

@@ -0,0 +1,48 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Database Base Business Objects
Description:
Abstract base class 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 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_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,82 @@
"""
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.base import Base
from extensions import db
from helpers.helper_app import Helper_App
# external
from pydantic import BaseModel
from typing import ClassVar
class Access_Level(db.Model, Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Base.ATTR_ID_ACCESS_LEVEL
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Base.FLAG_NAME
__tablename__ = 'PH_Access_Level_Temp'
id_access_level = db.Column(db.Integer, primary_key=True)
code = db.Column(db.String(50))
name = db.Column(db.String(250))
priority = db.Column(db.Integer)
display_order = db.Column(db.Integer)
active = db.Column(db.Boolean)
def __init__(self):
super().__init__()
Base.__init__(self)
@classmethod
def from_DB_access_level(cls, query_row):
access_level = cls()
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.get_shared_json_attributes(self),
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: av.input_bool(self.active, self.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json')
}
def to_json_option(self):
return {
'value': self.id_access_level,
'text': self.name
}
@classmethod
def from_json(cls, 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

View File

View File

@@ -0,0 +1,136 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Command Business Object
"""
# internal
from business_objects.base import Base
from business_objects.db_base import SQLAlchemy_ABC
import lib.argument_validation as av
from extensions import db
from helpers.helper_app import Helper_App
# external
from dataclasses import dataclass
from typing import ClassVar
class Command(SQLAlchemy_ABC, Base):
FLAG_ALTCHA: ClassVar[str] = 'altcha'
FLAG_COMMAND: ClassVar[str] = 'command'
FLAG_NAME_COMPANY: ClassVar[str] = 'name-company'
FLAG_NAME_CONTACT: ClassVar[str] = 'name-contact'
FLAG_MESSAGE: ClassVar[str] = 'message'
FLAG_RECEIVE_MARKETING_COMMUNICATIONS: ClassVar[str] = 'receive-marketing-communications'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = FLAG_COMMAND
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Base.FLAG_EMAIL
__tablename__ = 'PH_Command'
__table_args__ = { 'extend_existing': True }
id_command = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(250))
name_contact = db.Column(db.String(250))
name_company = db.Column(db.String(250))
message = db.Column(db.Text)
receive_marketing_communications = db.Column(db.Boolean)
active = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
def __init__(self):
self.id_command = 0
self.is_new = False
super().__init__()
def from_DB_Command(query_row):
_m = 'Command.from_DB_Command'
command = Command()
command.id_command = query_row[0]
command.email = query_row[1]
command.name_contact = query_row[2]
command.name_company = query_row[3]
command.message = query_row[4]
command.receive_marketing_communications = av.input_bool(query_row[5], 'receive_marketing_communications', _m)
command.active = av.input_bool(query_row[6], 'active', _m)
command.created_on = query_row[7]
return command
@classmethod
def from_json(cls, json):
_m = 'Command.from_json'
command = cls()
if json is None: return Command
Helper_App.console_log(f'{_m}\njson: {json}')
command.id_command = -1
command.email = json[cls.FLAG_EMAIL]
command.name_contact = json[cls.FLAG_NAME_CONTACT]
command.name_company = json[cls.FLAG_NAME_COMPANY]
command.message = json[cls.FLAG_MESSAGE]
command.receive_marketing_communications = json[cls.FLAG_RECEIVE_MARKETING_COMMUNICATIONS]
command.active = json[cls.FLAG_ACTIVE]
command.created_on = json.get(cls.FLAG_CREATED_ON, None)
Helper_App.console_log(f'Command: {command}')
return command
def to_json(self):
as_json = {
self.FLAG_COMMAND: self.id_command
, self.FLAG_EMAIL: self.email
, self.FLAG_NAME_CONTACT: self.name_contact
, self.FLAG_NAME_COMPANY: self.name_company
, self.FLAG_MESSAGE: self.message
, self.FLAG_RECEIVE_MARKETING_COMMUNICATIONS: self.receive_marketing_communications
, self.FLAG_ACTIVE: self.active
, self.FLAG_CREATED_ON: self.created_on
}
Helper_App.console_log(f'as_json: {as_json}')
return as_json
def __repr__(self):
return f'''
{self.__class__.__name__}(
{self.FLAG_COMMAND}: {self.id_command}
{self.FLAG_EMAIL}: {self.email}
{self.FLAG_NAME_CONTACT}: {self.name_contact}
{self.FLAG_NAME_COMPANY}: {self.name_company}
{self.FLAG_MESSAGE}: {self.message}
{self.FLAG_RECEIVE_MARKETING_COMMUNICATIONS}: {self.receive_marketing_communications}
{self.FLAG_ACTIVE}: {self.active}
{self.FLAG_CREATED_ON}: {self.created_on}
)
'''
class Command_Temp(db.Model, Base):
__tablename__ = 'PH_Command_Temp'
__table_args__ = { 'extend_existing': True }
id_temp = db.Column(db.Integer, primary_key=True)
id_command = db.Column(db.Integer)
email = db.Column(db.String(250))
name_contact = db.Column(db.String(250))
name_company = db.Column(db.String(250))
message = db.Column(db.Text)
receive_marketing_communications = db.Column(db.Boolean)
active = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
guid: str = db.Column(db.String(36))
def __init__(self):
super().__init__()
@classmethod
def from_command(cls, command):
_m = 'Command_Temp.from_Command'
temp = cls()
temp.id_command = command.id_command
temp.email = command.email
temp.name_contact = command.name_contact
temp.name_company = command.name_company
temp.message = command.message
temp.receive_marketing_communications = command.receive_marketing_communications
temp.active = command.active
temp.created_on = command.created_on
return temp

126
business_objects/dog/dog.py Normal file
View File

@@ -0,0 +1,126 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: Dog Business Object
"""
# internal
from business_objects.base import Base
from business_objects.db_base import SQLAlchemy_ABC
import lib.argument_validation as av
from extensions import db
from helpers.helper_app import Helper_App
# external
from dataclasses import dataclass
from typing import ClassVar
class Dog(SQLAlchemy_ABC, Base):
FLAG_DOG: ClassVar[str] = 'dog'
FLAG_APPEARANCE: ClassVar[str] = 'appearance'
FLAG_MASS_KG: ClassVar[str] = 'mass-kg'
FLAG_NOTES: ClassVar[str] = 'notes'
NAME_ATTR_OPTION_VALUE: ClassVar[str] = FLAG_DOG
NAME_ATTR_OPTION_TEXT: ClassVar[str] = Base.FLAG_EMAIL
__tablename__ = 'PH_Dog'
__table_args__ = { 'extend_existing': True }
id_dog = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(250))
appearance = db.Column(db.String(1000))
mass_kg = db.Column(db.Numeric(precision=7, scale=3))
notes = db.Column(db.Text)
active = db.Column(db.Boolean)
def __init__(self):
self.id_dog = 0
self.is_new = False
super().__init__()
def from_DB_Dog(query_row):
_m = 'Dog.from_DB_Dog'
dog = Dog()
dog.id_dog = query_row[0]
dog.name = query_row[1]
dog.appearance = query_row[2]
dog.mass_kg = query_row[3]
dog.notes = query_row[4]
dog.active = av.input_bool(query_row[5], 'active', _m)
return dog
@classmethod
def from_json(cls, json):
_m = 'Dog.from_json'
dog = cls()
if json is None: return Dog
Helper_App.console_log(f'{_m}\njson: {json}')
dog.id_dog = -1
dog.name = json[cls.FLAG_NAME]
dog.appearance = json[cls.FLAG_APPEARANCE]
dog.mass_kg = json[cls.FLAG_MASS_KG]
dog.notes = json[cls.FLAG_NOTES]
dog.active = json[cls.FLAG_ACTIVE]
Helper_App.console_log(f'Dog: {dog}')
return dog
def to_json(self):
as_json = {
self.FLAG_DOG: self.id_dog
, self.FLAG_NAME: self.name
, self.FLAG_APPEARANCE: self.appearance
, self.FLAG_MASS_KG: self.mass_kg
, self.FLAG_NOTES: self.notes
, self.FLAG_ACTIVE: self.active
}
Helper_App.console_log(f'as_json: {as_json}')
return as_json
def __repr__(self):
return f'''
{self.__class__.__name__}(
{self.FLAG_DOG}: {self.id_dog}
{self.FLAG_NAME}: {self.name}
{self.FLAG_APPEARANCE}: {self.appearance}
{self.FLAG_MASS_KG}: {self.mass_kg}
{self.FLAG_NOTES}: {self.notes}
{self.FLAG_ACTIVE}: {self.active}
)
'''
"""
class Dog_Temp(db.Model, Base):
__tablename__ = 'PH_Dog_Temp'
__table_args__ = { 'extend_existing': True }
id_temp = db.Column(db.Integer, primary_key=True)
id_dog = db.Column(db.Integer)
email = db.Column(db.String(250))
name_contact = db.Column(db.String(250))
name_company = db.Column(db.String(250))
message = db.Column(db.Text)
receive_marketing_communications = db.Column(db.Boolean)
active = db.Column(db.Boolean)
created_on = db.Column(db.DateTime)
guid: str = db.Column(db.String(36))
def __init__(self):
super().__init__()
@classmethod
def from_dog(cls, dog):
_m = 'Dog_Temp.from_Dog'
temp = cls()
temp.id_dog = dog.id_dog
temp.email = dog.email
temp.name_contact = dog.name_contact
temp.name_company = dog.name_company
temp.message = dog.message
temp.receive_marketing_communications = dog.receive_marketing_communications
temp.active = dog.active
temp.created_on = dog.created_on
return temp
"""

View File

@@ -0,0 +1,133 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: User Business Object
"""
# internal
from business_objects.base import Base
from business_objects.db_base import SQLAlchemy_ABC
import lib.argument_validation as av
from forms.forms import Form_Contact
from extensions import db
from helpers.helper_app import Helper_App
# external
from dataclasses import dataclass
from typing import ClassVar
class User(SQLAlchemy_ABC, Base):
NAME_ATTR_OPTION_VALUE: ClassVar[str] = Base.ATTR_ID_USER
NAME_ATTR_OPTION_TEXT: ClassVar[str] = 'email'
__tablename__ = 'PH_User'
__table_args__ = { 'extend_existing': True }
id_user = db.Column(db.Integer, primary_key=True)
id_user_auth0 = db.Column(db.String(250))
firstname = db.Column(db.String(250))
surname = db.Column(db.String(250))
email = db.Column(db.String(250))
is_email_verified = db.Column(db.Boolean)
is_super_user = db.Column(db.Boolean)
is_new = db.Column(db.Boolean)
def __init__(self):
self.id_user = 0
self.is_new = False
super().__init__()
def from_DB_user(query_row):
_m = 'User.from_DB_user'
user = User()
user.id_user = query_row[0]
user.id_user_auth0 = query_row[1]
user.firstname = query_row[2]
user.surname = query_row[3]
user.email = query_row[4]
user.is_email_verified = av.input_bool(query_row[5], 'is_email_verified', _m)
user.is_super_user = av.input_bool(query_row[9], 'is_super_user', _m)
user.is_new = av.input_bool(query_row[12], 'is_new', _m)
return user
@staticmethod
def from_json(json):
_m = 'User.from_json'
user = User()
if json is None: return user
Helper_App.console_log(f'{_m}\njson: {json}')
user.id_user = json['id_user']
user.id_user_auth0 = json['id_user_auth0']
user.firstname = json['firstname']
user.surname = json['surname']
user.email = json['email']
user.is_email_verified = av.input_bool(json['is_email_verified'], 'is_email_verified', _m)
user.is_super_user = av.input_bool(json['is_super_user'], 'is_super_user', _m)
Helper_App.console_log(f'user: {user}')
return user
@staticmethod
def from_json_auth0(json):
_m = 'User.from_json_auth0'
user = User()
if json is None: return user
Helper_App.console_log(f'{_m}\njson: {json}')
user_info = json['userinfo']
user.id_user = None
user.id_user_auth0 = user_info['sub']
user.firstname = None
user.surname = None
user.email = user_info['email']
user.is_email_verified = av.input_bool(user_info['email_verified'], 'is_email_verified', _m)
user.is_super_user = None
Helper_App.console_log(f'user: {user}')
return user
def to_json(self):
as_json = {
'id_user': self.id_user,
'id_user_auth0': self.id_user_auth0,
'firstname': self.firstname,
'surname': self.surname,
'email': self.email,
'is_email_verified': self.is_email_verified,
'is_super_user': self.is_super_user
}
Helper_App.console_log(f'as_json: {as_json}')
return as_json
def __repr__(self):
return f'''
id_user: {self.id_user}
id_user_auth0: {self.id_user_auth0}
firstname: {self.firstname}
surname: {self.surname}
email: {self.email}
is_email_verified: {self.is_email_verified}
is_super_user: {self.is_super_user}
'''
def get_is_logged_in(self):
return (self.id_user > 0 and self.id_user != Base.ID_USER_GUEST)
class User_Temp(db.Model, Base):
__tablename__ = 'Shop_User_Temp'
__table_args__ = { 'extend_existing': True }
id_user = db.Column(db.Integer, primary_key=True)
id_user_auth0 = db.Column(db.String(250))
firstname = db.Column(db.String(250))
surname = db.Column(db.String(250))
email = db.Column(db.String(250))
is_email_verified = db.Column(db.Boolean)
is_super_user = db.Column(db.Boolean)
id_currency_default = db.Column(db.Integer)
id_region_default = db.Column(db.Integer)
is_included_VAT_default = db.Column(db.Boolean)
# is_logged_in: bool
def __init__(self):
self.id_user = 0
super().__init__()

View File

@@ -0,0 +1,69 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Business Objects
Feature: SQL Error Business Object
Description:
Business object for SQL errors returned by Get Many Stored Procedures
"""
# internal
from business_objects.base import Base
import lib.argument_validation as av
from lib import data_types
from forms.forms import Form_Basket_Add, Form_Basket_Edit # Form_Product
# external
from enum import Enum
from datetime import datetime, timedelta
import locale
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class SQL_Error(db.Model):
display_order = db.Column(db.Integer, primary_key=True)
id_type = db.Column(db.Integer)
code = db.Column(db.String(50))
msg = db.Column(db.String(4000))
name = db.Column(db.String(500))
description = db.Column(db.String(4000))
"""
def __new__(cls, display_order, code, msg):
_m = 'SQL_Error.__new__'
v_arg_type = 'class attribute'
av.val_int(display_order, 'display_order', _m)
av.val_str(code, 'code', _m, max_len=50, v_arg_type=v_arg_type)
av.val_str(msg, 'msg', _m, max_len=4000, v_arg_type=v_arg_type)
return super(SQL_Error, cls).__new__(cls)
def __init__(self, display_order, code, msg):
self.display_order = display_order
self.code = code
self.msg = msg
super().__init__()
"""
def from_DB_record(record):
error = SQL_Error()
error.display_order = record[0]
error.id_type = record[1]
error.code = record[2]
error.msg = record[3]
error.name = record[4]
error.description = record[5]
return error
def to_json(self):
return {
Base.FLAG_DISPLAY_ORDER: self.display_order,
Base.ATTR_ID_MSG_ERROR_TYPE: self.id_type,
Base.FLAG_CODE: self.code,
Base.FLAG_MESSAGE: self.msg,
Base.FLAG_NAME: self.name,
Base.FLAG_DESCRIPTION: self.description,
}

131
config.py Normal file
View File

@@ -0,0 +1,131 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Configuration
Description:
Configuration variables
"""
# IMPORTS
import os
from dotenv import load_dotenv, find_dotenv
from flask import current_app
load_dotenv(find_dotenv())
# CLASSES
class Config:
is_development = False
is_production = False
# Miscellaneous
DEBUG = False # av.input_bool(os.getenv('DEBUG'), 'DEBUG', 'Config')
TESTING = False
URL_HOST = os.getenv('URL_HOST')
SECRET_KEY = os.getenv('KEY_SECRET_FLASK') # gen cmd: openssl rand -hex 32
# Add other configuration variables as needed
# MySQL
SQLALCHEMY_DATABASE_URI = os.getenv('SQLALCHEMY_DATABASE_URI')
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size': 10,
'pool_recycle': 280,
'pool_pre_ping': True,
'pool_timeout': 30,
}
# Auth0
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Strict'
REMEMBER_COOKIE_SECURE = True
WTF_CSRF_ENABLED = True
# WTF_CSRF_CHECK_DEFAULT = False # We'll check it manually for API routes
# WTF_CSRF_HEADERS = ['X-CSRFToken'] # Accept CSRF token from this header
WTF_CSRF_TIME_LIMIT = None
WTF_CSRF_SSL_STRICT = False # Allows testing without HTTPS
ID_AUTH0_CLIENT = os.getenv('ID_AUTH0_CLIENT')
ID_AUTH0_CLIENT_SECRET = os.getenv('ID_AUTH0_CLIENT_SECRET')
DOMAIN_AUTH0 = os.getenv('DOMAIN_AUTH0')
ID_TOKEN_USER = 'user'
# PostgreSQL
DB_NAME = os.getenv('parts')
DB_USER = os.getenv('DB_USER')
DB_PASSWORD = os.getenv('DB_PASSWORD')
DB_HOST = os.getenv('DB_HOST')
# DB_PORT = os.getenv('DB_PORT')
# Store
# is_included_VAT = True
"""
KEY_IS_INCLUDED_VAT = 'is_included_VAT'
code_currency = 1
KEY_CODE_CURRENCY = 'id_currency'
code_region_delivery = 1
KEY_CODE_REGION_DELIVERY = 'id_region_delivery'
KEY_ID_CURRENCY = 'id_currency'
KEY_ID_REGION_DELIVERY = 'id_region_delivery'
"""
# id_currency = 1
# id_region_delivery = 1
# Mail
MAIL_DEBUG = False # av.input_bool(os.getenv('DEBUG'), 'DEBUG', 'Config')
MAIL_SERVER = 'mail.partsltd.co.uk' # 'smtp.gmail.com'
MAIL_PORT = 465 # 587
MAIL_USE_TLS = False
MAIL_USE_SSL = True
MAIL_USERNAME = os.getenv('MAIL_DEFAULT_SENDER')
MAIL_PASSWORD = os.getenv('MAIL_PASSWORD')
MAIL_DEFAULT_SENDER = os.getenv('MAIL_DEFAULT_SENDER')
MAIL_CONTACT_PUBLIC = os.getenv('MAIL_CONTACT_PUBLIC')
"""
# Recaptcha
RECAPTCHA_PUBLIC_KEY = os.getenv('RECAPTCHA_PUBLIC_KEY')
RECAPTCHA_PRIVATE_KEY = os.getenv('RECAPTCHA_PRIVATE_KEY')
"""
# ALTCHA
ALTCHA_API_KEY = os.getenv('ALTCHA_API_KEY')
ALTCHA_SECRET_KEY = os.getenv('ALTCHA_SECRET_KEY')
ALTCHA_REGION = 'eu'
class DevelopmentConfig(Config):
is_development = True
# Add development-specific configuration variables
DEBUG = True
MAIL_DEBUG = True
SESSION_COOKIE_SECURE = False
class ProductionConfig(Config):
is_production = True
# Add production-specific configuration variables
pass
# Set the configuration class based on the environment
# You can change 'development' to 'production' when deploying
config_env = os.getenv('FLASK_ENV', "development")
with open('app.log', 'a') as f:
print(f'config_env: {config_env}')
f.write(f'config_env: {config_env}\n')
# current_app.logger.error(f'config_env: {config_env}') # logger not yet initialised
if config_env == 'development':
app_config = DevelopmentConfig
elif config_env == 'production':
app_config = ProductionConfig
else:
raise ValueError("Invalid configuration environment")
# environment variables
"""
SET KEY_SECRET_FLASK=nips
SET ID_AUTH0_CLIENT=
SET ID_AUTH0_CLIENT_SECRET=
SET DOMAIN_AUTH0=
SET MAIL_PASSWORD=
SET RECAPTCHA_PUBLIC_KEY=
SET RECAPTCHA_PRIVATE_KEY=
SET SQLALCHEMY_DATABASE_URI=
SET URL_HOST=
"""
# SET SQLALCHEMY_DATABASE_URI = mysql://username:password@localhost/dbname
# Replace 'username', 'password', 'localhost', and 'dbname' with your actual database credentials

0
controllers/__init__.py Normal file
View File

View File

143
controllers/core/contact.py Normal file
View File

@@ -0,0 +1,143 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Core - Contact Routes
Description:
Contact Page Controller.
"""
# IMPORTS
# internal
from business_objects.api import API
from business_objects.project_hub.command import Command
from datastores.project_hub.datastore_command import DataStore_Command
from forms.contact import Form_Contact
from helpers.helper_app import Helper_App
from models.model_view_contact import Model_View_Contact
from models.model_view_contact_success import Model_View_Contact_Success
from models.model_view_home import Model_View_Home
import lib.argument_validation as av
# external
from flask import Flask, render_template, jsonify, request, render_template_string, send_from_directory, redirect, url_for, session, Blueprint, current_app, flash
from flask_mail import Mail, Message
from extensions import db, oauth, mail
from urllib.parse import quote_plus, urlencode
from authlib.integrations.flask_client import OAuth
from authlib.integrations.base_client import OAuthError
from urllib.parse import quote, urlparse, parse_qs
import json
import base64
import hmac
import hashlib
import datetime
from altcha import ChallengeOptions, create_challenge, verify_solution
routes_core_contact = Blueprint('routes_core_contact', __name__)
@routes_core_contact.route(Model_View_Contact.HASH_PAGE_CONTACT, methods=['GET'])
def contact():
try:
form = Form_Contact()
model = Model_View_Contact(form)
html_body = render_template('pages/core/_contact.html', model = model)
return html_body
except Exception as e:
return API.get_standard_response(
success = False,
status_code = 500,
message = f"Error: {e}",
data = None,
errors = [str(e)],
meta = None
)
@routes_core_contact.route(Model_View_Contact.HASH_GET_ALTCHA_CHALLENGE, methods=['GET'])
def create_altcha_challenge():
options = ChallengeOptions(
expires = datetime.datetime.now() + datetime.timedelta(hours=1),
max_number = 100000, # The maximum random number
hmac_key = current_app.app_config.ALTCHA_SECRET_KEY,
)
challenge = create_challenge(options)
Helper_App.console_log(f"Challenge created: {challenge}")
# return jsonify({"challenge": challenge})
return jsonify({
"algorithm": challenge.algorithm,
"challenge": challenge.challenge,
"salt": challenge.salt,
"signature": challenge.signature,
})
@routes_core_contact.route(Model_View_Contact.HASH_PAGE_CONTACT, methods=['POST'])
def contact_post():
try:
form = Form_Contact()
if form.validate_on_submit():
try:
email = form.email.data
# CC = form.CC.data # not in use
contact_name = form.contact_name.data
company_name = form.company_name.data
message = form.message.data
receive_marketing = form.receive_marketing.data
receive_marketing_text = "I would like to receive marketing emails.\n" if receive_marketing else ""
# send email
mailItem = Message("PARTS Website Contact Us Message", recipients=[current_app.config['MAIL_CONTACT_PUBLIC']])
mailItem.body = f"Dear Lord Edward Middleton-Smith,\n\n{message}\n{receive_marketing_text}\nKind regards,\n{contact_name}\n{company_name}\n{email}"
mail.send(mailItem)
# save to database
datastore = DataStore_Command()
command = Command.from_json(form.to_json())
datastore.save_commands(
comment = command.message
, commands = [command]
)
return redirect(url_for(Model_View_Contact.ENDPOINT_PAGE_CONTACT_SUCCESS))
except Exception as e:
return API.get_standard_response(
success = False,
status_code = 500,
message = f"Error: {e}",
data = None,
errors = [str(e)],
meta = None
)
return API.get_standard_response(
success = False,
status_code = 500,
message = f"Error: {form.errors}",
data = None,
errors = [str(form.errors)],
meta = None
)
# html_body = render_template('pages/core/_contact.html', model = model)
except Exception as e:
return API.get_standard_response(
success = False,
status_code = 500,
message = f"Error: {e}",
data = None,
errors = [str(e)],
meta = None
)
@routes_core_contact.route(Model_View_Contact.HASH_PAGE_CONTACT_SUCCESS, methods=['GET'])
def contact_success():
try:
model = Model_View_Contact_Success()
html_body = render_template('pages/core/_contact_success.html', model = model)
return html_body
except Exception as e:
return API.get_standard_response(
success = False,
status_code = 500,
message = f"Error: {e}",
data = None,
errors = [str(e)],
meta = None
)

31
controllers/core/home.py Normal file
View File

@@ -0,0 +1,31 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Core - Home Routes
Description:
Home Page Controller.
"""
# internal
from business_objects.api import API
from models.model_view_home import Model_View_Home
# external
from flask import render_template, jsonify, Blueprint
routes_core_home = Blueprint('routes_core_home', __name__)
@routes_core_home.route(Model_View_Home.HASH_PAGE_HOME, methods=['GET'])
def home():
try:
model = Model_View_Home()
html_body = render_template('pages/core/_home.html', model = model)
except Exception as e:
return jsonify(error=str(e)), 403
return html_body

View File

View File

@@ -0,0 +1,70 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: App Routing
Feature: Legal Routes
Description:
Legal Section Controller.
"""
# IMPORTS
# internal
# from models.model_view_home import Model_View_Home
from models.model_view_license import Model_View_License
from models.model_view_privacy_policy import Model_View_Privacy_Policy
from models.model_view_accessibility_report import Model_View_Accessibility_Report
from models.model_view_accessibility_statement import Model_View_Accessibility_Statement
from models.model_view_retention_schedule import Model_View_Retention_Schedule
import lib.argument_validation as av
# external
from flask import render_template, Blueprint
routes_legal = Blueprint('routes_legal', __name__)
# snore
@routes_legal.route('/license', methods=['GET'])
def license():
try:
model = Model_View_License()
html_body = render_template('pages/legal/_license.html', model = model)
except Exception as e:
return str(e)
return html_body
@routes_legal.route('/accessibility-statement', methods=['GET'])
def accessibility_statement():
try:
model = Model_View_Accessibility_Statement()
html_body = render_template('pages/legal/_accessibility_statement.html', model = model)
except Exception as e:
return str(e)
return html_body
@routes_legal.route('/accessibility-report', methods=['GET'])
def accessibility_report():
try:
model = Model_View_Accessibility_Report()
html_body = render_template('pages/legal/_accessibility_report.html', model = model)
except Exception as e:
return str(e)
return html_body
@routes_legal.route('/retention-schedule', methods=['GET'])
def retention_schedule():
try:
model = Model_View_Retention_Schedule()
html_body = render_template('pages/legal/_retention_schedule.html', model = model)
except Exception as e:
return str(e)
return html_body
@routes_legal.route('/privacy-policy', methods=['GET'])
def privacy_policy():
try:
model = Model_View_Privacy_Policy()
html_body = render_template('pages/legal/_privacy_policy.html', model = model)
except Exception as e:
return str(e)
return html_body

11
datastores/__init__.py Normal file
View File

@@ -0,0 +1,11 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Module Initialisation
Feature: DataStores
Description:
Initialises datastores module.
"""

View File

@@ -0,0 +1,114 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: Base DataStore
Description:
Datastore for Store
"""
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.sql_error import SQL_Error
from business_objects.project_hub.user import User
# 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
from forms.access_level import Filters_Access_Level
from forms.unit_measurement import Filters_Unit_Measurement
from helpers.helper_app import Helper_App
# external
from sqlalchemy import text
from flask import Flask, session, current_app
from pydantic import BaseModel, ConfigDict
import time
from sqlalchemy.exc import OperationalError
class DataStore_Base(BaseModel):
def __init__(self, **kwargs):
super().__init__(**kwargs)
@staticmethod
def db_procedure_execute(proc_name, argument_dict_list = None):
# Argument validation
_m = 'DataStore_Base.db_procedure_execute'
av.val_str(proc_name, 'proc_name', _m)
proc_string = f'CALL {proc_name}('
has_arguments = not str(type(argument_dict_list)) == "<class 'NoneType'>"
if has_arguments:
arg_keys = list(argument_dict_list.keys())
for i in range(len(arg_keys)):
proc_string += f'{"" if i == 0 else ", "}:{arg_keys[i]}'
proc_string += ')'
proc_string = text(proc_string)
Helper_App.console_log(f'{_m}\nproc_string: {proc_string}\nargs: {argument_dict_list}')
if has_arguments:
result = db.session.execute(proc_string, argument_dict_list)
else:
result = db.session.execute(proc_string)
Helper_App.console_log(f'result: {result}')
# conn.session.remove()
return result
@staticmethod
def db_cursor_clear(cursor):
while cursor.nextset():
Helper_App.console_log(f'new result set: {cursor.fetchall()}')
@staticmethod
def get_user_session():
Helper_App.console_log('DataStore_Base.get_user_session')
user = User.from_json(session.get(User.FLAG_USER))
if user.id_user <= 0:
user.id_user = 3
return user
@staticmethod
def upload_bulk(permanent_table_name, records, batch_size):
_m = 'DataStore_Base.upload_bulk'
Helper_App.console_log(f'{_m}\nstarting...')
Helper_App.console_log(f'permanent_table_name: {permanent_table_name}')
if db.session.dirty or db.session.new or db.session.deleted:
Helper_App.console_log("Session is not clean")
return
# Assuming `permanent_table_name` is a string representing the table name
table_object = db.metadata.tables.get(permanent_table_name)
if table_object is None:
Helper_App.console_log(f"Table {permanent_table_name} not found in metadata.")
return
else:
expected_columns = set(column.name for column in db.inspect(table_object).columns)
Helper_App.console_log(f'expected_columns: {expected_columns}')
max_retries = 3
initial_backoff = 1
for i in range(0, len(records), batch_size):
batch = records[i:i + batch_size]
try:
retries = 0
while retries < max_retries:
try:
db.session.bulk_save_objects(batch)
db.session.commit()
break
except OperationalError as e:
if "Lock wait timeout exceeded" not in str(e) or retries == max_retries - 1:
raise
wait_time = initial_backoff * (2 ** retries)
current_app.logger.warning(f"Lock timeout encountered. Retrying in {wait_time} seconds... (Attempt {retries + 1}/{max_retries})")
time.sleep(wait_time)
retries += 1
# Ensure the session is clean for the retry
db.session.rollback()
except Exception as e:
db.session.rollback()
raise e

View File

@@ -0,0 +1,113 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: DataStores
Feature: User DataStore
Description:
Datastore for Users
"""
# internal
# from routes import bp_home
import lib.argument_validation as av
from business_objects.sql_error import SQL_Error
from business_objects.project_hub.command import Command, Command_Temp
from datastores.datastore_base import DataStore_Base
from helpers.helper_app import Helper_App
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 flask_sqlalchemy import SQLAlchemy
from datetime import datetime
db = SQLAlchemy()
class DataStore_Command(DataStore_Base):
def __init__(self):
super().__init__()
@classmethod
def get_many_command(cls):
_m = f'{cls.__qualname__}.get_many_command'
user = cls.get_user_session()
argument_dict = {
'a_id_user': user.id_user
, 'a_debug': 0
}
Helper_App.console_log(f'argument_dict: {argument_dict}')
result = cls.db_procedure_execute('p_ph_get_many_command', argument_dict)
cursor = result.cursor
# Commands
result_set_1 = cursor.fetchall()
Helper_App.console_log(f'raw commands: {result_set_1}')
commands = []
command_indexes = {}
for row in result_set_1:
new_command = Command.from_DB_command(row)
command_indexes[new_command.id_command] = len(commands)
commands.append(new_command)
# Errors
cursor.nextset()
result_set_e = cursor.fetchall()
Helper_App.console_log(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]
for error in errors:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
cls.db_cursor_clear(cursor)
return commands, errors
@classmethod
def save_commands(cls, comment, commands):
_m = f'{cls}.save_commands'
av.val_str(comment, 'comment', _m)
guid = Helper_DB_MySQL.create_guid_str()
now = datetime.now()
user = cls.get_user_session()
Helper_App.console_log(f'saving commands: {commands}')
rows = []
for command in commands:
row = Command_Temp.from_command(command)
row.guid = guid
rows.append(row)
cls.upload_bulk(Command_Temp.__tablename__, rows, 1000)
Helper_App.console_log('Commands uploaded')
argument_dict_list = {
'a_comment': comment,
'a_guid': guid,
'a_id_user': user.id_user,
'a_debug': 0
}
result = cls.db_procedure_execute('p_ph_save_command', argument_dict_list)
Helper_App.console_log('Commands saved')
# Errors
cursor = result.cursor
cursor.nextset()
result_set_e = cursor.fetchall()
errors = []
if len(result_set_e) > 0:
errors = [SQL_Error.from_DB_record(row) for row in result_set_e]
for error in errors:
Helper_App.console_log(f"Error [{error.code}]: {error.msg}")
cls.db_cursor_clear(cursor)
return errors

14
extensions.py Normal file
View File

@@ -0,0 +1,14 @@
from flask import Flask, render_template, jsonify, request, render_template_string, send_from_directory, redirect, url_for, session, current_app
from flask_cors import CORS
from flask_sqlalchemy import SQLAlchemy
from flask_mail import Mail, Message
from flask_wtf.csrf import CSRFProtect
from authlib.integrations.flask_client import OAuth
csrf = CSRFProtect()
# cors = CORS()
db = SQLAlchemy()
mail = Mail()
oauth = OAuth()

0
forms/__init__.py Normal file
View File

37
forms/access_level.py Normal file
View File

@@ -0,0 +1,37 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Forms - Access Level Filters data input
Description:
Defines Flask-WTF forms for handling access level filter input.
"""
# internal
from business_objects.base import Base
from forms.base import Form_Base
import lib.argument_validation as av
# external
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField, BooleanField, IntegerField, SelectField, FloatField
from wtforms.validators import InputRequired, NumberRange, Regexp, DataRequired, Optional
from flask_wtf.recaptcha import RecaptchaField
from abc import ABCMeta, abstractmethod
class Filters_Access_Level(Form_Base):
active = BooleanField("Active only?", default = True)
def __repr__(self):
return f'{self.__class__.__name__}(active={self.active.data})'
@classmethod
def from_json(cls, json):
form = Filters_Access_Level()
form.active.data = av.input_bool(json[Base.FLAG_ACTIVE], Base.FLAG_ACTIVE, f'{cls.__name__}.from_json')
return form
def to_json(self):
return {
Base.FLAG_ACTIVE: 1 if av.input_bool(self.active.data, Base.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json') else 0,
}

78
forms/base.py Normal file
View File

@@ -0,0 +1,78 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Form Base and Meta Classes - data input
Description:
Defines Flask-WTF base forms for handling user input.
"""
# internal
# external
from flask_wtf import FlaskForm
from abc import ABCMeta, abstractmethod
class Form_Base_Meta(type(FlaskForm), ABCMeta):
pass
class Form_Base(FlaskForm, metaclass=Form_Base_Meta):
"""
@classmethod
@abstractmethod
def from_filters(cls, filters):
pass
@abstractmethod
def __repr__(self):
pass
"""
def __repr__(self):
fields = ', '.join(
f"{name}={field.data}" for name, field in self._fields.items()
)
return f"{self.__class__.__name__}({fields})"
@classmethod
@abstractmethod
def from_json(cls, json):
pass
@classmethod
def get_default(cls):
return cls()
"""
@abstractmethod
def test_69(self):
pass
def get_Filters_Product_Category(data_request):
data_form = data_request[Model_View_Store_Product_Category.FLAG_FORM]
form_filters = Filters_Product_Category(**data_form)
form_filters.is_not_empty.data = av.input_bool(data_form['is_not_empty'], 'is_not_empty', 'filter_category')
form_filters.active.data = av.input_bool(data_form['active'], 'active', 'filter_category')
return form_filters
"""
@classmethod
def get_choices_blank(cls):
return [('', 'Select')]
@classmethod
def get_choice_all(cls):
return ('', 'All')
'''
class Filters_Stored_Procedure_Base(Form_Base):
"""
@abstractmethod
def __repr__(self):
pass
@classmethod
@abstractmethod
def from_json(cls, json):
pass
"""
@abstractmethod
def to_json(self):
pass
'''

100
forms/contact.py Normal file
View File

@@ -0,0 +1,100 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Contact Us Form
Description:
Defines Flask-WTF form for handling user input on Contact Us page.
"""
# IMPORTS
# internal
from business_objects.base import Base
from business_objects.project_hub.command import Command
# from models.model_view_store import Model_View_Store # circular
from models.model_view_base import Model_View_Base
from forms.base import Form_Base
# external
from flask import Flask, render_template, request, flash, redirect, url_for, current_app
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField, HiddenField, BooleanField, Field, EmailField
from wtforms.validators import DataRequired, Email, ValidationError
import markupsafe
from flask_wtf.recaptcha import RecaptchaField
from abc import ABCMeta, abstractmethod
import json
from altcha import verify_solution
import base64
class ALTCHAValidator:
def __init__(self, message=None):
self.message = message or 'ALTCHA verification failed'
def __call__(self, form, field):
altcha_data = field.data
if not altcha_data:
raise ValidationError(self.message)
try:
# The data is base64 encoded JSON
try:
# First try to decode it as JSON directly (if it's not base64 encoded)
altcha_payload = json.loads(altcha_data)
except json.JSONDecodeError:
# If direct JSON decoding fails, try base64 decoding first
decoded_data = base64.b64decode(altcha_data).decode('utf-8')
altcha_payload = json.loads(decoded_data)
ok, err = verify_solution(altcha_payload, current_app.app_config.ALTCHA_SECRET_KEY, check_expires=True)
if err or not ok:
raise ValidationError(self.message + ': ' + (err or 'Invalid solution'))
except Exception as e:
raise ValidationError(f'Invalid ALTCHA data: {str(e)}')
class ALTCHAField(Field):
def __init__(self, label='', validators=None, **kwargs):
validators = validators or []
validators.append(ALTCHAValidator())
super(ALTCHAField, self).__init__(label, validators, **kwargs)
def __call__(self, **kwargs):
html = f"""
<altcha-widget
challengeurl="/get-challenge"
auto="onload"
id="{self.id}"
name="{self.name}">
</altcha-widget>
"""
return markupsafe.Markup(html)
class Form_Contact(FlaskForm):
email = EmailField('Email')
contact_name = StringField('Name')
company_name = StringField('Company')
message = TextAreaField('Message')
receive_marketing = BooleanField('I would like to receive marketing emails.')
# recaptcha = RecaptchaField()
# altcha = HiddenField('ALTCHA') # , validators=[validate_altcha]
altcha = ALTCHAField('Verify you are human')
submit = SubmitField('Send Message')
def to_json(self):
return {
Base.FLAG_EMAIL: self.email.data
, Command.FLAG_NAME_CONTACT: self.contact_name.data
, Command.FLAG_NAME_COMPANY: self.company_name.data
, Command.FLAG_MESSAGE: self.message.data
, Command.FLAG_RECEIVE_MARKETING_COMMUNICATIONS: self.receive_marketing.data
, Command.FLAG_ALTCHA: self.altcha.data
, Base.FLAG_ACTIVE: True
, Base.FLAG_CREATED_ON: None
}

134
forms/forms.py Normal file
View File

@@ -0,0 +1,134 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Forms - User data input
Description:
Defines Flask-WTF forms for handling user input.
"""
# IMPORTS
# internal
# from business_objects.store.product_category import Filters_Product_Category # circular
# from models.model_view_store import Model_View_Store # circular
from forms.base import Form_Base
# external
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField, BooleanField, IntegerField, SelectField, FloatField
from wtforms.validators import InputRequired, NumberRange, Regexp, DataRequired, Optional
from flask_wtf.recaptcha import RecaptchaField
from abc import ABCMeta, abstractmethod
class Form_Contact(FlaskForm):
email = StringField('Email address')
CC = BooleanField('Would you like to receive a copy of this email request?') # not in use
name = StringField('Name')
message = TextAreaField('Message')
recaptcha = RecaptchaField()
submit = SubmitField('Submit')
class Form_Register(FlaskForm):
email = StringField('Email address')
CC = BooleanField('Would you like to receive a copy of this email request?') # not in use
name = StringField('Name')
message = TextAreaField('Message')
submit = SubmitField('Submit')
"""
class Form_Product(FlaskForm): # for basket, product tiles, product add
# PositiveIntegerField with validation constraints
quantity = IntegerField(
'Quantity',
validators=[
# InputRequired(message='Quantity'),
NumberRange(min=1, message='Please enter a positive integer')
],
default=1
)
"""
class Form_Basket_Add(FlaskForm): # for basket, product tiles, product add
# PositiveIntegerField with validation constraints
quantity = IntegerField(
'Quantity',
validators=[
# InputRequired(message='Quantity'),
NumberRange(min=1, message='Please enter a positive integer')
],
default=1
# render_kw={'id-product': ''} # {Model_View_Store.attr_id_product: ''}
)
submit = SubmitField('Add')
form_type = 'Form_Basket_Add'
class Form_Basket_Edit(FlaskForm): # for basket, product tiles, product add
# PositiveIntegerField with validation constraints
quantity = IntegerField(
'Quantity',
validators=[
# InputRequired(message='Quantity'),
NumberRange(min=1, message='Please enter a positive integer')
],
default=1
# render_kw={'id-product': ''} # {Model_View_Store.attr_id_product: ''}
)
submit = SubmitField('Update')
form_type = 'Form_Basket_Edit'
class Form_Billing(FlaskForm):
identical = BooleanField('Use delivery address')
region = SelectField('Country / region', choices=[('uk', 'UK'), ('international', 'International')], validators=[DataRequired()])
name_full = StringField('Full name')
phone_number = StringField('Phone number', validators=[Regexp(r'^\+?[0-9\s]{5,20}$', message='Only numbers, plus symbol, and space are allowed.'), DataRequired()])
postcode = StringField('Post code', validators=[DataRequired()])
address_1 = StringField('Address line 1', validators=[DataRequired()])
address_2 = StringField('Address line 2 (optional)')
city = StringField('City', validators=[DataRequired()])
county = StringField('County', validators=[DataRequired()])
submit = SubmitField('Submit')
form_type_billing_not_delivery = False
def output_id(self):
return 'formBilling' if self.form_type_billing_not_delivery else 'formDeliver'
class Form_Is_Included_VAT(FlaskForm):
is_included = BooleanField('Include VAT')
class Form_Delivery_Region(FlaskForm):
id_id_region_delivery = 'id_region_delivery'
id_region_delivery = SelectField('Region', id='id_region_delivery')
class Form_Currency(FlaskForm):
id_id_currency = 'id_currency'
id_currency = SelectField('Currency', id='id_currency')
# Store
class Form_Supplier(FlaskForm):
id_id_supplier = 'id_supplier'
id_supplier = SelectField('Supplier', id='id_supplier')
name_company = StringField('Company name')
name_contact = StringField('Contact name')
department_contact = StringField('Contact department')
id_address = SelectField('Address ID')
phone_number = StringField('Phone number')
email = StringField('Email address')
fax = StringField('Fax number')
website = StringField('Website')
id_currency = SelectField('Currency ID')
is_active = BooleanField('Active', default = True)
# class Form_Supplier_Purchase_Order(FlaskForm):
# User
class Form_Filters_User(FlaskForm):
active = BooleanField('Active only?', default = True)
id_user = SelectField('User ID', validators=[Optional()], choices=[])

42
forms/unit_measurement.py Normal file
View File

@@ -0,0 +1,42 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Forms - Unit of Measurement Filters data input
Description:
Defines Flask-WTF forms for handling unit of measurement filter input.
"""
# internal
from business_objects.base import Base
from forms.base import Form_Base
import lib.argument_validation as av
# external
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, SubmitField, BooleanField, IntegerField, SelectField, FloatField
from wtforms.validators import InputRequired, NumberRange, Regexp, DataRequired, Optional
from flask_wtf.recaptcha import RecaptchaField
from abc import ABCMeta, abstractmethod
class Filters_Unit_Measurement(Form_Base):
active = BooleanField("Active only?", default = True)
@classmethod
def from_filters(cls, filters):
form = Filters_Unit_Measurement()
form.active.data = filters.active
return form
def __repr__(self):
return f'Filters_Unit_Measurement(active={self.active.data})'
@classmethod
def from_json(cls, json):
form = Filters_Unit_Measurement()
form.active.data = av.input_bool(json[Base.FLAG_ACTIVE], 'active', 'Filters_Unit_Measurement')
return form
def to_json(self):
return {
Base.FLAG_ACTIVE: av.input_bool(self.active.data, Base.FLAG_ACTIVE, f'{self.__class__.__name__}.to_json'),
}

11
helpers/__init__.py Normal file
View File

@@ -0,0 +1,11 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Module Initialisation
Feature: Helpers
Description:
Initialises helpers module.
"""

43
helpers/helper_app.py Normal file
View File

@@ -0,0 +1,43 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Helpers
Feature: App Helper
"""
# internal
# external
from pydantic import BaseModel, ConfigDict
from flask import current_app
# from flask_sqlalchemy import SQLAlchemy
class Helper_App(BaseModel):
@staticmethod
def get_request_data(request):
Helper_App.console_log(f'request={request}')
data = {}
try:
data = request.json
except:
try:
data = request.data
except:
try:
data = request.form
except:
pass
Helper_App.console_log(f'data={data}')
return data
@staticmethod
def console_log(message):
if current_app.app_config.is_development:
print(message)
elif current_app.app_config.is_production:
pass
current_app.logger.info(message)

View File

@@ -0,0 +1,42 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Helpers
Feature: MySQL Database Helper
Notes: This architecture does not work with Flask-SQLAlchemy - db connection must be initialised with Flask app initialisation
"""
# internal
# external
from pydantic import BaseModel, ConfigDict
from flask import Flask, render_template, jsonify, request, render_template_string, send_from_directory, redirect, url_for, session
from flask_sqlalchemy import SQLAlchemy
import uuid
class Helper_DB_MySQL(BaseModel):
app: Flask
model_config = ConfigDict(arbitrary_types_allowed=True)
def __init__(self, app):
super().__init__(app=app)
# self.app = app
def get_db_connection(self):
db = SQLAlchemy()
db.init_app(self.app)
with self.app.app_context():
db.create_all()
db.engine.url = self.app.config['SQLALCHEMY_DATABASE_URI']
return db
@staticmethod
def create_guid_str():
return str(uuid.uuid4())
@staticmethod
def create_guid():
return uuid.uuid4().bytes

11
lib/__init__.py Normal file
View File

@@ -0,0 +1,11 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Module Initialisation
Feature: Library
Description:
Initialises library module.
"""

1301
lib/argument_validation.py Normal file

File diff suppressed because it is too large Load Diff

37
lib/data_types.py Normal file
View File

@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
"""
Created on Thu Apr 27 12:33:59 2023
@author: Edward Middleton-Smith
Argument Validation
"""
# CLASSES
# ATTRIBUTE DECLARATION
# METHODS
# FUNCTION
# ARGUMENTS
# ARGUMENT VALIDATION
# ATTRIBUTE + VARIABLE INSTANTIATION
# METHODS
# RETURNS
# NORMAL METHODS
# FUNCTION
# ARGUMENTS
# ARGUMENT VALIDATION
# VARIABLE INSTANTIATION
# METHODS
# RETURNS
# IMPORTS
# CLASSES
# METHODS
def get_enum_member_by_text(enum_class, text):
for member in enum_class.__members__.values():
if member.name == text:
return member
raise ValueError(f'{text} is not in {enum_class}')

11
models/__init__.py Normal file
View File

@@ -0,0 +1,11 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Module Initialisation
Feature: Models
Description:
Initialises view data models module.
"""

View File

@@ -0,0 +1,24 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Legal View Models
Feature: Accessibility Report View Model
Description:
Data model for accessibility report view
"""
# internal
from models.model_view_base import Model_View_Base
# from routes import bp_home
# external
class Model_View_Accessibility_Report(Model_View_Base):
@property
def title(self):
return 'Accessibility Report'
def __init__(self, hash_page_current=Model_View_Base.HASH_PAGE_ACCESSIBILITY_REPORT):
super().__init__(hash_page_current=hash_page_current)

View File

@@ -0,0 +1,24 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Legal View Models
Feature: Accessibility Statement View Model
Description:
Data model for accessibility statement view
"""
# internal
from models.model_view_base import Model_View_Base
# from routes import bp_home
# external
class Model_View_Accessibility_Statement(Model_View_Base):
@property
def title(self):
return 'Accessibility Statement'
def __init__(self, hash_page_current=Model_View_Base.HASH_PAGE_ACCESSIBILITY_STATEMENT):
super().__init__(hash_page_current=hash_page_current)

263
models/model_view_base.py Normal file
View File

@@ -0,0 +1,263 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: View Models
Feature: Base View Model
Description:
Base data model for views
"""
# IMPORTS
# VARIABLE INSTANTIATION
# METHODS
# IMPORTS
# internal
# from routes import bp_home
from business_objects.base import Base
from business_objects.project_hub.user import User
from business_objects.project_hub.command import Command
from datastores.datastore_base import DataStore_Base
from datastores.project_hub.datastore_command import DataStore_Command
from forms.access_level import Filters_Access_Level
from forms.forms import Form_Is_Included_VAT, Form_Delivery_Region, Form_Currency
from forms.unit_measurement import Filters_Unit_Measurement
from helpers.helper_app import Helper_App
import lib.argument_validation as av
# external
from abc import ABC, abstractmethod
from flask_sqlalchemy import SQLAlchemy
from flask import Flask, session, current_app, jsonify
from pydantic import BaseModel, ConfigDict
from typing import ClassVar
class Model_View_Base(BaseModel, ABC):
ATTR_TEXT_COLLAPSED: ClassVar[str] = 'textCollapsed'
ATTR_TEXT_EXPANDED: ClassVar[str] = 'textExpanded'
ATTR_VALUE_CURRENT: ClassVar[str] = 'current-value'
ATTR_VALUE_PREVIOUS: ClassVar[str] = 'previous-value'
COMPANY_ADDRESS_SHORT: ClassVar[str] = '53 Alfred Green Close, Rugby, United Kingdom, CV22 6DN'
COMPANY_NUMBER: ClassVar[str] = '13587499'
ENDPOINT_GET_ALTCHA_CHALLENGE: ClassVar[str] = 'routes_core_contact.create_altcha_challenge'
ENDPOINT_PAGE_ACCESSIBILITY_REPORT: ClassVar[str] = 'routes_legal.accessibility_report'
ENDPOINT_PAGE_ACCESSIBILITY_STATEMENT: ClassVar[str] = 'routes_legal.accessibility_statement'
ENDPOINT_PAGE_CONTACT: ClassVar[str] = 'routes_core_contact.contact'
ENDPOINT_PAGE_CONTACT_SUCCESS: ClassVar[str] = 'routes_core_contact.contact_success'
ENDPOINT_PAGE_DATA_RETENTION_SCHEDULE: ClassVar[str] = 'routes_legal.retention_schedule'
ENDPOINT_PAGE_ERROR_NO_PERMISSION: ClassVar[str] = 'routes_core.error_no_permission'
ENDPOINT_PAGE_HOME: ClassVar[str] = 'routes_core_home.home'
ENDPOINT_PAGE_LICENSE: ClassVar[str] = 'routes_legal.license'
ENDPOINT_PAGE_PRIVACY_POLICY: ClassVar[str] = 'routes_legal.privacy_policy'
ENDPOINT_POST_COMMAND: ClassVar[str] = 'routes_core_contact.contact_post'
FLAG_ACTIVE: ClassVar[str] = Base.FLAG_ACTIVE
FLAG_ADD: ClassVar[str] = 'add'
# FLAG_ADD_DELETE: ClassVar[str] = 'add-delete'
FLAG_BOOL_FALSE: ClassVar[str] = 'false'
FLAG_BOOL_TRUE: ClassVar[str] = 'true'
FLAG_BUTTON: ClassVar[str] = 'button'
FLAG_BUTTON_LIGHT: ClassVar[str] = 'button-light'
FLAG_BUTTON_PRIMARY: ClassVar[str] = 'button-primary'
FLAG_CANCEL: ClassVar[str] = 'button-cancel'
FLAG_CALLBACK: ClassVar[str] = 'callback'
FLAG_CAPTCHA: ClassVar[str] = 'captcha'
FLAG_CARD: ClassVar[str] = 'card'
FLAG_CLOSE_TEMPORARY_ELEMENT: ClassVar[str] = 'button-temporary-element-close'
FLAG_CODE: ClassVar[str] = Base.FLAG_CODE
FLAG_COLLAPSED: ClassVar[str] = 'collapsed'
FLAG_COLLAPSIBLE: ClassVar[str] = 'collapsible'
FLAG_COLUMN: ClassVar[str] = 'column'
FLAG_COMMENT: ClassVar[str] = 'comment'
FLAG_CONTAINER: ClassVar[str] = 'container'
FLAG_CONTAINER_CHECKBOX: ClassVar[str] = 'container-checkbox'
FLAG_CONTAINER_ICON_AND_LABEL: ClassVar[str] = 'container-icon-label'
FLAG_CONTAINER_INPUT: ClassVar[str] = 'container-input'
FLAG_CSRF_TOKEN: ClassVar[str] = 'X-CSRFToken'
FLAG_DATA: ClassVar[str] = 'data'
FLAG_DATE_FROM: ClassVar[str] = Base.FLAG_DATE_FROM
FLAG_DATE_TO: ClassVar[str] = Base.FLAG_DATE_TO
FLAG_DELETE: ClassVar[str] = 'delete'
FLAG_DESCRIPTION: ClassVar[str] = Base.FLAG_DESCRIPTION
FLAG_DETAIL: ClassVar[str] = 'detail'
FLAG_DIALOG: ClassVar[str] = 'dialog'
FLAG_DIRTY: ClassVar[str] = 'dirty'
FLAG_DISPLAY_ORDER: ClassVar[str] = Base.FLAG_DISPLAY_ORDER
FLAG_EDIT: ClassVar[str] = 'edit'
FLAG_EMAIL: ClassVar[str] = Base.FLAG_EMAIL
FLAG_ERROR: ClassVar[str] = 'error'
FLAG_EXPANDED: ClassVar[str] = 'expanded'
FLAG_FAILURE: ClassVar[str] = 'failure'
FLAG_FILTER: ClassVar[str] = 'filter'
FLAG_FORM: ClassVar[str] = 'form'
FLAG_FORM_FILTERS: ClassVar[str] = 'form-filters'
FLAG_HAMBURGER: ClassVar[str] = 'hamburger'
FLAG_IMAGE_LOGO: ClassVar[str] = 'image-logo'
FLAG_INITIALISED: ClassVar[str] = 'initialised'
FLAG_LEFT_HAND_STUB: ClassVar[str] = 'lhs'
FLAG_LOGO: ClassVar[str] = 'logo'
FLAG_MESSAGE: ClassVar[str] = Command.FLAG_MESSAGE
FLAG_MODAL: ClassVar[str] = 'modal'
FLAG_NAME: ClassVar[str] = Base.FLAG_NAME
FLAG_NAME_ATTR_OPTION_TEXT: ClassVar[str] = Base.FLAG_NAME_ATTR_OPTION_TEXT
FLAG_NAME_ATTR_OPTION_VALUE: ClassVar[str] = Base.FLAG_NAME_ATTR_OPTION_VALUE
FLAG_NAME_PLURAL: ClassVar[str] = Base.FLAG_NAME_PLURAL
# FLAG_NAME_SINGULAR: ClassVar[str] = Base.FLAG_NAME_SINGULAR
FLAG_NAV_CONTACT: ClassVar[str] = 'navContact'
FLAG_NAV_HOME: ClassVar[str] = 'navHome'
FLAG_OVERLAY: ClassVar[str] = 'overlay'
FLAG_PAGE_BODY: ClassVar[str] = 'page-body'
FLAG_RIGHT_HAND_SIDE: ClassVar[str] = 'rhs'
FLAG_ROW: ClassVar[str] = 'row'
FLAG_ROW_NEW: ClassVar[str] = 'row-new'
FLAG_ROWS: ClassVar[str] = Base.FLAG_ROWS
FLAG_SAVE: ClassVar[str] = 'save'
FLAG_SCROLLABLE: ClassVar[str] = 'scrollable'
FLAG_SLIDER: ClassVar[str] = 'slider'
FLAG_STATUS: ClassVar[str] = 'status'
FLAG_SUBMIT: ClassVar[str] = 'submit'
FLAG_SUCCESS: ClassVar[str] = 'success'
FLAG_TEMPORARY_ELEMENT: ClassVar[str] = 'temporary-element'
FLAG_USER: ClassVar[str] = User.FLAG_USER
FLAG_WEBSITE: ClassVar[str] = Base.FLAG_WEBSITE
HASH_GET_ALTCHA_CHALLENGE: ClassVar[str] = '/altcha/create-challenge'
HASH_PAGE_ACCESSIBILITY_REPORT: ClassVar[str] = '/accessibility-report'
HASH_PAGE_ACCESSIBILITY_STATEMENT: ClassVar[str] = '/accessibility-statement'
HASH_PAGE_CONTACT: ClassVar[str] = '/contact'
HASH_PAGE_CONTACT_SUCCESS: ClassVar[str] = '/contact-success'
HASH_PAGE_DATA_RETENTION_SCHEDULE: ClassVar[str] = '/retention-schedule'
HASH_PAGE_ERROR_NO_PERMISSION: ClassVar[str] = '/error'
HASH_PAGE_HOME: ClassVar[str] = '/'
HASH_PAGE_LICENSE: ClassVar[str] = '/license'
HASH_PAGE_PRIVACY_POLICY: ClassVar[str] = '/privacy-policy'
ID_BUTTON_ADD: ClassVar[str] = 'buttonAdd'
ID_BUTTON_APPLY_FILTERS: ClassVar[str] = 'buttonApplyFilters'
ID_BUTTON_CANCEL: ClassVar[str] = 'buttonCancel'
ID_BUTTON_HAMBURGER: ClassVar[str] = 'buttonHamburger'
ID_BUTTON_SAVE: ClassVar[str] = 'buttonSave'
ID_CSRF_TOKEN: ClassVar[str] = 'X-CSRFToken'
ID_FORM_CONTACT: ClassVar[str] = 'formContact'
ID_FORM_FILTERS: ClassVar[str] = 'formFilters'
ID_LABEL_ERROR: ClassVar[str] = 'labelError'
ID_OVERLAY_CONFIRM: ClassVar[str] = 'overlayConfirm'
ID_OVERLAY_ERROR: ClassVar[str] = 'overlayError'
ID_OVERLAY_HAMBURGER: ClassVar[str] = 'overlayHamburger'
ID_PAGE_BODY: ClassVar[str] = 'pageBody'
ID_TABLE_MAIN: ClassVar[str] = 'tableMain'
ID_TEXTAREA_CONFIRM: ClassVar[str] = 'textareaConfirm'
NAME_COMPANY: ClassVar[str] = 'Precision And Research Technology Systems Limited'
NAME_COMPANY_SHORT: ClassVar[str] = 'PARTS Ltd'
NAME_CSRF_TOKEN: ClassVar[str] = 'csrf-token'
URL_GITHUB: ClassVar[str] = 'https://github.com/Teddy-1024'
URL_LINKEDIN: ClassVar[str] = 'https://uk.linkedin.com/in/teddyms'
hash_page_current: str
app: Flask = None
session: None = None
is_page_store: bool = None
is_user_logged_in: bool = None
user: User = None
access_levels: list = None
model_config = ConfigDict(arbitrary_types_allowed=True)
@property
@abstractmethod
def title(self):
pass
def __init__(self, hash_page_current, **kwargs):
BaseModel.__init__(self, hash_page_current=hash_page_current, **kwargs)
self.app = current_app
with self.app.app_context():
self.session = session
self.is_page_store = False
Helper_App.console_log(f'session: {self.session}')
datastore_base = DataStore_Base()
self.user = datastore_base.get_user_session()
self.is_user_logged_in = self.user.get_is_logged_in()
# Helper_App.console_log(f'model_view_base init end - model.user: {self.user}')
def output_bool(self, boolean):
return str(boolean).lower()
def get_url_host(self):
return self.app.config['URL_HOST']
def get_user_session(self):
datastore_user = DataStore_User()
return datastore_user.get_user_session()
def get_many_access_level(self, filters=None):
_m = 'Model_View_Store.get_many_access_level'
# av.val_instance(filters, 'filters', _m, Filters_Access_Level)
access_levels, errors = DataStore_Base.get_many_access_level(filters)
return access_levels
def get_many_unit_measurement(self, filters=None):
_m = 'Model_View_Store.get_many_unit_measurement'
# av.val_instance(filters, 'filters', _m, Filters_Unit_Measurement)
units_measurement, errors = DataStore_Base.get_many_unit_measurement(filters)
return units_measurement
@staticmethod
def convert_list_objects_to_json(list_objects):
return [obj.to_json() for obj in list_objects]
@staticmethod
def convert_list_objects_to_list_options(list_objects):
return Base.convert_list_objects_to_list_options(list_objects)
@staticmethod
def convert_list_objects_to_dict_by_attribute_key(list_objects, key):
return {getattr(obj, key): obj for obj in list_objects}
@staticmethod
def convert_list_objects_to_dict_json_by_attribute_key(list_objects, key):
return {getattr(obj, key): obj.to_json() for obj in list_objects}
@staticmethod
def convert_list_objects_to_dict_by_attribute_key_default(list_objects):
if list_objects is None or len(list_objects) == 0:
return {}
obj_class = list_objects[0].__class__
return Model_View_Base.convert_list_objects_to_dict_by_attribute_key(list_objects, getattr(obj_class, obj_class.FLAG_NAME_ATTR_OPTION_VALUE))
@staticmethod
def convert_list_objects_to_dict_json_by_attribute_key_default(list_objects):
if list_objects is None or len(list_objects) == 0:
return {}
obj_class = list_objects[0].__class__
return Model_View_Base.convert_list_objects_to_dict_json_by_attribute_key(list_objects, getattr(obj_class, obj_class.FLAG_NAME_ATTR_OPTION_VALUE))
@staticmethod
def convert_dict_values_to_json(dict):
return {key: dict[key].to_json() for key in dict.keys()}
@staticmethod
def convert_list_objects_to_preview_str(list_objects):
preview_str = ''
for obj in list_objects:
if preview_str != '':
preview_str += '\n'
obj_json = obj.to_json()
preview_str += obj_json[obj_json[Base.FLAG_NAME_ATTR_OPTION_TEXT]]
return preview_str
@staticmethod
def join_with_linebreaks(strs):
str_multiline = ''
for str in strs:
if str_multiline != '':
str_multiline += '\n'
str_multiline += str
return str_multiline
@staticmethod
def format_date(date):
if date is None:
return ''
return date.strftime('%Y-%m-%d')
@staticmethod
def format_datetime(date_time):
if date_time is None:
return ''
return date_time.strftime('%Y-%m-%dT%H:%M')
@staticmethod
def jsonify(data):
return jsonify(data)
def get_mail_contact_public(self):
return self.app.config['MAIL_CONTACT_PUBLIC']

View File

@@ -0,0 +1,35 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: View Models
Feature: Contact View Model
Description:
Data model for contact view
"""
# internal
from business_objects.project_hub.command import Command
from models.model_view_base import Model_View_Base
# from routes import bp_home
from lib import argument_validation as av
from forms.contact import Form_Contact
# external
from flask_wtf import FlaskForm
from abc import abstractproperty
from pydantic import BaseModel
from typing import ClassVar
class Model_View_Contact(Model_View_Base):
form_contact: Form_Contact
@property
def title(self):
return 'Contact'
def __init__(self, form_contact, hash_page_current=Model_View_Base.HASH_PAGE_CONTACT, **kwargs):
super().__init__(hash_page_current=hash_page_current, form_contact=form_contact, **kwargs)
# self.form = form

View File

@@ -0,0 +1,31 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: View Models
Feature: Contact View Model
Description:
Data model for contact view
"""
# internal
from models.model_view_base import Model_View_Base
# from routes import bp_home
from lib import argument_validation as av
# from forms.contact import Form_Contact
# external
from flask_wtf import FlaskForm
from abc import abstractproperty
from pydantic import BaseModel
from typing import ClassVar
class Model_View_Contact_Success(Model_View_Base):
@property
def title(self):
return 'Contact Success'
def __init__(self, hash_page_current=Model_View_Base.HASH_PAGE_CONTACT_SUCCESS, **kwargs):
super().__init__(hash_page_current=hash_page_current, **kwargs)

24
models/model_view_home.py Normal file
View File

@@ -0,0 +1,24 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Core View Models
Feature: Home View Model
Description:
Data model for home view
"""
# internal
from models.model_view_base import Model_View_Base
# from routes import bp_home
# external
class Model_View_Home(Model_View_Base):
@property
def title(self):
return 'Home'
def __init__(self, hash_page_current=Model_View_Base.HASH_PAGE_HOME):
super().__init__(hash_page_current=hash_page_current)

View File

@@ -0,0 +1,24 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Legal View Models
Feature: License View Model
Description:
Data model for license view
"""
# internal
from models.model_view_base import Model_View_Base
# from routes import bp_home
# external
class Model_View_License(Model_View_Base):
@property
def title(self):
return 'License'
def __init__(self, hash_page_current=Model_View_Base.HASH_PAGE_LICENSE):
super().__init__(hash_page_current=hash_page_current)

View File

@@ -0,0 +1,24 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Legal View Models
Feature: Privacy Policy View Model
Description:
Data model for privacy policy view
"""
# internal
from models.model_view_base import Model_View_Base
# from routes import bp_home
# external
class Model_View_Privacy_Policy(Model_View_Base):
@property
def title(self):
return 'Privacy Policy'
def __init__(self, hash_page_current=Model_View_Base.HASH_PAGE_PRIVACY_POLICY):
super().__init__(hash_page_current=hash_page_current)

View File

@@ -0,0 +1,24 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Legal View Models
Feature: Retention Schedule View Model
Description:
Data model for retention schedule view
"""
# internal
from models.model_view_base import Model_View_Base
# from routes import bp_home
# external
class Model_View_Retention_Schedule(Model_View_Base):
@property
def title(self):
return 'Retention Schedule'
def __init__(self, hash_page_current=Model_View_Base.HASH_PAGE_DATA_RETENTION_SCHEDULE):
super().__init__(hash_page_current=hash_page_current)

4296
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

40
package.json Normal file
View File

@@ -0,0 +1,40 @@
{
"name": "app",
"version": "1.0.0",
"description": "Precision and Research Technology Systems Limited\r Website with online store",
"main": "webpack.config.js",
"directories": {
"lib": "lib"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode production",
"watch": "webpack --mode development --watch"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Teddy-1024/parts_website.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/Teddy-1024/parts_website/issues"
},
"homepage": "https://github.com/Teddy-1024/parts_website#readme",
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/preset-env": "^7.25.4",
"babel-loader": "^9.1.3",
"css-loader": "^7.1.2",
"glob": "^11.0.0",
"mini-css-extract-plugin": "^2.9.1",
"style-loader": "^4.0.0",
"webpack": "^5.94.0",
"webpack-cli": "^5.1.4"
},
"dependencies": {
"aos": "^2.3.4"
}
}

BIN
packages-microsoft-prod.deb Normal file

Binary file not shown.

15
passenger_wsgi.py Normal file
View File

@@ -0,0 +1,15 @@
import os
import sys
sys.path.insert(0, os.path.dirname(__file__))
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
message = 'It works!\n'
version = 'Python %s\n' % sys.version.split()[0]
response = '\n'.join([message, version])
return [response.encode()]
from app import app as application # Import the Flask application from app.py

18
requirements.txt Normal file
View File

@@ -0,0 +1,18 @@
Flask==3.0.3
gunicorn==23.0.0
flask_wtf
flask_sqlalchemy
flask_cors
flask_mail
authlib
jwt
mysqlclient
stripe
python_dotenv
authlib
pydantic
# psycopg2
requests
cryptography
altcha

3
robots.txt Normal file
View File

@@ -0,0 +1,3 @@
User-agent: *
Disallow: /qa
Disallow: /dev

54
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 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)
"""

17
run.py Normal file
View File

@@ -0,0 +1,17 @@
"""
Project: PARTS Website
Author: Edward Middleton-Smith
Precision And Research Technology Systems Limited
Technology: Backend
Feature: Launcher
Description:
Runs project.
"""
from app import app
if __name__ == '__main__':
app.run(debug=True)
# app.run(debug=True, host="0.0.0.0", port=5000)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
CREATE DATABASE IF NOT EXISTS parts;
GRANT ALL PRIVILEGES ON parts.* TO 'teddy'@'%';
FLUSH PRIVILEGES;
USE parts;
-- Permanent Temp Tables
DROP TABLE IF EXISTS parts.tmp_DOG_Calc_User;
-- DROP TABLE IF EXISTS parts.tmp_core_Msg_Error;
DROP TABLE IF EXISTS parts.tmp_DOG_User;
DROP TABLE IF EXISTS parts.tmp_DOG_User_Role_Link;
-- Permanent Tables
DROP TABLE IF EXISTS parts.DOG_Dog_Drive_Link_Temp;
DROP TABLE IF EXISTS parts.DOG_Dog_Drive_Link_Audit;
DROP TABLE IF EXISTS parts.DOG_Dog_Drive_Link;
DROP TABLE IF EXISTS parts.DOG_Drive_Temp;
DROP TABLE IF EXISTS parts.DOG_Drive_Audit;
DROP TABLE IF EXISTS parts.DOG_Drive;
DROP TABLE IF EXISTS parts.DOG_Command_Button_Link_Temp;
DROP TABLE IF EXISTS parts.DOG_Command_Button_Link_Audit;
DROP TABLE IF EXISTS parts.DOG_Command_Button_Link;
DROP TABLE IF EXISTS parts.DOG_Button_Icon_Temp;
DROP TABLE IF EXISTS parts.DOG_Button_Icon_Audit;
DROP TABLE IF EXISTS parts.DOG_Button_Icon;
DROP TABLE IF EXISTS parts.DOG_Image_Temp;
DROP TABLE IF EXISTS parts.DOG_Image_Audit;
DROP TABLE IF EXISTS parts.DOG_Image;
DROP TABLE IF EXISTS parts.DOG_Button_Shape_Temp;
DROP TABLE IF EXISTS parts.DOG_Button_Shape_Audit;
DROP TABLE IF EXISTS parts.DOG_Button_Shape;
DROP TABLE IF EXISTS parts.DOG_Colour_Temp;
DROP TABLE IF EXISTS parts.DOG_Colour_Audit;
DROP TABLE IF EXISTS parts.DOG_Colour;
DROP TABLE IF EXISTS parts.DOG_Location_Link_Temp;
DROP TABLE IF EXISTS parts.DOG_Location_Link_Audit;
DROP TABLE IF EXISTS parts.DOG_Location_Link;
DROP TABLE IF EXISTS parts.DOG_Location_Temp;
DROP TABLE IF EXISTS parts.DOG_Location_Audit;
DROP TABLE IF EXISTS parts.DOG_Location;
DROP TABLE IF EXISTS parts.DOG_Personal_Best_Temp;
DROP TABLE IF EXISTS parts.DOG_Personal_Best_Audit;
DROP TABLE IF EXISTS parts.DOG_Personal_Best;
DROP TABLE IF EXISTS parts.DOG_Dog_Command_Link_Temp;
DROP TABLE IF EXISTS parts.DOG_Dog_Command_Link_Audit;
DROP TABLE IF EXISTS parts.DOG_Dog_Command_Link;
DROP TABLE IF EXISTS parts.DOG_Command_Temp;
DROP TABLE IF EXISTS parts.DOG_Command_Audit;
DROP TABLE IF EXISTS parts.DOG_Command;
DROP TABLE IF EXISTS parts.DOG_Command_Category_Temp;
DROP TABLE IF EXISTS parts.DOG_Command_Category_Audit;
DROP TABLE IF EXISTS parts.DOG_Command_Category;
DROP TABLE IF EXISTS parts.DOG_Obedience_Level_Temp;
DROP TABLE IF EXISTS parts.DOG_Obedience_Level_Audit;
DROP TABLE IF EXISTS parts.DOG_Obedience_Level;
DROP TABLE IF EXISTS parts.DOG_Understanding_Level_Temp;
DROP TABLE IF EXISTS parts.DOG_Understanding_Level_Audit;
DROP TABLE IF EXISTS parts.DOG_Understanding_Level;
DROP TABLE IF EXISTS parts.DOG_Competency_Level_Temp;
DROP TABLE IF EXISTS parts.DOG_Competency_Level_Audit;
DROP TABLE IF EXISTS parts.DOG_Competency_Level;
DROP TABLE IF EXISTS parts.DOG_Dog_Breed_Link_Temp;
DROP TABLE IF EXISTS parts.DOG_Dog_Breed_Link_Audit;
DROP TABLE IF EXISTS parts.DOG_Dog_Breed_Link;
DROP TABLE IF EXISTS parts.DOG_Breed_Temp;
DROP TABLE IF EXISTS parts.DOG_Breed_Audit;
DROP TABLE IF EXISTS parts.DOG_Breed;
DROP TABLE IF EXISTS parts.DOG_Dog_Temp;
DROP TABLE IF EXISTS parts.DOG_Dog_Audit;
DROP TABLE IF EXISTS parts.DOG_Dog;
DROP TABLE IF EXISTS parts.DOG_Dog_Change_Set;
DROP TABLE IF EXISTS parts.DOG_Calc_User_Temp;
DROP TABLE IF EXISTS parts.DOG_User_Role_Link_Audit;
DROP TABLE IF EXISTS parts.DOG_User_Role_Link;
DROP TABLE IF EXISTS parts.DOG_Role_Permission_Link_Audit;
DROP TABLE IF EXISTS parts.DOG_Role_Permission_Link;
DROP TABLE IF EXISTS parts.DOG_Role_Audit;
DROP TABLE IF EXISTS parts.DOG_Role;
DROP TABLE IF EXISTS parts.DOG_User_Temp;
DROP TABLE IF EXISTS parts.DOG_User_Audit;
DROP TABLE IF EXISTS parts.DOG_User;
DROP TABLE IF EXISTS parts.DOG_Permission_Audit;
DROP TABLE IF EXISTS parts.DOG_Permission;
DROP TABLE IF EXISTS parts.DOG_Permission_Group_Audit;
DROP TABLE IF EXISTS parts.DOG_Permission_Group;
DROP TABLE IF EXISTS parts.DOG_Access_Level_Audit;
DROP TABLE IF EXISTS parts.DOG_Access_Level;
DROP TABLE IF EXISTS parts.DOG_User_Change_Set;
/*
DROP TABLE IF EXISTS parts.CORE_Split_Key_Value_Pair_Csv_Temp;
DROP TABLE IF EXISTS parts.CORE_Split_Temp;
*/
DROP TABLE IF EXISTS parts.CORE_File_Type;
DROP TABLE IF EXISTS parts.CORE_Msg_Error_Type;
-- Stored Procedures
DROP PROCEDURE IF EXISTS parts.p_DOG_test_get_many_dog_command;
DROP PROCEDURE IF EXISTS parts.p_DOG_test_get_many_command;
DROP PROCEDURE IF EXISTS parts.p_DOG_get_many_command;
DROP PROCEDURE IF EXISTS parts.p_DOG_test_save_command;
DROP PROCEDURE IF EXISTS parts.p_DOG_save_command;
DROP PROCEDURE IF EXISTS parts.p_DOG_clear_calc_user;
DROP PROCEDURE IF EXISTS parts.p_DOG_calc_user;
/*
DROP PROCEDURE IF EXISTS parts.p_core_clear_split_key_value_pair_csv;
DROP PROCEDURE IF EXISTS parts.p_core_split_key_value_pair_csv;
DROP PROCEDURE IF EXISTS parts.p_core_clear_split;
DROP PROCEDURE IF EXISTS parts.p_core_split;
DROP PROCEDURE IF EXISTS parts.p_clear_split_key_value_pair_csv;
DROP PROCEDURE IF EXISTS parts.p_split_key_value_pair_csv;
DROP PROCEDURE IF EXISTS parts.p_clear_split;
DROP PROCEDURE IF EXISTS parts.p_split;
DROP PROCEDURE IF EXISTS parts.p_core_debug_timing_reporting;
DROP PROCEDURE IF EXISTS parts.p_debug_timing_reporting;
DROP PROCEDURE IF EXISTS parts.p_core_validate_guid;
DROP PROCEDURE IF EXISTS parts.p_core_validate_guid_test;
*/

View File

@@ -0,0 +1,19 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'CORE_Msg_Error_Type'
;
CREATE TABLE IF NOT EXISTS parts.CORE_Msg_Error_Type (
id_type INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, code VARCHAR(100) NOT NULL
, name VARCHAR(500) NOT NULL
, description VARCHAR(1000)
, is_breaking_error BIT NOT NULL
, background_colour VARCHAR(32) NOT NULL DEFAULT '#111111'
, text_colour VARCHAR(32) NOT NULL DEFAULT '#110000'
);

View File

@@ -0,0 +1,17 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'CORE_File_Type'
;
CREATE TABLE IF NOT EXISTS parts.CORE_File_Type (
id_file_type INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, code VARCHAR(100) NOT NULL
, name VARCHAR(250) NOT NULL
, is_image BIT NOT NULL DEFAULT 0
, active BIT NOT NULL DEFAULT 1
);

View File

@@ -0,0 +1,15 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'CORE_Split_Temp'
;
CREATE TABLE IF NOT EXISTS parts.CORE_Split_Temp (
guid BINARY(36) NOT NULL
, display_order INT NOT NULL
, substring VARCHAR(4000) NOT NULL
);

View File

@@ -0,0 +1,16 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'CORE_Split_Key_Value_Pair_Csv_Temp'
;
CREATE TABLE IF NOT EXISTS parts.CORE_Split_Key_Value_Pair_Csv_Temp (
guid BINARY(36) NOT NULL
, id INT NOT NULL
, key_column VARCHAR(4000) NULL
, value_column VARCHAR(4000) NULL
);

View File

@@ -0,0 +1,16 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_User_Change_Set'
;
CREATE TABLE IF NOT EXISTS parts.DOG_User_Change_Set (
id_change_set INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, comment VARCHAR(500)
, updated_last_on DATETIME
, id_user_updated_last_by INT
);

View File

@@ -0,0 +1,20 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Access_Level'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Access_Level (
id_access_level INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, code VARCHAR(100)
, name VARCHAR(250)
, priority INT NOT NULL
, display_order INT NOT NULL
, active BIT NOT NULL DEFAULT 1
);

View File

@@ -0,0 +1,17 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Permission_Group'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Permission_Group (
id_group INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, code VARCHAR(100)
, name VARCHAR(250)
, display_order INT NOT NULL
, active BIT NOT NULL DEFAULT 1
);

View File

@@ -0,0 +1,25 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Permission'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Permission (
id_permission INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, code VARCHAR(100)
, name VARCHAR(250)
, id_permission_group INT NOT NULL
, CONSTRAINT FK_DOG_Permission_id_permission_group
FOREIGN KEY (id_permission_group)
REFERENCES parts.DOG_Permission_Group(id_group)
, id_access_level_required INT NOT NULL
, CONSTRAINT FK_DOG_Permission_id_access_level_required
FOREIGN KEY (id_access_level_required)
REFERENCES parts.DOG_Access_Level(id_access_level)
, display_order INT NOT NULL
, active BIT NOT NULL DEFAULT 1
);

View File

@@ -0,0 +1,29 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_User'
;
CREATE TABLE IF NOT EXISTS parts.DOG_User (
id_user INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_user_auth0 VARCHAR(200)
, firstname VARCHAR(250)
, surname VARCHAR(250)
, email VARCHAR(254)
, is_email_verified BIT NOT NULL DEFAULT 0
, is_super_user BIT NOT NULL DEFAULT 0
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_User_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_User_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_User_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,24 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_User_Audit'
;
CREATE TABLE IF NOT EXISTS parts.DOG_User_Audit (
id_audit INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_user INT NOT NULL
, CONSTRAINT FK_DOG_User_Audit_id_user
FOREIGN KEY (id_user)
REFERENCES parts.DOG_User(id_user)
, name_field VARCHAR(100) NOT NULL
, value_prev VARCHAR(500)
, value_new VARCHAR(500)
, id_change_set INT NOT NULL
, CONSTRAINT FK_DOG_User_Audit_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_User_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,22 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_User_Temp'
;
CREATE TABLE IF NOT EXISTS parts.DOG_User_Temp (
id_temp INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_user INT NOT NULL
, id_user_auth0 VARCHAR(200)
, firstname VARCHAR(250)
, surname VARCHAR(250)
, email VARCHAR(254)
, is_email_verified BIT
, is_super_user BIT
, active BIT
, guid BINARY(36) NOT NULL
);

View File

@@ -0,0 +1,26 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Role'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Role (
id_role INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, code VARCHAR(100)
, name VARCHAR(250)
, display_order INT NOT NULL
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_Role_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_Role_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_User_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,24 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Role_Audit'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Role_Audit (
id_audit INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_role INT NOT NULL
, CONSTRAINT FK_DOG_Role_Audit_id_role
FOREIGN KEY (id_role)
REFERENCES parts.DOG_Role(id_role)
, name_field VARCHAR(100) NOT NULL
, value_prev VARCHAR(500)
, value_new VARCHAR(500)
, id_change_set INT NOT NULL
, CONSTRAINT FK_DOG_Role_Audit_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_User_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,35 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Role_Permission_Link'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Role_Permission_Link (
id_link INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_role INT NOT NULL
, CONSTRAINT FK_DOG_Role_Permission_Link_id_role
FOREIGN KEY (id_role)
REFERENCES parts.DOG_Role(id_role)
, id_permission INT NOT NULL
, CONSTRAINT FK_DOG_Role_Permission_Link_id_permission
FOREIGN KEY (id_permission)
REFERENCES parts.DOG_Permission(id_permission)
, id_access_level INT NOT NULL
, CONSTRAINT FK_DOG_Role_Permission_Link_id_access_level
FOREIGN KEY (id_access_level)
REFERENCES parts.DOG_Access_Level(id_access_level)
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_Role_Permission_Link_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_Role_Permission_Link_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_User_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,23 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Role_Permission_Link_Audit';
CREATE TABLE IF NOT EXISTS parts.DOG_Role_Permission_Link_Audit (
id_audit INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_link INT NOT NULL
, CONSTRAINT FK_DOG_Role_Permission_Link_Audit_id_link
FOREIGN KEY (id_link)
REFERENCES parts.DOG_Role_Permission_Link(id_link)
, name_field VARCHAR(100) NOT NULL
, value_prev VARCHAR(500)
, value_new VARCHAR(500)
, id_change_set INT NOT NULL
, CONSTRAINT FK_DOG_Role_Permission_Link_Audit_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_User_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,31 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_User_Role_Link'
;
CREATE TABLE IF NOT EXISTS parts.DOG_User_Role_Link (
id_link INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_user INT NOT NULL
, CONSTRAINT FK_DOG_User_Role_Link_id_user
FOREIGN KEY (id_user)
REFERENCES parts.DOG_User(id_user)
, id_role INT NOT NULL
, CONSTRAINT FK_DOG_User_Role_Link_id_role
FOREIGN KEY (id_role)
REFERENCES parts.DOG_Role(id_role)
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_User_Role_Link_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_User_Role_Link_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_User_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,24 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_User_Role_Link_Audit'
;
CREATE TABLE IF NOT EXISTS parts.DOG_User_Role_Link_Audit (
id_audit INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_link INT NOT NULL
, CONSTRAINT FK_DOG_User_Role_Link_Audit_id_link
FOREIGN KEY (id_link)
REFERENCES parts.DOG_User_Role_Link(id_link)
, name_field VARCHAR(100) NOT NULL
, value_prev VARCHAR(500)
, value_new VARCHAR(500)
, id_change_set INT NOT NULL
, CONSTRAINT FK_DOG_User_Role_Link_Audit_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_User_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,22 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Calc_User_Temp'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Calc_User_Temp (
guid BINARY(36) NOT NULL
, id_user INT
, id_permission_required INT NOT NULL
, CONSTRAINT FK_DOG_Calc_User_Temp_id_permission_required
FOREIGN KEY (id_permission_required)
REFERENCES parts.DOG_Permission (id_permission)
, priority_access_level_required INT NOT NULL
, is_super_user BIT
, priority_access_level_user INT
, has_access BIT
);

View File

@@ -0,0 +1,19 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Dog_Change_Set'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Dog_Change_Set (
id_change_set INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, comment VARCHAR(500)
, updated_last_on DATETIME
, id_user_updated_last_by INT
, CONSTRAINT FK_DOG_Role_id_user_updated_last_by
FOREIGN KEY (id_user_updated_last_by)
REFERENCES parts.DOG_User(id_user)
);

View File

@@ -0,0 +1,27 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Dog'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Dog (
id_dog INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, name VARCHAR(250) NOT NULL
, appearance VARCHAR(1000)
, mass_kg DECIMAL(7, 3)
, notes TEXT
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_Dog_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_Dog_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,24 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Dog_Audit'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Dog_Audit (
id_audit INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_dog INT NOT NULL
, CONSTRAINT FK_DOG_Dog_Audit_id_dog
FOREIGN KEY (id_dog)
REFERENCES parts.DOG_Dog(id_dog)
, name_field VARCHAR(100) NOT NULL
, value_prev TEXT
, value_new TEXT
, id_change_set INT NOT NULL
, CONSTRAINT FK_DOG_Dog_Audit_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,20 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Dog_Temp'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Dog_Temp (
id_temp INT NOT NULL PRIMARY KEY AUTO_INCREMENT
, id_dog INT
, name VARCHAR(250)
, appearance VARCHAR(1000)
, mass_kg DECIMAL(7, 3)
, notes TEXT
, active BIT
, guid BINARY(36)
);

View File

@@ -0,0 +1,25 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Breed'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Breed (
id_breed INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, code VARCHAR(100) NOT NULL
, name VARCHAR(250) NOT NULL
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_Breed_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_Breed_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,24 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Breed_Audit'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Breed_Audit (
id_audit INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_breed INT NOT NULL
, CONSTRAINT FK_DOG_Breed_Audit_id_breed
FOREIGN KEY (id_breed)
REFERENCES parts.DOG_Breed(id_breed)
, name_field VARCHAR(100) NOT NULL
, value_prev TEXT
, value_new TEXT
, id_change_set INT NOT NULL
, CONSTRAINT FK_DOG_Breed_Audit_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,18 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Breed_Temp'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Breed_Temp (
id_temp INT NOT NULL PRIMARY KEY AUTO_INCREMENT
, id_breed INT
, code VARCHAR(100)
, name VARCHAR(250)
, active BIT
, guid BINARY(36)
);

View File

@@ -0,0 +1,32 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Dog_Breed_Link'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Dog_Breed_Link (
id_link INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_dog INT NOT NULL
, CONSTRAINT FK_DOG_Dog_Breed_Link_id_dog
FOREIGN KEY (id_dog)
REFERENCES parts.DOG_Dog(id_dog)
, id_breed INT NOT NULL
, CONSTRAINT FK_DOG_Dog_Breed_Link_id_breed
FOREIGN KEY (id_breed)
REFERENCES parts.DOG_Breed(id_breed)
, lineage_ratio DECIMAL(5, 4) NOT NULL
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_Dog_Breed_Link_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_Dog_Breed_Link_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,24 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Dog_Breed_Link_Audit'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Dog_Breed_Link_Audit (
id_audit INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_link INT NOT NULL
, CONSTRAINT FK_DOG_Dog_Breed_Link_Audit_id_link
FOREIGN KEY (id_link)
REFERENCES parts.DOG_Dog_Breed_Link(id_link)
, name_field VARCHAR(100) NOT NULL
, value_prev TEXT
, value_new TEXT
, id_change_set INT NOT NULL
, CONSTRAINT FK_DOG_Dog_Breed_Link_Audit_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,19 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Dog_Breed_Link_Temp'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Dog_Breed_Link_Temp (
id_temp INT NOT NULL PRIMARY KEY AUTO_INCREMENT
, id_link INT
, id_dog INT
, id_breed INT
, lineage_ratio DECIMAL(5, 4)
, active BIT
, guid BINARY(36)
);

View File

@@ -0,0 +1,25 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Understanding_Level'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Understanding_Level (
id_understanding_level INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, code VARCHAR(100) NOT NULL
, name VARCHAR(250) NOT NULL
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_Understanding_Level_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_Understanding_Level_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,24 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Understanding_Level_Audit'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Understanding_Level_Audit (
id_audit INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_understanding_level INT NOT NULL
, CONSTRAINT FK_DOG_Understanding_Level_Audit_id_understanding_level
FOREIGN KEY (id_understanding_level)
REFERENCES parts.DOG_Understanding_Level(id_understanding_level)
, name_field VARCHAR(100) NOT NULL
, value_prev TEXT
, value_new TEXT
, id_change_set INT NOT NULL
, CONSTRAINT FK_DOG_Understanding_Level_Audit_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,18 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Understanding_Level_Temp'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Understanding_Level_Temp (
id_temp INT NOT NULL PRIMARY KEY AUTO_INCREMENT
, id_understanding_level INT
, code VARCHAR(100)
, name VARCHAR(250)
, active BIT
, guid BINARY(36)
);

View File

@@ -0,0 +1,25 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Obedience_Level'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Obedience_Level (
id_obedience_level INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, code VARCHAR(100) NOT NULL
, name VARCHAR(250) NOT NULL
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_Obedience_Level_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_Obedience_Level_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,24 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Obedience_Level_Audit'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Obedience_Level_Audit (
id_audit INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_obedience_level INT NOT NULL
, CONSTRAINT FK_DOG_Obedience_Level_Audit_id_obedience_level
FOREIGN KEY (id_obedience_level)
REFERENCES parts.DOG_Obedience_Level(id_obedience_level)
, name_field VARCHAR(100) NOT NULL
, value_prev TEXT
, value_new TEXT
, id_change_set INT NOT NULL
, CONSTRAINT FK_DOG_Obedience_Level_Audit_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,18 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Obedience_Level_Temp'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Obedience_Level_Temp (
id_temp INT NOT NULL PRIMARY KEY AUTO_INCREMENT
, id_obedience_level INT
, code VARCHAR(100)
, name VARCHAR(250)
, active BIT
, guid BINARY(36)
);

View File

@@ -0,0 +1,25 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Command_Category'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Command_Category (
id_command_category INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, code VARCHAR(100) NOT NULL
, name VARCHAR(250) NOT NULL
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_Command_Category_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_Command_Category_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,24 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Command_Category_Audit'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Command_Category_Audit (
id_audit INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_command_category INT NOT NULL
, CONSTRAINT FK_DOG_Command_Category_Audit_id_command_category
FOREIGN KEY (id_command_category)
REFERENCES parts.DOG_Command_Category(id_command_category)
, name_field VARCHAR(100) NOT NULL
, value_prev TEXT
, value_new TEXT
, id_change_set INT NOT NULL
, CONSTRAINT FK_DOG_Command_Category_Audit_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,18 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Command_Category_Temp'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Command_Category_Temp (
id_temp INT NOT NULL PRIMARY KEY AUTO_INCREMENT
, id_command_category INT
, code VARCHAR(100)
, name VARCHAR(250)
, active BIT
, guid BINARY(36)
);

View File

@@ -0,0 +1,31 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Command'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Command (
id_command INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_command_category INT NOT NULL
, CONSTRAINT FK_DOG_Command_id_command_category
FOREIGN KEY (id_command_category)
REFERENCES parts.DOG_Command_Category(id_command_category)
, name VARCHAR(250) NOT NULL
, hand_signal_default_description TEXT
, can_have_button BIT NOT NULL DEFAULT 0
, notes TEXT
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_Command_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_Command_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,24 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Command_Audit'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Command_Audit (
id_audit INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_command INT NOT NULL
, CONSTRAINT FK_DOG_Command_Audit_id_command
FOREIGN KEY (id_command)
REFERENCES parts.DOG_Command(id_command)
, name_field VARCHAR(100) NOT NULL
, value_prev TEXT
, value_new TEXT
, id_change_set INT NOT NULL
, CONSTRAINT FK_DOG_Command_Audit_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

View File

@@ -0,0 +1,23 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Command_Temp'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Command_Temp (
id_temp INT NOT NULL PRIMARY KEY AUTO_INCREMENT
, id_command INT
, id_command_category INT
, name VARCHAR(250)
, hand_signal_default_description TEXT
, can_have_button BIT
, notes TEXT
, active BIT
, guid BINARY(36)
, name_command_category VARCHAR(250)
);

View File

@@ -0,0 +1,42 @@
USE parts;
SELECT CONCAT('WARNING: Table ', TABLE_SCHEMA, '.', TABLE_NAME, ' already exists.') AS msg_warning
FROM INFORMATION_SCHEMA.TABLES
WHERE
TABLE_SCHEMA = 'parts'
AND TABLE_NAME = 'DOG_Dog_Command_Link'
;
CREATE TABLE IF NOT EXISTS parts.DOG_Dog_Command_Link (
id_link INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, id_dog INT NOT NULL
, CONSTRAINT FK_DOG_Dog_Command_Link_id_dog
FOREIGN KEY (id_dog)
REFERENCES parts.DOG_Dog(id_dog)
, id_command INT NOT NULL
, CONSTRAINT FK_DOG_Dog_Command_Link_id_command
FOREIGN KEY (id_command)
REFERENCES parts.DOG_Command(id_command)
, id_understanding_level INT NOT NULL
, CONSTRAINT FK_DOG_Dog_Command_Link_id_understanding_level
FOREIGN KEY (id_understanding_level)
REFERENCES parts.DOG_Understanding_Level(id_understanding_level)
, id_obedience_level INT NOT NULL
, CONSTRAINT FK_DOG_Dog_Command_Link_id_obedience_level
FOREIGN KEY (id_obedience_level)
REFERENCES parts.DOG_Obedience_Level(id_obedience_level)
, hand_signal_description TEXT
, notes TEXT
-- , has_button BIT NOT NULL DEFAULT 0
, active BIT NOT NULL DEFAULT 1
, created_on DATETIME
, id_user_created_by INT
, CONSTRAINT FK_DOG_Dog_Command_Link_id_user_created_by
FOREIGN KEY (id_user_created_by)
REFERENCES parts.DOG_User(id_user)
, id_change_set INT
, CONSTRAINT FK_DOG_Dog_Command_Link_id_change_set
FOREIGN KEY (id_change_set)
REFERENCES parts.DOG_Dog_Change_Set(id_change_set)
);

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