diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 2830fea..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,606 +0,0 @@ -# AGENTS.md - AI Coding Agent Guidelines - -This document provides guidelines for AI coding agents working in the labhelper repository. - -## Project Overview - -- **Type**: Django web application (lab inventory management system) -- **Python**: 3.13.7 -- **Django**: 5.2.9 -- **Database**: SQLite (development) -- **Virtual Environment**: `.venv/` - -## Build/Run Commands - -### Development Server - -```bash -python manage.py runserver # Start dev server on port 8000 -python manage.py runserver 0.0.0.0:8000 # Bind to all interfaces -``` - -### Database Operations - -```bash -python manage.py makemigrations # Create migration files -python manage.py makemigrations boxes # Create migrations for specific app -python manage.py migrate # Apply all migrations -python manage.py showmigrations # List migration status -``` - -### Testing - -```bash -# Run all tests -python manage.py test - -# Run tests for a specific app -python manage.py test boxes - -# Run a specific test class -python manage.py test boxes.tests.TestClassName - -# Run a single test method -python manage.py test boxes.tests.TestClassName.test_method_name - -# Run tests with verbosity -python manage.py test -v 2 - -# Run tests with coverage -coverage run manage.py test -coverage report -coverage html # Generate HTML report -``` - -### Django Shell - -```bash -python manage.py shell # Interactive Django shell -python manage.py createsuperuser # Create admin user -python manage.py collectstatic # Collect static files -``` - -### Production - -```bash -gunicorn labhelper.wsgi:application # Run with Gunicorn -``` - -### Custom Management Commands - -```bash -# Create default users and groups -python manage.py create_default_users - -# Clean up orphaned files from deleted things -python manage.py clean_orphaned_files -python manage.py clean_orphaned_files --dry-run - -# Clean up orphaned images and thumbnails -python manage.py clean_orphaned_images -python manage.py clean_orphaned_images --dry-run -``` - -## Code Style Guidelines - -### Python Style - -- Follow PEP 8 conventions -- Use 4-space indentation (no tabs) -- Maximum line length: 79 characters (PEP 8 standard) -- Use single quotes for strings: `'string'` -- Use double quotes for docstrings: `"""Docstring."""` - -### Import Order - -Organize imports in this order, with blank lines between groups: - -```python -# 1. Standard library imports -import os -import sys -from pathlib import Path - -# 2. Django imports -from django.db import models -from django.contrib import admin -from django.shortcuts import render, redirect -from django.http import HttpResponse, JsonResponse - -# 3. Third-party imports -import requests -from markdown import markdown - -# 4. Local application imports -from .models import MyModel -from .forms import MyForm -``` - -### Naming Conventions - -| Type | Convention | Example | -|------|------------|---------| -| Modules | lowercase_with_underscores | `user_profile.py` | -| Classes | PascalCase | `UserProfile` | -| Functions | lowercase_with_underscores | `get_user_data()` | -| Constants | UPPERCASE_WITH_UNDERSCORES | `MAX_CONNECTIONS` | -| Variables | lowercase_with_underscores | `user_count` | -| Django Models | PascalCase (singular) | `Box`, `UserProfile` | -| Django Apps | lowercase (short) | `boxes`, `users` | - -### Django-Specific Conventions - -**Models:** -```python -from django.db import models - -class Box(models.Model): - """A storage box in the lab.""" - - name = models.CharField(max_length=255) - created_at = models.DateTimeField(auto_now_add=True) - updated_at = models.DateTimeField(auto_now=True) - - class Meta: - verbose_name_plural = 'boxes' - ordering = ['-created_at'] - - def __str__(self): - return self.name -``` - -**Views:** -```python -from django.shortcuts import render, get_object_or_404 -from django.http import Http404 - -def box_detail(request, box_id): - """Display details for a specific box.""" - box = get_object_or_404(Box, pk=box_id) - return render(request, 'boxes/detail.html', {'box': box}) -``` - -### Error Handling - -```python -# Use specific exceptions -try: - result = some_operation() -except SpecificError as exc: - raise CustomError('Descriptive message') from exc - -# Django: Use get_object_or_404 for model lookups -box = get_object_or_404(Box, pk=box_id) - -# Log errors appropriately -import logging -logger = logging.getLogger(__name__) -logger.error('Error message: %s', error_detail) -``` - -### Type Hints (Recommended) - -```python -from typing import Optional -from django.http import HttpRequest, HttpResponse - -def get_box(request: HttpRequest, box_id: int) -> HttpResponse: - """Retrieve a box by ID.""" - ... -``` - -## Project Structure - -``` -labhelper/ -├── .gitea/ -│ └── workflows/ -│ └── build-containers-on-demand.yml # CI/CD workflow -├── argocd/ # Kubernetes deployment manifests -│ ├── 001_pvc.yaml # PersistentVolumeClaim -│ ├── deployment.yaml # Deployment + Service -│ ├── ingress.yaml # Traefik ingress -│ ├── nfs-pv.yaml # NFS PersistentVolume -│ ├── nfs-storageclass.yaml # NFS StorageClass -│ └── secret.yaml # Django secret key template -├── boxes/ # Main Django app -│ ├── management/ -│ │ └── commands/ -│ │ ├── clean_orphaned_files.py # Cleanup orphaned ThingFile attachments -│ │ └── clean_orphaned_images.py # Cleanup orphaned Thing images -│ ├── migrations/ # Database migrations -│ ├── static/ -│ │ └── css/ -│ │ ├── base.css # Base styles (layout, navbar, buttons, alerts, etc.) -│ │ ├── edit_thing.css # Edit thing form styles -│ │ └── thing_detail.css # Markdown content + lightbox styles -│ ├── templates/ -│ │ └── boxes/ -│ │ ├── add_things.html # Form to add multiple things -│ │ ├── box_detail.html # Box contents view -│ │ ├── box_management.html # Box/BoxType CRUD management -│ │ ├── boxes_list.html # Boxes list page with tabular view -│ │ ├── edit_thing.html # Edit thing page (name, description, picture, tags, files, links) -│ │ ├── index.html # Home page with search and tags -│ │ ├── resources_list.html # List all links and files from things -│ │ └── thing_detail.html # Read-only thing details view -│ ├── templatetags/ -│ │ └── dict_extras.py # Custom template filters: get_item, render_markdown, truncate_markdown -│ ├── admin.py # Admin configuration -│ ├── apps.py # App configuration -│ ├── forms.py # All forms and formsets -│ ├── models.py # Data models -│ ├── tests.py # Test cases -│ └── views.py # View functions -├── data-loader/ # Init container for database preload -│ ├── Dockerfile # Alpine-based init container -│ └── preload.sqlite3 # Preloaded database for deployment -├── labhelper/ # Project configuration -│ ├── management/ -│ │ └── commands/ -│ │ └── create_default_users.py # Create default users/groups -│ ├── templates/ -│ │ ├── base.html # Base template with navigation -│ │ └── login.html # Login page -│ ├── asgi.py # ASGI configuration -│ ├── settings.py # Django settings -│ ├── urls.py # Root URL configuration -│ └── wsgi.py # WSGI configuration -├── scripts/ -│ ├── deploy_secret.sh # Generate and deploy Django secret -│ ├── full_deploy.sh # Bump both container versions + copy DB -│ └── partial_deploy.sh # Bump main container version only -├── .gitignore -├── AGENTS.md # This file -├── Dockerfile # Multi-stage build for main container -├── manage.py # Django CLI entry point -└── requirements.txt # Python dependencies -``` - -## Data Models - -### boxes app - -| Model | Description | Key Fields | -|-------|-------------|------------| -| **BoxType** | Type of storage box with dimensions | `name`, `width`, `height`, `length` (in mm) | -| **Box** | A storage box in the lab | `id` (CharField PK, max 10), `box_type` (FK) | -| **Facet** | A category of tags (e.g., Priority, Category) | `name`, `slug`, `color`, `cardinality` (single/multiple) | -| **Tag** | A tag value for a specific facet | `facet` (FK), `name` | -| **Thing** | An item stored in a box | `name`, `box` (FK), `description` (Markdown), `picture`, `tags` (M2M) | -| **ThingFile** | File attachment for a Thing | `thing` (FK), `file`, `title`, `uploaded_at` | -| **ThingLink** | Hyperlink for a Thing | `thing` (FK), `url`, `title`, `uploaded_at` | - -**Model Relationships:** -- BoxType -> Box (1:N via `boxes` related_name, PROTECT on delete) -- Box -> Thing (1:N via `things` related_name, PROTECT on delete) -- Facet -> Tag (1:N via `tags` related_name, CASCADE on delete) -- Thing <-> Tag (M2M via `tags` related_name on Thing, `things` related_name on Tag) -- Thing -> ThingFile (1:N via `files` related_name, CASCADE on delete) -- Thing -> ThingLink (1:N via `links` related_name, CASCADE on delete) - -**Facet Cardinality:** -- `single`: A thing can have at most one tag from this facet (e.g., Priority: High/Medium/Low) -- `multiple`: A thing can have multiple tags from this facet (e.g., Category: Electronics, Tools) - -## Available Django Extensions - -The project includes these pre-installed packages: - -- **django-mptt**: Tree structures (categories, hierarchies) -- **django-mptt-admin**: Admin interface for MPTT models -- **django-admin-sortable2**: Drag-and-drop ordering in admin -- **django-nested-admin**: Nested inline forms in admin -- **django-nested-inline**: Additional nested inline support -- **django-revproxy**: Reverse proxy functionality -- **sorl-thumbnail**: Image thumbnailing -- **Pillow**: Image processing -- **gunicorn**: Production WSGI server -- **Markdown**: Markdown processing -- **bleach**: HTML sanitization -- **coverage**: Test coverage -- **Font Awesome**: Icon library (loaded via CDN) -- **jQuery**: JavaScript library (loaded via CDN) - -## Frontend/CSS Guidelines - -### Base Template - -The project uses a base template system at `labhelper/templates/base.html`. All page templates should extend this base: - -```django -{% extends "base.html" %} - -{% block title %}Page Title - LabHelper{% endblock %} - -{% block page_header %} - -{% endblock %} - -{% block content %} - -{% endblock %} - -{% block extra_css %}{% endblock %} -{% block extra_js %}{% endblock %} -``` - -### Design System - -**Color Scheme:** -- Primary gradient: `#667eea` (purple) to `#764ba2` (purple-blue) -- Success: Green gradient -- Error: Red gradient -- Background: Light gray `#f5f5f5` with gradient overlays -- Cards: White with subtle shadows - -**Components:** -- **Navigation**: Glassmorphism effect with blur backdrop. Desktop (≥769px) shows horizontal menu with dropdown for authenticated user (contains Box Management, Resources, Admin, Logout) -- **Buttons**: Gradient backgrounds with hover lift effect -- **Cards**: White with rounded corners and box shadows -- **Tables**: Gradient headers with hover row effects -- **Alerts**: Gradient backgrounds with icons -- **Form Inputs**: Focused states with color transitions - -**Typography:** -- System fonts: `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif` -- Headings: Bold, colored -- Body: Regular, dark gray - -**Icons:** -- Font Awesome 6.5.1 (CDN) -- Use semantic icons for actions -- Color: Match context or inherit from parent - -**Responsive Design:** -- Mobile-first approach -- Grid layouts with `repeat(auto-fill, minmax(250px, 1fr))` -- Flexbox for component layouts -- Breakpoints handled by grid and flex-wrap -- **Navigation**: Responsive navbar with hamburger menu on mobile (≤768px) and horizontal menu with user dropdown on desktop (≥769px). Mobile keeps all items in the dropdown list - -### CSS Guidelines - -**Static CSS Files:** -- Base styles live in `boxes/static/css/base.css` (loaded by `base.html` via `{% static %}`) -- Page-specific styles live in separate static CSS files (e.g., `thing_detail.css`, `edit_thing.css`) -- Child templates load their CSS via `{% block extra_css %}` with `` tags -- WhiteNoise serves and cache-busts static files via `CompressedManifestStaticFilesStorage` -- Run `python manage.py collectstatic` after adding or modifying static CSS files - -**Naming:** -- Use descriptive class names -- BEM pattern encouraged for complex components -- Inline styles allowed for template-specific one-off styling - -**Styles:** -- Use base CSS classes when possible -- Page-specific styles in dedicated static CSS files loaded via `{% block extra_css %}` -- JavaScript in `{% block extra_js %}` -- Smooth transitions (0.2s - 0.3s) -- Hover effects with transform and box-shadow - -**jQuery Usage:** -- Loaded in base template -- Use for interactive elements (toggles, hovers) -- Event delegation for dynamically added elements -- Focus/blur events for form inputs - -### Available Pages/Views - -| View Function | URL Pattern | Name | Description | -|---------------|-------------|------|-------------| -| `index` | `/` | `index` | Home page with search and tags overview | -| `boxes_list` | `/search/` | `search`, `boxes_list` | Boxes list page with tabular view | -| `box_management` | `/box-management/` | `box_management` | Manage boxes and box types | -| `add_box_type` | `/box-type/add/` | `add_box_type` | Add new box type | -| `edit_box_type` | `/box-type//edit/` | `edit_box_type` | Edit box type | -| `delete_box_type` | `/box-type//delete/` | `delete_box_type` | Delete box type | -| `add_box` | `/box/add/` | `add_box` | Add new box | -| `edit_box` | `/box//edit/` | `edit_box` | Edit box | -| `delete_box` | `/box//delete/` | `delete_box` | Delete box | -| `box_detail` | `/box//` | `box_detail` | View box contents | -| `thing_detail` | `/thing//` | `thing_detail` | Read-only view of thing details | -| `edit_thing` | `/thing//edit/` | `edit_thing` | Edit thing (name, description, picture, tags, files, links, move) | -| `add_things` | `/box//add/` | `add_things` | Add multiple things to a box | -| `search_api` | `/search/api/` | `search_api` | AJAX search endpoint | -| `resources_list` | `/resources/` | `resources_list` | List all links and files from things (sorted by thing name) | -| `LoginView` | `/login/` | `login` | Django auth login | -| `LogoutView` | `/logout/` | `logout` | Django auth logout | -| `admin.site` | `/admin/` | - | Django admin | - -**All views except login require authentication via `@login_required`.** - -### Template Best Practices - -1. **Always extend base template** - ```django - {% extends "base.html" %} - ``` - -2. **Use block system for content injection** - - `title`: Page title tag - - `page_header`: Page header with breadcrumbs - - `content`: Main page content - - `extra_css`: Additional CSS via `` tags to static files - - `extra_head`: Additional head elements - - `extra_js`: Additional JavaScript - -3. **Load required template tags** - ```django - {% load static %} {# Required when using {% static %} for CSS/asset links #} - {% load mptt_tags %} - {% load thumbnail %} - {% load dict_extras %} - ``` - -4. **Link page-specific CSS from static files** - ```django - {% block extra_css %} - - {% endblock %} - ``` - -5. **Use URL names for links** - ```django - - ``` - -6. **Use icons with Font Awesome** - ```django - - ``` - -7. **Add breadcrumbs for navigation** - ```django - - ``` - -8. **Icon alignment in lists**: When using icons in list items, use fixed width containers to ensure proper alignment - ```django - - - - ``` - -### Markdown Support - -The `Thing.description` field supports Markdown formatting with HTML sanitization for security. - -**Available Template Filters:** - -- `render_markdown`: Converts Markdown text to sanitized HTML with automatic link handling - - Converts Markdown syntax (headers, lists, bold, italic, links, code, tables, etc.) - - Sanitizes HTML using `bleach` to prevent XSS attacks - - Automatically adds `target="_blank"` and `rel="noopener noreferrer"` to external links - - Use in `thing_detail.html` for full rendered Markdown - -- `truncate_markdown`: Converts Markdown to plain text and truncates - - Strips HTML tags after Markdown conversion - - Adds ellipsis (`...`) if text exceeds specified length (default: 100) - - Use in `box_detail.html` or search API previews where space is limited - -**Usage Examples:** -```django - -
- {{ thing.description|render_markdown }} -
- - -{{ thing.description|truncate_markdown:100 }} -``` - -**Supported Markdown Features:** -- Bold: `**text**` or `__text__` -- Italic: `*text*` or `_text_` -- Headers: `# Header 1`, `## Header 2`, etc. -- Lists: `- item` or `1. item` -- Links: `[text](url)` -- Code: `` `code` `` or ` ```code block``` -- Blockquotes: `> quote` -- Tables: `| A | B |\n|---|---|` - -**Security:** -- All Markdown is sanitized before rendering -- Dangerous HTML tags (`