Commit Graph

110 Commits

Author SHA1 Message Date
9fc3de4798 feat(5-03-01): create export service with CSV and Excel generation for inventory and audit trails 2026-04-22 17:43:05 +03:00
a7746a14ea test(5.1): add integration and unit tests for quick quantity adjustment
- Vitest hook tests for useQuantityAdjustment (optimistic updates, debouncing)
- Pytest backend tests for PATCH /items/{itemId} endpoint
- Tests cover success, failure, validation, and audit logging scenarios
2026-04-22 17:42:08 +03:00
9fd20fff7c feat(5.1): implement quantity display component and adjustment hook
- Create QuantityDisplay component with tap-to-edit UI + +/- buttons
- Implement useQuantityAdjustment hook with optimistic updates, debouncing
- Add PATCH /items/{itemId} backend endpoint with audit logging
- Components support inline quantity adjustment without modal friction
2026-04-22 17:42:04 +03:00
8dca073c50 feat(4.1-03,4.1-04): implement search orchestrator and integration tests 2026-04-22 16:37:38 +03:00
6e5642ff06 feat(4.1-02): implement web scraper and spec extractor services for spare-parts search 2026-04-22 16:37:01 +03:00
2ecaa6b2e8 feat(4.1-01): create spare-parts classification whitelist module with fuzzy matching 2026-04-22 16:34:10 +03:00
c46c8414b8 feat: store original EXIF-stripped image for debugging
- Save original image before crop/rotation with '_debug_original' variant
- Store original_photo_path in labels_data.image_processing for debug access
- Update DebugRotationPanel to display original image instead of processed
- Update ItemDetailModal to pass original image path to debug panel
- Enables accurate crop/rotation visualization with true original image
2026-04-22 11:13:09 +03:00
500d090dfc fix: strip EXIF from image blob in backend before processing
Backend receives the original blob which may have EXIF orientation metadata.
Strip it before processing to ensure backend analyzes the same raw image space
that Gemini analyzed (which had EXIF stripped before sending).

This ensures rotation_degrees are applied correctly to the same image state.
2026-04-22 10:32:38 +03:00
bc2a6219fe refactor: strip EXIF orientation before Gemini analysis for coordinate accuracy
Instead of transforming coordinates after Gemini returns crop_bounds, strip EXIF
orientation from image before sending to Gemini. This ensures:
- Gemini analyzes the same raw image as our backend
- crop_bounds are in raw image coordinate space
- No coordinate transformation needed
- Works for all images (with or without EXIF)

Added strip_exif_orientation() utility that removes orientation tag and
re-encodes image. Used in extract_label endpoint before sending to Gemini.
2026-04-22 10:29:11 +03:00
1425856af5 fix: transform crop_bounds from EXIF-applied to raw image space
Gemini analyzes images with EXIF orientation applied (e.g., 90° CW rotation),
returning crop_bounds in that coordinate space. We need to transform them back
to raw image coordinates before cropping.

For EXIF orientation 6 (Rotate 90 CW):
- Raw: 4032×3024 (landscape)
- Displayed (after EXIF): 3024×4032 (portrait)
- Transform portrait coords back to landscape before cropping

This fixes the issue where crop was applied to wrong image region.
2026-04-22 10:25:44 +03:00
ee1fcfbee6 fix: remove duplicate log imports in create_item function 2026-04-22 10:17:34 +03:00
79d8a71c97 fix: import log at module level for extraction logging 2026-04-22 10:15:25 +03:00
9586aecfdc debug: log file sizes at extraction and processing stages
Add logging to compare file sizes:
- [EXTRACT] sent to Gemini
- [CREATE_ITEM] received when creating item

This will reveal if image is being processed/changed between extraction and local processing.
2026-04-22 10:14:03 +03:00
4e23899f87 fix: remove negation from rotation to match prompt semantics
New prompt defines rotation_degrees as already signed:
- positive = counter-clockwise rotation
- negative = clockwise rotation

Old code negated it: rotate(-rotation_degrees), which inverted the direction.
Now: rotate(rotation_degrees) directly uses Gemini's signed value.
2026-04-22 10:06:29 +03:00
92c8517663 debug: add explicit logging to show crop bounds and rotation values 2026-04-22 09:55:53 +03:00
197dcadfee fix: apply crop bounds before EXIF rotation to match AI analysis
Gemini analyzes raw image and returns crop_bounds for that coordinate space.
Previous code applied EXIF rotation first, changing image dimensions, then
used crop_bounds on the rotated image (coordinate mismatch).

Now: crop on raw image (matches AI) → then apply EXIF + manual rotation.
This ensures cropped region contains the actual item, not background.
2026-04-22 09:50:08 +03:00
a2847092ac debug: add detailed logging to image processing pipeline
- Log input image size, crop bounds, and rotation degrees
- Log crop rectangle coordinates and result size
- Log OpenCV smart crop success/failure details
- Track pixel count to detect zero-size crops
2026-04-22 09:23:51 +03:00
e5615826d6 Revert "fix: apply rotation before cropping for better text orientation"
This reverts commit f8e54d0f8b.
2026-04-22 09:02:22 +03:00
f8e54d0f8b fix: apply rotation before cropping for better text orientation
- Move manual rotation before cropping and text detection
- Detect text orientation on full rotated image (not just cropped region)
- This allows text angle detection to see full context and properly orient labels
- Crop happens after orientation correction for cleaner results
2026-04-22 08:58:17 +03:00
09a66bd3d5 feat: clean up image files when item is deleted from catalog
- Delete photo_path and photo_thumbnail_path files on item deletion
- Handle file not found gracefully with logging
- Preserves audit logs while removing actual image files
2026-04-22 08:54:22 +03:00
64d177e791 fix: complete image pipeline - rotation, URL, preview
- Add rotation_degrees parameter to ImageProcessor.process_photo()
- Pass rotation through _auto_save_photo_from_extraction() to processor
- Allow no-crop fallback when crop_bounds is None
- Add buildPhotoUrl() helper to resolve backend URLs correctly
- Update frontend components to use backend URL for image sources
- Replace Use/Skip Photo buttons with checkbox in AI extraction UI
- Add images/ to .gitignore to prevent accidental commits

Addresses: rotation never applied, image 404s (relative to Next.js not backend), preview blank in edit form
2026-04-22 08:50:25 +03:00
bbe60bb471 test: add integration tests for item creation with auto-photo-save 2026-04-21 19:31:26 +03:00
4f63b3b99e feat: integrate auto-photo-save into item creation endpoint
- Extend ItemCreate schema with optional extracted_image_bytes (base64) and image_processing (dict)
- Update create_item endpoint to call _auto_save_photo_from_extraction after item creation
- Decode base64 image bytes and pass crop_bounds, rotation_degrees to helper
- Don't block item creation if photo save fails (log warning instead)
- Item returned with photo_path, photo_thumbnail_path populated if save succeeded
- Full backward compatibility: old clients without image fields work unchanged
- Add 5 integration tests covering all scenarios:
  - Create item WITH image_processing → photo auto-saved
  - Create item WITHOUT image_processing → no photo (backward compatible)
  - Create item WITH invalid image_processing → item created, photo skipped
  - Create item WITH crop_bounds=None → item created, photo skipped
  - Create item WITH bytes but NO processing metadata → item created, photo skipped
- All 158 backend tests passing, zero regressions
2026-04-21 19:00:06 +03:00
eca1ab7fd0 feat: add _auto_save_photo_from_extraction helper with graceful fallbacks 2026-04-21 18:56:17 +03:00
ada3669217 test: add tests for image_processing field from AI extraction
- Added 11 comprehensive tests for image_processing parsing
- Tests validate crop_bounds structure: {x, y, width, height} all ints >= 0
- Tests validate rotation_degrees: int/float, -360 to +360
- Tests validate confidence: float, 0.0 to 1.0
- Tests graceful handling when image_processing field is missing
- Tests multiple items with image_processing data
- Tests partial data handling (optional fields)
- Tests with both Gemini and Claude providers
- Updated extract_label_info() to preserve and validate image_processing field
- All tests passing, no regressions
2026-04-21 18:53:04 +03:00
770b02864d fix: handle CORS preflight OPTIONS requests before routing 2026-04-21 18:17:00 +03:00
6f1e7731d7 fix: implement subnet-aware CORS middleware to replace insecure wildcard origins 2026-04-21 17:58:14 +03:00
904e153d8a temp: allow all CORS origins for debugging LDAP login issue
Temporarily using allow_origins=['*'] to debug whether CORS is blocking
LDAP login requests from VPN client. This is insecure for production.
TODO: Fix subnet pattern matching in ALLOWED_ORIGINS configuration.
2026-04-21 15:32:01 +03:00
2078cd9ade fix: resolve CORS preflight issues and Next.js dev origin warnings
- Simplify backend CORS middleware to use standard FastAPI implementation
- Keep subnet validation function for future use in route-level checks
- Add Tailscale subnet pattern to Next.js allowedDevOrigins config
- Both individual IPs and subnet configurations now work correctly
2026-04-21 15:19:45 +03:00
983d6e4bb4 feat: add subnet-based CORS validation support for VPN/Tailscale origins
- Add ipaddress module for subnet parsing (10.0.0.0/24 format)
- Implement subnet validation in CORS middleware
- Separate individual IPs from subnet definitions in EXTRA_ALLOWED_ORIGINS
- Custom SubnetAwareCORSMiddleware for dynamic origin validation
- Support both exact IP matches and subnet ranges
- Backward compatible with existing ALLOWED_ORIGINS list
2026-04-21 15:17:29 +03:00
e46777b933 merge: Phase 1 image system implementation complete
- Database: Added photo_path, photo_thumbnail_path, photo_upload_date fields
- Services: ImageStorage (file ops) + ImageProcessor (image processing pipeline)
- API: POST/PUT /api/items/{id}/photo upload endpoints with validation
- API: GET /api/items/{id} returns photo URLs
- Static: FastAPI StaticFiles mount for /images/ directory
- Tests: 127+ comprehensive tests across all components
- Security: Fixed race conditions, path traversal, crop validation
- Ready for Phase 2 (frontend UI)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-20 22:47:31 +03:00
294555c574 feat(phase1): add static file serving for /images/ 2026-04-20 22:43:33 +03:00
af8dcbae3a fix(phase1): fix race condition, path traversal, double processing, validation in photo API 2026-04-20 22:39:02 +03:00
8d2750cfa3 fix(phase1): fix deprecated PIL APIs, private API, exception handling, magic numbers, transparency, DoS prevention 2026-04-20 22:21:51 +03:00
3aafacab12 feat(phase1): implement OpenCV image processing pipeline
- Create ImageProcessor service with EXIF orientation detection
- Implement smart cropping via OpenCV contour detection (10% padding)
- Add text orientation detection using Hough line transform
- Resize and compress images to 1200px with 85% JPEG quality
- Generate 200px square thumbnails with center crop
- Fallback to Pillow if OpenCV fails
- Comprehensive test suite: 28 tests all passing
- File size validation (reject >10MB)
- Graceful error handling for corrupted/invalid images
- Update requirements.txt with opencv-python, piexif, python-magic
2026-04-20 22:17:11 +03:00
01321bf607 fix(phase1): restore API contract while preserving N+1 optimization
The N+1 optimization in save_image() pre-lowercases the existing_files list
before passing to get_unique_filename(). However, this broke the API contract:
the function should handle any-case input to remain robust.

Changed: get_unique_filename() now defensively lowercases the input list,
ensuring collision detection works regardless of input case.

Benefits:
- Fixes implicit API contract change (function expected any-case input)
- Maintains N+1 optimization (pre-lowercasing still works)
- Supports both optimization and edge cases (direct function calls)
- All 22 tests pass
2026-04-20 22:09:58 +03:00
2951ed81eb fix(phase1): add logging, remove unused import, add error handling 2026-04-20 22:06:18 +03:00
ea49cd6e4a feat(phase1): add image storage utilities
- Create backend/services/image_storage.py with 4 core functions:
  - sanitize_filename(): remove unsafe chars, limit to 255 chars, convert to lowercase
  - get_unique_filename(): handle collisions with UUID suffix (format: {name}_{uuid8}_{variant}.jpg)
  - ensure_image_directories(): create /images/ root and category subdirs on startup
  - save_image(): save bytes to /images/{category}/{filename}, returns relative path
- Create comprehensive test suite (22 tests) covering all functionality
- Integrate ensure_image_directories() into FastAPI startup event
- Directory structure: /images/{category}/{filename}
- Collision handling: auto-suffix with UUID if filename exists
- All tests passing, pathlib.Path for safe operations
2026-04-20 21:57:26 +03:00
92f6977cae fix(phase1): add photo fields to schemas and set datetime default 2026-04-20 21:49:51 +03:00
6ed88fdb84 feat(phase1): add photo fields to Item model 2026-04-20 21:36:18 +03:00
d9ead1aafd fix: repair login endpoint registration in auth router 2026-04-19 17:20:11 +03:00
8fcd4150e5 refactor: split admin/config.py into ai_config and db_config 2026-04-19 17:10:56 +03:00
239368e595 refactor: split schemas.py into schemas/ package 2026-04-19 17:08:11 +03:00
6dc300d339 refactor: split bulk-sync into backend/routers/sync.py 2026-04-19 12:37:09 +03:00
90e9a60640 refactor: split LDAP auth into backend/routers/auth.py 2026-04-19 12:35:40 +03:00
145fa21805 fix: update backend tests to match actual API - all 41 tests passing 2026-04-19 09:08:21 +03:00
0c70e216e1 fix: update test files to match actual backend schemas and endpoints 2026-04-19 08:43:56 +03:00
5895215209 test: add offline sync and UUID idempotency tests 2026-04-18 17:03:26 +00:00
436a3cdd97 test: add AI extraction pipeline tests (mocked) 2026-04-18 17:02:41 +00:00
2734a7f4d2 test: add category CRUD tests 2026-04-18 17:01:42 +00:00