|
| 1 | +--- |
| 2 | +name: Backend Caching and Organization |
| 3 | +overview: Add IMemoryCache for seed sources with Cloudflare CDN-friendly HTTP headers, implement category-based organization using _CATEGORY__filename.ext convention, add CSV support with seed validation, sequential search continue options, and multi-source hydrate search. |
| 4 | +todos: |
| 5 | + - id: add_memory_cache |
| 6 | + content: Register IMemoryCache and add cache with Cloudflare-friendly HTTP headers |
| 7 | + status: completed |
| 8 | + - id: category_parsing |
| 9 | + content: Parse _CATEGORY__filename.ext format and extract category/displayName |
| 10 | + status: completed |
| 11 | + - id: organize_by_category |
| 12 | + content: Group seed sources by category, always include categories even if empty |
| 13 | + status: completed |
| 14 | + - id: csv_support |
| 15 | + content: Add CSV file support with seed validation (0-8 chars, dictionary only, uppercase conversion) |
| 16 | + status: completed |
| 17 | + - id: seed_validation |
| 18 | + content: Validate seeds - convert 0→O, lowercase→uppercase, reject invalid chars |
| 19 | + status: completed |
| 20 | + - id: icons_metadata |
| 21 | + content: Add icon metadata (🦆 for .db, 📊 for .csv, 📄 for .txt) to seed source objects |
| 22 | + status: completed |
| 23 | + - id: sequential_continue |
| 24 | + content: Add sequential search continue options with progress percentage |
| 25 | + status: completed |
| 26 | + - id: multi_source_hydrate |
| 27 | + content: Add API route for hydrate search with collection of sources |
| 28 | + status: completed |
| 29 | + - id: order_filters |
| 30 | + content: Order filters - unsaved first, then alphabetical by name |
| 31 | + status: completed |
| 32 | +--- |
| 33 | + |
| 34 | +**File:** `Motely.API/Program.cs` |
| 35 | + |
| 36 | +- Register `IMemoryCache` in service collection (if not already registered) |
| 37 | +- Create cache key constant: `"seed_sources_cache"` |
| 38 | +- Modify `/seed-sources` endpoint: |
| 39 | +- Check cache first |
| 40 | +- If cache miss, scan directories and build list |
| 41 | +- Store in cache with **absolute expiration** (30 minutes or 1 hour) - sources rarely change |
| 42 | +- **Important:** Add HTTP headers for Cloudflare CDN: |
| 43 | + - `Cache-Control: public, max-age=1800` (30 minutes) or `max-age=3600` (1 hour) |
| 44 | + - `ETag` header for cache validation |
| 45 | +- Return cached or fresh data |
| 46 | +- **No automatic invalidation** - cache expires naturally after long period, respects Cloudflare's caching |
| 47 | + |
| 48 | +### 2. Category-Based Organization System |
| 49 | + |
| 50 | +**File:** `Motely.API/Program.cs` |
| 51 | + |
| 52 | +- **Naming Convention:** `_CATEGORY__filename.ext` |
| 53 | +- Files starting with `_` have a category |
| 54 | +- Double underscore `__` separates category from filename |
| 55 | +- Example: `_Erratic_Deck__2s.db` → category: "Erratic Deck", filename: "2s.db" |
| 56 | +- Underscores in category name render as spaces in display (e.g., `_Erratic_Deck__` → "Erratic Deck") |
| 57 | +- **Helper Methods:** |
| 58 | +- `ParseCategoryFromFileName(string fileName)` → returns `(category: string?, displayName: string)` |
| 59 | + - If starts with `_`, extract category (everything before `__`) |
| 60 | + - Replace underscores in category with spaces for display |
| 61 | + - Extract filename (everything after `__`) |
| 62 | + - If no category, `category = null`, `displayName = fileName` |
| 63 | +- **Organization:** |
| 64 | +- Always include categories (even if empty) |
| 65 | +- Group by category, then sort alphabetically within category |
| 66 | +- Uncategorized files go in "Uncategorized" category |
| 67 | +- Built-in sources (always first): "All Seeds (default)", "Random 1M" |
| 68 | +- Actions (always last): "New word list…" |
| 69 | + |
| 70 | +### 3. CSV Support with Seed Validation |
| 71 | + |
| 72 | +**File:** `Motely.API/Program.cs`**File:** `Motely/Executors/JsonSearchExecutor.cs` (if needed) |
| 73 | + |
| 74 | +- Add `.csv` file support in `/seed-sources` endpoint |
| 75 | +- Parse CSV files (simple: one seed per line, or comma-separated) |
| 76 | +- **Seed Validation Rules:** |
| 77 | +- Seeds must be 0-8 characters |
| 78 | +- Valid dictionary: `[ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789]` (35 chars) |
| 79 | +- **UX Conversions:** |
| 80 | + - Convert `0` → `O` (zero to letter O) |
| 81 | + - Convert lowercase → uppercase |
| 82 | + - Reject any character not in dictionary |
| 83 | +- Skip invalid seeds (log warning, continue processing) |
| 84 | +- **Helper Method:** |
| 85 | +- `ValidateAndNormalizeSeed(string seed)` → returns normalized seed or null if invalid |
| 86 | + - Trim whitespace |
| 87 | + - Replace `0` with `O` |
| 88 | + - Convert to uppercase |
| 89 | + - Check length (0-8 chars) |
| 90 | + - Check all chars in dictionary |
| 91 | + - Return normalized seed or null |
| 92 | + |
| 93 | +### 4. Icons and Metadata |
| 94 | + |
| 95 | +**File:** `Motely.API/Program.cs` |
| 96 | + |
| 97 | +- Add `icon` field to seed source objects: |
| 98 | +- `.db` files → `"🦆"` (duck icon) |
| 99 | +- `.csv` files → `"📊"` (spreadsheet icon) |
| 100 | +- `.txt` files → `"📄"` (document icon) |
| 101 | +- Built-in → `"⭐"` (star icon) |
| 102 | +- Actions → `"➕"` (plus icon) |
| 103 | +- Add `category` field (string or null) |
| 104 | +- Add `displayName` field (clean version without category prefix) |
| 105 | +- Add `fileName` field (original filename) |
| 106 | + |
| 107 | +### 5. Sequential Search Continue Options |
| 108 | + |
| 109 | +**File:** `Motely.API/Program.cs`**File:** `Motely.API/wwwroot/JAML/jaml.js` |
| 110 | + |
| 111 | +- Modify `/seed-sources` endpoint to include sequential search options: |
| 112 | +- `"all"` → "All Seeds (Start from beginning)" |
| 113 | +- `"all:continue"` → "All Seeds (Continue Saved Search - {progress}%)" |
| 114 | + - Calculate progress from `SearchManager` if there's a paused/resumable search |
| 115 | + - Format: `"All Seeds (Continue Saved Search - 2.3456%)"` (4 decimal places) |
| 116 | +- **Backend Logic:** |
| 117 | +- Check if there's a resumable sequential search (check DuckDB for saved batch position) |
| 118 | +- If resumable, include continue option with calculated progress |
| 119 | +- Progress calculation: `(currentBatch / totalBatches) * 100` with 4 decimal precision |
| 120 | +- **Frontend Logic (jaml.js):** |
| 121 | +- When progress updates come through SignalR, check if search is resumable |
| 122 | +- Auto-select continue option if available |
| 123 | +- Update dropdown options dynamically |
| 124 | + |
| 125 | +### 6. Multi-Source Hydrate Search |
| 126 | + |
| 127 | +**File:** `Motely.API/Program.cs`**File:** `Motely.API/SearchManager.cs` |
| 128 | + |
| 129 | +- Add new endpoint: `POST /search/hydrate` or modify existing `/search` to accept array of sources |
| 130 | +- Request body: |
| 131 | + ```csharp |
| 132 | + { |
| 133 | + filterJaml: string, |
| 134 | + seedSources: string[], // Array of source keys: ["db:file1.db", "txt:file2.txt", "csv:file3.csv"] |
| 135 | + seedCount: long?, |
| 136 | + startBatch: long?, |
| 137 | + cutoff: int? |
| 138 | + } |
| 139 | + ``` |
| 140 | + |
| 141 | + |
| 142 | + |
| 143 | + |
| 144 | +- **Backend Implementation:** |
| 145 | +- Parse each source key (format: `{type}:{filename}`) |
| 146 | +- Load seeds from all sources (combine into single list) |
| 147 | +- Validate and normalize all seeds |
| 148 | +- Remove duplicates |
| 149 | +- Sort seeds (if needed) |
| 150 | +- Pass combined seed list to search executor |
| 151 | + |
| 152 | +### 7. Filter Ordering |
| 153 | + |
| 154 | +**File:** `Motely.API/Program.cs` |
| 155 | + |
| 156 | +- Order filters: |
| 157 | + |
| 158 | +1. **Unsaved/running searches first** (already implemented with `Insert(0)`) |
| 159 | +2. **Then by name** (alphabetical, case-insensitive) |
| 160 | + |
| 161 | +- Use `OrderBy(f => f.name, StringComparer.OrdinalIgnoreCase)` after inserting unsaved ones |
| 162 | + |
| 163 | +### 8. Seed Source Response Structure |
| 164 | + |
| 165 | +**File:** `Motely.API/Program.cs` |
| 166 | + |
| 167 | +- Response structure: |
| 168 | + ```csharp |
| 169 | + { |
| 170 | + sources: [ |
| 171 | + { |
| 172 | + key: "all", |
| 173 | + label: "All Seeds (default)", |
| 174 | + kind: "builtin", |
| 175 | + icon: "⭐", |
| 176 | + category: null, |
| 177 | + displayName: "All Seeds (default)" |
| 178 | + }, |
| 179 | + { |
| 180 | + key: "db:_Erratic_Deck__2s.db", |
| 181 | + label: "2s", // or full name for display |
| 182 | + kind: "db", |
| 183 | + icon: "🦆", |
| 184 | + category: "Erratic Deck", |
| 185 | + displayName: "2s", |
| 186 | + fileName: "_Erratic_Deck__2s.db" |
| 187 | + }, |
| 188 | + // ... grouped by category |
| 189 | + ], |
| 190 | + categories: [ |
| 191 | + { |
| 192 | + name: "Built-in", |
| 193 | + sources: [...] |
| 194 | + }, |
| 195 | + { |
| 196 | + name: "Erratic Deck", |
| 197 | + sources: [...] |
| 198 | + }, |
| 199 | + { |
| 200 | + name: "Uncategorized", |
| 201 | + sources: [...] |
| 202 | + } |
| 203 | + ] |
| 204 | + } |
| 205 | + ``` |
| 206 | + |
| 207 | + |
| 208 | + |
| 209 | + |
| 210 | +## Files to Modify |
| 211 | + |
| 212 | +1. `Motely.API/Program.cs` - Main implementation |
| 213 | +2. `Motely.API/wwwroot/JAML/jaml.js` - Frontend updates for continue options |
| 214 | +3. `Motely/Executors/JsonSearchExecutor.cs` - CSV parsing (if needed) |
| 215 | +4. Potentially create `Motely.API/SeedSourceManager.cs` service class (optional) |
| 216 | + |
| 217 | +## Testing Checklist |
| 218 | + |
| 219 | +- [ ] Seed sources are cached and don't hit disk on every request |
| 220 | +- [ ] HTTP headers respect Cloudflare CDN caching |
| 221 | +- [ ] Category parsing works correctly (`_CATEGORY__filename.ext`) |
| 222 | +- [ ] Underscores in category names render as spaces |
| 223 | +- [ ] Seed sources are grouped by category |
0 commit comments