Initial commit: Shop Database Flask Application

Flask backend with Vue 3 frontend for shop floor machine management.
Includes database schema export for MySQL shopdb_flask database.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-01-13 16:07:34 -05:00
commit 1196de6e88
188 changed files with 19921 additions and 0 deletions

20
shopdb/utils/__init__.py Normal file
View File

@@ -0,0 +1,20 @@
"""Utility modules."""
from .responses import (
api_response,
success_response,
error_response,
paginated_response,
ErrorCodes
)
from .pagination import get_pagination_params, paginate_query
__all__ = [
'api_response',
'success_response',
'error_response',
'paginated_response',
'ErrorCodes',
'get_pagination_params',
'paginate_query'
]

View File

@@ -0,0 +1,43 @@
"""Pagination utilities."""
from flask import request, current_app
from typing import Tuple
def get_pagination_params(req=None) -> Tuple[int, int]:
"""
Extract pagination parameters from request.
Returns:
Tuple of (page, per_page)
"""
if req is None:
req = request
default_size = current_app.config.get('DEFAULT_PAGE_SIZE', 20)
max_size = current_app.config.get('MAX_PAGE_SIZE', 100)
try:
page = max(1, int(req.args.get('page', 1)))
except (TypeError, ValueError):
page = 1
try:
per_page = int(req.args.get('per_page', default_size))
per_page = max(1, min(per_page, max_size))
except (TypeError, ValueError):
per_page = default_size
return page, per_page
def paginate_query(query, page: int, per_page: int):
"""
Apply pagination to a SQLAlchemy query.
Returns:
Tuple of (items, total)
"""
total = query.count()
items = query.offset((page - 1) * per_page).limit(per_page).all()
return items, total

171
shopdb/utils/responses.py Normal file
View File

@@ -0,0 +1,171 @@
"""Standardized API response helpers."""
from flask import jsonify, make_response
from typing import Any, Dict, List, Optional
from datetime import datetime
import uuid
class ErrorCodes:
"""Standard error codes."""
VALIDATION_ERROR = 'VALIDATION_ERROR'
NOT_FOUND = 'NOT_FOUND'
UNAUTHORIZED = 'UNAUTHORIZED'
FORBIDDEN = 'FORBIDDEN'
CONFLICT = 'CONFLICT'
INTERNAL_ERROR = 'INTERNAL_ERROR'
BAD_REQUEST = 'BAD_REQUEST'
PLUGIN_ERROR = 'PLUGIN_ERROR'
def api_response(
data: Any = None,
message: str = None,
status: str = 'success',
meta: Dict = None,
http_code: int = 200
):
"""
Create standardized API response.
Response format:
{
"status": "success" | "error",
"data": {...} | [...],
"message": "Optional message",
"meta": {
"timestamp": "2025-01-12T...",
"request_id": "uuid"
}
}
"""
response = {
'status': status,
'meta': {
'timestamp': datetime.utcnow().isoformat() + 'Z',
'request_id': str(uuid.uuid4())[:8],
**(meta or {})
}
}
if data is not None:
response['data'] = data
if message:
response['message'] = message
return make_response(jsonify(response), http_code)
def success_response(
data: Any = None,
message: str = None,
meta: Dict = None,
http_code: int = 200
):
"""Success response helper."""
return api_response(
data=data,
message=message,
meta=meta,
status='success',
http_code=http_code
)
def error_response(
code: str,
message: str,
details: Dict = None,
http_code: int = 400
):
"""
Error response helper.
Response format:
{
"status": "error",
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable message",
"details": {...}
}
}
"""
error_data = {
'code': code,
'message': message
}
if details:
error_data['details'] = details
return api_response(
data={'error': error_data},
status='error',
http_code=http_code
)
def api_error(
message: str,
code: str = ErrorCodes.BAD_REQUEST,
details: Dict = None,
http_code: int = 400
):
"""
Simplified error response helper.
Args:
message: Human-readable error message
code: Error code (default BAD_REQUEST)
details: Optional error details
http_code: HTTP status code (default 400)
"""
return error_response(code=code, message=message, details=details, http_code=http_code)
def paginated_response(
items: List,
page: int,
per_page: int,
total: int,
schema=None
):
"""
Paginated list response.
Response format:
{
"status": "success",
"data": [...],
"meta": {
"pagination": {
"page": 1,
"per_page": 20,
"total": 150,
"total_pages": 8,
"has_next": true,
"has_prev": false
}
}
}
"""
total_pages = (total + per_page - 1) // per_page if per_page > 0 else 0
if schema:
items = schema.dump(items, many=True)
return api_response(
data=items,
meta={
'pagination': {
'page': page,
'per_page': per_page,
'total': total,
'total_pages': total_pages,
'has_next': page < total_pages,
'has_prev': page > 1
}
}
)