Initial commit: Clean Mold Cost Calculator application
- Flask-based mold cost calculation application - PostgreSQL database with SQLAlchemy ORM - Docker/Podman containerization - Comprehensive documentation in docs/ - Clean, organized codebase without obsolete files - Production-ready deployment configuration - Enhanced pytest configuration with coverage reporting - Master/Development branch structure
This commit is contained in:
+227
@@ -0,0 +1,227 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
# Base directories
|
||||
BASE_DIR = Path(__file__).parent.parent
|
||||
INSTANCE_DIR = BASE_DIR / 'instance'
|
||||
|
||||
# Default values
|
||||
DEFAULT_NET_BASE = 12.5
|
||||
|
||||
# Allowed emails for registration
|
||||
ALLOWED_EMAILS = {
|
||||
'jenny.tay@adidas.com',
|
||||
'gema.yostisiano@adidas.com',
|
||||
'dirgasapto.nugratama@adidas.com',
|
||||
'test@example.com', # Original test email
|
||||
'test1@example.com',
|
||||
'test2@example.com',
|
||||
'test3@example.com',
|
||||
'test4@example.com',
|
||||
'test5@example.com',
|
||||
'test6@example.com',
|
||||
'test7@example.com',
|
||||
'test8@example.com',
|
||||
'test9@example.com',
|
||||
'test10@example.com'
|
||||
}
|
||||
|
||||
# Tax rates by country
|
||||
TAX_RATES = {
|
||||
'China': 0.06,
|
||||
'Vietnam': 0.10,
|
||||
'Indonesia': 0.07
|
||||
}
|
||||
|
||||
# Cost factors for calculations
|
||||
COST_FACTORS = {
|
||||
'mold': {
|
||||
'Flat & Thin': 1.0,
|
||||
'Flat & Thick': 1.25,
|
||||
'Flat with Top/Heat Tip': 1.35,
|
||||
'Top PL': 1.05,
|
||||
'Short PL (Heat/Forefoot)': 1.18,
|
||||
'Visible PL': 1.28,
|
||||
'Wave PL': 1.32,
|
||||
'2 Plates Mold': 1.0,
|
||||
'3 Plates Mold': 1.35,
|
||||
'Breathable + In-mold channel': 1.45,
|
||||
'With Upper Bandage': 1.15,
|
||||
'2 plate without Bandage': 1.05,
|
||||
'Co-shot mold': 1.25,
|
||||
'Foaming mold': 1.4
|
||||
},
|
||||
'complexity': {
|
||||
'high_sidewall': 0.18,
|
||||
'slider': 0.1,
|
||||
'cavity': 0.12
|
||||
},
|
||||
'texture': {
|
||||
'standard': 0.0,
|
||||
'custom': 0.15,
|
||||
'laser': 0.2
|
||||
}
|
||||
}
|
||||
|
||||
class Config:
|
||||
"""Base configuration class."""
|
||||
|
||||
# Flask configuration
|
||||
SECRET_KEY = os.getenv('SECRET_KEY') # Get SECRET_KEY directly from environment
|
||||
FLASK_APP = 'app'
|
||||
FLASK_ENV = 'development'
|
||||
FLASK_DEBUG = True
|
||||
|
||||
# Security configuration
|
||||
SESSION_COOKIE_SECURE = True
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||
PERMANENT_SESSION_LIFETIME = timedelta(days=7)
|
||||
REMEMBER_COOKIE_DURATION = timedelta(days=30)
|
||||
REMEMBER_COOKIE_SECURE = True
|
||||
REMEMBER_COOKIE_HTTPONLY = True
|
||||
REMEMBER_COOKIE_SAMESITE = 'Lax'
|
||||
|
||||
# Database configuration
|
||||
SQLALCHEMY_DATABASE_URI = None # Will be set from environment variable
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
SQLALCHEMY_ENGINE_OPTIONS = {
|
||||
'pool_size': 10,
|
||||
'pool_recycle': 3600,
|
||||
'pool_pre_ping': True
|
||||
}
|
||||
|
||||
# Email configuration
|
||||
MAIL_SERVER = None # Will be set from environment variable
|
||||
MAIL_PORT = 587
|
||||
MAIL_USE_TLS = True
|
||||
MAIL_USERNAME = None # Will be set from environment variable
|
||||
MAIL_PASSWORD = None # Will be set from environment variable
|
||||
MAIL_DEFAULT_SENDER = None # Will be set from environment variable
|
||||
|
||||
# Admin account configuration
|
||||
ADMIN_EMAIL = None # Will be set from environment variable
|
||||
ADMIN_PASSWORD = None # Will be set from environment variable
|
||||
|
||||
# Logging configuration
|
||||
LOG_LEVEL = 'INFO'
|
||||
LOG_TO_STDOUT = True
|
||||
LOG_FORMAT = '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
|
||||
LOG_DATE_FORMAT = '%Y-%m-%d %H:%M:%S'
|
||||
|
||||
# Application config
|
||||
ALLOWED_EMAILS = ALLOWED_EMAILS
|
||||
TAX_RATES = TAX_RATES
|
||||
DEFAULT_NET_BASE = DEFAULT_NET_BASE
|
||||
WTF_CSRF_ENABLED = True
|
||||
|
||||
# Rate Limiting Configuration
|
||||
RATELIMIT_DEFAULT = "200 per day;50 per hour;10 per minute"
|
||||
RATELIMIT_STORAGE_URL = "memory://"
|
||||
RATELIMIT_STRATEGY = "fixed-window"
|
||||
RATELIMIT_HEADERS_ENABLED = True
|
||||
RATELIMIT_HEADERS_RESET = True
|
||||
RATELIMIT_HEADERS_RETRY_AFTER = True
|
||||
|
||||
# Cache Configuration
|
||||
CACHE_TYPE = "SimpleCache"
|
||||
CACHE_DEFAULT_TIMEOUT = 300
|
||||
CACHE_THRESHOLD = 1000
|
||||
|
||||
@classmethod
|
||||
def init_app(cls, app):
|
||||
"""Initialize application with configuration."""
|
||||
# Load configuration from environment variables
|
||||
db_user = os.getenv('POSTGRES_USER')
|
||||
db_password = os.getenv('POSTGRES_PASSWORD')
|
||||
db_host = os.getenv('DATABASE_HOST')
|
||||
db_name = os.getenv('POSTGRES_DB')
|
||||
|
||||
if db_user and db_password and db_host and db_name:
|
||||
cls.SQLALCHEMY_DATABASE_URI = f"postgresql://{db_user}:{db_password}@{db_host}/{db_name}"
|
||||
else:
|
||||
cls.SQLALCHEMY_DATABASE_URI = os.getenv('DATABASE_URL') # Fallback for other setups
|
||||
|
||||
cls.MAIL_SERVER = os.getenv('MAIL_SERVER')
|
||||
cls.MAIL_USERNAME = os.getenv('MAIL_USERNAME')
|
||||
cls.MAIL_PASSWORD = os.getenv('MAIL_PASSWORD')
|
||||
cls.MAIL_DEFAULT_SENDER = os.getenv('MAIL_DEFAULT_SENDER')
|
||||
cls.ADMIN_EMAIL = os.getenv('ADMIN_EMAIL')
|
||||
cls.ADMIN_PASSWORD = os.getenv('ADMIN_PASSWORD')
|
||||
cls.LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
|
||||
cls.LOG_TO_STDOUT = os.getenv('LOG_TO_STDOUT', 'True').lower() == 'true'
|
||||
|
||||
# Ensure SECRET_KEY is set
|
||||
if not cls.SECRET_KEY:
|
||||
raise ValueError("SECRET_KEY environment variable is not set")
|
||||
|
||||
# Set SQLALCHEMY_DATABASE_URI on the app config
|
||||
if not cls.SQLALCHEMY_DATABASE_URI:
|
||||
raise ValueError("Database connection URI is not set. Please check DATABASE_URL or its components.")
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = cls.SQLALCHEMY_DATABASE_URI
|
||||
|
||||
# Configure logging
|
||||
if not os.path.exists('logs'):
|
||||
os.mkdir('logs')
|
||||
file_handler = RotatingFileHandler('logs/app.log',
|
||||
maxBytes=10240,
|
||||
backupCount=10)
|
||||
file_handler.setFormatter(logging.Formatter(cls.LOG_FORMAT))
|
||||
file_handler.setLevel(logging.INFO)
|
||||
app.logger.addHandler(file_handler)
|
||||
app.logger.setLevel(logging.INFO)
|
||||
app.logger.info('Application startup')
|
||||
|
||||
class DevelopmentConfig(Config):
|
||||
DEBUG = True
|
||||
TESTING = False
|
||||
REMEMBER_COOKIE_SECURE = False
|
||||
SESSION_COOKIE_SECURE = False
|
||||
SQLALCHEMY_ECHO = True
|
||||
LOG_LEVEL = 'DEBUG'
|
||||
WTF_CSRF_ENABLED = True
|
||||
|
||||
# Development-specific settings
|
||||
RATELIMIT_DEFAULT = "1000 per day;100 per hour;20 per minute"
|
||||
CACHE_TYPE = "SimpleCache"
|
||||
|
||||
class ProductionConfig(Config):
|
||||
DEBUG = False
|
||||
TESTING = False
|
||||
REMEMBER_COOKIE_SECURE = True
|
||||
SESSION_COOKIE_SECURE = True
|
||||
SQLALCHEMY_ECHO = False
|
||||
LOG_LEVEL = 'INFO'
|
||||
WTF_CSRF_ENABLED = True
|
||||
|
||||
# Production-specific settings
|
||||
CACHE_TYPE = "SimpleCache"
|
||||
CACHE_DEFAULT_TIMEOUT = 300
|
||||
|
||||
class TestingConfig(Config):
|
||||
TESTING = True
|
||||
DEBUG = True
|
||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
|
||||
WTF_CSRF_ENABLED = False
|
||||
LOG_LEVEL = 'DEBUG'
|
||||
|
||||
# Testing-specific settings
|
||||
SECRET_KEY = 'testing-secret-key-not-used-in-production'
|
||||
MAIL_SUPPRESS_SEND = True
|
||||
RATELIMIT_ENABLED = False
|
||||
RATELIMIT_DEFAULT = "1000 per day;100 per hour;20 per minute"
|
||||
CACHE_TYPE = "SimpleCache"
|
||||
SQLALCHEMY_ENGINE_OPTIONS = {}
|
||||
|
||||
def get_config():
|
||||
"""Get the appropriate configuration class based on environment."""
|
||||
config_name = os.getenv('FLASK_CONFIG', 'development')
|
||||
config_map = {
|
||||
'development': DevelopmentConfig,
|
||||
'production': ProductionConfig,
|
||||
'testing': TestingConfig
|
||||
}
|
||||
return config_map.get(config_name, DevelopmentConfig)
|
||||
Reference in New Issue
Block a user