vibes-plug 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/BLUEPRINT.md +125 -0
- package/CHANGELOG.md +195 -0
- package/CONTRIBUTING.md +199 -0
- package/LICENSE +21 -0
- package/README.md +263 -0
- package/SECURITY.md +21 -0
- package/banner.png +0 -0
- package/package.json +25 -0
- package/plugin.json +8 -0
- package/scripts/update_skills.js +75 -0
- package/skills/ai-llm-integration-expert/SKILL.md +162 -0
- package/skills/api-design-expert/SKILL.md +310 -0
- package/skills/app-analyzer-optimizer/SKILL.md +189 -0
- package/skills/asisten_ramah/SKILL.md +41 -0
- package/skills/authentication-identity-expert/SKILL.md +45 -0
- package/skills/auto-doc-updater/SKILL.md +204 -0
- package/skills/bootstrap-to-modern/SKILL.md +87 -0
- package/skills/brainstorming/SKILL.md +353 -0
- package/skills/bun-runtime-expert/SKILL.md +211 -0
- package/skills/ci-cd-devops-architect/SKILL.md +45 -0
- package/skills/cloud-hosting-expert/SKILL.md +244 -0
- package/skills/coderabbit/SKILL.md +192 -0
- package/skills/data-telemetry-expert/SKILL.md +213 -0
- package/skills/database-orm-expert/SKILL.md +294 -0
- package/skills/design-system-architect/SKILL.md +243 -0
- package/skills/e2e-testing-expert/SKILL.md +315 -0
- package/skills/event-driven-architect/SKILL.md +81 -0
- package/skills/firebase-security-expert/SKILL.md +195 -0
- package/skills/fullstack-expert/SKILL.md +202 -0
- package/skills/fullstack-expert/references/api_design_guide.md +466 -0
- package/skills/fullstack-expert/references/devops_infrastructure.md +477 -0
- package/skills/fullstack-expert/references/multi_language_backend.md +528 -0
- package/skills/fullstack-expert/references/system_design_patterns.md +358 -0
- package/skills/fullstack-expert/scripts/api_contract_validator.py +253 -0
- package/skills/fullstack-expert/scripts/architecture_analyzer.py +326 -0
- package/skills/gemini-agent-booster/SKILL.md +135 -0
- package/skills/global-a11y-i18n-expert/SKILL.md +81 -0
- package/skills/go-programming-expert/SKILL.md +295 -0
- package/skills/hig/SKILL.md +188 -0
- package/skills/js-backend-expert/SKILL.md +192 -0
- package/skills/mcp-server-architect/SKILL.md +194 -0
- package/skills/mobile-expo-expert/SKILL.md +186 -0
- package/skills/monday-design-aesthetic/SKILL.md +67 -0
- package/skills/monorepo-architect/SKILL.md +227 -0
- package/skills/mpa-orchestrator/SKILL.md +101 -0
- package/skills/multi-agent-orchestration/SKILL.md +234 -0
- package/skills/multiple-entry-points/SKILL.md +55 -0
- package/skills/mvc-expert/SKILL.md +231 -0
- package/skills/payment-gateway-expert/SKILL.md +45 -0
- package/skills/performance-web-vitals/SKILL.md +332 -0
- package/skills/prd-architect/SKILL.md +191 -0
- package/skills/production-ready-hardener/SKILL.md +469 -0
- package/skills/production-ready-hardener/references/performance_optimization.md +441 -0
- package/skills/production-ready-hardener/references/production_checklist.md +161 -0
- package/skills/production-ready-hardener/references/security_hardening_guide.md +379 -0
- package/skills/production-ready-hardener/scripts/production_readiness_scanner.py +875 -0
- package/skills/python-programming-expert/SKILL.md +271 -0
- package/skills/realtime-collaboration-expert/SKILL.md +45 -0
- package/skills/rust-programming-expert/SKILL.md +235 -0
- package/skills/saas-billing/SKILL.md +377 -0
- package/skills/saas-multi-tenant/SKILL.md +237 -0
- package/skills/saas-mvp-launcher/SKILL.md +231 -0
- package/skills/saas-transformer/SKILL.md +446 -0
- package/skills/saas-transformer/references/billing_integration_guide.md +401 -0
- package/skills/saas-transformer/references/feature_gating_patterns.md +137 -0
- package/skills/saas-transformer/references/saas_transformation_checklist.md +121 -0
- package/skills/saas-transformer/scripts/saas_transformation_scanner.py +254 -0
- package/skills/scalability-clean-code/SKILL.md +229 -0
- package/skills/secure-fuzz-testing/SKILL.md +201 -0
- package/skills/senior-frontend/SKILL.md +161 -0
- package/skills/senior-frontend/references/frontend_best_practices.md +806 -0
- package/skills/senior-frontend/references/nextjs_optimization_guide.md +724 -0
- package/skills/senior-frontend/references/react_patterns.md +746 -0
- package/skills/senior-frontend/scripts/bundle_analyzer.py +407 -0
- package/skills/senior-frontend/scripts/component_generator.py +329 -0
- package/skills/senior-frontend/scripts/frontend_scaffolder.py +1005 -0
- package/skills/senior-fullstack/SKILL.md +167 -0
- package/skills/senior-fullstack/references/architecture_patterns.md +160 -0
- package/skills/senior-fullstack/references/development_workflows.md +222 -0
- package/skills/senior-fullstack/references/tech_stack_guide.md +190 -0
- package/skills/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
- package/skills/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
- package/skills/senior-fullstack/scripts/project_scaffolder.py +114 -0
- package/skills/seo/SKILL.md +225 -0
- package/skills/seo/references/cwv-thresholds.md +108 -0
- package/skills/seo/references/eeat-framework.md +214 -0
- package/skills/seo/references/quality-gates.md +155 -0
- package/skills/seo/references/schema-types.md +118 -0
- package/skills/seo-aeo-landing-page-writer/SKILL.md +97 -0
- package/skills/seo-geo/SKILL.md +188 -0
- package/skills/session-handoff-resume/SKILL.md +158 -0
- package/skills/skill_baru/SKILL.md +147 -0
- package/skills/spa-orchestrator/SKILL.md +288 -0
- package/skills/state-management-expert/SKILL.md +272 -0
- package/skills/supabase-migration/SKILL.md +45 -0
- package/skills/supabase-security-expert/SKILL.md +243 -0
- package/skills/tailwind-expert/SKILL.md +188 -0
- package/skills/tanstack-query-expert/SKILL.md +199 -0
- package/skills/tauri-expert/SKILL.md +97 -0
- package/skills/token-saver/SKILL.md +111 -0
- package/skills/typescript-expert/SKILL.md +279 -0
- package/skills/ui-components-expert/SKILL.md +63 -0
- package/skills/ui-ux-pro-max/SKILL.md +201 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/prompts.csv +24 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +59 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-310.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-312.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-310.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-312.pyc +0 -0
- package/skills/ui-ux-pro-max/scripts/core.py +257 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +493 -0
- package/skills/ui-ux-pro-max/scripts/search.py +81 -0
- package/skills/ui_ux_expert/SKILL.md +114 -0
- package/skills/vibe-code-gardener/SKILL.md +173 -0
- package/skills/web-scraper/SKILL.md +205 -0
- package/skills/web-scraper/references/data-transforms.md +397 -0
- package/skills/web-scraper/references/extraction-patterns.md +475 -0
- package/skills/web-scraper/references/output-templates.md +481 -0
- package/skills/zero-to-prod-orchestrator/SKILL.md +180 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# Data Transforms Reference
|
|
2
|
+
|
|
3
|
+
Patterns for cleaning, normalizing, deduplicating, and enriching
|
|
4
|
+
extracted web data. Apply these transforms in Phase 5 (Transform)
|
|
5
|
+
between extraction and validation.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Automatic Transforms
|
|
10
|
+
|
|
11
|
+
Always apply these to every extraction result.
|
|
12
|
+
|
|
13
|
+
### Whitespace Cleanup
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
# Remove leading/trailing whitespace, collapse internal whitespace
|
|
17
|
+
value = ' '.join(value.split())
|
|
18
|
+
|
|
19
|
+
# Remove zero-width characters
|
|
20
|
+
import re
|
|
21
|
+
value = re.sub(r'[\u200b\u200c\u200d\ufeff\u00a0]', ' ', value).strip()
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Patterns to handle:
|
|
25
|
+
- `\n`, `\r`, `\t` inside cell values -> single space
|
|
26
|
+
- Multiple consecutive spaces -> single space
|
|
27
|
+
- Non-breaking spaces (` `, `\u00a0`) -> regular space
|
|
28
|
+
- Zero-width characters -> remove
|
|
29
|
+
|
|
30
|
+
### HTML Entity Decode
|
|
31
|
+
|
|
32
|
+
| Entity | Character | Entity | Character |
|
|
33
|
+
|:------------|:----------|:-----------|:----------|
|
|
34
|
+
| `&` | `&` | `"` | `"` |
|
|
35
|
+
| `<` | `<` | `'` | `'` |
|
|
36
|
+
| `>` | `>` | `'` | `'` |
|
|
37
|
+
| ` ` | ` ` | `’` | (curly ') |
|
|
38
|
+
| `—` | `--` | `—` | `--` |
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import html
|
|
42
|
+
value = html.unescape(value)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Unicode Normalization
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import unicodedata
|
|
49
|
+
value = unicodedata.normalize('NFKC', value)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This handles:
|
|
53
|
+
- Fancy quotes -> standard quotes
|
|
54
|
+
- Ligatures -> separate characters (e.g. `fi` -> `fi`)
|
|
55
|
+
- Full-width characters -> standard (e.g. `A` -> `A`)
|
|
56
|
+
- Superscript/subscript numbers -> regular numbers
|
|
57
|
+
|
|
58
|
+
### Empty Value Standardization
|
|
59
|
+
|
|
60
|
+
| Input | Markdown Output | JSON Output |
|
|
61
|
+
|:------------------------|:----------------|:------------|
|
|
62
|
+
| `""` (empty string) | `N/A` | `null` |
|
|
63
|
+
| `"-"` or `"--"` | `N/A` | `null` |
|
|
64
|
+
| `"N/A"`, `"n/a"`, `"NA"`| `N/A` | `null` |
|
|
65
|
+
| `"None"`, `"null"` | `N/A` | `null` |
|
|
66
|
+
| `"TBD"`, `"TBA"` | `TBD` | `"TBD"` |
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Price Normalization
|
|
71
|
+
|
|
72
|
+
Apply when extracting product, pricing, or financial data.
|
|
73
|
+
|
|
74
|
+
### Extraction Pattern
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import re
|
|
78
|
+
|
|
79
|
+
def normalize_price(raw):
|
|
80
|
+
if not raw:
|
|
81
|
+
return None
|
|
82
|
+
# Remove currency words
|
|
83
|
+
cleaned = re.sub(r'(?i)(USD|EUR|GBP|BRL|R\$|US\$)', '', raw)
|
|
84
|
+
# Extract numeric value (handles 1,234.56 and 1.234,56 formats)
|
|
85
|
+
match = re.search(r'[\d.,]+', cleaned)
|
|
86
|
+
if not match:
|
|
87
|
+
return None
|
|
88
|
+
num_str = match.group()
|
|
89
|
+
# Detect format: if last separator is comma with 2 digits after, it's decimal
|
|
90
|
+
if re.search(r',\d{2}$', num_str):
|
|
91
|
+
num_str = num_str.replace('.', '').replace(',', '.')
|
|
92
|
+
else:
|
|
93
|
+
num_str = num_str.replace(',', '')
|
|
94
|
+
return float(num_str)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Currency Detection
|
|
98
|
+
|
|
99
|
+
| Symbol/Code | Currency | Symbol/Code | Currency |
|
|
100
|
+
|:------------|:---------|:------------|:---------|
|
|
101
|
+
| `$`, `US$`, `USD` | US Dollar | `R$`, `BRL` | Brazilian Real |
|
|
102
|
+
| `€`, `EUR` | Euro | `£`, `GBP` | British Pound |
|
|
103
|
+
| `¥`, `JPY` | Yen | `₹`, `INR` | Indian Rupee |
|
|
104
|
+
| `C$`, `CAD` | Canadian Dollar | `A$`, `AUD` | Australian Dollar |
|
|
105
|
+
|
|
106
|
+
### Output Format
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"price": 29.99,
|
|
111
|
+
"currency": "USD",
|
|
112
|
+
"rawPrice": "$29.99"
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
For Markdown, show formatted: `$29.99` (right-aligned in table).
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## Date Normalization
|
|
121
|
+
|
|
122
|
+
Normalize all dates to ISO-8601 format.
|
|
123
|
+
|
|
124
|
+
### Common Formats to Handle
|
|
125
|
+
|
|
126
|
+
| Input Format | Example | Normalized |
|
|
127
|
+
|:------------------------|:---------------------|:-------------------|
|
|
128
|
+
| Full text | February 25, 2026 | 2026-02-25 |
|
|
129
|
+
| Short text | Feb 25, 2026 | 2026-02-25 |
|
|
130
|
+
| US numeric | 02/25/2026 | 2026-02-25 |
|
|
131
|
+
| EU numeric | 25/02/2026 | 2026-02-25 |
|
|
132
|
+
| ISO already | 2026-02-25 | 2026-02-25 |
|
|
133
|
+
| Relative | 3 days ago | (compute from now) |
|
|
134
|
+
| Relative | Yesterday | (compute from now) |
|
|
135
|
+
| Timestamp | 1740441600 | 2025-02-25 |
|
|
136
|
+
| With time | 2026-02-25T14:30:00Z | 2026-02-25 14:30 |
|
|
137
|
+
|
|
138
|
+
### Ambiguous Dates
|
|
139
|
+
|
|
140
|
+
When format is ambiguous (e.g. `03/04/2026`):
|
|
141
|
+
- Default to US format (MM/DD/YYYY) unless site is clearly non-US
|
|
142
|
+
- Check page `lang` attribute or URL TLD for locale hints
|
|
143
|
+
- Note ambiguity in delivery notes
|
|
144
|
+
|
|
145
|
+
### Relative Date Resolution
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
from datetime import datetime, timedelta
|
|
149
|
+
import re
|
|
150
|
+
|
|
151
|
+
def resolve_relative_date(text):
|
|
152
|
+
text = text.lower().strip()
|
|
153
|
+
today = datetime.now()
|
|
154
|
+
|
|
155
|
+
if 'today' in text: return today.strftime('%Y-%m-%d')
|
|
156
|
+
if 'yesterday' in text: return (today - timedelta(days=1)).strftime('%Y-%m-%d')
|
|
157
|
+
|
|
158
|
+
match = re.search(r'(\d+)\s*(hour|day|week|month|year)s?\s*ago', text)
|
|
159
|
+
if match:
|
|
160
|
+
n, unit = int(match.group(1)), match.group(2)
|
|
161
|
+
deltas = {'hour': 0, 'day': n, 'week': n*7, 'month': n*30, 'year': n*365}
|
|
162
|
+
return (today - timedelta(days=deltas.get(unit, 0))).strftime('%Y-%m-%d')
|
|
163
|
+
|
|
164
|
+
return text # Return as-is if can't parse
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## URL Resolution
|
|
170
|
+
|
|
171
|
+
Convert relative URLs to absolute.
|
|
172
|
+
|
|
173
|
+
### Patterns
|
|
174
|
+
|
|
175
|
+
| Input | Base URL | Resolved |
|
|
176
|
+
|:-------------------------|:----------------------------|:--------------------------------------|
|
|
177
|
+
| `/products/item-1` | `https://example.com/shop` | `https://example.com/products/item-1` |
|
|
178
|
+
| `item-1` | `https://example.com/shop/` | `https://example.com/shop/item-1` |
|
|
179
|
+
| `//cdn.example.com/img` | `https://example.com` | `https://cdn.example.com/img` |
|
|
180
|
+
| `https://other.com/page` | (any) | `https://other.com/page` (absolute) |
|
|
181
|
+
|
|
182
|
+
### JavaScript Resolution
|
|
183
|
+
|
|
184
|
+
```javascript
|
|
185
|
+
function resolveUrl(relative, base) {
|
|
186
|
+
try { return new URL(relative, base || window.location.href).href; }
|
|
187
|
+
catch { return relative; }
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Phone Normalization
|
|
194
|
+
|
|
195
|
+
For contact mode extraction.
|
|
196
|
+
|
|
197
|
+
### Pattern
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
import re
|
|
201
|
+
|
|
202
|
+
def normalize_phone(raw):
|
|
203
|
+
if not raw:
|
|
204
|
+
return None
|
|
205
|
+
# Remove all non-digit chars except leading +
|
|
206
|
+
digits = re.sub(r'[^\d+]', '', raw)
|
|
207
|
+
if not digits or len(digits) < 7:
|
|
208
|
+
return None
|
|
209
|
+
# Add + prefix if looks international
|
|
210
|
+
if len(digits) >= 11 and not digits.startswith('+'):
|
|
211
|
+
digits = '+' + digits
|
|
212
|
+
return digits
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Format by Context
|
|
216
|
+
|
|
217
|
+
| Context | Format Example |
|
|
218
|
+
|:-----------------|:---------------------|
|
|
219
|
+
| JSON output | `"+5511999998888"` |
|
|
220
|
+
| Markdown table | `+55 11 99999-8888` |
|
|
221
|
+
| CSV output | `"+5511999998888"` |
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Deduplication
|
|
226
|
+
|
|
227
|
+
### Exact Deduplication
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
def deduplicate(records, key_fields=None):
|
|
231
|
+
"""Remove exact duplicate records.
|
|
232
|
+
If key_fields provided, deduplicate by those fields only.
|
|
233
|
+
"""
|
|
234
|
+
seen = set()
|
|
235
|
+
unique = []
|
|
236
|
+
for record in records:
|
|
237
|
+
if key_fields:
|
|
238
|
+
key = tuple(record.get(f) for f in key_fields)
|
|
239
|
+
else:
|
|
240
|
+
key = tuple(sorted(record.items()))
|
|
241
|
+
if key not in seen:
|
|
242
|
+
seen.add(key)
|
|
243
|
+
unique.append(record)
|
|
244
|
+
return unique, len(records) - len(unique) # returns (unique_list, removed_count)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Near-Duplicate Detection
|
|
248
|
+
|
|
249
|
+
When records share key fields but differ in details:
|
|
250
|
+
1. Group by key fields (e.g. product name + source)
|
|
251
|
+
2. For each group, keep the record with fewest null values
|
|
252
|
+
3. If tie, keep the first occurrence
|
|
253
|
+
4. Report in notes: "Merged N near-duplicate records"
|
|
254
|
+
|
|
255
|
+
### Dedup Key Selection by Mode
|
|
256
|
+
|
|
257
|
+
| Mode | Key Fields |
|
|
258
|
+
|:---------|:----------------------------------|
|
|
259
|
+
| product | name + source (or name + brand) |
|
|
260
|
+
| contact | name + email (or name + org) |
|
|
261
|
+
| jobs | title + company + location |
|
|
262
|
+
| events | title + date + location |
|
|
263
|
+
| table | all fields (exact match) |
|
|
264
|
+
| list | first 2-3 identifying fields |
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Text Cleaning
|
|
269
|
+
|
|
270
|
+
### Remove Noise
|
|
271
|
+
|
|
272
|
+
Common noise patterns to strip from extracted text:
|
|
273
|
+
|
|
274
|
+
| Pattern | Action |
|
|
275
|
+
|:-----------------------------------|:--------------------------|
|
|
276
|
+
| `\[edit\]`, `\[citation needed\]` | Remove (Wikipedia) |
|
|
277
|
+
| `Read more...`, `See more` | Remove (truncation markers)|
|
|
278
|
+
| `Sponsored`, `Ad`, `Promoted` | Remove or flag |
|
|
279
|
+
| Cookie consent text | Remove |
|
|
280
|
+
| Navigation breadcrumbs | Remove |
|
|
281
|
+
| Footer boilerplate | Remove |
|
|
282
|
+
|
|
283
|
+
### Sentence Case Normalization
|
|
284
|
+
|
|
285
|
+
When extracting ALL-CAPS or inconsistent-case text:
|
|
286
|
+
|
|
287
|
+
```python
|
|
288
|
+
def normalize_case(text):
|
|
289
|
+
if text.isupper() and len(text) > 3:
|
|
290
|
+
return text.title() # ALL CAPS -> Title Case
|
|
291
|
+
return text
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Only apply when: field is clearly ALL-CAPS input (common in older sites),
|
|
295
|
+
user requests it, or data looks better normalized.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Data Type Coercion
|
|
300
|
+
|
|
301
|
+
### Automatic Type Detection
|
|
302
|
+
|
|
303
|
+
| Raw Value | Detected Type | Coerced Value |
|
|
304
|
+
|:--------------|:--------------|:------------------|
|
|
305
|
+
| `"123"` | integer | `123` |
|
|
306
|
+
| `"12.99"` | float | `12.99` |
|
|
307
|
+
| `"true"` | boolean | `true` |
|
|
308
|
+
| `"false"` | boolean | `false` |
|
|
309
|
+
| `"2026-02-25"`| date string | `"2026-02-25"` |
|
|
310
|
+
| `"$29.99"` | price | `29.99` + currency|
|
|
311
|
+
| `"4.5/5"` | rating | `4.5` |
|
|
312
|
+
| `"1,234"` | integer | `1234` |
|
|
313
|
+
|
|
314
|
+
### Rating Normalization
|
|
315
|
+
|
|
316
|
+
```python
|
|
317
|
+
import re
|
|
318
|
+
|
|
319
|
+
def normalize_rating(raw):
|
|
320
|
+
if not raw:
|
|
321
|
+
return None
|
|
322
|
+
match = re.search(r'([\d.]+)\s*(?:/\s*([\d.]+))?', str(raw))
|
|
323
|
+
if match:
|
|
324
|
+
score = float(match.group(1))
|
|
325
|
+
max_score = float(match.group(2)) if match.group(2) else 5.0
|
|
326
|
+
return round(score / max_score * 5, 1) # Normalize to /5 scale
|
|
327
|
+
return None
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Enrichment Patterns
|
|
333
|
+
|
|
334
|
+
### Domain Extraction
|
|
335
|
+
|
|
336
|
+
Add domain from full URLs:
|
|
337
|
+
```python
|
|
338
|
+
from urllib.parse import urlparse
|
|
339
|
+
|
|
340
|
+
def extract_domain(url):
|
|
341
|
+
try:
|
|
342
|
+
parsed = urlparse(url)
|
|
343
|
+
domain = parsed.netloc.replace('www.', '')
|
|
344
|
+
return domain
|
|
345
|
+
except:
|
|
346
|
+
return None
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Word Count
|
|
350
|
+
|
|
351
|
+
For article mode:
|
|
352
|
+
```python
|
|
353
|
+
def word_count(text):
|
|
354
|
+
return len(text.split()) if text else 0
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Relative Time
|
|
358
|
+
|
|
359
|
+
Add human-readable time since date:
|
|
360
|
+
```python
|
|
361
|
+
def time_since(date_str):
|
|
362
|
+
from datetime import datetime
|
|
363
|
+
try:
|
|
364
|
+
dt = datetime.fromisoformat(date_str)
|
|
365
|
+
delta = datetime.now() - dt
|
|
366
|
+
if delta.days == 0: return "Today"
|
|
367
|
+
if delta.days == 1: return "Yesterday"
|
|
368
|
+
if delta.days < 7: return f"{delta.days} days ago"
|
|
369
|
+
if delta.days < 30: return f"{delta.days // 7} weeks ago"
|
|
370
|
+
if delta.days < 365: return f"{delta.days // 30} months ago"
|
|
371
|
+
return f"{delta.days // 365} years ago"
|
|
372
|
+
except:
|
|
373
|
+
return None
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Transform Pipeline Order
|
|
379
|
+
|
|
380
|
+
Apply transforms in this sequence:
|
|
381
|
+
|
|
382
|
+
1. **HTML entity decode** - raw text cleanup
|
|
383
|
+
2. **Unicode normalization** - character standardization
|
|
384
|
+
3. **Whitespace cleanup** - spacing normalization
|
|
385
|
+
4. **Empty value standardization** - null/N/A handling
|
|
386
|
+
5. **URL resolution** - relative to absolute
|
|
387
|
+
6. **Data type coercion** - strings to numbers/dates
|
|
388
|
+
7. **Price normalization** - if applicable
|
|
389
|
+
8. **Date normalization** - if applicable
|
|
390
|
+
9. **Phone normalization** - if applicable
|
|
391
|
+
10. **Text cleaning** - noise removal
|
|
392
|
+
11. **Deduplication** - remove duplicates
|
|
393
|
+
12. **Sorting** - user-requested order
|
|
394
|
+
13. **Enrichment** - domain, word count, etc.
|
|
395
|
+
|
|
396
|
+
Not all steps apply to every extraction. Apply only what's relevant
|
|
397
|
+
to the data type and extraction mode.
|