refactor: modernize typing annotations and docstrings
This commit is contained in:
@@ -2,7 +2,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from flask import Flask
|
from flask import Flask
|
||||||
@@ -26,12 +25,12 @@ def setup_logging(app: Flask) -> None:
|
|||||||
app.logger.setLevel(logging.DEBUG)
|
app.logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
def create_app(config_name: Optional[str] = None) -> Flask:
|
def create_app(config_name: str | None = None) -> Flask:
|
||||||
"""
|
"""
|
||||||
Application factory pattern for Flask.
|
Application factory pattern for Flask.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
config_name: Optional configuration name (development, production, etc.)
|
config_name: str | None configuration name (development, production, etc.)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Flask application instance
|
Flask application instance
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Blueprint for task-related routes."""
|
"""Blueprint for task-related routes."""
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, date, timezone
|
from datetime import datetime, date, timezone
|
||||||
from typing import Dict, Any, Tuple
|
from typing import Any
|
||||||
|
|
||||||
from flask import Blueprint, render_template, request, jsonify, Response
|
from flask import Blueprint, render_template, request, jsonify, Response
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
@@ -93,7 +93,7 @@ def index() -> str:
|
|||||||
|
|
||||||
|
|
||||||
@tasks_blueprint.route('/api/tasks', methods=['POST'])
|
@tasks_blueprint.route('/api/tasks', methods=['POST'])
|
||||||
def api_create_task() -> Tuple[Response, int]:
|
def api_create_task() -> tuple[Response, int]:
|
||||||
"""API endpoint to create a new task."""
|
"""API endpoint to create a new task."""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
@@ -142,7 +142,7 @@ def api_create_task() -> Tuple[Response, int]:
|
|||||||
|
|
||||||
|
|
||||||
@tasks_blueprint.route('/api/tasks/<int:task_id>', methods=['GET'])
|
@tasks_blueprint.route('/api/tasks/<int:task_id>', methods=['GET'])
|
||||||
def api_get_task(task_id: int) -> Tuple[Response, int]:
|
def api_get_task(task_id: int) -> tuple[Response, int]:
|
||||||
"""API endpoint to get a single task."""
|
"""API endpoint to get a single task."""
|
||||||
try:
|
try:
|
||||||
task = Task.query.get_or_404(task_id)
|
task = Task.query.get_or_404(task_id)
|
||||||
@@ -153,14 +153,14 @@ def api_get_task(task_id: int) -> Tuple[Response, int]:
|
|||||||
|
|
||||||
|
|
||||||
@tasks_blueprint.route('/api/tasks/<int:task_id>', methods=['PUT', 'PATCH'])
|
@tasks_blueprint.route('/api/tasks/<int:task_id>', methods=['PUT', 'PATCH'])
|
||||||
def api_update_task(task_id: int) -> Tuple[Response, int]:
|
def api_update_task(task_id: int) -> tuple[Response, int]:
|
||||||
"""API endpoint to update a task."""
|
"""API endpoint to update a task."""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
if not isinstance(data, dict):
|
if not isinstance(data, dict):
|
||||||
return jsonify({'error': 'Invalid JSON payload'}), 400
|
return jsonify({'error': 'Invalid JSON payload'}), 400
|
||||||
|
|
||||||
update_data: Dict[str, Any] = {}
|
update_data: dict[str, Any] = {}
|
||||||
|
|
||||||
# Validate and update title
|
# Validate and update title
|
||||||
if 'title' in data:
|
if 'title' in data:
|
||||||
@@ -217,7 +217,7 @@ def api_update_task(task_id: int) -> Tuple[Response, int]:
|
|||||||
|
|
||||||
|
|
||||||
@tasks_blueprint.route('/api/tasks/<int:task_id>', methods=['DELETE'])
|
@tasks_blueprint.route('/api/tasks/<int:task_id>', methods=['DELETE'])
|
||||||
def api_delete_task(task_id: int) -> Tuple[Response, int]:
|
def api_delete_task(task_id: int) -> tuple[Response, int]:
|
||||||
"""API endpoint to delete a task."""
|
"""API endpoint to delete a task."""
|
||||||
try:
|
try:
|
||||||
delete_task(task_id)
|
delete_task(task_id)
|
||||||
@@ -229,7 +229,7 @@ def api_delete_task(task_id: int) -> Tuple[Response, int]:
|
|||||||
|
|
||||||
|
|
||||||
@tasks_blueprint.route('/api/tasks/reorder', methods=['POST'])
|
@tasks_blueprint.route('/api/tasks/reorder', methods=['POST'])
|
||||||
def api_reorder_tasks() -> Tuple[Response, int]:
|
def api_reorder_tasks() -> tuple[Response, int]:
|
||||||
"""API endpoint to reorder tasks."""
|
"""API endpoint to reorder tasks."""
|
||||||
try:
|
try:
|
||||||
data = request.get_json()
|
data = request.get_json()
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
"""Error handlers for Flask application."""
|
"""Error handlers for Flask application."""
|
||||||
from typing import Tuple
|
|
||||||
from flask import Flask, jsonify, Response
|
from flask import Flask, jsonify, Response
|
||||||
from flask_wtf.csrf import CSRFError
|
from flask_wtf.csrf import CSRFError
|
||||||
from werkzeug.exceptions import HTTPException
|
from werkzeug.exceptions import HTTPException
|
||||||
@@ -9,23 +8,23 @@ def register_error_handlers(app: Flask) -> None:
|
|||||||
"""Register all error handlers with the Flask app."""
|
"""Register all error handlers with the Flask app."""
|
||||||
|
|
||||||
@app.errorhandler(400)
|
@app.errorhandler(400)
|
||||||
def bad_request(error: HTTPException) -> Tuple[Response, int]:
|
def bad_request(error: HTTPException) -> tuple[Response, int]:
|
||||||
"""Handle 400 errors with JSON response."""
|
"""Handle 400 errors with JSON response."""
|
||||||
return jsonify({'error': 'Bad request', 'message': str(error)}), 400
|
return jsonify({'error': 'Bad request', 'message': str(error)}), 400
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def not_found(error: HTTPException) -> Tuple[Response, int]:
|
def not_found(error: HTTPException) -> tuple[Response, int]:
|
||||||
"""Handle 404 errors with JSON response."""
|
"""Handle 404 errors with JSON response."""
|
||||||
return jsonify({'error': 'Not found', 'message': str(error)}), 404
|
return jsonify({'error': 'Not found', 'message': str(error)}), 404
|
||||||
|
|
||||||
@app.errorhandler(CSRFError)
|
@app.errorhandler(CSRFError)
|
||||||
def handle_csrf_error(e: CSRFError) -> Tuple[Response, int]:
|
def handle_csrf_error(e: CSRFError) -> tuple[Response, int]:
|
||||||
"""Handle CSRF errors with JSON response."""
|
"""Handle CSRF errors with JSON response."""
|
||||||
app.logger.warning(f'CSRF error: {e}')
|
app.logger.warning(f'CSRF error: {e}')
|
||||||
return jsonify({'error': 'CSRF token missing or invalid'}), 400
|
return jsonify({'error': 'CSRF token missing or invalid'}), 400
|
||||||
|
|
||||||
@app.errorhandler(500)
|
@app.errorhandler(500)
|
||||||
def internal_error(error: Exception) -> Tuple[Response, int]:
|
def internal_error(error: Exception) -> tuple[Response, int]:
|
||||||
"""Handle 500 errors with JSON response."""
|
"""Handle 500 errors with JSON response."""
|
||||||
app.logger.error(f'Server error: {error}')
|
app.logger.error(f'Server error: {error}')
|
||||||
return jsonify({'error': 'Internal server error'}), 500
|
return jsonify({'error': 'Internal server error'}), 500
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
"""SQLAlchemy models for Flado."""
|
"""SQLAlchemy models for Flado."""
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import Dict, Any
|
from typing import Any
|
||||||
|
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ class Task(db.Model):
|
|||||||
tags = db.relationship('Tag', secondary='task_tags',
|
tags = db.relationship('Tag', secondary='task_tags',
|
||||||
back_populates='tasks', lazy='select')
|
back_populates='tasks', lazy='select')
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> dict[str, Any]:
|
||||||
"""Convert task to dictionary for JSON serialization."""
|
"""Convert task to dictionary for JSON serialization."""
|
||||||
return {
|
return {
|
||||||
'id': self.id,
|
'id': self.id,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"""Business logic helpers for Flado."""
|
"""Business logic helpers for Flado."""
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, date, timezone
|
from datetime import datetime, date, timezone
|
||||||
from typing import Optional, List
|
|
||||||
|
|
||||||
from sqlalchemy import or_, func
|
from sqlalchemy import or_, func
|
||||||
from sqlalchemy.orm import Query
|
from sqlalchemy.orm import Query
|
||||||
@@ -11,13 +10,13 @@ from .models import db, Task, Tag
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_tasks(filter_type: str = 'all', search_query: Optional[str] = None) -> Query:
|
def get_tasks(filter_type: str = 'all', search_query: str | None = None) -> Query:
|
||||||
"""
|
"""
|
||||||
Retrieve tasks based on filter type.
|
Retrieve tasks based on filter type.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filter_type: 'all', 'today', 'upcoming', 'completed', 'active'
|
filter_type: 'all', 'today', 'upcoming', 'completed', 'active'
|
||||||
search_query: Optional search string to filter by title/description
|
search_query: str | None search string to filter by title/description
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Query object of tasks
|
Query object of tasks
|
||||||
@@ -49,18 +48,18 @@ def get_tasks(filter_type: str = 'all', search_query: Optional[str] = None) -> Q
|
|||||||
|
|
||||||
def create_task(
|
def create_task(
|
||||||
title: str,
|
title: str,
|
||||||
description: Optional[str] = None,
|
description: str | None = None,
|
||||||
due_date: Optional[date] = None,
|
due_date: date | None = None,
|
||||||
tag_names: Optional[List[str]] = None
|
tag_names: list[str] | None = None
|
||||||
) -> Task:
|
) -> Task:
|
||||||
"""
|
"""
|
||||||
Create a new task.
|
Create a new task.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
title: Task title (required, already validated)
|
title: Task title (required, already validated)
|
||||||
description: Optional task description (already validated)
|
description: str | None task description (already validated)
|
||||||
due_date: Optional due date (date object, already validated)
|
due_date: date | None due date (date object, already validated)
|
||||||
tag_names: Optional list of tag names to associate (already validated)
|
tag_names: list[str] | None list of tag names to associate (already validated)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Created Task object
|
Created Task object
|
||||||
@@ -162,12 +161,12 @@ def delete_task(task_id: int) -> bool:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def reorder_tasks(task_ids: List[int]) -> bool:
|
def reorder_tasks(task_ids: list[int]) -> bool:
|
||||||
"""
|
"""
|
||||||
Reorder tasks by updating their positions.
|
Reorder tasks by updating their positions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
task_ids: List of task IDs in desired order (already validated)
|
task_ids: list[int] of task IDs in desired order (already validated)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True if reordered successfully
|
True if reordered successfully
|
||||||
@@ -209,15 +208,15 @@ def reorder_tasks(task_ids: List[int]) -> bool:
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def get_or_create_tags(tag_names: List[str]) -> List[Tag]:
|
def get_or_create_tags(tag_names: list[str]) -> list[Tag]:
|
||||||
"""
|
"""
|
||||||
Get existing tags or create new ones.
|
Get existing tags or create new ones.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tag_names: List of tag names (already validated)
|
tag_names: list[str] of tag names (already validated)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List of Tag objects
|
list[Tag] of tag objects
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
Does not commit the session. Caller is responsible for committing.
|
Does not commit the session. Caller is responsible for committing.
|
||||||
@@ -235,6 +234,6 @@ def get_or_create_tags(tag_names: List[str]) -> List[Tag]:
|
|||||||
return tags
|
return tags
|
||||||
|
|
||||||
|
|
||||||
def get_all_tags() -> List[Tag]:
|
def get_all_tags() -> list[Tag]:
|
||||||
"""Get all tags."""
|
"""Get all tags."""
|
||||||
return Tag.query.order_by(Tag.name.asc()).all()
|
return Tag.query.order_by(Tag.name.asc()).all()
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
"""Input validation utilities for Flado."""
|
"""Input validation utilities for Flado."""
|
||||||
import re
|
import re
|
||||||
from typing import Optional, List, Tuple
|
|
||||||
|
|
||||||
|
|
||||||
def validate_title(title: Optional[str]) -> Tuple[bool, Optional[str]]:
|
def validate_title(title: str | None) -> tuple[bool, str | None]:
|
||||||
"""
|
"""
|
||||||
Validate task title.
|
Validate task title.
|
||||||
|
|
||||||
@@ -11,7 +10,7 @@ def validate_title(title: Optional[str]) -> Tuple[bool, Optional[str]]:
|
|||||||
title: Title to validate
|
title: Title to validate
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple of (is_valid, error_message)
|
tuple[bool, str | None]: (is_valid, error_message)
|
||||||
"""
|
"""
|
||||||
if not title:
|
if not title:
|
||||||
return False, "Title is required"
|
return False, "Title is required"
|
||||||
@@ -26,7 +25,7 @@ def validate_title(title: Optional[str]) -> Tuple[bool, Optional[str]]:
|
|||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
|
||||||
def validate_description(description: Optional[str]) -> Tuple[bool, Optional[str]]:
|
def validate_description(description: str | None) -> tuple[bool, str | None]:
|
||||||
"""
|
"""
|
||||||
Validate task description.
|
Validate task description.
|
||||||
|
|
||||||
@@ -34,7 +33,7 @@ def validate_description(description: Optional[str]) -> Tuple[bool, Optional[str
|
|||||||
description: Description to validate
|
description: Description to validate
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple of (is_valid, error_message)
|
tuple[bool, str | None]: (is_valid, error_message)
|
||||||
"""
|
"""
|
||||||
if description is None:
|
if description is None:
|
||||||
return True, None
|
return True, None
|
||||||
@@ -46,7 +45,7 @@ def validate_description(description: Optional[str]) -> Tuple[bool, Optional[str
|
|||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
|
||||||
def validate_date_format(date_str: Optional[str]) -> Tuple[bool, Optional[str]]:
|
def validate_date_format(date_str: str | None) -> tuple[bool, str | None]:
|
||||||
"""
|
"""
|
||||||
Validate date string format (YYYY-MM-DD).
|
Validate date string format (YYYY-MM-DD).
|
||||||
|
|
||||||
@@ -54,7 +53,7 @@ def validate_date_format(date_str: Optional[str]) -> Tuple[bool, Optional[str]]:
|
|||||||
date_str: Date string to validate
|
date_str: Date string to validate
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple of (is_valid, error_message)
|
tuple[bool, str | None]: (is_valid, error_message)
|
||||||
"""
|
"""
|
||||||
if not date_str:
|
if not date_str:
|
||||||
return True, None
|
return True, None
|
||||||
@@ -70,7 +69,7 @@ def validate_date_format(date_str: Optional[str]) -> Tuple[bool, Optional[str]]:
|
|||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
|
||||||
def validate_tag_name(tag_name: Optional[str]) -> Tuple[bool, Optional[str]]:
|
def validate_tag_name(tag_name: str | None) -> tuple[bool, str | None]:
|
||||||
"""
|
"""
|
||||||
Validate tag name.
|
Validate tag name.
|
||||||
|
|
||||||
@@ -78,7 +77,7 @@ def validate_tag_name(tag_name: Optional[str]) -> Tuple[bool, Optional[str]]:
|
|||||||
tag_name: Tag name to validate
|
tag_name: Tag name to validate
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple of (is_valid, error_message)
|
tuple[bool, str | None]: (is_valid, error_message)
|
||||||
"""
|
"""
|
||||||
if not tag_name:
|
if not tag_name:
|
||||||
return False, "Tag name is required"
|
return False, "Tag name is required"
|
||||||
@@ -97,15 +96,15 @@ def validate_tag_name(tag_name: Optional[str]) -> Tuple[bool, Optional[str]]:
|
|||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
|
||||||
def validate_tag_names(tag_names: Optional[List[str]]) -> Tuple[bool, Optional[str]]:
|
def validate_tag_names(tag_names: list[str] | None) -> tuple[bool, str | None]:
|
||||||
"""
|
"""
|
||||||
Validate list of tag names.
|
Validate list of tag names.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tag_names: List of tag names to validate
|
tag_names: list[str] of tag names to validate
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple of (is_valid, error_message)
|
tuple[bool, str | None]: (is_valid, error_message)
|
||||||
"""
|
"""
|
||||||
if tag_names is None:
|
if tag_names is None:
|
||||||
return True, None
|
return True, None
|
||||||
@@ -121,7 +120,7 @@ def validate_tag_names(tag_names: Optional[List[str]]) -> Tuple[bool, Optional[s
|
|||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
|
||||||
def validate_hex_color(color: Optional[str]) -> Tuple[bool, Optional[str]]:
|
def validate_hex_color(color: str | None) -> tuple[bool, str | None]:
|
||||||
"""
|
"""
|
||||||
Validate hex color format.
|
Validate hex color format.
|
||||||
|
|
||||||
@@ -129,7 +128,7 @@ def validate_hex_color(color: Optional[str]) -> Tuple[bool, Optional[str]]:
|
|||||||
color: Hex color string (e.g., #RRGGBB)
|
color: Hex color string (e.g., #RRGGBB)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple of (is_valid, error_message)
|
tuple[bool, str | None]: (is_valid, error_message)
|
||||||
"""
|
"""
|
||||||
if not color:
|
if not color:
|
||||||
return True, None
|
return True, None
|
||||||
@@ -144,15 +143,15 @@ def validate_hex_color(color: Optional[str]) -> Tuple[bool, Optional[str]]:
|
|||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
|
||||||
def validate_task_ids(task_ids: Optional[List[int]]) -> Tuple[bool, Optional[str]]:
|
def validate_task_ids(task_ids: list[int] | None) -> tuple[bool, str | None]:
|
||||||
"""
|
"""
|
||||||
Validate list of task IDs.
|
Validate list of task IDs.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
task_ids: List of task IDs to validate
|
task_ids: list[int] of task IDs to validate
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Tuple of (is_valid, error_message)
|
tuple[bool, str | None]: (is_valid, error_message)
|
||||||
"""
|
"""
|
||||||
if not task_ids:
|
if not task_ids:
|
||||||
return False, "task_ids is required"
|
return False, "task_ids is required"
|
||||||
|
|||||||
Reference in New Issue
Block a user