lovarch-cli 0.2.1__py3-none-any.whl
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.
- lovarch_cli/__init__.py +16 -0
- lovarch_cli/__main__.py +10 -0
- lovarch_cli/ai/__init__.py +21 -0
- lovarch_cli/ai/gateway.py +240 -0
- lovarch_cli/api.py +111 -0
- lovarch_cli/auth/__init__.py +32 -0
- lovarch_cli/auth/keyring_store.py +214 -0
- lovarch_cli/auth/local_server.py +165 -0
- lovarch_cli/auth/pkce.py +57 -0
- lovarch_cli/auth/session.py +189 -0
- lovarch_cli/cli.py +262 -0
- lovarch_cli/clients/__init__.py +33 -0
- lovarch_cli/clients/factory.py +54 -0
- lovarch_cli/clients/local_client.py +432 -0
- lovarch_cli/clients/lovarch_storage.py +174 -0
- lovarch_cli/clients/lovarch_supabase.py +295 -0
- lovarch_cli/clients/persistence.py +166 -0
- lovarch_cli/clients/storage.py +66 -0
- lovarch_cli/commands/__init__.py +10 -0
- lovarch_cli/commands/account.py +172 -0
- lovarch_cli/commands/audit.py +394 -0
- lovarch_cli/commands/config_cmd.py +80 -0
- lovarch_cli/commands/consolidate.py +217 -0
- lovarch_cli/commands/context_cmd.py +73 -0
- lovarch_cli/commands/dev.py +287 -0
- lovarch_cli/commands/do_cmd.py +120 -0
- lovarch_cli/commands/init.py +218 -0
- lovarch_cli/commands/jobs_cmd.py +95 -0
- lovarch_cli/commands/login.py +202 -0
- lovarch_cli/commands/mcp_cmd.py +26 -0
- lovarch_cli/commands/run.py +375 -0
- lovarch_cli/commands/signup.py +185 -0
- lovarch_cli/commands/status.py +243 -0
- lovarch_cli/commands/upgrade.py +108 -0
- lovarch_cli/commands/verifica_cmd.py +174 -0
- lovarch_cli/config.py +101 -0
- lovarch_cli/config_store.py +111 -0
- lovarch_cli/credits/__init__.py +35 -0
- lovarch_cli/credits/base.py +84 -0
- lovarch_cli/credits/factory.py +36 -0
- lovarch_cli/credits/local.py +34 -0
- lovarch_cli/credits/lovarch.py +56 -0
- lovarch_cli/i18n/__init__.py +27 -0
- lovarch_cli/i18n/loader.py +121 -0
- lovarch_cli/i18n/translations/en.json +168 -0
- lovarch_cli/i18n/translations/es.json +168 -0
- lovarch_cli/i18n/translations/it.json +168 -0
- lovarch_cli/i18n/translations/pt.json +168 -0
- lovarch_cli/mcp/__init__.py +9 -0
- lovarch_cli/mcp/server.py +199 -0
- lovarch_cli/mcp/tools.py +372 -0
- lovarch_cli/sample_downloader.py +255 -0
- lovarch_cli/squad/README.md +206 -0
- lovarch_cli/squad/agents/auditor-input.md +353 -0
- lovarch_cli/squad/agents/bim-engineer.md +404 -0
- lovarch_cli/squad/agents/briefing-architect.md +249 -0
- lovarch_cli/squad/agents/cad-engineer.md +278 -0
- lovarch_cli/squad/agents/capitolato-writer.md +256 -0
- lovarch_cli/squad/agents/computo-engineer.md +258 -0
- lovarch_cli/squad/agents/concept-designer.md +399 -0
- lovarch_cli/squad/agents/contratto-architect.md +243 -0
- lovarch_cli/squad/agents/deliverable-builder.md +253 -0
- lovarch_cli/squad/agents/energy-prelim.md +388 -0
- lovarch_cli/squad/agents/pratiche-it.md +251 -0
- lovarch_cli/squad/agents/progetto-chief.md +768 -0
- lovarch_cli/squad/agents/quality-dati.md +409 -0
- lovarch_cli/squad/agents/quality-misure.md +418 -0
- lovarch_cli/squad/agents/quality-normativa.md +417 -0
- lovarch_cli/squad/agents/quality-output.md +436 -0
- lovarch_cli/squad/agents/regolatorio-it.md +278 -0
- lovarch_cli/squad/checklists/handoff-quality-gate.md +232 -0
- lovarch_cli/squad/checklists/quality-dati-checklist.md +134 -0
- lovarch_cli/squad/checklists/quality-misure-checklist.md +139 -0
- lovarch_cli/squad/checklists/quality-normativa-checklist.md +121 -0
- lovarch_cli/squad/checklists/quality-output-checklist.md +116 -0
- lovarch_cli/squad/config.yaml +408 -0
- lovarch_cli/squad/data/CHANGELOG.md +272 -0
- lovarch_cli/squad/data/agents-prd.md +428 -0
- lovarch_cli/squad/data/architettura-progetto-rules.md +328 -0
- lovarch_cli/squad/data/handoff-card-template.md +231 -0
- lovarch_cli/squad/data/mocks/catasto-visura.json +72 -0
- lovarch_cli/squad/data/mocks/firma-envelope.json +43 -0
- lovarch_cli/squad/data/prezzario-lombardia-sample.json +312 -0
- lovarch_cli/squad/scripts/api_clients.py +206 -0
- lovarch_cli/squad/scripts/architect_profile.py +276 -0
- lovarch_cli/squad/scripts/deliverable_generators.py +844 -0
- lovarch_cli/squad/scripts/generate_attico_brera_dwg.py +369 -0
- lovarch_cli/squad/scripts/generate_chianti_dxf.py +368 -0
- lovarch_cli/squad/scripts/generate_chianti_images.py +223 -0
- lovarch_cli/squad/scripts/generate_real_sample_images.py +189 -0
- lovarch_cli/squad/scripts/generate_sample_assets.py +382 -0
- lovarch_cli/squad/scripts/lovarch_client.py +1046 -0
- lovarch_cli/squad/scripts/pipeline_runner.py +2095 -0
- lovarch_cli/squad/scripts/render_dxf_to_png.py +57 -0
- lovarch_cli/squad/scripts/run_palestra_demo.sh +277 -0
- lovarch_cli/squad/scripts/simulate_squad_execution.py +515 -0
- lovarch_cli/squad/scripts/validate-squad.py +383 -0
- lovarch_cli/squad/tasks/audit-input.md +146 -0
- lovarch_cli/squad/tasks/compute-metric.md +105 -0
- lovarch_cli/squad/tasks/consolidate-dossier.md +187 -0
- lovarch_cli/squad/tasks/generate-cad-plan.md +120 -0
- lovarch_cli/squad/tasks/generate-ifc-model.md +108 -0
- lovarch_cli/squad/tasks/write-capitolato.md +100 -0
- lovarch_cli/squad/templates/asseverazione-tecnica.md +126 -0
- lovarch_cli/squad/templates/capitolato-uni-11337.md +235 -0
- lovarch_cli/squad/templates/cila-comune-milano.md +177 -0
- lovarch_cli/squad/templates/contratto-cnappc.md +220 -0
- lovarch_cli/squad/workflows/dal-brief-al-cantiere.yaml +218 -0
- lovarch_cli/squad_loader.py +114 -0
- lovarch_cli/verify/__init__.py +15 -0
- lovarch_cli/verify/contratto.py +110 -0
- lovarch_cli/verify/dossier.py +97 -0
- lovarch_cli/verify/misure.py +83 -0
- lovarch_cli/verify/normativa.py +178 -0
- lovarch_cli/version.py +13 -0
- lovarch_cli/workflows/__init__.py +9 -0
- lovarch_cli/workflows/platform.py +212 -0
- lovarch_cli-0.2.1.dist-info/METADATA +232 -0
- lovarch_cli-0.2.1.dist-info/RECORD +122 -0
- lovarch_cli-0.2.1.dist-info/WHEEL +4 -0
- lovarch_cli-0.2.1.dist-info/entry_points.txt +3 -0
- lovarch_cli-0.2.1.dist-info/licenses/LICENSE +38 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Architect Profile Loader.
|
|
3
|
+
|
|
4
|
+
Consolidates all platform data about the architect/professional into a single dict.
|
|
5
|
+
Pulls from: profiles, user_settings, brand_profiles, style_profiles, avatars, user_tax_settings.
|
|
6
|
+
|
|
7
|
+
Italian-specific professional credentials (OAPPC, matricola, PEC) are loaded from:
|
|
8
|
+
1. Environment variables (ARCH_OAPPC_ORDINE, ARCH_MATRICOLA, ARCH_PEC, ARCH_ALBO)
|
|
9
|
+
2. data/sample-input/architetto-info.json (fallback)
|
|
10
|
+
3. Hardcoded placeholders (last resort, with WARN)
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
from architect_profile import load_architect_profile
|
|
14
|
+
profile = load_architect_profile(client, user_id)
|
|
15
|
+
print(profile["full_name"], profile["company_name"], profile["fiscal_address"])
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
import os
|
|
19
|
+
import json
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Dict, Any, Optional
|
|
23
|
+
|
|
24
|
+
ROOT = Path(__file__).resolve().parents[1]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _safe_get(client, table: str, user_id: str, select: str = "*") -> Optional[Dict[str, Any]]:
|
|
28
|
+
"""GET single row · returns None if not found."""
|
|
29
|
+
try:
|
|
30
|
+
result = client._rest("GET", f"/rest/v1/{table}?user_id=eq.{user_id}&select={select}&limit=1")
|
|
31
|
+
if isinstance(result, list) and result:
|
|
32
|
+
return result[0]
|
|
33
|
+
return None
|
|
34
|
+
except Exception as e:
|
|
35
|
+
print(f" ⚠ failed to load {table}: {e}", file=sys.stderr)
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _safe_get_by_id(client, table: str, row_id: str, select: str = "*") -> Optional[Dict[str, Any]]:
|
|
40
|
+
"""GET by id · returns None if not found."""
|
|
41
|
+
try:
|
|
42
|
+
result = client._rest("GET", f"/rest/v1/{table}?id=eq.{row_id}&select={select}&limit=1")
|
|
43
|
+
if isinstance(result, list) and result:
|
|
44
|
+
return result[0]
|
|
45
|
+
return None
|
|
46
|
+
except Exception:
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _load_italian_credentials() -> Dict[str, str]:
|
|
51
|
+
"""Load Italian-specific credentials from env vars or sample-input JSON.
|
|
52
|
+
|
|
53
|
+
Required fields for Italian arch deliverables:
|
|
54
|
+
- ordine_professionale (e.g., "OAPPC Milano")
|
|
55
|
+
- matricola (license number)
|
|
56
|
+
- pec (certified email)
|
|
57
|
+
- albo (e.g., "Arch. Sez. A")
|
|
58
|
+
"""
|
|
59
|
+
# Try env vars first
|
|
60
|
+
creds = {
|
|
61
|
+
"ordine_professionale": os.environ.get("ARCH_OAPPC_ORDINE", ""),
|
|
62
|
+
"matricola": os.environ.get("ARCH_MATRICOLA", ""),
|
|
63
|
+
"pec": os.environ.get("ARCH_PEC", ""),
|
|
64
|
+
"albo": os.environ.get("ARCH_ALBO", ""),
|
|
65
|
+
}
|
|
66
|
+
if all(creds.values()):
|
|
67
|
+
return creds
|
|
68
|
+
|
|
69
|
+
# Try sample-input JSON
|
|
70
|
+
sample_path = ROOT / "data" / "sample-input" / "architetto-info.json"
|
|
71
|
+
if sample_path.exists():
|
|
72
|
+
try:
|
|
73
|
+
with open(sample_path) as f:
|
|
74
|
+
data = json.load(f)
|
|
75
|
+
for k in creds:
|
|
76
|
+
if not creds[k] and data.get(k):
|
|
77
|
+
creds[k] = data[k]
|
|
78
|
+
except Exception as e:
|
|
79
|
+
print(f" ⚠ failed to read {sample_path}: {e}", file=sys.stderr)
|
|
80
|
+
|
|
81
|
+
# Fallback placeholders with WARN
|
|
82
|
+
if not creds["ordine_professionale"]:
|
|
83
|
+
print(f" ⚠ ARCH_OAPPC_ORDINE not configured · using placeholder", file=sys.stderr)
|
|
84
|
+
creds["ordine_professionale"] = "OAPPC Milano"
|
|
85
|
+
if not creds["matricola"]:
|
|
86
|
+
print(f" ⚠ ARCH_MATRICOLA not configured · using placeholder", file=sys.stderr)
|
|
87
|
+
creds["matricola"] = "XXXX"
|
|
88
|
+
if not creds["pec"]:
|
|
89
|
+
creds["pec"] = "[da configurare]@archiworldpec.it"
|
|
90
|
+
if not creds["albo"]:
|
|
91
|
+
creds["albo"] = "Arch. Sez. A"
|
|
92
|
+
|
|
93
|
+
return creds
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def load_architect_profile(client, user_id: str) -> Dict[str, Any]:
|
|
97
|
+
"""Load complete architect profile from all platform tables.
|
|
98
|
+
|
|
99
|
+
Returns dict with structure:
|
|
100
|
+
{
|
|
101
|
+
user_id, email, full_name, first_name, last_name,
|
|
102
|
+
phone, instagram, position, subscription_status,
|
|
103
|
+
company_name, company_size,
|
|
104
|
+
fiscal: {country, city, region, postal_code, street, province, tax_id, tax_id_type},
|
|
105
|
+
currency, preferred_language,
|
|
106
|
+
brand: {name, mission, vision, values, promise, palette, fonts, logo_url, favicon_url, tone_of_voice},
|
|
107
|
+
style: {style_name, keywords, palette, materials, strengths, weaknesses},
|
|
108
|
+
avatar: {name, summary, demographic_profile, lifestyle_preferences, motivations_goals},
|
|
109
|
+
tax_settings: {tax_rate, tax_regime, tax_rate_imposta, tax_rate_inps, tax_rate_irap, profitability_coefficient},
|
|
110
|
+
italian_credentials: {ordine_professionale, matricola, pec, albo},
|
|
111
|
+
}
|
|
112
|
+
"""
|
|
113
|
+
profile = {"user_id": user_id}
|
|
114
|
+
|
|
115
|
+
# === profiles ===
|
|
116
|
+
p = _safe_get(client, "profiles", user_id) or {}
|
|
117
|
+
# profiles uses id not user_id · try by id
|
|
118
|
+
if not p:
|
|
119
|
+
p = _safe_get_by_id(client, "profiles", user_id) or {}
|
|
120
|
+
profile["email"] = p.get("email", "")
|
|
121
|
+
profile["full_name"] = p.get("full_name", "")
|
|
122
|
+
profile["phone"] = p.get("phone", "")
|
|
123
|
+
profile["instagram"] = p.get("instagram_handle", "")
|
|
124
|
+
profile["position"] = p.get("position", "")
|
|
125
|
+
profile["subscription_status"] = p.get("subscription_status", "")
|
|
126
|
+
profile["is_admin"] = p.get("is_admin", False)
|
|
127
|
+
|
|
128
|
+
# === user_settings (fiscal info) ===
|
|
129
|
+
s = _safe_get(client, "user_settings", user_id) or {}
|
|
130
|
+
profile["first_name"] = s.get("first_name", "") or profile.get("full_name", "")
|
|
131
|
+
profile["last_name"] = s.get("last_name", "")
|
|
132
|
+
full_name_combined = f"{profile['first_name']} {profile['last_name']}".strip()
|
|
133
|
+
if full_name_combined:
|
|
134
|
+
profile["full_name"] = full_name_combined
|
|
135
|
+
profile["company_name"] = s.get("company_name", "") or p.get("company", "")
|
|
136
|
+
profile["company_size"] = s.get("company_size", "")
|
|
137
|
+
profile["professional_role"] = s.get("professional_role", "")
|
|
138
|
+
profile["preferred_language"] = s.get("preferred_language", "it")
|
|
139
|
+
profile["currency"] = s.get("currency", "EUR")
|
|
140
|
+
profile["fiscal"] = {
|
|
141
|
+
"country": s.get("fiscal_address_country") or s.get("country", ""),
|
|
142
|
+
"city": s.get("fiscal_address_city") or s.get("city", ""),
|
|
143
|
+
"region": s.get("region", ""),
|
|
144
|
+
"postal_code": s.get("fiscal_address_postal_code", ""),
|
|
145
|
+
"street": s.get("fiscal_address_street", ""),
|
|
146
|
+
"province": s.get("fiscal_address_province", ""),
|
|
147
|
+
"tax_id": s.get("tax_id", ""),
|
|
148
|
+
"tax_id_type": s.get("tax_id_type", ""),
|
|
149
|
+
"fiscal_country_code": s.get("fiscal_country", ""),
|
|
150
|
+
"fiscal_regime": s.get("fiscal_regime", ""),
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# === brand_profiles ===
|
|
154
|
+
b = _safe_get(client, "brand_profiles", user_id) or {}
|
|
155
|
+
profile["brand"] = {
|
|
156
|
+
"name": b.get("name", "") or profile.get("company_name", ""),
|
|
157
|
+
"mission": b.get("mission", ""),
|
|
158
|
+
"vision": b.get("vision", ""),
|
|
159
|
+
"values": b.get("values", ""),
|
|
160
|
+
"promise": b.get("promise", ""),
|
|
161
|
+
"tone_of_voice": b.get("tone_of_voice", ""),
|
|
162
|
+
"palette": b.get("palette", []),
|
|
163
|
+
"fonts": b.get("fonts", {}),
|
|
164
|
+
"logo_url": b.get("logo_url", ""),
|
|
165
|
+
"favicon_url": b.get("favicon_url", ""),
|
|
166
|
+
"keywords": b.get("keywords", []),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# === style_profiles ===
|
|
170
|
+
st = _safe_get(client, "style_profiles", user_id) or {}
|
|
171
|
+
profile["style"] = {
|
|
172
|
+
"style_name": st.get("style_name", ""),
|
|
173
|
+
"keywords": st.get("keywords", []),
|
|
174
|
+
"palette": st.get("palette", []),
|
|
175
|
+
"materials": st.get("materials", []),
|
|
176
|
+
"strengths": st.get("strengths", []),
|
|
177
|
+
"weaknesses": st.get("weaknesses", []),
|
|
178
|
+
"summary": st.get("summary", ""),
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
# === avatars (target cliente persona) ===
|
|
182
|
+
av = _safe_get(client, "avatars", user_id) or {}
|
|
183
|
+
profile["avatar"] = {
|
|
184
|
+
"name": av.get("name", ""),
|
|
185
|
+
"summary": av.get("summary", ""),
|
|
186
|
+
"demographic_profile": av.get("demographic_profile", {}),
|
|
187
|
+
"lifestyle_preferences": av.get("lifestyle_preferences", {}),
|
|
188
|
+
"motivations_goals": av.get("motivations_goals", {}),
|
|
189
|
+
"barriers_objections": av.get("barriers_objections", {}),
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# === user_tax_settings ===
|
|
193
|
+
tx = _safe_get(client, "user_tax_settings", user_id) or {}
|
|
194
|
+
profile["tax_settings"] = {
|
|
195
|
+
"tax_rate": tx.get("tax_rate", 22),
|
|
196
|
+
"tax_regime": tx.get("tax_regime", "manual"),
|
|
197
|
+
"tax_rate_imposta": tx.get("tax_rate_imposta", 25),
|
|
198
|
+
"tax_rate_inps": tx.get("tax_rate_inps", 4),
|
|
199
|
+
"tax_rate_irap": tx.get("tax_rate_irap", 0),
|
|
200
|
+
"profitability_coefficient": tx.get("profitability_coefficient", 78),
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
# === Italian professional credentials (env or JSON) ===
|
|
204
|
+
profile["italian_credentials"] = _load_italian_credentials()
|
|
205
|
+
|
|
206
|
+
return profile
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def format_address(fiscal: Dict[str, str]) -> str:
|
|
210
|
+
"""Format fiscal address as readable string."""
|
|
211
|
+
parts = []
|
|
212
|
+
if fiscal.get("street"):
|
|
213
|
+
parts.append(fiscal["street"])
|
|
214
|
+
city_zip = ""
|
|
215
|
+
if fiscal.get("postal_code"):
|
|
216
|
+
city_zip = fiscal["postal_code"]
|
|
217
|
+
if fiscal.get("city"):
|
|
218
|
+
city_zip = f"{city_zip} {fiscal['city']}".strip()
|
|
219
|
+
if fiscal.get("province"):
|
|
220
|
+
city_zip = f"{city_zip} ({fiscal['province']})".strip()
|
|
221
|
+
if city_zip:
|
|
222
|
+
parts.append(city_zip)
|
|
223
|
+
if fiscal.get("country"):
|
|
224
|
+
parts.append(fiscal["country"])
|
|
225
|
+
return ", ".join(parts) if parts else "[indirizzo da configurare]"
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def format_tax_id(fiscal: Dict[str, str]) -> str:
|
|
229
|
+
"""Format tax_id with country prefix."""
|
|
230
|
+
tax_id = fiscal.get("tax_id", "")
|
|
231
|
+
tid_type = fiscal.get("tax_id_type", "")
|
|
232
|
+
if not tax_id:
|
|
233
|
+
return "[P.IVA da configurare]"
|
|
234
|
+
if tid_type.startswith("vat_"):
|
|
235
|
+
country = tid_type.replace("vat_", "").upper()
|
|
236
|
+
return f"P.IVA {country} {tax_id}"
|
|
237
|
+
return f"P.IVA {tax_id}"
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def format_architect_signature(profile: Dict[str, Any]) -> str:
|
|
241
|
+
"""Format full architect signature line for documents.
|
|
242
|
+
|
|
243
|
+
Example: "Pablo Ruan · architetto · OAPPC Milano n. matr. 12345 · ArchPrime · Lambeth (GB)"
|
|
244
|
+
"""
|
|
245
|
+
parts = []
|
|
246
|
+
if profile.get("full_name"):
|
|
247
|
+
parts.append(profile["full_name"])
|
|
248
|
+
parts.append("architetto")
|
|
249
|
+
creds = profile.get("italian_credentials", {})
|
|
250
|
+
if creds.get("ordine_professionale") and creds.get("matricola"):
|
|
251
|
+
parts.append(f"{creds['ordine_professionale']} n. matr. {creds['matricola']}")
|
|
252
|
+
if profile.get("company_name"):
|
|
253
|
+
parts.append(profile["company_name"])
|
|
254
|
+
fiscal = profile.get("fiscal", {})
|
|
255
|
+
if fiscal.get("city"):
|
|
256
|
+
loc = fiscal["city"]
|
|
257
|
+
if fiscal.get("country"):
|
|
258
|
+
loc = f"{loc} ({fiscal['country']})"
|
|
259
|
+
parts.append(loc)
|
|
260
|
+
return " · ".join(parts)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# ----------------------------------------------------------------------------
|
|
264
|
+
# CLI test
|
|
265
|
+
# ----------------------------------------------------------------------------
|
|
266
|
+
if __name__ == "__main__":
|
|
267
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
268
|
+
from lovarch_client import LovarchClient
|
|
269
|
+
client = LovarchClient()
|
|
270
|
+
user_id = sys.argv[1] if len(sys.argv) > 1 else "a5f3ee03-a7df-4571-a68d-baf168bd4ba8"
|
|
271
|
+
profile = load_architect_profile(client, user_id)
|
|
272
|
+
print(json.dumps(profile, indent=2, ensure_ascii=False))
|
|
273
|
+
print("\n=== Formatted ===")
|
|
274
|
+
print(f"Signature: {format_architect_signature(profile)}")
|
|
275
|
+
print(f"Address: {format_address(profile['fiscal'])}")
|
|
276
|
+
print(f"Tax ID: {format_tax_id(profile['fiscal'])}")
|