17 KiB
17 KiB
PunimTag Code Conventions
Overview
This document defines the coding standards and conventions for PunimTag development.
Python Conventions
Code Style
Follow PEP 8 with these specific guidelines:
# Imports
import os
import sys
from typing import List, Dict, Optional
from flask import Flask, request, jsonify
# Constants
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB
ALLOWED_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif'}
# Functions
def process_image(image_path: str, max_size: int = MAX_FILE_SIZE) -> Dict[str, any]:
"""
Process an image file and extract metadata.
Args:
image_path: Path to the image file
max_size: Maximum file size in bytes
Returns:
Dictionary containing image metadata
Raises:
FileNotFoundError: If image file doesn't exist
ValueError: If file size exceeds limit
"""
if not os.path.exists(image_path):
raise FileNotFoundError(f"Image file not found: {image_path}")
file_size = os.path.getsize(image_path)
if file_size > max_size:
raise ValueError(f"File size {file_size} exceeds limit {max_size}")
# Process the image
metadata = extract_metadata(image_path)
return metadata
# Classes
class ImageProcessor:
"""Handles image processing operations."""
def __init__(self, config: Dict[str, any]):
"""
Initialize the image processor.
Args:
config: Configuration dictionary
"""
self.config = config
self.supported_formats = config.get('supported_formats', ALLOWED_EXTENSIONS)
def process_batch(self, image_paths: List[str]) -> List[Dict[str, any]]:
"""
Process multiple images in batch.
Args:
image_paths: List of image file paths
Returns:
List of processed image metadata
"""
results = []
for path in image_paths:
try:
result = self.process_single(path)
results.append(result)
except Exception as e:
logger.error(f"Failed to process {path}: {e}")
results.append({'error': str(e), 'path': path})
return results
Naming Conventions
Variables and Functions
# Use snake_case for variables and functions
user_name = "john_doe"
photo_count = 150
max_file_size = 10 * 1024 * 1024
def get_user_photos(user_id: int) -> List[Dict]:
"""Get photos for a specific user."""
pass
def calculate_face_similarity(face1: List[float], face2: List[float]) -> float:
"""Calculate similarity between two face encodings."""
pass
Classes
# Use PascalCase for classes
class PhotoManager:
"""Manages photo operations."""
pass
class FaceRecognitionEngine:
"""Handles face recognition operations."""
pass
Constants
# Use UPPER_CASE for constants
DATABASE_PATH = "punimtag_simple.db"
MAX_THUMBNAIL_SIZE = (200, 200)
DEFAULT_PAGE_SIZE = 20
Type Hints
from typing import List, Dict, Optional, Union, Tuple
def get_photos(
user_id: int,
page: int = 1,
per_page: int = DEFAULT_PAGE_SIZE,
filters: Optional[Dict[str, any]] = None
) -> Dict[str, Union[List[Dict], int]]:
"""
Get photos with pagination and filtering.
Returns:
Dictionary with 'photos' list and 'total' count
"""
pass
def process_face_encodings(
encodings: List[List[float]]
) -> Tuple[List[float], float]:
"""
Process face encodings and return average encoding and confidence.
Returns:
Tuple of (average_encoding, confidence_score)
"""
pass
Error Handling
import logging
from typing import Optional
logger = logging.getLogger(__name__)
def safe_operation(func):
"""Decorator for safe operation execution."""
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
logger.error(f"Error in {func.__name__}: {e}")
return None
return wrapper
@safe_operation
def load_image_safely(image_path: str) -> Optional[PIL.Image.Image]:
"""Load image with error handling."""
return PIL.Image.open(image_path)
def process_user_request(user_data: Dict) -> Dict[str, any]:
"""Process user request with comprehensive error handling."""
try:
# Validate input
if not user_data.get('user_id'):
return {'success': False, 'error': 'Missing user_id'}
# Process request
result = perform_operation(user_data)
return {'success': True, 'data': result}
except ValueError as e:
logger.warning(f"Validation error: {e}")
return {'success': False, 'error': str(e)}
except FileNotFoundError as e:
logger.error(f"File not found: {e}")
return {'success': False, 'error': 'File not found'}
except Exception as e:
logger.error(f"Unexpected error: {e}")
return {'success': False, 'error': 'Internal server error'}
JavaScript Conventions
Code Style
Follow ESLint with these specific guidelines:
// Constants
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
const ALLOWED_EXTENSIONS = [".jpg", ".jpeg", ".png", ".gif"];
// Functions
function processImage(imagePath, maxSize = MAX_FILE_SIZE) {
/**
* Process an image file and extract metadata.
* @param {string} imagePath - Path to the image file
* @param {number} maxSize - Maximum file size in bytes
* @returns {Promise<Object>} Image metadata
*/
return new Promise((resolve, reject) => {
if (!imagePath) {
reject(new Error("Image path is required"));
return;
}
// Process the image
resolve(extractMetadata(imagePath));
});
}
// Classes
class ImageProcessor {
/**
* Handles image processing operations.
* @param {Object} config - Configuration object
*/
constructor(config) {
this.config = config;
this.supportedFormats = config.supportedFormats || ALLOWED_EXTENSIONS;
}
/**
* Process multiple images in batch.
* @param {string[]} imagePaths - Array of image file paths
* @returns {Promise<Object[]>} Array of processed image metadata
*/
async processBatch(imagePaths) {
const results = [];
for (const path of imagePaths) {
try {
const result = await this.processSingle(path);
results.push(result);
} catch (error) {
console.error(`Failed to process ${path}:`, error);
results.push({ error: error.message, path });
}
}
return results;
}
}
Naming Conventions
Variables and Functions
// Use camelCase for variables and functions
const userName = "johnDoe";
const photoCount = 150;
const maxFileSize = 10 * 1024 * 1024;
function getUserPhotos(userId) {
// Get photos for a specific user
}
function calculateFaceSimilarity(face1, face2) {
// Calculate similarity between two face encodings
}
Classes
// Use PascalCase for classes
class PhotoManager {
// Manages photo operations
}
class FaceRecognitionEngine {
// Handles face recognition operations
}
Constants
// Use UPPER_SNAKE_CASE for constants
const DATABASE_PATH = "punimtag_simple.db";
const MAX_THUMBNAIL_SIZE = { width: 200, height: 200 };
const DEFAULT_PAGE_SIZE = 20;
Error Handling
// Async/await with try-catch
async function processUserRequest(userData) {
try {
// Validate input
if (!userData.userId) {
return { success: false, error: "Missing userId" };
}
// Process request
const result = await performOperation(userData);
return { success: true, data: result };
} catch (error) {
console.error("Error processing request:", error);
return { success: false, error: "Internal server error" };
}
}
// Promise-based error handling
function loadImageSafely(imagePath) {
return new Promise((resolve, reject) => {
if (!imagePath) {
reject(new Error("Image path is required"));
return;
}
// Load image logic
resolve(imageData);
}).catch((error) => {
console.error("Error loading image:", error);
return null;
});
}
Database Conventions
Table Naming
-- Use snake_case for table names
CREATE TABLE user_profiles (
id INTEGER PRIMARY KEY,
user_name TEXT NOT NULL,
email_address TEXT UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE photo_metadata (
id INTEGER PRIMARY KEY,
image_id INTEGER REFERENCES images(id),
exif_data TEXT,
gps_coordinates TEXT,
processing_status TEXT DEFAULT 'pending'
);
Column Naming
-- Use snake_case for column names
CREATE TABLE images (
id INTEGER PRIMARY KEY,
file_name TEXT NOT NULL,
file_path TEXT NOT NULL,
file_size INTEGER,
date_taken TIMESTAMP,
upload_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_processed BOOLEAN DEFAULT FALSE
);
Index Naming
-- Use descriptive names for indexes
CREATE INDEX idx_images_date_taken ON images(date_taken);
CREATE INDEX idx_faces_person_id ON faces(person_id);
CREATE INDEX idx_photos_user_id_date ON photos(user_id, date_taken);
File Organization
Python Files
# File: src/backend/photo_manager.py
"""
Photo management module.
This module handles all photo-related operations including
upload, processing, and metadata extraction.
"""
import os
import logging
from typing import List, Dict, Optional
from PIL import Image
# Constants
MAX_FILE_SIZE = 10 * 1024 * 1024
ALLOWED_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif'}
# Logging
logger = logging.getLogger(__name__)
class PhotoManager:
"""Manages photo operations."""
def __init__(self, config: Dict[str, any]):
self.config = config
self.storage_path = config.get('storage_path', './photos')
def process_photo(self, photo_path: str) -> Dict[str, any]:
"""Process a single photo."""
# Implementation
pass
# Main execution (if applicable)
if __name__ == "__main__":
# Test or standalone execution
pass
JavaScript Files
// File: src/frontend/photoManager.js
/**
* Photo management module.
*
* This module handles all photo-related operations including
* upload, processing, and metadata extraction.
*/
// Constants
const MAX_FILE_SIZE = 10 * 1024 * 1024;
const ALLOWED_EXTENSIONS = [".jpg", ".jpeg", ".png", ".gif"];
// Logging
const logger = {
info: (msg) => console.log(`[INFO] ${msg}`),
error: (msg) => console.error(`[ERROR] ${msg}`),
warn: (msg) => console.warn(`[WARN] ${msg}`),
};
class PhotoManager {
/**
* Manages photo operations.
* @param {Object} config - Configuration object
*/
constructor(config) {
this.config = config;
this.storagePath = config.storagePath || "./photos";
}
/**
* Process a single photo.
* @param {string} photoPath - Path to the photo
* @returns {Promise<Object>} Processing result
*/
async processPhoto(photoPath) {
// Implementation
}
}
// Export for module systems
if (typeof module !== "undefined" && module.exports) {
module.exports = PhotoManager;
}
Documentation Standards
Function Documentation
def extract_face_features(image_path: str, face_coordinates: Tuple[int, int, int, int]) -> List[float]:
"""
Extract face features from an image region.
This function takes an image and face coordinates, then extracts
128-dimensional feature vectors using dlib's face recognition model.
Args:
image_path: Path to the source image file
face_coordinates: Tuple of (left, top, right, bottom) coordinates
Returns:
List of 128 float values representing face features
Raises:
FileNotFoundError: If image file doesn't exist
ValueError: If face coordinates are invalid
RuntimeError: If face recognition model fails
Example:
>>> coords = (100, 100, 200, 200)
>>> features = extract_face_features("photo.jpg", coords)
>>> len(features)
128
"""
pass
Class Documentation
class FaceRecognitionEngine:
"""
Engine for face recognition operations.
This class provides methods for detecting faces in images,
extracting face features, and comparing face similarities.
Attributes:
model_path (str): Path to the face recognition model
confidence_threshold (float): Minimum confidence for face detection
max_faces (int): Maximum number of faces to detect per image
Example:
>>> engine = FaceRecognitionEngine()
>>> faces = engine.detect_faces("group_photo.jpg")
>>> print(f"Found {len(faces)} faces")
"""
def __init__(self, model_path: str = None, confidence_threshold: float = 0.6):
"""
Initialize the face recognition engine.
Args:
model_path: Path to the face recognition model file
confidence_threshold: Minimum confidence for face detection
"""
pass
Testing Conventions
Test File Structure
# File: tests/unit/test_photo_manager.py
"""
Unit tests for PhotoManager class.
"""
import pytest
from unittest.mock import Mock, patch
from src.backend.photo_manager import PhotoManager
class TestPhotoManager:
"""Test cases for PhotoManager class."""
@pytest.fixture
def photo_manager(self):
"""Create a PhotoManager instance for testing."""
config = {'storage_path': '/test/path'}
return PhotoManager(config)
def test_process_photo_with_valid_file(self, photo_manager):
"""Test processing a valid photo file."""
# Test implementation
pass
def test_process_photo_with_invalid_file(self, photo_manager):
"""Test processing an invalid photo file."""
# Test implementation
pass
Git Conventions
Commit Messages
feat: add face recognition feature
fix: resolve duplicate photo detection issue
docs: update API documentation
test: add unit tests for photo processing
refactor: improve error handling in face detection
style: format code according to PEP 8
perf: optimize thumbnail generation
chore: update dependencies
Branch Naming
feature/face-recognition
bugfix/duplicate-detection
hotfix/security-vulnerability
docs/api-documentation
test/photo-processing
refactor/error-handling
Performance Guidelines
Python Performance
# Use list comprehensions instead of loops when appropriate
# Good
squares = [x**2 for x in range(1000)]
# Avoid
squares = []
for x in range(1000):
squares.append(x**2)
# Use generators for large datasets
def process_large_dataset(file_path):
"""Process large dataset using generator."""
with open(file_path, 'r') as file:
for line in file:
yield process_line(line)
# Use appropriate data structures
from collections import defaultdict, Counter
# Use defaultdict for counting
word_count = defaultdict(int)
for word in words:
word_count[word] += 1
# Use Counter for frequency analysis
word_freq = Counter(words)
JavaScript Performance
// Use appropriate array methods
// Good
const squares = Array.from({ length: 1000 }, (_, i) => i ** 2);
// Avoid
const squares = [];
for (let i = 0; i < 1000; i++) {
squares.push(i ** 2);
}
// Use async/await for I/O operations
async function processImages(imagePaths) {
const results = await Promise.all(
imagePaths.map((path) => processImage(path))
);
return results;
}
// Use appropriate data structures
const wordCount = new Map();
words.forEach((word) => {
wordCount.set(word, (wordCount.get(word) || 0) + 1);
});
Security Guidelines
Input Validation
import re
from pathlib import Path
def validate_filename(filename: str) -> bool:
"""Validate filename for security."""
# Check for dangerous characters
dangerous_chars = r'[<>:"/\\|?*]'
if re.search(dangerous_chars, filename):
return False
# Check for path traversal
if '..' in filename or filename.startswith('/'):
return False
# Check length
if len(filename) > 255:
return False
return True
def sanitize_user_input(user_input: str) -> str:
"""Sanitize user input to prevent injection attacks."""
# Remove HTML tags
import html
sanitized = html.escape(user_input)
# Remove SQL injection patterns
sql_patterns = [';', '--', '/*', '*/', 'union', 'select', 'drop']
for pattern in sql_patterns:
sanitized = sanitized.replace(pattern.lower(), '')
return sanitized
Database Security
# Always use parameterized queries
def get_user_photos(user_id: int):
"""Get photos for a user using parameterized query."""
cursor.execute(
'SELECT * FROM photos WHERE user_id = ?',
(user_id,)
)
return cursor.fetchall()
# Never use string formatting for SQL
# BAD - vulnerable to SQL injection
def bad_get_user_photos(user_id: int):
cursor.execute(f'SELECT * FROM photos WHERE user_id = {user_id}')
return cursor.fetchall()