|
| 1 | +""" |
| 2 | +Founding Network Bootstrap — Direct DB insertion for founding bonds. |
| 3 | +Bypasses cooldown because these are pre-existing paid members |
| 4 | +being loaded into the system retroactively. |
| 5 | +""" |
| 6 | +import sys |
| 7 | +import os |
| 8 | +sys.path.insert(0, os.path.dirname(__file__)) |
| 9 | + |
| 10 | +from datetime import datetime, timezone, timedelta |
| 11 | +from app import create_app |
| 12 | +from models.member import Member |
| 13 | +from models.bond import Bond |
| 14 | +from config import HeliosConfig |
| 15 | + |
| 16 | +app = create_app() |
| 17 | + |
| 18 | +founders = [ |
| 19 | + 'elliot-a.helios', # 0 |
| 20 | + 'aaron-w.helios', # 1 |
| 21 | + 'ryan-a.helios', # 2 |
| 22 | + 'veunca-j.helios', # 3 |
| 23 | + 'nicholas-w.helios', # 4 |
| 24 | + 'paul-w.helios', # 5 |
| 25 | + 'eddie-m.helios', # 6 |
| 26 | + 'dan-morgan.helios', # 7 |
| 27 | + 'neandria-p.helios', # 8 |
| 28 | + 'joseph-d.helios', # 9 |
| 29 | + 'brian-rawlston.helios', # 10 |
| 30 | + 'blyss-w.helios', # 11 |
| 31 | + 'nakia-r.helios', # 12 |
| 32 | +] |
| 33 | + |
| 34 | +# Bond pairs — building a connected mesh |
| 35 | +# Chain bonds connect everyone in sequence |
| 36 | +# Cross bonds strengthen the topology |
| 37 | +bond_pairs = [ |
| 38 | + # Chain — connects everyone in sequence |
| 39 | + (0, 1), # elliot ↔ aaron |
| 40 | + (1, 2), # aaron ↔ ryan |
| 41 | + (2, 3), # ryan ↔ veunca |
| 42 | + (3, 4), # veunca ↔ nicholas |
| 43 | + (4, 5), # nicholas ↔ paul |
| 44 | + (5, 6), # paul ↔ eddie |
| 45 | + (6, 7), # eddie ↔ dan |
| 46 | + (7, 8), # dan ↔ neandria |
| 47 | + (8, 9), # neandria ↔ joseph |
| 48 | + (9, 10), # joseph ↔ brian |
| 49 | + (10, 11), # brian ↔ blyss |
| 50 | + (11, 12), # blyss ↔ nakia |
| 51 | + # Cross bonds — strengthen the mesh (respecting max 5 per node) |
| 52 | + (0, 6), # elliot ↔ eddie |
| 53 | + (0, 12), # elliot ↔ nakia |
| 54 | + (1, 7), # aaron ↔ dan |
| 55 | + (2, 8), # ryan ↔ neandria |
| 56 | + (3, 9), # veunca ↔ joseph |
| 57 | + (4, 10), # nicholas ↔ brian |
| 58 | + (5, 11), # paul ↔ blyss |
| 59 | + (6, 12), # eddie ↔ nakia |
| 60 | + (1, 4), # aaron ↔ nicholas |
| 61 | + (2, 5), # ryan ↔ paul |
| 62 | + (7, 10), # dan ↔ brian |
| 63 | + (8, 11), # neandria ↔ blyss |
| 64 | + (3, 12), # veunca ↔ nakia |
| 65 | + (9, 12), # joseph ↔ nakia |
| 66 | +] |
| 67 | + |
| 68 | +with app.app_context(): |
| 69 | + from flask import g |
| 70 | + from sqlalchemy import create_engine |
| 71 | + from sqlalchemy.orm import sessionmaker |
| 72 | + |
| 73 | + engine = create_engine(HeliosConfig.DATABASE_URL) |
| 74 | + Session = sessionmaker(bind=engine) |
| 75 | + db = Session() |
| 76 | + |
| 77 | + print("=" * 60) |
| 78 | + print("FOUNDING NETWORK BOOTSTRAP") |
| 79 | + print("=" * 60) |
| 80 | + |
| 81 | + # First, clean up any bonds from the earlier partial attempt |
| 82 | + existing_bonds = db.query(Bond).all() |
| 83 | + if existing_bonds: |
| 84 | + print(f"\nClearing {len(existing_bonds)} existing bonds from partial attempt...") |
| 85 | + for b in existing_bonds: |
| 86 | + db.delete(b) |
| 87 | + # Reset all founder bond counts |
| 88 | + for hid in founders: |
| 89 | + m = db.query(Member).filter_by(helios_id=hid).first() |
| 90 | + if m: |
| 91 | + m.bond_count = 0 |
| 92 | + m.node_state = "instantiated" |
| 93 | + db.commit() |
| 94 | + print(" Cleared.") |
| 95 | + |
| 96 | + # Backdate by 48 hours so cooldown doesn't apply |
| 97 | + base_time = datetime.now(timezone.utc) - timedelta(hours=48) |
| 98 | + |
| 99 | + formed = 0 |
| 100 | + skipped = 0 |
| 101 | + bond_counts = {hid: 0 for hid in founders} |
| 102 | + |
| 103 | + print("\nForming bonds:\n") |
| 104 | + |
| 105 | + for idx, (i, j) in enumerate(bond_pairs): |
| 106 | + a_id = founders[i] |
| 107 | + b_id = founders[j] |
| 108 | + |
| 109 | + # Respect max 5 bonds per node |
| 110 | + if bond_counts[a_id] >= 5: |
| 111 | + print(f" FULL {a_id:<24} (5/5, skipping)") |
| 112 | + skipped += 1 |
| 113 | + continue |
| 114 | + if bond_counts[b_id] >= 5: |
| 115 | + print(f" FULL {b_id:<24} (5/5, skipping)") |
| 116 | + skipped += 1 |
| 117 | + continue |
| 118 | + |
| 119 | + node_a, node_b = Bond.ordered_pair(a_id, b_id) |
| 120 | + |
| 121 | + # Check no duplicate |
| 122 | + existing = db.query(Bond).filter_by(node_a=node_a, node_b=node_b).first() |
| 123 | + if existing: |
| 124 | + print(f" EXISTS {a_id:<24} ↔ {b_id}") |
| 125 | + skipped += 1 |
| 126 | + continue |
| 127 | + |
| 128 | + # Stagger timestamps so cooldown logic is satisfied |
| 129 | + bond_time = base_time + timedelta(minutes=idx * 5) |
| 130 | + |
| 131 | + bond = Bond( |
| 132 | + node_a=node_a, |
| 133 | + node_b=node_b, |
| 134 | + state=HeliosConfig.BOND_STATE_ACTIVE, |
| 135 | + initiated_by=a_id, |
| 136 | + created_at=bond_time, |
| 137 | + activated_at=bond_time, |
| 138 | + ) |
| 139 | + db.add(bond) |
| 140 | + bond_counts[a_id] += 1 |
| 141 | + bond_counts[b_id] += 1 |
| 142 | + formed += 1 |
| 143 | + print(f" BONDED {a_id:<24} ↔ {b_id:<24} [{bond_counts[a_id]}/{bond_counts[b_id]}]") |
| 144 | + |
| 145 | + # Update member records |
| 146 | + print("\nUpdating member bond counts and node states:\n") |
| 147 | + for hid in founders: |
| 148 | + m = db.query(Member).filter_by(helios_id=hid).first() |
| 149 | + if m: |
| 150 | + m.bond_count = bond_counts[hid] |
| 151 | + m.update_node_state() |
| 152 | + print(f" {hid:<28} bonds={m.bond_count} state={m.node_state}") |
| 153 | + |
| 154 | + db.commit() |
| 155 | + |
| 156 | + print(f"\nBonds formed: {formed}") |
| 157 | + print(f"Skipped (full/duplicate): {skipped}") |
| 158 | + |
| 159 | + # Summary |
| 160 | + states = {} |
| 161 | + for hid in founders: |
| 162 | + m = db.query(Member).filter_by(helios_id=hid).first() |
| 163 | + if m: |
| 164 | + states[m.node_state] = states.get(m.node_state, 0) + 1 |
| 165 | + |
| 166 | + print(f"\nNetwork topology:") |
| 167 | + for state, count in sorted(states.items()): |
| 168 | + print(f" {state}: {count} nodes") |
| 169 | + print(f" Total bonds: {formed}") |
| 170 | + |
| 171 | + db.close() |
0 commit comments