-
Notifications
You must be signed in to change notification settings - Fork 0
feat(backend): sync item history with current_location #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
… implement signal for history updates
vivjd
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall! Please review copilot's comments and re-request review!
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ON_CHANGING_EVENTS before updating
|
Instead of creating another copy of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 5 out of 7 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| try: | ||
| item: CollectionItem = instance.item | ||
| item.update_location_from_history() | ||
| except Exception as e: | ||
| # Log error but don't raise to prevent disrupting the original save | ||
| logger.error(f"Failed to update item location for item {instance.item_id}: {e}") |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bare except catching all Exception types could hide critical bugs or system errors. Consider either catching more specific exceptions (e.g., DatabaseError, ValidationError) or re-raising certain critical exceptions after logging. This ensures that genuine system errors don't get silently swallowed.
|
|
||
| for item in items: | ||
| try: | ||
| item.update_location_from_history() | ||
| updated_count += 1 | ||
| except Exception as e: | ||
| self.stdout.write(self.style.ERROR(f"Error updating item {item.id}: {e}")) | ||
|
|
||
| self.stdout.write(self.style.SUCCESS(f"Updated {updated_count} items")) |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The command tracks updated_count for successful updates but doesn't track or report the number of failures. Consider adding a failed_count variable and including it in the final summary message to give users complete visibility into the rebuild operation results.
| for item in items: | |
| try: | |
| item.update_location_from_history() | |
| updated_count += 1 | |
| except Exception as e: | |
| self.stdout.write(self.style.ERROR(f"Error updating item {item.id}: {e}")) | |
| self.stdout.write(self.style.SUCCESS(f"Updated {updated_count} items")) | |
| failed_count = 0 | |
| for item in items: | |
| try: | |
| item.update_location_from_history() | |
| updated_count += 1 | |
| except Exception as e: | |
| failed_count += 1 | |
| self.stdout.write(self.style.ERROR(f"Error updating item {item.id}: {e}")) | |
| self.stdout.write( | |
| self.style.SUCCESS( | |
| f"Updated {updated_count} items, {failed_count} failures" | |
| ) | |
| ) |
| def test_command_all_items(self): | ||
| """Test command rebuilds all items.""" | ||
| out = StringIO() | ||
| call_command("rebuild_item_locations", stdout=out) | ||
|
|
||
| output = out.getvalue() | ||
| self.assertIn("Updated", output) |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test only verifies that the success message is printed but doesn't verify that the item's location fields were actually updated correctly. Consider adding assertions to check that current_location and is_on_floor were updated based on the item's history.
| class ItemLocationTest(TestCase): | ||
| """Test the item location functionality.""" | ||
|
|
||
| def setUp(self): | ||
| self.user = User.objects.create_user(email="test@example.com", name="Test User", password="testpass", role="VOLUNTEER") | ||
| self.location_storage = Location.objects.create(name="Storage A", location_type="STORAGE") | ||
| self.location_floor = Location.objects.create(name="Main Floor", location_type="FLOOR") | ||
| self.item = CollectionItem.objects.create( | ||
| item_code="TEST001", title="Test Item", current_location=self.location_storage | ||
| ) | ||
|
|
||
| def test_update_location_from_history_initial_event(self): | ||
| """Test location update with INITIAL event.""" | ||
| # Create INITIAL history event | ||
| ItemHistory.objects.create(item=self.item, event_type="INITIAL", to_location=self.location_storage, acted_by=self.user) | ||
|
|
||
| # Update location using model method | ||
| self.item.update_location_from_history() | ||
|
|
||
| self.assertEqual(self.item.current_location, self.location_storage) | ||
| self.assertFalse(self.item.is_on_floor) | ||
|
|
||
| def test_update_location_from_history_floor_move(self): | ||
| """Test location update when item moves to floor.""" | ||
| # Create events | ||
| ItemHistory.objects.create(item=self.item, event_type="INITIAL", to_location=self.location_storage) | ||
| ItemHistory.objects.create( | ||
| item=self.item, event_type="ARRIVED", from_location=self.location_storage, to_location=self.location_floor | ||
| ) | ||
|
|
||
| self.item.update_location_from_history() | ||
|
|
||
| self.assertEqual(self.item.current_location, self.location_floor) | ||
| self.assertTrue(self.item.is_on_floor) | ||
|
|
||
| def test_signal_triggers_on_location_changing_event(self): | ||
| """Test that signal updates item location for location-changing events.""" | ||
| # Create ARRIVED event - should trigger signal | ||
| ItemHistory.objects.create( | ||
| item=self.item, event_type="ARRIVED", to_location=self.location_floor, from_location=self.location_storage | ||
| ) | ||
|
|
||
| # Refresh item from database | ||
| self.item.refresh_from_db() | ||
|
|
||
| # Verify location was updated by signal | ||
| self.assertEqual(self.item.current_location, self.location_floor) | ||
| self.assertTrue(self.item.is_on_floor) | ||
|
|
||
| def test_signal_does_not_update_location_for_workflow_events(self): | ||
| """Test that signal triggers but doesn't update location for workflow-only events.""" | ||
| original_location = self.item.current_location | ||
| original_is_on_floor = self.item.is_on_floor | ||
|
|
||
| # Create MOVE_REQUESTED event - should NOT trigger signal | ||
| ItemHistory.objects.create( | ||
| item=self.item, event_type="MOVE_REQUESTED", to_location=self.location_floor, from_location=self.location_storage | ||
| ) | ||
|
|
||
| # Refresh item from database | ||
| self.item.refresh_from_db() | ||
|
|
||
| # Verify location was NOT updated | ||
| self.assertEqual(self.item.current_location, original_location) | ||
| self.assertEqual(self.item.is_on_floor, original_is_on_floor) |
Copilot
AI
Jan 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test coverage is missing for VERIFIED and LOCATION_CORRECTION event types, which are defined as location-changing events in LOCATION_CHANGING_EVENTS. Consider adding test cases to verify that these event types also trigger the signal and correctly update the item's location.
Summary
Implements automatic synchronization between ItemHistory events and CollectionItem location fields. When location-changing history events are created (INITIAL, ARRIVED, VERIFIED, LOCATION_CORRECTION), the item's current_location and is_on_floor fields are automatically updated via Django signals. Also includes a management command for rebuilding all item locations from history.
Related Issues
Changes
Auto-sync Logic:
post_savesignal onItemHistorymodel to automatically trigger location updatescurrent_locationandis_on_floorfields when location-changing events are created (INITIAL, ARRIVED, VERIFIED, LOCATION_CORRECTION)Maintenance Command:
rebuild_item_locationsDjango management command for data recovery and consistency checks--item-idflag to rebuild single items or processes all items when no flag providedTesting & Code Quality:
How to Test