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:
@@ -0,0 +1,317 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generera alla quiXzoom landningssidor
|
||||
15 marknader × 20 use cases = 300 sidor
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
|
||||
BASE_DIR = "/home/bernt/.openclaw/workspace/quixzoom-market-pages"
|
||||
|
||||
# Marknadskonfigurationer
|
||||
MARKETS = {
|
||||
"se": {"lang": "sv", "country": "Sverige", "country_en": "Sweden", "code": "SE",
|
||||
"pay": "150–500 kr", "tax": "Skatteverket", "curr": "kr",
|
||||
"badge": "🇸🇪 Lanseras i Sverige · 1 augusti 2026", "domain": "quixzoom.se"},
|
||||
"de": {"lang": "de", "country": "Deutschland", "country_en": "Germany", "code": "DE",
|
||||
"pay": "15–50 €", "tax": "Finanzamt", "curr": "€",
|
||||
"badge": "🇩🇪 Startet in Deutschland · August 2026", "domain": "quixzoom.de"},
|
||||
"fr": {"lang": "fr", "country": "France", "country_en": "France", "code": "FR",
|
||||
"pay": "15–50 €", "tax": "DGFiP", "curr": "€",
|
||||
"badge": "🇫🇷 Lance en France · Août 2026", "domain": "quixzoom.fr"},
|
||||
"nl": {"lang": "nl", "country": "Nederland", "country_en": "Netherlands", "code": "NL",
|
||||
"pay": "15–50 €", "tax": "Belastingdienst", "curr": "€",
|
||||
"badge": "🇳🇱 Lanceert in Nederland · Augustus 2026", "domain": "quixzoom.nl"},
|
||||
"uk": {"lang": "en-gb", "country": "United Kingdom", "country_en": "United Kingdom", "code": "GB",
|
||||
"pay": "£12–42", "tax": "HMRC", "curr": "£",
|
||||
"badge": "🇬🇧 Launching in the UK · August 2026", "domain": "quixzoom.co.uk"},
|
||||
"it": {"lang": "it", "country": "Italia", "country_en": "Italy", "code": "IT",
|
||||
"pay": "15–50 €", "tax": "Agenzia delle Entrate", "curr": "€",
|
||||
"badge": "🇮🇹 Lancio in Italia · Agosto 2026", "domain": "quixzoom.it"},
|
||||
"es": {"lang": "es", "country": "España", "country_en": "Spain", "code": "ES",
|
||||
"pay": "15–50 €", "tax": "Agencia Tributaria", "curr": "€",
|
||||
"badge": "🇪🇸 Lanzamiento en España · Agosto 2026", "domain": "quixzoom.es"},
|
||||
"fi": {"lang": "fi", "country": "Suomi", "country_en": "Finland", "code": "FI",
|
||||
"pay": "15–50 €", "tax": "Verohallinto", "curr": "€",
|
||||
"badge": "🇫🇮 Lanserar i Finland · Elokuu 2026", "domain": "quixzoom.fi"},
|
||||
"da": {"lang": "da", "country": "Danmark", "country_en": "Denmark", "code": "DK",
|
||||
"pay": "100–350 kr", "tax": "Skattestyrelsen", "curr": "kr",
|
||||
"badge": "🇩🇰 Lancering i Danmark · August 2026", "domain": "quixzoom.dk"},
|
||||
"at": {"lang": "de", "country": "Österreich", "country_en": "Austria", "code": "AT",
|
||||
"pay": "15–50 €", "tax": "BMF", "curr": "€",
|
||||
"badge": "🇦🇹 Start in Österreich · August 2026", "domain": "quixzoom.at"},
|
||||
"be": {"lang": "nl", "country": "België", "country_en": "Belgium", "code": "BE",
|
||||
"pay": "15–50 €", "tax": "FOD Financiën", "curr": "€",
|
||||
"badge": "🇧🇪 Lancering in België · Augustus 2026", "domain": "quixzoom.be"},
|
||||
"ch": {"lang": "de", "country": "Schweiz", "country_en": "Switzerland", "code": "CH",
|
||||
"pay": "15–50 CHF", "tax": "ESTV", "curr": "CHF",
|
||||
"badge": "🇨🇭 Start in der Schweiz · August 2026", "domain": "quixzoom.ch"},
|
||||
"no": {"lang": "no", "country": "Norge", "country_en": "Norway", "code": "NO",
|
||||
"pay": "150–500 kr", "tax": "Skatteetaten", "curr": "kr",
|
||||
"badge": "🇳🇴 Lansering i Norge · August 2026", "domain": "quixzoom.no"},
|
||||
"ie": {"lang": "en-ie", "country": "Ireland", "country_en": "Ireland", "code": "IE",
|
||||
"pay": "€15–50", "tax": "Revenue Commissioners", "curr": "€",
|
||||
"badge": "🇮🇪 Launching in Ireland · August 2026", "domain": "quixzoom.ie"},
|
||||
"pl": {"lang": "pl", "country": "Polska", "country_en": "Poland", "code": "PL",
|
||||
"pay": "60–200 zł", "tax": "Ministerstwo Finansów", "curr": "zł",
|
||||
"badge": "🇵🇱 Start w Polsce · Sierpień 2026", "domain": "quixzoom.pl"},
|
||||
}
|
||||
|
||||
# Use cases med översättningar
|
||||
USE_CASES = {
|
||||
"bridge-inspection": {
|
||||
"sv": "Broinspektion", "de": "Brückeninspektion", "fr": "Inspection de ponts",
|
||||
"nl": "Bruginspectie", "en-gb": "Bridge Inspection", "it": "Ispezione ponti",
|
||||
"es": "Inspección de puentes", "fi": "Sillan tarkastus", "da": "Broinspektion",
|
||||
"no": "Broinspeksjon", "en-ie": "Bridge Inspection", "pl": "Inspekcja mostów"
|
||||
},
|
||||
"infrastructure-monitoring": {
|
||||
"sv": "Infrastrukturövervakning", "de": "Infrastrukturüberwachung", "fr": "Surveillance d'infrastructure",
|
||||
"nl": "Infrastructuurmonitoring", "en-gb": "Infrastructure Monitoring", "it": "Monitoraggio infrastrutture",
|
||||
"es": "Monitoreo de infraestructura", "fi": "Infrastruktuurin valvonta", "da": "Infrastrukturovervågning",
|
||||
"no": "Infrastrukturovervåking", "en-ie": "Infrastructure Monitoring", "pl": "Monitorowanie infrastruktury"
|
||||
},
|
||||
"property-documentation": {
|
||||
"sv": "Fastighetsdokumentation", "de": "Immobiliendokumentation", "fr": "Documentation immobilière",
|
||||
"nl": "Vastgoeddocumentatie", "en-gb": "Property Documentation", "it": "Documentazione immobiliare",
|
||||
"es": "Documentación de propiedades", "fi": "Kiinteistödokumentaatio", "da": "Ejendomsdokumentation",
|
||||
"no": "Eiendomsdokumentasjon", "en-ie": "Property Documentation", "pl": "Dokumentacja nieruchomości"
|
||||
},
|
||||
"storm-damage": {
|
||||
"sv": "Stormskadebedömning", "de": "Sturmschadenbewertung", "fr": "Évaluation des dégâts de tempête",
|
||||
"nl": "Stormschadebeoordeling", "en-gb": "Storm Damage Assessment", "it": "Valutazione danni da tempesta",
|
||||
"es": "Evaluación de daños por tormenta", "fi": "Myrskyvaurioarvio", "da": "Stormskadevurdering",
|
||||
"no": "Stormskadevurdering", "en-ie": "Storm Damage Assessment", "pl": "Ocena szkód po burzy"
|
||||
},
|
||||
"retail-audit": {
|
||||
"sv": "Butiksaudit", "de": "Einzelhandelsaudit", "fr": "Audit de vente au détail",
|
||||
"nl": "Retail audit", "en-gb": "Retail Audit", "it": "Audit retail",
|
||||
"es": "Auditoría retail", "fi": "Vähittäiskaupan tarkastus", "da": "Detailhandelsaudit",
|
||||
"no": "Detaljhandelsrevisjon", "en-ie": "Retail Audit", "pl": "Audyt detaliczny"
|
||||
},
|
||||
"ooh-audit": {
|
||||
"sv": "OOH-audit", "de": "OOH-Audit", "fr": "Audit OOH",
|
||||
"nl": "OOH-audit", "en-gb": "OOH Audit", "it": "Audit OOH",
|
||||
"es": "Auditoría OOH", "fi": "OOH-tarkastus", "da": "OOH-audit",
|
||||
"no": "OOH-revisjon", "en-ie": "OOH Audit", "pl": "Audyt OOH"
|
||||
},
|
||||
"construction-progress": {
|
||||
"sv": "Byggframsteg", "de": "Baufortschritt", "fr": "Progrès de construction",
|
||||
"nl": "Bouwvoortgang", "en-gb": "Construction Progress", "it": "Avanzamento lavori",
|
||||
"es": "Progreso de construcción", "fi": "Rakennustyön edistyminen", "da": "Byggefremgang",
|
||||
"no": "Byggefremskridt", "en-ie": "Construction Progress", "pl": "Postęp budowy"
|
||||
},
|
||||
"facade-inspection": {
|
||||
"sv": "Fasadinspektion", "de": "Fassadeninspektion", "fr": "Inspection de façade",
|
||||
"nl": "Gevelinspectie", "en-gb": "Facade Inspection", "it": "Ispezione facciata",
|
||||
"es": "Inspección de fachadas", "fi": "Julkisivutarkastus", "da": "Facadeinspektion",
|
||||
"no": "Fasadeinspeksjon", "en-ie": "Facade Inspection", "pl": "Inspekcja elewacji"
|
||||
},
|
||||
"roof-inspection": {
|
||||
"sv": "Takinspektion", "de": "Dachinspektion", "fr": "Inspection de toit",
|
||||
"nl": "Dakinspectie", "en-gb": "Roof Inspection", "it": "Ispezione tetto",
|
||||
"es": "Inspección de tejados", "fi": "Kattotarkastus", "da": "Taginspektion",
|
||||
"no": "Takinspeksjon", "en-ie": "Roof Inspection", "pl": "Inspekcja dachu"
|
||||
},
|
||||
"road-condition": {
|
||||
"sv": "Vägcondition", "de": "Straßenzustand", "fr": "État des routes",
|
||||
"nl": "Wegconditie", "en-gb": "Road Condition", "it": "Condizione stradale",
|
||||
"es": "Condición de carreteras", "fi": "Tien kunto", "da": "Vejtilstand",
|
||||
"no": "Veitilstand", "en-ie": "Road Condition", "pl": "Stan dróg"
|
||||
},
|
||||
"signage-audit": {
|
||||
"sv": "Skyltaudit", "de": "Beschilderungsaudit", "fr": "Audit de signalisation",
|
||||
"nl": "Signalisatie-audit", "en-gb": "Signage Audit", "it": "Audit segnaletica",
|
||||
"es": "Auditoría de señalización", "fi": "Opastetarkastus", "da": "Skilteaudit",
|
||||
"no": "Skiltingrevisjon", "en-ie": "Signage Audit", "pl": "Audyt oznakowania"
|
||||
},
|
||||
"parking-audit": {
|
||||
"sv": "Parkeringsaudit", "de": "Parkplatz-Audit", "fr": "Audit de stationnement",
|
||||
"nl": "Parkeer-audit", "en-gb": "Parking Audit", "it": "Audit parcheggi",
|
||||
"es": "Auditoría de aparcamiento", "fi": "Pysäköintitarkastus", "da": "Parkeringsaudit",
|
||||
"no": "Parkeringsrevisjon", "en-ie": "Parking Audit", "pl": "Audyt parkingów"
|
||||
},
|
||||
"asset-tracking": {
|
||||
"sv": "Tillgångsspårning", "de": "Asset Tracking", "fr": "Suivi d'actifs",
|
||||
"nl": "Asset tracking", "en-gb": "Asset Tracking", "it": "Tracciamento asset",
|
||||
"es": "Seguimiento de activos", "fi": "Omaisuuden seuranta", "da": "Asset tracking",
|
||||
"no": "Eiendomssporing", "en-ie": "Asset Tracking", "pl": "Śledzenie zasobów"
|
||||
},
|
||||
"environmental-survey": {
|
||||
"sv": "Miljöinventering", "de": "Umwelterhebung", "fr": "Enquête environnementale",
|
||||
"nl": "Milieudenquête", "en-gb": "Environmental Survey", "it": "Indagine ambientale",
|
||||
"es": "Estudio medioambiental", "fi": "Ympäristöselvitys", "da": "Miljøundersøgelse",
|
||||
"no": "Miljøundersøkelse", "en-ie": "Environmental Survey", "pl": "Badanie środowiskowe"
|
||||
},
|
||||
"insurance-claim": {
|
||||
"sv": "Försäkringsärende", "de": "Versicherungsfall", "fr": "Réclamation d'assurance",
|
||||
"nl": "Verzekeringsclaim", "en-gb": "Insurance Claim", "it": "Sinistro assicurativo",
|
||||
"es": "Reclamación de seguro", "fi": "Vakuutusvaade", "da": "Forsikringskrav",
|
||||
"no": "Forsikringskrav", "en-ie": "Insurance Claim", "pl": "Roszczenie ubezpieczeniowe"
|
||||
},
|
||||
"lease-audit": {
|
||||
"sv": "Hyresaudit", "de": "Lease-Audit", "fr": "Audit de bail",
|
||||
"nl": "Lease audit", "en-gb": "Lease Audit", "it": "Audit locazione",
|
||||
"es": "Auditoría de arrendamiento", "fi": "Vuokraustarkastus", "da": "Leasingaudit",
|
||||
"no": "Leasingrevisjon", "en-ie": "Lease Audit", "pl": "Audyt leasingowy"
|
||||
},
|
||||
"event-documentation": {
|
||||
"sv": "Eventdokumentation", "de": "Eventdokumentation", "fr": "Documentation d'événement",
|
||||
"nl": "Evenementdocumentatie", "en-gb": "Event Documentation", "it": "Documentazione eventi",
|
||||
"es": "Documentación de eventos", "fi": "Tapahtumadokumentaatio", "da": "Begivenhedsdokumentation",
|
||||
"no": "Arrangementsdokumentasjon", "en-ie": "Event Documentation", "pl": "Dokumentacja wydarzeń"
|
||||
},
|
||||
"pre-purchase-inspection": {
|
||||
"sv": "Köpesbesiktning", "de": "Kaufinspektion", "fr": "Inspection pré-achat",
|
||||
"nl": "Pre-aankoopinspectie", "en-gb": "Pre-Purchase Inspection", "it": "Ispezione pre-acquisto",
|
||||
"es": "Inspección pre-compra", "fi": "Esihankintatarkastus", "da": "Forhåndskøbsinspektion",
|
||||
"no": "Forhåndsinspeksjon", "en-ie": "Pre-Purchase Inspection", "pl": "Inspekcja przedzakupowa"
|
||||
},
|
||||
"rental-inspection": {
|
||||
"sv": "Hyresinspektion", "de": "Mietinspektion", "fr": "Inspection locative",
|
||||
"nl": "Huurinspectie", "en-gb": "Rental Inspection", "it": "Ispezione locazione",
|
||||
"es": "Inspección de alquiler", "fi": "Vuokraustarkastus", "da": "Lejeinspektion",
|
||||
"no": "Leieinspeksjon", "en-ie": "Rental Inspection", "pl": "Inspekcja wynajmu"
|
||||
},
|
||||
"accessibility-audit": {
|
||||
"sv": "Tillgänglighetsaudit", "de": "Barrierefreiheits-Audit", "fr": "Audit d'accessibilité",
|
||||
"nl": "Toegankelijkheidsaudit", "en-gb": "Accessibility Audit", "it": "Audit accessibilità",
|
||||
"es": "Auditoría de accesibilidad", "fi": "Saavutettavuustarkastus", "da": "Tilgængelighedsaudit",
|
||||
"no": "Universell utforming revisjon", "en-ie": "Accessibility Audit", "pl": "Audyt dostępności"
|
||||
},
|
||||
}
|
||||
|
||||
def generate_page(market, use_case):
|
||||
m = MARKETS[market]
|
||||
lang = m["lang"]
|
||||
|
||||
# Hämta use case-namn på rätt språk
|
||||
uc_names = USE_CASES[use_case]
|
||||
uc_name = uc_names.get(lang, uc_names.get("en-gb", use_case.replace("-", " ").title()))
|
||||
|
||||
# Skapa katalog
|
||||
dir_path = os.path.join(BASE_DIR, market, use_case)
|
||||
os.makedirs(dir_path, exist_ok=True)
|
||||
|
||||
# Generera HTML
|
||||
html = f"""<!DOCTYPE html>
|
||||
<html lang="{lang}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>{uc_name} — quiXzoom | {m['country']}</title>
|
||||
<meta name="description" content="{uc_name} med quiXzoom i {m['country']}. Tjäna {m['pay']} per uppdrag. Betalt varje vecka.">
|
||||
<meta name="geo.region" content="{m['code']}">
|
||||
<meta name="geo.country" content="{m['country_en']}">
|
||||
<meta name="language" content="{m['country']}">
|
||||
<link rel="canonical" href="https://{m['domain']}/{use_case}/">
|
||||
<style>
|
||||
:root{{--qx-black:#0a0a0a;--qx-white:#fff;--qx-blue:#06f;--qx-gray:#f5f5f7;--qx-dark-gray:#1a1a1a}}
|
||||
*{{margin:0;padding:0;box-sizing:border-box}}
|
||||
body{{font-family:-apple-system,BlinkMacSystemFont,'SF Pro Display','Helvetica Neue',sans-serif;color:var(--qx-black);line-height:1.5}}
|
||||
.hero{{background:var(--qx-black);color:var(--qx-white);padding:80px 20px;text-align:center}}
|
||||
.hero h1{{font-size:2.5rem;margin-bottom:1rem}}
|
||||
.hero p{{font-size:1.25rem;color:#ccc;margin-bottom:2rem}}
|
||||
.badge{{display:inline-block;background:var(--qx-blue);color:#fff;padding:8px 16px;border-radius:20px;font-size:.875rem;margin-bottom:1rem}}
|
||||
.container{{max-width:1200px;margin:0 auto;padding:40px 20px}}
|
||||
.section{{padding:60px 20px}}
|
||||
.section-gray{{background:var(--qx-gray)}}
|
||||
.grid{{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:24px}}
|
||||
.card{{background:#fff;padding:24px;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.1)}}
|
||||
.card h3{{margin-bottom:.5rem}}
|
||||
.cta{{display:inline-block;background:var(--qx-blue);color:#fff;padding:16px 32px;border-radius:8px;text-decoration:none;font-weight:600;margin-top:1rem}}
|
||||
.footer{{background:var(--qx-dark-gray);color:#fff;padding:40px 20px;text-align:center}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="hero">
|
||||
<div class="badge">{m['badge']}</div>
|
||||
<h1>{uc_name}</h1>
|
||||
<p>quiXzoom — Crowdsourcad datainsamling i {m['country']}. Tjäna {m['pay']} per uppdrag.</p>
|
||||
<a href="https://app.quixzoom.com/join" class="cta">Bli Zoomer nu</a>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="section">
|
||||
<h2>Hur det fungerar</h2>
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<h3>1. Hitta uppdrag</h3>
|
||||
<p>Se tillgängliga {uc_name}-uppdrag nära dig i appen.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>2. Dokumentera</h3>
|
||||
<p>Följ instruktionerna och ta bilder med din smartphone.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>3. Få betalt</h3>
|
||||
<p>Betalt varje vecka. {m['pay']} per godkänt uppdrag.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section section-gray">
|
||||
<div class="container">
|
||||
<h2>Varför quiXzoom?</h2>
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<h3>Flexibelt</h3>
|
||||
<p>Jobba när du vill. Inga scheman, inga minimikrav.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Betalt snabbt</h3>
|
||||
<p>Varje vecka direkt till ditt konto via Stripe.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Tillförlitligt</h3>
|
||||
<p>Global plattform med verifierade uppdrag och tydlig feedback.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>© 2026 quiXzoom AB — En del av <a href="https://aamos.systems" style="color:#fff">AAMOS</a></p>
|
||||
<p>{m['domain']}</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>"""
|
||||
|
||||
filepath = os.path.join(dir_path, "index.html")
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
f.write(html)
|
||||
|
||||
return True
|
||||
|
||||
def main():
|
||||
total = 0
|
||||
done = 0
|
||||
|
||||
# Räkna totalt
|
||||
for market in MARKETS:
|
||||
for use_case in USE_CASES:
|
||||
total += 1
|
||||
|
||||
print(f"Genererar {total} landningssidor...")
|
||||
print()
|
||||
|
||||
# Generera alla sidor
|
||||
for market in sorted(MARKETS.keys()):
|
||||
print(f"=== Marknad: {market} ===")
|
||||
for use_case in USE_CASES:
|
||||
if generate_page(market, use_case):
|
||||
done += 1
|
||||
print()
|
||||
|
||||
print()
|
||||
print(f"✅ Klart! {done} landningssidor genererade.")
|
||||
print()
|
||||
print("Nästa steg:")
|
||||
print("1. Deploya till S3: aws s3 sync quixzoom-market-pages s3://quixzoom-landing-prod/markets/")
|
||||
print("2. Invalida CloudFront-cache")
|
||||
print("3. Verifiera att alla sidor är tillgängliga")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user