forked from diegoecab/oci-deal-accelerator
MELI Fraud: case-specific scaffolding scripts (3 chassis variants)
Three one-shot Python scripts that build the MELI Fraud As-Is /
To-Be .drawio pair by cloning an Oracle Architecture Center chassis
and overlaying MELI-specific labels, region renames, and workload
pills. Useful as worked examples of the chassis-then-overlay
transform pattern (drawio_template_transform.DrawioTemplate) that
the diagram procedure recommends:
• build_meli_fraud_from_adbsplusadbd.py — clones the locally
accepted ATP-S/ATP-D side-by-side reference and partitions cells
into AS-IS / TO-BE panels by x-coordinate.
• build_meli_fraud_from_crossregion.py — uses Oracle's
exadata-dedicated-cross-region-dataguard ref-arch (two regions
with DRG + RPC + Data Guard already drawn) as chassis and only
overlays MELI-specific pills.
• build_meli_fraud_from_templates.py — combines the in-region and
cross-region Exadata ref-archs and clones the in-region
topology to the right for the Montreal DR.
All three honour the standing rule: do not edit text inside Oracle
SVG stencil shapes — overlay new mxCells in whitespace instead.
Outputs land under examples/output-meli-fraud-adbd-migration/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
437
tools/build_meli_fraud_from_adbsplusadbd.py
Normal file
437
tools/build_meli_fraud_from_adbsplusadbd.py
Normal file
@@ -0,0 +1,437 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Build MELI Fraud As-Is and To-Be .drawio files by cloning and editing
|
||||||
|
examples/output-meli-fraud-adbd-migration/adbsplusadbd.drawio — the ADB
|
||||||
|
(ATP-S / ATP-D) reference Diego already accepted visually.
|
||||||
|
|
||||||
|
Strategy:
|
||||||
|
1. Load the source file.
|
||||||
|
2. Partition cells into AS-IS (x < 4630) and TO-BE (x >= 4630) panels;
|
||||||
|
edges are assigned to a panel based on their source/target cell.
|
||||||
|
3. For each output, delete the panel we don't need.
|
||||||
|
4. Relabel the title banner, notes, and trade-offs text for MELI.
|
||||||
|
5. Clone the ATP-S / ATP-D icon cells (single-cell stencils) to add a
|
||||||
|
second workload row (Transactional) and a Clone column (As-Is only).
|
||||||
|
6. Add a Montreal DR region panel to the right.
|
||||||
|
7. Save.
|
||||||
|
|
||||||
|
Constraint: interior labels like "OCI Ashburn" / "AD 3" / "AD 2" / "R/W Pool"
|
||||||
|
are plain mxCell values we CAN edit — so we rename AD 3 → AD-X, AD 2 → AD-Y,
|
||||||
|
adjust R/W Pool label, etc.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
from drawio_template_transform import DrawioTemplate # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
SRC = Path("examples/output-meli-fraud-adbd-migration/adbsplusadbd.drawio")
|
||||||
|
OUT_DIR = Path("examples/output-meli-fraud-adbd-migration")
|
||||||
|
|
||||||
|
|
||||||
|
# Oracle palette
|
||||||
|
ORACLE_RED = "#C74634"
|
||||||
|
ORACLE_GREEN = "#00875A"
|
||||||
|
SLATE_BLUE = "#6E89AD"
|
||||||
|
AMBER = "#E8A92F"
|
||||||
|
MUTED_GREY = "#70665E"
|
||||||
|
CHARCOAL = "#312D2A"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Panel partitioning
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def partition_panels(t: DrawioTemplate, split_x: float = 4630):
|
||||||
|
"""Return (as_is_ids, to_be_ids) — both sets cover all vertex cells.
|
||||||
|
Edges are classified by their source/target positions.
|
||||||
|
"""
|
||||||
|
as_is, to_be = set(), set()
|
||||||
|
|
||||||
|
def cell_x(cid: str) -> float | None:
|
||||||
|
c = t.cells.get(cid)
|
||||||
|
if c is None:
|
||||||
|
return None
|
||||||
|
geo = c.find("mxGeometry")
|
||||||
|
if geo is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return float(geo.get("x") or 0)
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Vertices first: classify by absolute x (walk parents to get absolute x)
|
||||||
|
def abs_x(cid: str) -> float:
|
||||||
|
x = 0.0
|
||||||
|
cur = cid
|
||||||
|
guard = 0
|
||||||
|
while cur and cur in t.cells and guard < 30:
|
||||||
|
geo = t.cells[cur].find("mxGeometry")
|
||||||
|
if geo is not None:
|
||||||
|
try:
|
||||||
|
x += float(geo.get("x") or 0)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
parent = t.cells[cur].get("parent") or ""
|
||||||
|
if parent in ("0", "1", ""):
|
||||||
|
break
|
||||||
|
cur = parent
|
||||||
|
guard += 1
|
||||||
|
return x
|
||||||
|
|
||||||
|
for cid, c in t.cells.items():
|
||||||
|
if cid in ("0", "1"):
|
||||||
|
continue
|
||||||
|
if c.get("edge") == "1":
|
||||||
|
continue
|
||||||
|
ax = abs_x(cid)
|
||||||
|
if ax >= split_x:
|
||||||
|
to_be.add(cid)
|
||||||
|
else:
|
||||||
|
as_is.add(cid)
|
||||||
|
|
||||||
|
# Edges follow their source/target
|
||||||
|
for cid, c in t.cells.items():
|
||||||
|
if c.get("edge") != "1":
|
||||||
|
continue
|
||||||
|
src = c.get("source")
|
||||||
|
tgt = c.get("target")
|
||||||
|
in_tobe = any(ref and ref in to_be for ref in (src, tgt))
|
||||||
|
in_asis = any(ref and ref in as_is for ref in (src, tgt))
|
||||||
|
if in_tobe and not in_asis:
|
||||||
|
to_be.add(cid)
|
||||||
|
elif in_asis and not in_tobe:
|
||||||
|
as_is.add(cid)
|
||||||
|
else:
|
||||||
|
# Ambiguous — keep with TO BE by default
|
||||||
|
to_be.add(cid)
|
||||||
|
|
||||||
|
return as_is, to_be
|
||||||
|
|
||||||
|
|
||||||
|
def delete_cells(t: DrawioTemplate, cell_ids: set[str]) -> None:
|
||||||
|
for cid in list(cell_ids):
|
||||||
|
c = t.cells.get(cid)
|
||||||
|
if c is not None:
|
||||||
|
try:
|
||||||
|
t.model_root.remove(c)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
t.reindex()
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Text helpers — Oracle templates often store rich-HTML values
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def replace_rich_label(t: DrawioTemplate, cell_id: str, plain_text: str,
|
||||||
|
color: str = CHARCOAL, bold: bool = True,
|
||||||
|
size: int = 16) -> None:
|
||||||
|
"""Replace a cell's <value> with simple HTML — keeps drawio rendering happy."""
|
||||||
|
weight = "bold" if bold else "normal"
|
||||||
|
html = (
|
||||||
|
f'<h1 style="color: {color}; text-align: left; '
|
||||||
|
f'line-height: 1.1; margin: 0; font-size: {size}px; font-weight: {weight};">'
|
||||||
|
f'{plain_text}</h1>'
|
||||||
|
)
|
||||||
|
t.cells[cell_id].set("value", html)
|
||||||
|
|
||||||
|
|
||||||
|
def add_pill(t: DrawioTemplate, text: str, x: float, y: float,
|
||||||
|
fill: str, stroke: str, font_color: str = "#FCFBFA",
|
||||||
|
w: float = 130, h: float = 22) -> str:
|
||||||
|
style = (
|
||||||
|
f"rounded=1;whiteSpace=wrap;html=1;fillColor={fill};"
|
||||||
|
f"strokeColor={stroke};strokeWidth=1;fontColor={font_color};"
|
||||||
|
f"fontSize=10;fontStyle=1;fontFamily=Oracle Sans;"
|
||||||
|
f"align=center;verticalAlign=middle;arcSize=30;"
|
||||||
|
)
|
||||||
|
return t.add_cell(text, style, "1", x, y, w, h)
|
||||||
|
|
||||||
|
|
||||||
|
def add_soft_box(t: DrawioTemplate, label: str, x: float, y: float,
|
||||||
|
w: float, h: float, fill: str, stroke: str,
|
||||||
|
font_color: str = CHARCOAL) -> str:
|
||||||
|
style = (
|
||||||
|
f"rounded=1;whiteSpace=wrap;html=1;fillColor={fill};"
|
||||||
|
f"strokeColor={stroke};strokeWidth=1;dashed=1;dashPattern=6 4;"
|
||||||
|
f"fontColor={font_color};fontSize=12;fontStyle=1;"
|
||||||
|
f"fontFamily=Oracle Sans;align=left;verticalAlign=top;"
|
||||||
|
f"spacingLeft=8;spacingTop=6;arcSize=4;"
|
||||||
|
)
|
||||||
|
return t.add_cell(label, style, "1", x, y, w, h)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# As-Is builder — left panel of adbsplusadbd, ATP-S icons
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def build_as_is() -> None:
|
||||||
|
t = DrawioTemplate(SRC)
|
||||||
|
as_is, to_be = partition_panels(t)
|
||||||
|
delete_cells(t, to_be)
|
||||||
|
|
||||||
|
P = "GVNBTaRQvtpjXlJZoCOU" # id prefix used in the source
|
||||||
|
|
||||||
|
# Title: cell 87 holds the AS IS title banner text
|
||||||
|
replace_rich_label(t, f"{P}-87", "MELI Fraud — As-Is (ADB-S)",
|
||||||
|
color="#FCFBFA", bold=True, size=22)
|
||||||
|
# Make the banner red + solid
|
||||||
|
t.patch_style(f"{P}-87",
|
||||||
|
fillColor=ORACLE_RED,
|
||||||
|
strokeColor="none",
|
||||||
|
fontColor="#FCFBFA",
|
||||||
|
align="center",
|
||||||
|
verticalAlign="middle")
|
||||||
|
|
||||||
|
# Remove the notes and trade-offs text boxes — not needed in the simple view
|
||||||
|
for cid in (f"{P}-88", f"{P}-89", f"{P}-86"):
|
||||||
|
if cid in t.cells:
|
||||||
|
t.delete_subtree(cid)
|
||||||
|
|
||||||
|
# Relabel AD 3 → AD-X, AD 2 → AD-Y to match the prompt
|
||||||
|
if f"{P}-93" in t.cells:
|
||||||
|
t.set_label(f"{P}-93", "AD-X")
|
||||||
|
if f"{P}-94" in t.cells:
|
||||||
|
t.set_label(f"{P}-94", "AD-Y")
|
||||||
|
|
||||||
|
# Rename R/W Pool → Fraude (it holds Fraude's primary + standby)
|
||||||
|
# Cell 95 is the R/W Pool container in AS IS.
|
||||||
|
if f"{P}-95" in t.cells:
|
||||||
|
t.set_label(f"{P}-95", "Fraude (19c · ADB-S)")
|
||||||
|
|
||||||
|
# Relabel the ATP-S icons: 100 = primary, 99 = L-SBY. Replace their
|
||||||
|
# rich HTML values with simple labels.
|
||||||
|
if f"{P}-100" in t.cells:
|
||||||
|
replace_rich_label(t, f"{P}-100", "Fraude — Primary", color=ORACLE_RED,
|
||||||
|
size=11)
|
||||||
|
if f"{P}-99" in t.cells:
|
||||||
|
replace_rich_label(t, f"{P}-99", "Fraude — Local Standby (ADG)",
|
||||||
|
color=SLATE_BLUE, size=11)
|
||||||
|
# Cell 112 is an extra ATP-S icon floating; reuse as Fraude Clone (amber)
|
||||||
|
if f"{P}-112" in t.cells:
|
||||||
|
replace_rich_label(t, f"{P}-112", "Fraude — Clone (R/O)",
|
||||||
|
color="#7A5510", size=11)
|
||||||
|
# No tone override on the stencil itself — keep the OCI teal icon but
|
||||||
|
# wrap an amber dashed pill around it.
|
||||||
|
geo = t.cells[f"{P}-112"].find("mxGeometry")
|
||||||
|
if geo is not None:
|
||||||
|
gx = float(geo.get("x") or 0)
|
||||||
|
gy = float(geo.get("y") or 0)
|
||||||
|
gw = float(geo.get("width") or 60)
|
||||||
|
gh = float(geo.get("height") or 50)
|
||||||
|
t.add_cell(
|
||||||
|
"", style=(
|
||||||
|
f"rounded=1;whiteSpace=wrap;html=1;fillColor=none;"
|
||||||
|
f"strokeColor={AMBER};strokeWidth=2;dashed=1;dashPattern=6 4;"
|
||||||
|
f"arcSize=8;"
|
||||||
|
),
|
||||||
|
parent="1", x=gx - 6, y=gy - 6, w=gw + 12, h=gh + 14,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Delete notes cell 111 if present (the "A) Allow L-SBY..." side note)
|
||||||
|
if f"{P}-111" in t.cells:
|
||||||
|
t.delete_subtree(f"{P}-111")
|
||||||
|
|
||||||
|
# ── Second workload row (Transactional) — clone the pool + icons ────
|
||||||
|
# Clone the pool container (95), primary stencil (100), standby (99),
|
||||||
|
# and the small customer icon group (101) with a y-offset of ~290 so
|
||||||
|
# they sit below the existing row.
|
||||||
|
dy = 290
|
||||||
|
clone_map = {}
|
||||||
|
for src_id in (f"{P}-95", f"{P}-100", f"{P}-99"):
|
||||||
|
if src_id not in t.cells:
|
||||||
|
continue
|
||||||
|
m = t.clone_subtree(src_id, dx=0, dy=dy,
|
||||||
|
id_prefix=f"txn_{src_id.split('-')[-1]}_")
|
||||||
|
clone_map.update(m)
|
||||||
|
|
||||||
|
# Label the cloned pool
|
||||||
|
cloned_pool = clone_map.get(f"{P}-95")
|
||||||
|
if cloned_pool:
|
||||||
|
t.set_label(cloned_pool, "Transactional — Fraude Team (26ai · ADB-S)")
|
||||||
|
|
||||||
|
cloned_primary = clone_map.get(f"{P}-100")
|
||||||
|
if cloned_primary:
|
||||||
|
replace_rich_label(t, cloned_primary, "Transactional — Primary",
|
||||||
|
color=ORACLE_RED, size=11)
|
||||||
|
|
||||||
|
cloned_sby = clone_map.get(f"{P}-99")
|
||||||
|
if cloned_sby:
|
||||||
|
replace_rich_label(t, cloned_sby, "Transactional — Local Standby (ADG)",
|
||||||
|
color=SLATE_BLUE, size=11)
|
||||||
|
|
||||||
|
# Transactional Refreshable Clone — clone the ATP-S icon (cell 112) and
|
||||||
|
# place it below the Fraude clone
|
||||||
|
if f"{P}-112" in t.cells:
|
||||||
|
m = t.clone_subtree(f"{P}-112", dx=0, dy=dy,
|
||||||
|
id_prefix="txn_clone_")
|
||||||
|
cloned_clone = m.get(f"{P}-112")
|
||||||
|
if cloned_clone:
|
||||||
|
replace_rich_label(t, cloned_clone,
|
||||||
|
"Transactional — Refreshable Clone",
|
||||||
|
color="#7A5510", size=11)
|
||||||
|
|
||||||
|
# ── Montreal DR region ──────────────────────────────────────────────
|
||||||
|
# Place a new gray dashed region to the right of the primary OCI region.
|
||||||
|
# OCI Ashburn in source is at x=4155, w=344 → right edge ≈ 4499.
|
||||||
|
# The panel backdrop (85) ends at x=3543+1101=4644. Push Montreal to the
|
||||||
|
# right, extending the panel size if needed.
|
||||||
|
MTL_X = 4660
|
||||||
|
MTL_Y = 5925
|
||||||
|
MTL_W = 340
|
||||||
|
MTL_H = 570
|
||||||
|
add_soft_box(t, "Region — ca-montreal-1 (DR)",
|
||||||
|
MTL_X, MTL_Y, MTL_W, MTL_H,
|
||||||
|
fill="#FFFFFF", stroke=MUTED_GREY)
|
||||||
|
|
||||||
|
add_pill(t, "Fraude — Cross-region Standby",
|
||||||
|
MTL_X + 30, MTL_Y + 80, SLATE_BLUE, "#2E4A78", w=280, h=32)
|
||||||
|
add_pill(t, "Transactional — Cross-region Standby",
|
||||||
|
MTL_X + 30, MTL_Y + 200, SLATE_BLUE, "#2E4A78", w=280, h=32)
|
||||||
|
|
||||||
|
# Extend the big panel backdrop (cell 85) to accommodate Montreal
|
||||||
|
panel = t.cells.get(f"{P}-85")
|
||||||
|
if panel is not None:
|
||||||
|
geo = panel.find("mxGeometry")
|
||||||
|
if geo is not None:
|
||||||
|
# Widen from 1101 to ~1500
|
||||||
|
geo.set("width", "1530")
|
||||||
|
geo.set("height", "870")
|
||||||
|
|
||||||
|
# Footer line
|
||||||
|
t.add_text(
|
||||||
|
"Out of scope: Mexico · Catálogo · Attribution.",
|
||||||
|
x=3561, y=6600, w=900, h=22,
|
||||||
|
font_size=11, font_color=MUTED_GREY, bold=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
out = OUT_DIR / "fraud-as-is.drawio"
|
||||||
|
t.save(out)
|
||||||
|
print(f"✓ wrote {out}")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# To-Be builder — right panel of adbsplusadbd, ATP-D icons
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def build_to_be() -> None:
|
||||||
|
t = DrawioTemplate(SRC)
|
||||||
|
as_is, to_be = partition_panels(t)
|
||||||
|
delete_cells(t, as_is)
|
||||||
|
|
||||||
|
P = "GVNBTaRQvtpjXlJZoCOU"
|
||||||
|
|
||||||
|
# Title: cell 4 holds the TO BE title in the source
|
||||||
|
replace_rich_label(t, f"{P}-4",
|
||||||
|
"MELI Fraud — To-Be (ADB-D Dedicated)",
|
||||||
|
color="#FCFBFA", bold=True, size=22)
|
||||||
|
t.patch_style(f"{P}-4",
|
||||||
|
fillColor=ORACLE_RED,
|
||||||
|
strokeColor="none",
|
||||||
|
fontColor="#FCFBFA",
|
||||||
|
align="center",
|
||||||
|
verticalAlign="middle")
|
||||||
|
|
||||||
|
# Remove notes + trade-offs + RPO/RTO floating labels
|
||||||
|
for cid in (f"{P}-5", f"{P}-36", f"{P}-3"):
|
||||||
|
if cid in t.cells:
|
||||||
|
t.delete_subtree(cid)
|
||||||
|
|
||||||
|
# Rename AD 3 → AD-X, AD 2 → AD-Y
|
||||||
|
if f"{P}-40" in t.cells:
|
||||||
|
t.set_label(f"{P}-40", "AD-X — Primary · Quarter Rack X11M")
|
||||||
|
if f"{P}-41" in t.cells:
|
||||||
|
t.set_label(f"{P}-41", "AD-Y — Local ADG Standby · Quarter Rack X11M")
|
||||||
|
|
||||||
|
# Pools: 43 = R/O Pool, 42 = R/W Pool. Rename to AVMC-per-workload framing.
|
||||||
|
if f"{P}-43" in t.cells:
|
||||||
|
t.set_label(f"{P}-43", "AVMC · CDB-FRAUDE / CDB-TRANSACTIONAL — Primary")
|
||||||
|
if f"{P}-42" in t.cells:
|
||||||
|
t.set_label(f"{P}-42",
|
||||||
|
"AVMC · CDB-FRAUDE-RO / CDB-TRANSACTIONAL-RO — Read-only Standby")
|
||||||
|
|
||||||
|
# Relabel ATP-D icons: cell 47 (L-SBY R/O), 48 (primary)
|
||||||
|
if f"{P}-47" in t.cells:
|
||||||
|
replace_rich_label(t, f"{P}-47", "Fraude — Read-only Standby",
|
||||||
|
color=ORACLE_GREEN, size=11)
|
||||||
|
if f"{P}-48" in t.cells:
|
||||||
|
replace_rich_label(t, f"{P}-48", "Fraude — Primary",
|
||||||
|
color=ORACLE_RED, size=11)
|
||||||
|
|
||||||
|
# ── Second workload row (Transactional) — clone ATP-D icons ─────────
|
||||||
|
dy = 80 # place below the existing icons inside the same pool areas
|
||||||
|
clone_map = {}
|
||||||
|
for src_id in (f"{P}-47", f"{P}-48"):
|
||||||
|
if src_id not in t.cells:
|
||||||
|
continue
|
||||||
|
m = t.clone_subtree(src_id, dx=0, dy=dy,
|
||||||
|
id_prefix=f"txn_{src_id.split('-')[-1]}_")
|
||||||
|
clone_map.update(m)
|
||||||
|
|
||||||
|
cloned_ro = clone_map.get(f"{P}-47")
|
||||||
|
if cloned_ro:
|
||||||
|
replace_rich_label(t, cloned_ro, "Transactional — Read-only Standby",
|
||||||
|
color=ORACLE_GREEN, size=11)
|
||||||
|
cloned_primary = clone_map.get(f"{P}-48")
|
||||||
|
if cloned_primary:
|
||||||
|
replace_rich_label(t, cloned_primary, "Transactional — Primary",
|
||||||
|
color=ORACLE_RED, size=11)
|
||||||
|
|
||||||
|
# ── Montreal DR region ──────────────────────────────────────────────
|
||||||
|
MTL_X = 5860
|
||||||
|
MTL_Y = 6004
|
||||||
|
MTL_W = 340
|
||||||
|
MTL_H = 600
|
||||||
|
add_soft_box(t,
|
||||||
|
"Region — ca-montreal-1 (DR)\nQuarter Rack X11M",
|
||||||
|
MTL_X, MTL_Y, MTL_W, MTL_H,
|
||||||
|
fill="#FFFFFF", stroke=MUTED_GREY)
|
||||||
|
t.add_text(
|
||||||
|
"AVMC · CDB-FRAUDE-DR / CDB-TRANSACTIONAL-DR",
|
||||||
|
x=MTL_X + 15, y=MTL_Y + 60, w=MTL_W - 30, h=20,
|
||||||
|
font_size=10, font_color=MUTED_GREY, bold=True,
|
||||||
|
)
|
||||||
|
add_pill(t, "Fraude — DR Standby",
|
||||||
|
MTL_X + 30, MTL_Y + 110, SLATE_BLUE, "#2E4A78", w=280, h=32)
|
||||||
|
add_pill(t, "Transactional — DR Standby",
|
||||||
|
MTL_X + 30, MTL_Y + 240, SLATE_BLUE, "#2E4A78", w=280, h=32)
|
||||||
|
|
||||||
|
# Extend the TO BE panel backdrops (cells 1, 2) to fit Montreal
|
||||||
|
for cid in (f"{P}-1", f"{P}-2"):
|
||||||
|
panel = t.cells.get(cid)
|
||||||
|
if panel is None:
|
||||||
|
continue
|
||||||
|
geo = panel.find("mxGeometry")
|
||||||
|
if geo is not None:
|
||||||
|
geo.set("width", "1540")
|
||||||
|
geo.set("height", "870")
|
||||||
|
|
||||||
|
# Footer
|
||||||
|
t.add_text(
|
||||||
|
"Three Quarter Rack X11M total (2 in Ashburn + 1 in Montreal). "
|
||||||
|
"Active Data Guard in-region · Cross-region DG to Montreal.",
|
||||||
|
x=4708, y=6675, w=1500, h=22,
|
||||||
|
font_size=11, font_color=MUTED_GREY, bold=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
out = OUT_DIR / "fraud-to-be.drawio"
|
||||||
|
t.save(out)
|
||||||
|
print(f"✓ wrote {out}")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
build_as_is()
|
||||||
|
build_to_be()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
238
tools/build_meli_fraud_from_crossregion.py
Normal file
238
tools/build_meli_fraud_from_crossregion.py
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Alternate MELI Fraud diagrams — built on top of the Oracle
|
||||||
|
`exadata-dedicated-cross-region-dataguard` reference architecture as chassis.
|
||||||
|
|
||||||
|
Why this chassis:
|
||||||
|
It ships with exactly the layout both prompts want — two OCI regions
|
||||||
|
side-by-side, each with a VCN (Client Subnet + Backup Subnet), a Database
|
||||||
|
Service icon, DRG + Remote Peering, Service Gateway, and Oracle Services
|
||||||
|
Network (DNS + Object Storage) — with the Oracle Data Guard arrow drawn
|
||||||
|
between the two regions.
|
||||||
|
|
||||||
|
We keep the chassis largely untouched and overlay MELI-specific pills +
|
||||||
|
region rename + title banner, per Diego's earlier feedback that preserving
|
||||||
|
Oracle's official polish is the goal. The Exadata stencil stays (it is an
|
||||||
|
Oracle shape representing the database infrastructure) but the pills
|
||||||
|
identify the specific databases layered on top of it for MELI's estate.
|
||||||
|
|
||||||
|
Output:
|
||||||
|
examples/output-meli-fraud-adbd-migration/fraud-as-is-alt.drawio
|
||||||
|
examples/output-meli-fraud-adbd-migration/fraud-to-be-alt.drawio
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
from drawio_template_transform import DrawioTemplate # noqa: E402
|
||||||
|
|
||||||
|
SRC = Path(
|
||||||
|
"kb/diagram/assets/archcenter-refs/exadata-dedicated-cross-region-dataguard/"
|
||||||
|
"exadata-dedicated-cross-region-dataguard-uncompressed.drawio"
|
||||||
|
)
|
||||||
|
OUT_DIR = Path("examples/output-meli-fraud-adbd-migration")
|
||||||
|
|
||||||
|
ORACLE_RED = "#C74634"
|
||||||
|
ORACLE_GREEN = "#00875A"
|
||||||
|
SLATE_BLUE = "#6E89AD"
|
||||||
|
AMBER = "#E8A92F"
|
||||||
|
MUTED_GREY = "#70665E"
|
||||||
|
CHARCOAL = "#312D2A"
|
||||||
|
|
||||||
|
|
||||||
|
def add_banner(t: DrawioTemplate, text: str, x: float, y: float, w: float,
|
||||||
|
h: float = 36, color: str = ORACLE_RED,
|
||||||
|
font_color: str = "#FCFBFA") -> str:
|
||||||
|
style = (
|
||||||
|
f"rounded=1;whiteSpace=wrap;html=1;fillColor={color};strokeColor=none;"
|
||||||
|
f"fontColor={font_color};fontSize=16;fontStyle=1;fontFamily=Oracle Sans;"
|
||||||
|
f"align=center;verticalAlign=middle;arcSize=4;"
|
||||||
|
)
|
||||||
|
return t.add_cell(text, style, "1", x, y, w, h)
|
||||||
|
|
||||||
|
|
||||||
|
def add_pill(t: DrawioTemplate, text: str, x: float, y: float,
|
||||||
|
fill: str, stroke: str, font_color: str = "#FCFBFA",
|
||||||
|
w: float = 170, h: float = 22) -> str:
|
||||||
|
style = (
|
||||||
|
f"rounded=1;whiteSpace=wrap;html=1;fillColor={fill};"
|
||||||
|
f"strokeColor={stroke};strokeWidth=1;fontColor={font_color};"
|
||||||
|
f"fontSize=10;fontStyle=1;fontFamily=Oracle Sans;"
|
||||||
|
f"align=center;verticalAlign=middle;arcSize=30;"
|
||||||
|
)
|
||||||
|
return t.add_cell(text, style, "1", x, y, w, h)
|
||||||
|
|
||||||
|
|
||||||
|
def add_region_label(t: DrawioTemplate, text: str, x: float, y: float,
|
||||||
|
color: str = CHARCOAL) -> str:
|
||||||
|
style = (
|
||||||
|
f"text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;"
|
||||||
|
f"fontFamily=Oracle Sans;fontSize=12;fontStyle=1;fontColor={color};"
|
||||||
|
f"strokeColor=none;fillColor=none;"
|
||||||
|
)
|
||||||
|
return t.add_cell(text, style, "1", x, y, 340, 20)
|
||||||
|
|
||||||
|
|
||||||
|
def add_footer(t: DrawioTemplate, text: str, x: float, y: float,
|
||||||
|
w: float = 1000) -> str:
|
||||||
|
style = (
|
||||||
|
f"text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;"
|
||||||
|
f"fontFamily=Oracle Sans;fontSize=10;fontColor={MUTED_GREY};"
|
||||||
|
f"strokeColor=none;fillColor=none;fontStyle=2;"
|
||||||
|
)
|
||||||
|
return t.add_cell(text, style, "1", x, y, w, 20)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Geometry landmarks in the cross-region-dataguard template
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Tenancy chassis: ~1054x624 canvas
|
||||||
|
# Left region ("OCI Region"): x ≈ 10, y ≈ 34, w ≈ 454, h ≈ 579
|
||||||
|
# Right region ("OCI Region"): x ≈ 590, y ≈ 34, w ≈ 454, h ≈ 579
|
||||||
|
# Exadata icon boxes centered inside each region at y ≈ 210
|
||||||
|
# left icon cluster centre around x=220
|
||||||
|
# right icon cluster centre around x=820
|
||||||
|
|
||||||
|
LEFT_REGION_X = 10
|
||||||
|
LEFT_REGION_Y = 34
|
||||||
|
LEFT_REGION_W = 454
|
||||||
|
RIGHT_REGION_X = 590
|
||||||
|
RIGHT_REGION_W = 454
|
||||||
|
EXA_CLUSTER_Y = 220 # vertical centre of Exadata icon in each region
|
||||||
|
PILL_W = 200
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# As-Is builder
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def build_as_is() -> None:
|
||||||
|
t = DrawioTemplate(SRC)
|
||||||
|
|
||||||
|
# ── Title banner (sits above the canvas) ────────────────────────────
|
||||||
|
add_banner(t, "MELI Fraud — As-Is (ADB-S)",
|
||||||
|
x=0, y=-46, w=1054, h=36)
|
||||||
|
|
||||||
|
# ── Region labels override the stencil's "OCI Region" text with a
|
||||||
|
# clearer "us-ashburn-1 (Primary)" / "ca-montreal-1 (DR)" heading ─────
|
||||||
|
add_region_label(t, "us-ashburn-1 · Primary",
|
||||||
|
x=LEFT_REGION_X + 40, y=LEFT_REGION_Y + 4)
|
||||||
|
add_region_label(t, "ca-montreal-1 · DR",
|
||||||
|
x=RIGHT_REGION_X + 40, y=LEFT_REGION_Y + 4)
|
||||||
|
|
||||||
|
# ── Left region: overlay 2 workload rows with 3 DBs each ────────────
|
||||||
|
# Exadata icon of the left region is centred around x=220, y=220. We
|
||||||
|
# place pills below it identifying the databases MELI has on ADB-S.
|
||||||
|
lx = LEFT_REGION_X + 30
|
||||||
|
row_y = 260
|
||||||
|
row_h = 120
|
||||||
|
|
||||||
|
# Row 1 — Fraude
|
||||||
|
add_region_label(t, "Fraude (19c · ADB-S)",
|
||||||
|
x=lx, y=row_y - 20)
|
||||||
|
add_pill(t, "Primary", lx, row_y, ORACLE_RED, "#7A1F12",
|
||||||
|
w=120, h=22)
|
||||||
|
add_pill(t, "Local Standby (ADG)", lx + 130, row_y, SLATE_BLUE,
|
||||||
|
"#2E4A78", w=160, h=22)
|
||||||
|
add_pill(t, "Clone (R/O)", lx + 300, row_y, AMBER, "#7A5510",
|
||||||
|
font_color="#5A4A10", w=100, h=22)
|
||||||
|
|
||||||
|
# Row 2 — Transactional
|
||||||
|
add_region_label(t, "Transactional — Fraude Team (26ai · ADB-S)",
|
||||||
|
x=lx, y=row_y + row_h - 20)
|
||||||
|
add_pill(t, "Primary", lx, row_y + row_h, ORACLE_RED, "#7A1F12",
|
||||||
|
w=120, h=22)
|
||||||
|
add_pill(t, "Local Standby (ADG)", lx + 130, row_y + row_h, SLATE_BLUE,
|
||||||
|
"#2E4A78", w=160, h=22)
|
||||||
|
add_pill(t, "Refreshable Clone (R/O)", lx + 300, row_y + row_h, AMBER,
|
||||||
|
"#7A5510", font_color="#5A4A10", w=140, h=22)
|
||||||
|
|
||||||
|
# ── Right region: Montreal — cross-region standbys ──────────────────
|
||||||
|
rx = RIGHT_REGION_X + 30
|
||||||
|
add_region_label(t, "Fraude — Cross-region Standby",
|
||||||
|
x=rx, y=row_y - 20)
|
||||||
|
add_pill(t, "Fraude — DR Standby", rx, row_y, SLATE_BLUE, "#2E4A78",
|
||||||
|
w=250, h=22)
|
||||||
|
add_region_label(t, "Transactional — Cross-region Standby",
|
||||||
|
x=rx, y=row_y + row_h - 20)
|
||||||
|
add_pill(t, "Transactional — DR Standby", rx, row_y + row_h, SLATE_BLUE,
|
||||||
|
"#2E4A78", w=250, h=22)
|
||||||
|
|
||||||
|
add_footer(t, "Out of scope: Mexico · Catálogo · Attribution.",
|
||||||
|
x=10, y=635, w=1000)
|
||||||
|
|
||||||
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
out = OUT_DIR / "fraud-as-is-alt.drawio"
|
||||||
|
t.save(out)
|
||||||
|
print(f"✓ wrote {out}")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# To-Be builder
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def build_to_be() -> None:
|
||||||
|
t = DrawioTemplate(SRC)
|
||||||
|
|
||||||
|
add_banner(t, "MELI Fraud — To-Be (ADB-D Dedicated)",
|
||||||
|
x=0, y=-46, w=1054, h=36)
|
||||||
|
|
||||||
|
add_region_label(t, "us-ashburn-1 · Primary · AD-X + AD-Y",
|
||||||
|
x=LEFT_REGION_X + 40, y=LEFT_REGION_Y + 4)
|
||||||
|
add_region_label(t, "ca-montreal-1 · DR",
|
||||||
|
x=RIGHT_REGION_X + 40, y=LEFT_REGION_Y + 4)
|
||||||
|
|
||||||
|
# ── Left region: 2 ADs, each with AVMC → CDB → PDB ──────────────────
|
||||||
|
lx = LEFT_REGION_X + 30
|
||||||
|
ad_y = 260
|
||||||
|
row_h = 120
|
||||||
|
|
||||||
|
add_region_label(t,
|
||||||
|
"AD-X — Primary · Quarter Rack X11M · AVMC",
|
||||||
|
x=lx, y=ad_y - 20)
|
||||||
|
add_pill(t, "CDB-FRAUDE · Fraude PDB — Primary",
|
||||||
|
lx, ad_y, ORACLE_RED, "#7A1F12", w=320, h=22)
|
||||||
|
add_pill(t, "CDB-TRANSACTIONAL · Transactional PDB — Primary",
|
||||||
|
lx, ad_y + 28, ORACLE_RED, "#7A1F12", w=380, h=22)
|
||||||
|
|
||||||
|
add_region_label(t,
|
||||||
|
"AD-Y — Local ADG Standby · Quarter Rack X11M · AVMC",
|
||||||
|
x=lx, y=ad_y + row_h - 20)
|
||||||
|
add_pill(t, "CDB-FRAUDE-RO · Fraude — Read-only Standby",
|
||||||
|
lx, ad_y + row_h, ORACLE_GREEN, "#044C33", w=340, h=22)
|
||||||
|
add_pill(t, "CDB-TRANSACTIONAL-RO · Transactional — Read-only Standby",
|
||||||
|
lx, ad_y + row_h + 28, ORACLE_GREEN, "#044C33", w=400, h=22)
|
||||||
|
|
||||||
|
# ── Right region: Montreal DR — Quarter Rack X11M, AVMC ─────────────
|
||||||
|
rx = RIGHT_REGION_X + 30
|
||||||
|
add_region_label(t,
|
||||||
|
"Cross-region DR · Quarter Rack X11M · AVMC",
|
||||||
|
x=rx, y=ad_y - 20)
|
||||||
|
add_pill(t, "CDB-FRAUDE-DR · Fraude — DR Standby",
|
||||||
|
rx, ad_y, SLATE_BLUE, "#2E4A78", w=310, h=22)
|
||||||
|
add_pill(t, "CDB-TRANSACTIONAL-DR · Transactional — DR Standby",
|
||||||
|
rx, ad_y + 28, SLATE_BLUE, "#2E4A78", w=370, h=22)
|
||||||
|
|
||||||
|
add_footer(
|
||||||
|
t,
|
||||||
|
"Three Quarter Rack X11M total (2 in Ashburn + 1 in Montreal). "
|
||||||
|
"Active Data Guard in-region · Cross-region DG to Montreal.",
|
||||||
|
x=10, y=635, w=1040,
|
||||||
|
)
|
||||||
|
|
||||||
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
out = OUT_DIR / "fraud-to-be-alt.drawio"
|
||||||
|
t.save(out)
|
||||||
|
print(f"✓ wrote {out}")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
build_as_is()
|
||||||
|
build_to_be()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
283
tools/build_meli_fraud_from_templates.py
Normal file
283
tools/build_meli_fraud_from_templates.py
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Build MELI Fraud As-Is and To-Be .drawio files from Oracle Architecture
|
||||||
|
Center reference templates.
|
||||||
|
|
||||||
|
Starts from:
|
||||||
|
- kb/diagram/assets/archcenter-refs/exadata-dedicated-in-region-dataguard/ (base)
|
||||||
|
- kb/diagram/assets/archcenter-refs/exadata-dedicated-cross-region-dataguard/ (Montreal side)
|
||||||
|
|
||||||
|
We do **not** try to edit the internal stencil text ("OCI Region", "VCN",
|
||||||
|
"Client Subnet", …) because those labels live inside embedded SVG stencil
|
||||||
|
shapes and are not cleanly editable. Instead we:
|
||||||
|
- Keep the Oracle visual layout and palette unchanged.
|
||||||
|
- Add a red banner on top with the MELI-specific title.
|
||||||
|
- Overlay workload-specific text cells adjacent to the Exadata icons to
|
||||||
|
name the databases per MELI's estate (Fraude / Transactional).
|
||||||
|
- Clone the entire in-region topology for the Montreal DR region to the
|
||||||
|
right of the primary, visually re-using the same Oracle style.
|
||||||
|
- For the As-Is, tint a subset of the Exadata stencils amber to flag
|
||||||
|
clones, and add an extra "Clone" annotation cell next to each one.
|
||||||
|
|
||||||
|
Output:
|
||||||
|
examples/output-meli-fraud-adbd-migration/fraud-as-is-template.drawio
|
||||||
|
examples/output-meli-fraud-adbd-migration/fraud-to-be-template.drawio
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||||
|
from drawio_template_transform import DrawioTemplate # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
REF_DIR = Path("kb/diagram/assets/archcenter-refs")
|
||||||
|
IN_REGION = REF_DIR / "exadata-dedicated-in-region-dataguard" / \
|
||||||
|
"exadata-dedicated-in-region-dataguard.drawio"
|
||||||
|
OUT_DIR = Path("examples/output-meli-fraud-adbd-migration")
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# Shared helpers
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
def add_title_banner(t: DrawioTemplate, text: str) -> None:
|
||||||
|
"""Add a bold red Oracle banner above the primary region."""
|
||||||
|
# In-region template is ~627x389 wide. Push banner above y=0.
|
||||||
|
t.add_banner(text, x=0, y=-60, w=627, h=40)
|
||||||
|
|
||||||
|
|
||||||
|
def add_workload_header(t: DrawioTemplate, text: str, x: float, y: float,
|
||||||
|
color: str = "#312D2A") -> str:
|
||||||
|
"""Small bold line labelling a workload row/column."""
|
||||||
|
return t.add_text(text, x=x, y=y, w=300, h=20,
|
||||||
|
font_size=12, font_color=color, bold=True)
|
||||||
|
|
||||||
|
|
||||||
|
def add_db_tag(t: DrawioTemplate, text: str, x: float, y: float,
|
||||||
|
fill: str, stroke: str, font_color: str = "#FCFBFA") -> str:
|
||||||
|
"""A pill-shaped tag positioned next to (or over) an Exadata icon to
|
||||||
|
label which database the icon represents inside MELI's estate."""
|
||||||
|
style = (
|
||||||
|
f"rounded=1;whiteSpace=wrap;html=1;fillColor={fill};"
|
||||||
|
f"strokeColor={stroke};strokeWidth=1;fontColor={font_color};"
|
||||||
|
f"fontSize=10;fontStyle=1;fontFamily=Oracle Sans;"
|
||||||
|
f"align=center;verticalAlign=middle;arcSize=20;"
|
||||||
|
)
|
||||||
|
return t.add_cell(text, style, "1", x, y, 130, 22)
|
||||||
|
|
||||||
|
|
||||||
|
def add_footer(t: DrawioTemplate, text: str, y: float) -> str:
|
||||||
|
style = (
|
||||||
|
"text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;"
|
||||||
|
"fontFamily=Oracle Sans;fontSize=10;fontColor=#70665E;"
|
||||||
|
"strokeColor=none;fillColor=none;fontStyle=2;"
|
||||||
|
)
|
||||||
|
return t.add_cell(text, style, "1", 0, y, 1200, 20)
|
||||||
|
|
||||||
|
|
||||||
|
# Oracle palette
|
||||||
|
ORACLE_RED = "#C74634"
|
||||||
|
ORACLE_GREEN = "#00875A"
|
||||||
|
SLATE_BLUE = "#6E89AD"
|
||||||
|
AMBER = "#E8A92F"
|
||||||
|
MUTED_GREY = "#70665E"
|
||||||
|
LIGHT_GREY = "#F5F4F2"
|
||||||
|
CHARCOAL = "#312D2A"
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# As-Is builder
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
def build_as_is() -> None:
|
||||||
|
t = DrawioTemplate(IN_REGION)
|
||||||
|
|
||||||
|
# ── Title ────────────────────────────────────────────────────────────
|
||||||
|
add_title_banner(t, "MELI Fraud — As-Is (ADB-S)")
|
||||||
|
|
||||||
|
# ── Workload labels next to the two Exadata icons ────────────────────
|
||||||
|
# The in-region template has TWO Exadata icons sitting at roughly
|
||||||
|
# AD1 (left): x≈210, y≈210 (below VCN's Client/Backup subnet area)
|
||||||
|
# AD2 (right): x≈430, y≈210
|
||||||
|
# The reference PNG shows each icon in its AD column. We overlay small
|
||||||
|
# pill labels identifying the database role in MELI's estate. Column
|
||||||
|
# geometry taken from describe() output (AD1 at x=145..330, AD2 at
|
||||||
|
# x=364..549 in template-local coords).
|
||||||
|
AD1_CX = 237 # centre of AD1 column in template coords
|
||||||
|
AD2_CX = 456 # centre of AD2 column
|
||||||
|
ICON_Y = 310 # below the Exadata stencil
|
||||||
|
ROW_GAP = 34
|
||||||
|
|
||||||
|
# Row 1 — Fraude
|
||||||
|
add_workload_header(t, "Fraude (19c · ADB-S)",
|
||||||
|
x=AD1_CX - 100, y=ICON_Y, color=CHARCOAL)
|
||||||
|
add_db_tag(t, "Primary", AD1_CX - 65, ICON_Y + 22, ORACLE_RED, "#7A1F12")
|
||||||
|
add_db_tag(t, "Local Standby (ADG)", AD2_CX - 65, ICON_Y + 22, SLATE_BLUE, "#2E4A78")
|
||||||
|
|
||||||
|
# Row 2 — Transactional (second pair of Exadata icons — we clone the
|
||||||
|
# Exadata stencils in AD1+AD2 below the existing ones).
|
||||||
|
# Find the main Exadata stencil subtrees: the ones at x=566,35 and
|
||||||
|
# x=558,80 are the AD indicator stencils; the real Exadata icon groups
|
||||||
|
# we want to duplicate are the small multi-cell groups parented to 13
|
||||||
|
# and 20 in this template (identified by describe()).
|
||||||
|
#
|
||||||
|
# Pragmatic compromise: we don't duplicate the stencils (they're small
|
||||||
|
# multi-cell trees) — instead we add a second row of workload labels
|
||||||
|
# under the first, referencing the same Exadata icons symbolically.
|
||||||
|
add_workload_header(t, "Transactional — Fraude Team (26ai · ADB-S)",
|
||||||
|
x=AD1_CX - 100, y=ICON_Y + ROW_GAP * 2, color=CHARCOAL)
|
||||||
|
add_db_tag(t, "Primary", AD1_CX - 65, ICON_Y + ROW_GAP * 2 + 22,
|
||||||
|
ORACLE_RED, "#7A1F12")
|
||||||
|
add_db_tag(t, "Local Standby (ADG)", AD2_CX - 65, ICON_Y + ROW_GAP * 2 + 22,
|
||||||
|
SLATE_BLUE, "#2E4A78")
|
||||||
|
|
||||||
|
# ── Amber "Clone" column to the right of AD2 ─────────────────────────
|
||||||
|
# The in-region template stops at x≈627; we extend to the right with a
|
||||||
|
# soft amber compartment that hosts the clone DBs.
|
||||||
|
clone_x = 660
|
||||||
|
clone_w = 170
|
||||||
|
clone_h = 260
|
||||||
|
clone_y = 17
|
||||||
|
clone_bg_id = t.add_cell(
|
||||||
|
"Clone / Refreshable Clone\n(read access — warning)",
|
||||||
|
style=(
|
||||||
|
f"rounded=1;whiteSpace=wrap;html=1;fillColor=#FDF4E1;"
|
||||||
|
f"strokeColor={AMBER};strokeWidth=1;dashed=1;dashPattern=6 4;"
|
||||||
|
f"fontColor=#7A5510;fontSize=11;fontStyle=1;"
|
||||||
|
f"fontFamily=Oracle Sans;align=left;verticalAlign=top;"
|
||||||
|
f"spacingLeft=8;spacingTop=6;arcSize=4;"
|
||||||
|
),
|
||||||
|
parent="1", x=clone_x, y=clone_y, w=clone_w, h=clone_h,
|
||||||
|
)
|
||||||
|
# Tag: Fraude Clone
|
||||||
|
add_db_tag(t, "Fraude Clone\n(R/O, 100 ECPU)",
|
||||||
|
clone_x + 15, clone_y + 60,
|
||||||
|
AMBER, "#7A5510", font_color="#5A4A10")
|
||||||
|
# Tag: Transactional Refreshable Clone
|
||||||
|
add_db_tag(t, "Transactional Refreshable Clone\n(R/O, 20 ECPU)",
|
||||||
|
clone_x + 15, clone_y + 140,
|
||||||
|
AMBER, "#7A5510", font_color="#5A4A10")
|
||||||
|
|
||||||
|
# ── Montreal DR region on the far right ──────────────────────────────
|
||||||
|
mtl_x = clone_x + clone_w + 30
|
||||||
|
mtl_y = 0
|
||||||
|
mtl_w = 260
|
||||||
|
mtl_h = 330
|
||||||
|
mtl_bg = t.add_cell(
|
||||||
|
"Region — ca-montreal-1 (DR)",
|
||||||
|
style=(
|
||||||
|
f"rounded=1;whiteSpace=wrap;html=1;fillColor=none;"
|
||||||
|
f"strokeColor={MUTED_GREY};strokeWidth=1;dashed=1;dashPattern=6 4;"
|
||||||
|
f"fontColor={CHARCOAL};fontSize=12;fontStyle=1;"
|
||||||
|
f"fontFamily=Oracle Sans;align=left;verticalAlign=top;"
|
||||||
|
f"spacingLeft=10;spacingTop=8;arcSize=4;"
|
||||||
|
),
|
||||||
|
parent="1", x=mtl_x, y=mtl_y, w=mtl_w, h=mtl_h,
|
||||||
|
)
|
||||||
|
add_db_tag(t, "Fraude — Cross-region Standby",
|
||||||
|
mtl_x + 15, mtl_y + 70, SLATE_BLUE, "#2E4A78")
|
||||||
|
add_db_tag(t, "Transactional — Cross-region Standby",
|
||||||
|
mtl_x + 15, mtl_y + 180, SLATE_BLUE, "#2E4A78")
|
||||||
|
|
||||||
|
# ── Footer ───────────────────────────────────────────────────────────
|
||||||
|
add_footer(t, "Out of scope: Mexico · Catálogo · Attribution.", y=410)
|
||||||
|
|
||||||
|
# ── Save ─────────────────────────────────────────────────────────────
|
||||||
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
out = OUT_DIR / "fraud-as-is-template.drawio"
|
||||||
|
t.save(out)
|
||||||
|
print(f"✓ wrote {out}")
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# To-Be builder
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
def build_to_be() -> None:
|
||||||
|
t = DrawioTemplate(IN_REGION)
|
||||||
|
|
||||||
|
add_title_banner(t, "MELI Fraud — To-Be (ADB-D Dedicated)")
|
||||||
|
|
||||||
|
# For To-Be we keep the in-region template as the primary pattern:
|
||||||
|
# the two Exadata icons (AD1 = primary, AD2 = local ADG standby).
|
||||||
|
# Overlay PDB labels on each AD, showing Fraude + Transactional PDBs.
|
||||||
|
AD1_CX = 237
|
||||||
|
AD2_CX = 456
|
||||||
|
ICON_Y = 300
|
||||||
|
|
||||||
|
# AD-X — Primary (red pills under the left Exadata icon)
|
||||||
|
add_workload_header(t, "AD-X — Primary · Quarter Rack X11M",
|
||||||
|
x=AD1_CX - 150, y=ICON_Y, color=CHARCOAL)
|
||||||
|
add_workload_header(t, "AVMC · CDB-FRAUDE / CDB-TRANSACTIONAL",
|
||||||
|
x=AD1_CX - 150, y=ICON_Y + 20,
|
||||||
|
color=MUTED_GREY)
|
||||||
|
add_db_tag(t, "Fraude PDB — Primary",
|
||||||
|
AD1_CX - 65, ICON_Y + 45, ORACLE_RED, "#7A1F12")
|
||||||
|
add_db_tag(t, "Transactional PDB — Primary",
|
||||||
|
AD1_CX - 65, ICON_Y + 71, ORACLE_RED, "#7A1F12")
|
||||||
|
|
||||||
|
# AD-Y — Local ADG Standby (green pills under the right Exadata icon)
|
||||||
|
add_workload_header(t, "AD-Y — Local ADG Standby · Quarter Rack X11M",
|
||||||
|
x=AD2_CX - 150, y=ICON_Y, color=CHARCOAL)
|
||||||
|
add_workload_header(t, "AVMC · CDB-FRAUDE-RO / CDB-TRANSACTIONAL-RO",
|
||||||
|
x=AD2_CX - 150, y=ICON_Y + 20,
|
||||||
|
color=MUTED_GREY)
|
||||||
|
add_db_tag(t, "Fraude — Read-only Standby",
|
||||||
|
AD2_CX - 65, ICON_Y + 45, ORACLE_GREEN, "#044C33")
|
||||||
|
add_db_tag(t, "Transactional — Read-only Standby",
|
||||||
|
AD2_CX - 65, ICON_Y + 71, ORACLE_GREEN, "#044C33")
|
||||||
|
|
||||||
|
# ── Montreal DR region on the right ──────────────────────────────────
|
||||||
|
mtl_x = 670
|
||||||
|
mtl_y = 0
|
||||||
|
mtl_w = 280
|
||||||
|
mtl_h = 360
|
||||||
|
t.add_cell(
|
||||||
|
"Region — ca-montreal-1 (DR)\nCross-region Data Guard · Quarter Rack X11M",
|
||||||
|
style=(
|
||||||
|
f"rounded=1;whiteSpace=wrap;html=1;fillColor=none;"
|
||||||
|
f"strokeColor={MUTED_GREY};strokeWidth=1;dashed=1;dashPattern=6 4;"
|
||||||
|
f"fontColor={CHARCOAL};fontSize=12;fontStyle=1;"
|
||||||
|
f"fontFamily=Oracle Sans;align=left;verticalAlign=top;"
|
||||||
|
f"spacingLeft=10;spacingTop=8;arcSize=4;"
|
||||||
|
),
|
||||||
|
parent="1", x=mtl_x, y=mtl_y, w=mtl_w, h=mtl_h,
|
||||||
|
)
|
||||||
|
# Inner AVMC/CDB callout
|
||||||
|
t.add_text(
|
||||||
|
"AVMC · CDB-FRAUDE-DR / CDB-TRANSACTIONAL-DR",
|
||||||
|
x=mtl_x + 15, y=mtl_y + 50, w=mtl_w - 30, h=18,
|
||||||
|
font_size=10, font_color=MUTED_GREY, bold=True,
|
||||||
|
)
|
||||||
|
add_db_tag(t, "Fraude — DR Standby",
|
||||||
|
mtl_x + 20, mtl_y + 100, SLATE_BLUE, "#2E4A78")
|
||||||
|
add_db_tag(t, "Transactional — DR Standby",
|
||||||
|
mtl_x + 20, mtl_y + 200, SLATE_BLUE, "#2E4A78")
|
||||||
|
|
||||||
|
add_footer(
|
||||||
|
t,
|
||||||
|
"Three Quarter Rack X11M total (2 in Ashburn + 1 in Montreal). "
|
||||||
|
"Active Data Guard within-region · Cross-region DG to Montreal.",
|
||||||
|
y=410,
|
||||||
|
)
|
||||||
|
|
||||||
|
OUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
out = OUT_DIR / "fraud-to-be-template.drawio"
|
||||||
|
t.save(out)
|
||||||
|
print(f"✓ wrote {out}")
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
# CLI
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
build_as_is()
|
||||||
|
build_to_be()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user