landvex: Fixar och tester klara för alla komponenter

- Datafabrik: Dockerfile fix, agentorkestrering fungerar
- Vision: Identify-modell, FAISS, OCR alla testade
- API: Alla 7 integrationstester passerade
- Upplösare: Entitetsupplösning verifierad
This commit is contained in:
Bernt
2026-07-05 06:41:32 +00:00
parent f4f853d94b
commit aee0f09db8
19583 changed files with 1450867 additions and 1153 deletions
@@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""
Applicera förenklad infographic på alla branschsidor.
Användning: python3 apply-simple-infographic.py [--dry-run]
"""
import argparse
from pathlib import Path
def apply_infographic(industry_dir, dry_run=False):
"""Applicera infographic på en branschsida."""
html_file = industry_dir / 'index.html'
if not html_file.exists():
return False, "index.html saknas"
# Läs befintlig HTML
content = html_file.read_text(encoding='utf-8')
# Kolla om redan har infographic
if 'qi-infographic' in content:
return False, "Har redan infographic"
# Läs infographic-komponent
infographic_path = industry_dir.parent / '_components' / 'infographic-industry-simple.html'
if not infographic_path.exists():
return False, f"Komponent saknas: {infographic_path}"
infographic = infographic_path.read_text(encoding='utf-8')
if dry_run:
return True, "Skulle applicera"
# Injicera före </body>
body_end = content.rfind('</body>')
if body_end == -1:
return False, "Ingen </body> hittad"
injection = f'''
<!-- === INFOGRAPHIC === -->
<div class="section section-gray">
<div class="container">
{infographic}
</div>
</div>
<!-- === END INFOGRAPHIC === -->
'''
new_content = content[:body_end] + injection + '\n' + content[body_end:]
html_file.write_text(new_content, encoding='utf-8')
return True, "Applicerad"
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--dry-run', action='store_true')
args = parser.parse_args()
root = Path(__file__).parent.parent / 'se'
print("=== Applicera förenklad infographic ===\n")
count = 0
for subdir in sorted(root.iterdir()):
if not subdir.is_dir():
continue
if subdir.name.startswith('_'):
continue
if not (subdir / 'index.html').exists():
continue
success, msg = apply_infographic(subdir, dry_run=args.dry_run)
status = "" if success else "⚠️"
print(f" {status} {subdir.name}: {msg}")
if success:
count += 1
print(f"\n=== {count} sidor uppdaterade ===")
if __name__ == '__main__':
main()
@@ -0,0 +1,145 @@
#!/usr/bin/env python3
"""
Anpassa branschspecifika infographics per undersektor.
Användning: python3 customize-industry.py [industry-dir]
"""
import os
import sys
import re
from pathlib import Path
# Branschspecifika konfigurationer
INDUSTRIES = {
'facade-inspection': {
'name': 'Fasadinspektion',
'step_1_title': 'Hitta fasader nära dig',
'step_1_desc': 'Se tillgängliga fasaduppdrag på kartan. Välj byggnader i ditt område.',
'step_2_title': 'Dokumentera skick',
'step_2_desc': 'Fotografera fasad, fönster och entré enligt instruktionerna i appen.',
'step_3_title': 'Få betalt',
'step_3_desc': 'Ladda upp bilderna. Godkännande inom 24h. Betalt varje måndag.',
'value_1_title': 'Snabbare än traditionell besiktning',
'value_1_desc': 'Få bilddata inom timmar istället för veckor.',
'value_2_title': 'Kvalitetssäkrat',
'value_2_desc': 'Varje submission granskas av AI och valideras mot GPS.',
'cta_subtext': 'Börja tjäna på fasadinspektion idag.',
'cta_button_text': 'Bli Zoomer',
},
'bridge-inspection': {
'name': 'Broinspektion',
'step_1_title': 'Hitta broar i ditt område',
'step_1_desc': 'Se tillgängliga broinspektionsuppdrag. Välj de som passar din rutt.',
'step_2_title': 'Dokumentera broskick',
'step_2_desc': 'Fotografera brokropp, leder, räcken och fundament enligt spec.',
'step_3_title': 'Bidra till säkerhet',
'step_3_desc': 'Dina bilder hjälper kommuner att upptäcka skador tidigt. Betalt varje måndag.',
'value_1_title': 'Kritisk infrastruktur',
'value_1_desc': 'Hjälp till att hålla Sveriges broar säkra.',
'value_2_title': 'Högre ersättning',
'value_2_desc': 'Broinspektion betalar 200500 kr per uppdrag.',
'cta_subtext': 'Bidra till säkrare infrastruktur och tjäna pengar.',
'cta_button_text': 'Bli broinspektör',
},
'storm-damage': {
'name': 'Stormskadebedömning',
'step_1_title': 'Få notis vid storm',
'step_1_desc': 'quiXzoom skickar push-notiser när stormskadeuppdrag aktiveras i ditt område.',
'step_2_title': 'Dokumentera skador',
'step_2_desc': 'Fotografera tak, fasader, träd och vägar. Prioritera säkerheten.',
'step_3_title': 'Snabbast betalt',
'step_3_desc': 'Stormskadeuppdrag har högst prioritet. Godkännande och betalning inom 48h.',
'value_1_title': 'Högst ersättning',
'value_1_desc': '300500 kr per uppdrag. Bonus för snabb insamling.',
'value_2_title': 'Hjälp samhället',
'value_2_desc': 'Dina bilder påskyndar skadebedömning och återställning.',
'cta_subtext': 'Bli först på plats efter stormen.',
'cta_button_text': 'Registrera dig',
},
'property-documentation': {
'name': 'Fastighetsdokumentation',
'step_1_title': 'Hitta fastigheter',
'step_1_desc': 'Se tillgängliga fastighetsuppdrag. Välj bostadsrätter, kontor eller butiker.',
'step_2_title': 'Dokumentera noggrant',
'step_2_desc': 'Fotografera exteriör, interiör och detaljer enligt checklista.',
'step_3_title': 'Få betalt veckovis',
'step_3_desc': 'Godkända uppdrag betalas ut varje måndag. Ingen väntetid.',
'value_1_title': 'Stort utbud',
'value_1_desc': 'Många fastighetsbolag behöver löpande dokumentation.',
'value_2_title': 'Flexibelt',
'value_2_desc': 'Välj uppdrag som passar din dag.',
'cta_subtext': 'Dokumentera fastigheter och tjäna pengar.',
'cta_button_text': 'Bli Zoomer',
},
}
# Default för branscher som inte finns i config
DEFAULT_CONFIG = {
'name': 'Fältdatainsamling',
'step_1_title': 'Hitta uppdrag',
'step_1_desc': 'Se tillgängliga uppdrag nära dig i quiXzoom-appen.',
'step_2_title': 'Dokumentera',
'step_2_desc': 'Följ instruktionerna och ta bilder med din smartphone.',
'step_3_title': 'Få betalt',
'step_3_desc': 'Betalt varje vecka för godkända uppdrag.',
'value_1_title': 'Flexibelt arbete',
'value_1_desc': 'Jobba när du vill, där du vill.',
'value_2_title': 'Snabb betalning',
'value_2_desc': 'Få betalt varje måndag via Stripe.',
'cta_subtext': 'Börja tjäna pengar på fältdatainsamling idag.',
'cta_button_text': 'Bli Zoomer',
}
def customize_industry_page(industry_dir):
"""Anpassa infographic för en specifik bransch."""
industry_name = industry_dir.name
config = INDUSTRIES.get(industry_name, DEFAULT_CONFIG)
# Läs template
template_path = industry_dir.parent / '_components' / 'infographic-industry.html'
if not template_path.exists():
print(f" ❌ Template saknas: {template_path}")
return False
template = template_path.read_text(encoding='utf-8')
# Ersätt placeholders
result = template
for key, value in config.items():
placeholder = '{{' + key.upper() + '}}'
result = result.replace(placeholder, value)
# Ta bort oersatta placeholders (men inte kommentaren)
result = re.sub(r'\{\{([A-Z_]+)\}\}', lambda m: '' if m.group(1) != 'PLACEHOLDERS' else m.group(0), result)
# Skriv till fil
output_path = industry_dir / 'infographic-custom.html'
output_path.write_text(result, encoding='utf-8')
print(f"{industry_name}/infographic-custom.html skapad")
return True
def main():
root = Path(__file__).parent.parent / 'se'
print("=== Branschanpassade Infographics ===\n")
count = 0
for subdir in sorted(root.iterdir()):
if not subdir.is_dir():
continue
if subdir.name.startswith('_'):
continue
if not (subdir / 'index.html').exists():
continue
if customize_industry_page(subdir):
count += 1
print(f"\n=== {count} branschspecifika infographics skapade ===")
print("\nNästa steg:")
print(" 1. Uppdatera integrate.py för att använda custom infographics")
print(" 2. Testa lokalt")
print(" 3. Deploy till S3")
if __name__ == '__main__':
main()
+137
View File
@@ -0,0 +1,137 @@
#!/bin/bash
# Integrera infographic-komponenter på alla quiXzoom landningssidor
# Körs från quixzoom-market-pages/ roten
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
COMPONENTS_DIR="$ROOT_DIR/se/_components"
echo "=== quiXzoom Infographic Integration ==="
echo "Root: $ROOT_DIR"
echo ""
# Funktion: injicera zoomer-infographic före footer
inject_zoomer_infographic() {
local file="$1"
local title="$2"
# Kontrollera om redan integrerad
if grep -q "qz-infographic" "$file" 2>/dev/null; then
echo " ⚠️ Skippar (redan integrerad): $(basename $(dirname $file))/$(basename $file)"
return
fi
# Läs in infographic-komponenten
local infographic=$(cat "$COMPONENTS_DIR/infographic-zoomer.html")
# Skapa temporär fil med infographic injicerad före </body>
local tmpfile="${file}.tmp"
# Extrahera allt före </body>
awk '
/<\/body>/ {
# Skriv ut infographic innan </body>
print ""
print "<!-- === INFOGRAPHIC: Zoomer Journey === -->"
print "<div class=\"section\">"
print " <div class=\"container\">"
print " <!-- Infographic injicerad automatiskt -->"
print infographic_content
print " </div>"
print "</div>"
print "<!-- === END INFOGRAPHIC === -->"
print ""
}
{ print }
' infographic_content="$infographic" "$file" > "$tmpfile"
mv "$tmpfile" "$file"
echo " ✅ Integrerad: $(basename $(dirname $file))/$(basename $file)"
}
# Funktion: injicera business-infographic på index-sidan
inject_business_infographic() {
local file="$1"
if grep -q "lb-infographic" "$file" 2>/dev/null; then
echo " ⚠️ Skippar (redan integrerad): $(basename $file)"
return
fi
local infographic=$(cat "$COMPONENTS_DIR/infographic-business.html")
local tmpfile="${file}.tmp"
awk '
/<\/body>/ {
print ""
print "<!-- === INFOGRAPHIC: Business Decision Flow === -->"
print "<div class=\"section section-gray\">"
print " <div class=\"container\">"
print infographic_content
print " </div>"
print "</div>"
print "<!-- === END INFOGRAPHIC === -->"
print ""
}
{ print }
' infographic_content="$infographic" "$file" > "$tmpfile"
mv "$tmpfile" "$file"
echo " ✅ Business infographic integrerad: $(basename $file)"
}
# Funktion: injicera combined flow infographic
inject_combined_infographic() {
local file="$1"
if grep -q "lv-infographic" "$file" 2>/dev/null; then
echo " ⚠️ Skippar (redan integrerad): $(basename $file)"
return
fi
local infographic=$(cat "$COMPONENTS_DIR/infographic-flow.html")
local tmpfile="${file}.tmp"
awk '
/<\/body>/ {
print ""
print "<!-- === INFOGRAPHIC: Landvex + quiXzoom Flow === -->"
print "<div class=\"section\">"
print " <div class=\"container\">"
print infographic_content
print " </div>"
print "</div>"
print "<!-- === END INFOGRAPHIC === -->"
print ""
}
{ print }
' infographic_content="$infographic" "$file" > "$tmpfile"
mv "$tmpfile" "$file"
echo " ✅ Combined flow integrerad: $(basename $file)"
}
echo "1. Uppdaterar huvudsida (se/index.html)..."
inject_combined_infographic "$ROOT_DIR/se/index.html"
echo ""
echo "2. Uppdaterar branschsidor (zoomer-fokus)..."
for dir in "$ROOT_DIR/se"/*/; do
# Hoppa över _components
[[ "$(basename "$dir")" == "_components" ]] && continue
html_file="${dir}index.html"
if [ -f "$html_file" ]; then
inject_zoomer_infographic "$html_file"
fi
done
echo ""
echo "=== Integration klar ==="
echo ""
echo "Manuella steg som krävs:"
echo " 1. Ladda upp till S3: aws s3 sync $ROOT_DIR/se/ s3://quixzoom-landing-prod/markets/se/"
echo " 2. CloudFront invalidation"
echo " 3. Verifiera på https://quixzoom.se"
+138
View File
@@ -0,0 +1,138 @@
#!/usr/bin/env python3
"""
Integrera infographic-komponenter på alla quiXzoom landningssidor.
Användning: python3 integrate.py [--dry-run]
"""
import os
import sys
import re
import argparse
from pathlib import Path
ROOT = Path(__file__).parent.parent
COMPONENTS = ROOT / "se" / "_components"
def read_component(name):
"""Läs en komponentfil och returnera innehållet."""
path = COMPONENTS / name
if not path.exists():
print(f"❌ Komponent saknas: {path}")
sys.exit(1)
return path.read_text(encoding='utf-8')
def inject_before_body_end(html_path, content_to_inject, wrapper_class="section"):
"""Injicera innehåll före </body>."""
content = html_path.read_text(encoding='utf-8')
# Kolla om redan injicerad
if 'infographic' in content and 'lv-infographic' in content_to_inject:
return False, "Redan integrerad"
if 'infographic' in content and 'qz-infographic' in content_to_inject:
return False, "Redan integrerad"
if 'infographic' in content and 'lb-infographic' in content_to_inject:
return False, "Redan integrerad"
# Hitta </body> och injicera före
body_end = content.rfind('</body>')
if body_end == -1:
return False, "Ingen </body> hittad"
# Bygg injektion
injection = f'''
<!-- === INFOGRAPHIC INJECTED === -->
<div class="{wrapper_class}">
<div class="container">
{content_to_inject}
</div>
</div>
<!-- === END INFOGRAPHIC === -->
'''
new_content = content[:body_end] + injection + '\n' + content[body_end:]
html_path.write_text(new_content, encoding='utf-8')
return True, "Integrerad"
def process_main_page(dry_run=False):
"""Uppdatera huvudsida med combined flow."""
path = ROOT / "se" / "index.html"
infographic = read_component("infographic-flow.html")
if dry_run:
print(f" [DRY-RUN] Skulle integrera combined flow på: {path}")
return
success, msg = inject_before_body_end(path, infographic, "section")
status = "" if success else "⚠️"
print(f" {status} {path.name}: {msg}")
def process_industry_pages(dry_run=False):
"""Uppdatera alla branschsidor med zoomer-journey."""
infographic = read_component("infographic-zoomer.html")
se_dir = ROOT / "se"
count = 0
for subdir in sorted(se_dir.iterdir()):
if not subdir.is_dir():
continue
if subdir.name.startswith('_'):
continue
if subdir.name == 'index.html':
continue
html_file = subdir / "index.html"
if not html_file.exists():
continue
if dry_run:
print(f" [DRY-RUN] Skulle integrera zoomer-journey på: {subdir.name}/index.html")
count += 1
continue
success, msg = inject_before_body_end(html_file, infographic, "section section-gray")
status = "" if success else "⚠️"
print(f" {status} {subdir.name}/index.html: {msg}")
if success:
count += 1
return count
def main():
parser = argparse.ArgumentParser(description="Integrera infographics på quiXzoom landningssidor")
parser.add_argument('--dry-run', action='store_true', help='Simulera utan att ändra filer')
args = parser.parse_args()
print("=== quiXzoom Infographic Integration ===\n")
# Verifiera komponenter finns
for comp in ['infographic-flow.html', 'infographic-zoomer.html', 'infographic-business.html']:
path = COMPONENTS / comp
if not path.exists():
print(f"❌ Komponent saknas: {comp}")
sys.exit(1)
print(f" 📦 {comp} ({path.stat().st_size:,} bytes)")
print()
# 1. Huvudsida
print("1. Uppdaterar huvudsida (se/index.html)...")
process_main_page(dry_run=args.dry_run)
# 2. Branschsidor
print("\n2. Uppdaterar branschsidor...")
count = process_industry_pages(dry_run=args.dry_run)
print(f"\n=== Sammanfattning ===")
if args.dry_run:
print(f"[DRY-RUN] Skulle uppdatera {count} filer")
else:
print(f"Uppdaterade {count} filer")
print("\nNästa steg:")
print(" 1. Testa lokalt: öppna filerna i webbläsare")
print(" 2. aws s3 sync se/ s3://quixzoom-landing-prod/markets/se/")
print(" 3. CloudFront invalidation")
print(" 4. Verifiera på https://quixzoom.se")
if __name__ == '__main__':
main()