refactor(elixir): remove unused qr_code

This commit is contained in:
Schuwi
2025-09-18 00:08:32 +02:00
parent 264adbfb98
commit e078770557
3 changed files with 0 additions and 313 deletions

View File

@@ -1,79 +0,0 @@
# AprilTag Migration Summary
## Completed Changes
### 1. Database Migration ✅
- Migrated from `qr_code` string field to `apriltag_id` integer field
- Added constraint to ensure valid AprilTag IDs (0-586)
- Created unique index for apriltag_id
- Preserved old qr_code data as qr_code_old for rollback safety
### 2. Schema Updates ✅
- Updated `StorageLocation` schema to use `apriltag_id` instead of `qr_code`
- Added validation for AprilTag ID range (0-586)
- Implemented auto-assignment of next available ID
- Added unique constraint validation
### 3. Business Logic Refactoring ✅
- Replaced `ComponentsElixir.QRCode` module with `ComponentsElixir.AprilTag` module
- Updated inventory functions to use AprilTag IDs instead of QR code strings
- Implemented AprilTag ID availability checking
- Added bulk SVG generation functionality
### 4. UI/UX Improvements ✅
- Replaced dropdown with 587 options with better UX:
- Radio buttons for "Auto-assign" vs "Manual selection"
- Number input for specific ID selection when manual mode selected
- Shows available ID count and examples
- Different interface for add vs edit forms
- Updated templates to show AprilTag information instead of QR codes
- Added download functionality for AprilTag SVGs
### 5. AprilTag Generation ✅
- Created `ComponentsElixir.AprilTag` module for managing tag36h11 family
- Generated all 587 placeholder SVG files with human-readable IDs
- Added Mix task `mix apriltag.generate_all` for batch generation
- SVG files served statically at `/apriltags/tag36h11_id_XXX.svg`
### 6. Event Handling ✅
- Updated LiveView event handlers for AprilTag scanning/assignment
- Added mode switching for manual vs automatic assignment
- Implemented proper form state management for different modes
## Benefits Achieved
1. **Better UX**: No more 587-option dropdown menu
2. **Future-Ready**: AprilTags designed for multi-tag detection scenarios
3. **Robust**: 587 unique IDs provide ample space without conflicts
4. **Maintainable**: Simpler integer ID system vs complex string encoding
5. **Industry Standard**: AprilTags widely used in robotics/AR applications
## Current State
- ✅ Database schema updated
- ✅ All 587 placeholder SVG files generated
- ✅ UI forms updated with better UX
- ✅ Business logic migrated to AprilTag system
-**Next**: Real AprilTag pattern generation (future enhancement)
-**Next**: Camera detection integration (future enhancement)
## Usage
### Generate AprilTag SVGs
```bash
mix apriltag.generate_all # Generate missing files
mix apriltag.generate_all --force # Regenerate all files
```
### Available AprilTag IDs
- Range: 0-586 (tag36h11 family)
- Auto-assignment picks next available ID
- Manual assignment allows specific ID selection
- Unique constraint prevents conflicts
### File Locations
- SVG files: `priv/static/apriltags/tag36h11_id_XXX.svg`
- URL pattern: `/apriltags/tag36h11_id_XXX.svg`
- Placeholder pattern includes human-readable ID label
The system is now ready for use with AprilTags instead of QR codes! The placeholder SVGs will work perfectly for testing and development until we implement actual AprilTag pattern generation.

View File

@@ -1,233 +0,0 @@
defmodule ComponentsElixir.QRCode do
@moduledoc """
QR Code generation and parsing for storage locations.
Provides functionality to generate QR codes for storage locations
and parse them back to retrieve location information.
"""
@doc """
Generates a QR code data string for a storage location.
Format: SL:{level}:{qr_code}:{parent_qr_or_ROOT}
## Examples
iex> location = %StorageLocation{level: 1, qr_code: "ABC123", parent: nil}
iex> ComponentsElixir.QRCode.generate_qr_data(location)
"SL:1:ABC123:ROOT"
iex> parent = %StorageLocation{qr_code: "SHELF1"}
iex> drawer = %StorageLocation{level: 2, qr_code: "DRAW01", parent: parent}
iex> ComponentsElixir.QRCode.generate_qr_data(drawer)
"SL:2:DRAW01:SHELF1"
"""
def generate_qr_data(storage_location) do
parent_code =
case storage_location.parent do
nil -> "ROOT"
parent -> parent.qr_code
end
"SL:#{storage_location.level}:#{storage_location.qr_code}:#{parent_code}"
end
@doc """
Parses a QR code string and extracts components.
## Examples
iex> ComponentsElixir.QRCode.parse_qr_data("SL:1:ABC123:ROOT")
{:ok, %{level: 1, code: "ABC123", parent: "ROOT"}}
iex> ComponentsElixir.QRCode.parse_qr_data("invalid")
{:error, :invalid_format}
"""
def parse_qr_data(qr_string) do
case String.split(qr_string, ":") do
["SL", level_str, code, parent] ->
case Integer.parse(level_str) do
{level, ""} ->
{:ok, %{level: level, code: code, parent: parent}}
_ ->
{:error, :invalid_level}
end
_ ->
{:error, :invalid_format}
end
end
@doc """
Validates if a string looks like a storage location QR code.
## Examples
iex> ComponentsElixir.QRCode.valid_storage_qr?("SL:1:ABC123:ROOT")
true
iex> ComponentsElixir.QRCode.valid_storage_qr?("COMP:12345")
false
"""
def valid_storage_qr?(qr_string) do
case parse_qr_data(qr_string) do
{:ok, _} -> true
_ -> false
end
end
@doc """
Generates a printable label data structure for a storage location.
This could be used to generate PDF labels or send to a label printer.
"""
def generate_label_data(storage_location) do
qr_data = generate_qr_data(storage_location)
%{
qr_code: qr_data,
name: storage_location.name,
path: storage_location.path,
level: storage_location.level,
description: storage_location.description
}
end
@doc """
Generates multiple QR codes for disambiguation testing.
This is useful for testing multi-QR detection scenarios.
"""
def generate_test_codes(storage_locations) when is_list(storage_locations) do
Enum.map(storage_locations, &generate_qr_data/1)
end
@doc """
Generates a QR code image (PNG) for a storage location.
Returns the binary PNG data that can be saved to disk or served directly.
## Options
- `:size` - The size of the QR code image in pixels (default: 200)
- `:background` - Background color as `{r, g, b}` tuple (default: white)
- `:foreground` - Foreground color as `{r, g, b}` tuple (default: black)
## Examples
iex> location = %StorageLocation{level: 1, qr_code: "ABC123"}
iex> {:ok, png_data} = ComponentsElixir.QRCode.generate_qr_image(location)
iex> File.write!("/tmp/qr_code.png", png_data)
"""
def generate_qr_image(storage_location, _opts \\ []) do
qr_data = generate_qr_data(storage_location)
qr_data
|> QRCode.create()
|> QRCode.render(:png)
end
@doc """
Generates and saves a QR code image to the specified file path.
## Examples
iex> location = %StorageLocation{level: 1, qr_code: "ABC123"}
iex> ComponentsElixir.QRCode.save_qr_image(location, "/tmp/qr_code.png")
:ok
"""
def save_qr_image(storage_location, file_path, opts \\ []) do
case generate_qr_image(storage_location, opts) do
{:ok, png_data} ->
# Ensure directory exists
file_path
|> Path.dirname()
|> File.mkdir_p!()
File.write!(file_path, png_data)
:ok
{:error, reason} ->
{:error, reason}
end
end
@doc """
Generates a QR code image URL for serving via Phoenix static files.
This function generates the QR code image and saves it to the static directory,
returning a URL that can be used in templates.
## Examples
iex> location = %StorageLocation{id: 123, qr_code: "ABC123"}
iex> ComponentsElixir.QRCode.get_qr_image_url(location)
"/qr_codes/storage_location_123.png"
"""
def get_qr_image_url(storage_location, opts \\ []) do
filename = "storage_location_#{storage_location.id}.png"
file_path = Path.join([Application.app_dir(:components_elixir, "priv/static/user_generated/qr_codes"), filename])
# Generate and save the image if it doesn't exist or if regeneration is forced
force_regenerate = Keyword.get(opts, :force_regenerate, false)
if force_regenerate || !File.exists?(file_path) do
case save_qr_image(storage_location, file_path, opts) do
:ok -> "/user_generated/qr_codes/#{filename}"
{:error, _reason} -> nil
end
else
"/user_generated/qr_codes/#{filename}"
end
end
@doc """
Generates QR code images for multiple storage locations (bulk generation).
Returns a list of results indicating success or failure for each location.
## Examples
iex> locations = [location1, location2, location3]
iex> ComponentsElixir.QRCode.bulk_generate_images(locations)
[
{:ok, "/qr_codes/storage_location_1.png"},
{:ok, "/qr_codes/storage_location_2.png"},
{:error, "Failed to generate for location 3"}
]
"""
def bulk_generate_images(storage_locations, opts \\ []) do
# Use Task.async_stream for concurrent generation with back-pressure
storage_locations
|> Task.async_stream(
fn location ->
case get_qr_image_url(location, Keyword.put(opts, :force_regenerate, true)) do
nil -> {:error, "Failed to generate QR code for location #{location.id}"}
url -> {:ok, url}
end
end,
timeout: :infinity,
max_concurrency: System.schedulers_online() * 2
)
|> Enum.map(fn {:ok, result} -> result end)
end
@doc """
Cleans up QR code images for deleted storage locations.
Should be called when storage locations are deleted to prevent orphaned files.
"""
def cleanup_qr_image(storage_location_id) do
filename = "storage_location_#{storage_location_id}.png"
file_path = Path.join([Application.app_dir(:components_elixir, "priv/static/user_generated/qr_codes"), filename])
if File.exists?(file_path) do
File.rm(file_path)
else
:ok
end
end
end

View File

@@ -60,7 +60,6 @@ defmodule ComponentsElixir.MixProject do
depth: 1},
{:swoosh, "~> 1.16"},
{:req, "~> 0.5"},
{:qr_code, "~> 3.1"},
{:telemetry_metrics, "~> 1.0"},
{:telemetry_poller, "~> 1.0"},
{:gettext, "~> 0.26"},