atlas-os/scripts/atlas_seasonal_strategy.py
Chaib Aarab 301cc75de9 init: Atlas OS — open-source patterns + tools for solo founder LLM cluster
3 browser-native chat surfaces (atlas-chat / atlas-hud / atlas-command, no build step)
+ 11 Python automation scripts (cluster, tray, finance, reminders, autonomy loops)
+ 2 patterns documented (cluster-then-polish, anti-impulse R1-R10)
MIT licensed, anonymized from production stack.
2026-05-26 16:21:16 +02:00

265 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
atlas_seasonal_strategy.py — Generate context-aware strategic recommendations
based on calendar season, NL economic windows, local Tilburg events, and
Google Trends signals where available.
Output: atlas-strategy.json — consumed by Atlas Command BRIEF tab "Strategy" card.
The engine knows about:
- Vakantiegeld payouts (25 mei 5 juni)
- Belastingteruggaaf windows (Mar-Apr)
- Schoolstart (late aug / early sept)
- Sint/Kerst spending (Nov-Dec)
- Tilburgse Kermis (juli)
- Q1/Q2/Q3/Q4 BTW aangifte deadlines
- Ramadan / Suikerfeest (year-variable)
Per current window, recommends:
- Which revenue stream to prioritize
- Which prospects to push
- Which framing to use
- What NOT to do
"""
import json
import subprocess
import sys
import urllib.request
from pathlib import Path
from datetime import datetime, date, timedelta
OUT_LOCAL = Path(r"C:\Users\Gebruiker\AppData\Local\Atlas\analyses\atlas-strategy.json")
SSH = "root@<YOUR_VPS_IP>"
REMOTE = "/opt/atlas/dashboard/atlas-strategy.json"
# Calendar windows (year-relative — adjusted to 2026)
WINDOWS_2026 = [
{
"name": "Vakantiegeld",
"start": "2026-05-25", "end": "2026-06-05",
"signal": "Consumer cash boost (€1.500-3.000 per consumer)",
"push": [
"Werkplek STARTER €799 (consumer-friendly price)",
"Kapper / horeca consumer-facing zaak-eigenaars (zij ervaren spend-piek)",
"Atlas Agency retainers (klanten hebben budget voor pakketten)"
],
"framing": "\"Vakantiegeld al binnen? Nieuwe werkplek-set is nu beste timing.\"",
"avoid": ["Geen B2B-koud sales — owners distracted door consumer-rush"],
"channels": ["WhatsApp", "Instagram", "Tilburg community"],
},
{
"name": "Pre-zomer rust",
"start": "2026-06-06", "end": "2026-07-05",
"signal": "Stabiele B2B window, vóór schoolvakantie",
"push": [
"Atlas Agency cold-outreach (max response rate)",
"POS Support Lite contract conversions (existing klanten)",
"Werkplek PRO + KASSA (besluitvorming-tijd)"
],
"framing": "\"Inrichten vóór zomerstop — na zomer = nieuwe kalender, schone start.\"",
"avoid": ["Geen consumer-spend campagnes — vakantiegeld op"],
"channels": ["WhatsApp", "Email B2B", "LinkedIn (1×/wk max)"],
},
{
"name": "Tilburgse Kermis",
"start": "2026-07-17", "end": "2026-07-26",
"signal": "Lokale horeca-piek (10 dagen)",
"push": [
"POS Support Lite NU verkopen voor july readiness",
"Kermis-tijdelijke POS rental aanbieden (€50/dag) — test market",
"WhatsApp campagne richting kermis-deelnemers vóór kermis"
],
"framing": "\"Kermis = jouw drukste week. Atlas Support Lite zorgt dat je POS niet stilvalt.\"",
"avoid": ["Geen large-budget B2B proposals — operationele focus"],
"channels": ["WhatsApp horeca", "Instagram Tilburg-tags"],
},
{
"name": "Schoolvakantie",
"start": "2026-07-06", "end": "2026-08-17",
"signal": "MKB-eigenaars meer tijd voor strategie + nieuwe leveranciers",
"push": [
"Atlas Agency cold-outreach (BEST window — owners hebben tijd)",
"Werkplek follow-ups op bestaande quotes",
"Workshop POS voor Horeca (eerste editie juli)"
],
"framing": "\"Tijd voor de zaak achter de zaak — Atlas helpt het systeem rond te krijgen.\"",
"avoid": ["Geen sales-druk eerste 2 weken juli (vakantie-spirit)"],
"channels": ["WhatsApp", "Instagram zaak-eigenaar netwerken", "Charif referral"],
},
{
"name": "Back to school + work",
"start": "2026-08-18", "end": "2026-08-31",
"signal": "Nieuwe office-setups, MKB equipment-piek",
"push": [
"Werkplek PRO + KASSA push (office-upgrade tijd)",
"Group-buy aanbod (5+ werkplekken voor MKB)",
"Studenten-werkplekken (TilburgU/Avans semesterstart)"
],
"framing": "\"Begin het nieuwe seizoen met systemen die werken.\"",
"avoid": ["Geen low-budget peripherals — push hoog-marge bundels"],
"channels": ["WhatsApp MKB", "TilburgU community", "Avans-area outreach"],
},
{
"name": "Najaar groei",
"start": "2026-09-01", "end": "2026-10-31",
"signal": "Najaars-momentum, beslissingen-tijd",
"push": [
"Atlas Agency retainers (€250/mo) — vol momentum",
"Bisogno + andere bestaande klanten verdieping",
"Workshops 2×/mo schaal"
],
"framing": "\"Voor december komt: maak je systeem klaar voor jaar-eind drukte.\"",
"avoid": [],
"channels": ["Alles open"],
},
{
"name": "Sint/Kerst opmaat",
"start": "2026-11-01", "end": "2026-11-30",
"signal": "B2C horeca-piek voorbereiden + boekhouders druk",
"push": [
"Werkplek KASSA voor horeca die met sint/kerst druk gaat krijgen",
"Cross-sell naar boekhouders (referral network — zij zien hun klanten worstelen)",
"Year-end Atlas Support Lite incentives"
],
"framing": "\"December = duurste maand om POS te laten falen. Nu is het tijd.\"",
"avoid": [],
"channels": ["Alles open"],
},
{
"name": "Eindejaarsspecial",
"start": "2026-12-01", "end": "2026-12-23",
"signal": "Cash + planning + 'kerstcadeau voor jezelf'-framing",
"push": [
"Werkplek STARTER €799 als kerstcadeau voor zzp'er",
"Q4 retainer-vernieuwingen + annual prepay deals",
"Atlas Support Lite jaarcontracten (12% korting bij vooruitbetaling)"
],
"framing": "\"Kerstcadeau aan jezelf én aan je zaak.\"",
"avoid": ["Geen B2B sales na 23 dec — niemand bereikbaar"],
"channels": ["WhatsApp warm-list", "Email bestaande klanten", "IG"],
},
{
"name": "Kerst-doodweek",
"start": "2026-12-24", "end": "2027-01-04",
"signal": "Holiday lull — minimaal contact",
"push": ["Eigen administratie afronden", "2027 plan schrijven", "Cluster onderhoud"],
"framing": "n.v.t. — geen sales",
"avoid": ["Alle externe outreach"],
"channels": [],
},
{
"name": "Q1 freshness",
"start": "2027-01-05", "end": "2027-01-31",
"signal": "\"Nieuw jaar nieuw systeem\" — annual contract renewals",
"push": [
"Werkplek upgrade calls naar Q1 klanten",
"Atlas Agency jaarcontracten ipv maandelijks",
"POS Support Lite hernieuwingen + uitbreidingen"
],
"framing": "\"Begin 2027 met systemen die in 2026 hebben bewezen te werken.\"",
"avoid": [],
"channels": ["Alles open"],
},
]
def current_window(today=None):
today = today or date.today()
for w in WINDOWS_2026:
start = date.fromisoformat(w["start"])
end = date.fromisoformat(w["end"])
if start <= today <= end:
days_in = (today - start).days
days_left = (end - today).days
return {**w, "days_in": days_in, "days_left": days_left}
return None
def upcoming_windows(today=None, n=3):
today = today or date.today()
upcoming = []
for w in WINDOWS_2026:
start = date.fromisoformat(w["start"])
if start > today:
upcoming.append({**w, "days_until": (start - today).days})
if len(upcoming) >= n: break
return upcoming
def btw_deadline(today=None):
"""Quarterly NL BTW aangifte deadlines: end of month after quarter end."""
today = today or date.today()
q_ends = [
(date(today.year, 4, 30), "Q1"),
(date(today.year, 7, 31), "Q2"),
(date(today.year, 10, 31), "Q3"),
(date(today.year + 1, 1, 31), "Q4"),
]
for d, q in q_ends:
if d >= today:
return {"quarter": q, "deadline": d.isoformat(), "days_until": (d - today).days}
return None
def days_to_next_payday():
"""Most NL employers pay 25th of month — quick proxy."""
today = date.today()
if today.day < 25:
nxt = date(today.year, today.month, 25)
else:
m = today.month + 1
y = today.year
if m > 12: m = 1; y += 1
nxt = date(y, m, 25)
return (nxt - today).days
def build_summary():
today = date.today()
curr = current_window(today)
upcoming = upcoming_windows(today, 3)
btw = btw_deadline(today)
summary = {
"generated_at": datetime.now().isoformat(timespec="seconds"),
"as_of": today.isoformat(),
"current_window": curr,
"upcoming_windows": upcoming,
"btw_aangifte": btw,
"days_to_next_payday": days_to_next_payday(),
"weekday": today.strftime("%A"),
"is_weekend": today.weekday() >= 5,
# Action triggers
"primary_recommendation": curr["push"][0] if curr and curr["push"] else "Geen actieve window — gebruik default plan",
"primary_framing": curr["framing"] if curr else "n.v.t.",
"avoid_today": curr["avoid"] if curr else [],
"channels_today": curr["channels"] if curr else [],
}
return summary
def push_to_atlas01(summary):
OUT_LOCAL.parent.mkdir(parents=True, exist_ok=True)
OUT_LOCAL.write_text(json.dumps(summary, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"wrote {OUT_LOCAL}")
try:
subprocess.run(["scp", str(OUT_LOCAL), f"{SSH}:{REMOTE}"], check=True, timeout=20)
print(f"pushed to {SSH}:{REMOTE}")
except Exception as e:
print(f"scp fail: {e}")
def main():
s = build_summary()
push_to_atlas01(s)
print(f"\nCurrent window: {s['current_window']['name'] if s['current_window'] else 'none'}")
if s['current_window']:
print(f" Days in: {s['current_window']['days_in']}, days left: {s['current_window']['days_left']}")
print(f" Push: {s['primary_recommendation']}")
print(f"BTW deadline: {s['btw_aangifte']['quarter']} on {s['btw_aangifte']['deadline']} ({s['btw_aangifte']['days_until']}d)")
print(f"Next payday (25th): {s['days_to_next_payday']}d")
if __name__ == "__main__":
main()