const fs=require('fs'); const all=JSON.parse(fs.readFileSync('parsed.json','utf8')); const finals={'2019-21':[2020,2021],'2021-23':[2022,2023],'2023-25':[2024,2025],'2025-27':[2026,2027]}; const rows=[]; for(const o of all){ if(!finals[o.bi]||o.body.startsWith('__TAIL__')) continue; let b=o.body.replace(/^\(\([ .]*\)\)\s*/,'').replace(/^\(\([ .]*\)\)\s*/,''); const ci=b.indexOf(':'); const src=b.slice(0,ci).trim(); const rest=b.slice(ci+1); const toGF=/for transfer to the (state )?general fund/i.test(rest); const fromGF=/^General Fund$/i.test(src); if(!toGF&&!fromGF) continue; const [y1,y2]=finals[o.bi]; let f1=0,f2=0; const re=/\$([\d,]+)\s+for\s+fiscal\s+year\s+(\d{4})|fiscal\s+year\s+(\d{4}),?\s*\$([\d,]+)/g; let m,found=false; while((m=re.exec(rest))){found=true; const amt=+(m[1]||m[4]).replace(/,/g,''); const y=+(m[2]||m[3]); if(y===y1)f1+=amt; else if(y===y2)f2+=amt;} if(!found||f1+f2!==o.total){ const ys=[...rest.matchAll(/fiscal year (\d{4})/g)].map(x=>+x[1]); if(!found){ if(ys.includes(y1)&&!ys.includes(y2)) f1=o.total; else if(ys.includes(y2)&&!ys.includes(y1)) f2=o.total; } } let cat='dedicated', note=''; if(/Cannabis|Marijuana|Tobacco Settlement/i.test(src)) cat='routine'; if(/Budget Stabilization/i.test(src)) cat='bsa'; if(fromGF) cat=''; if(/repayment/i.test(rest)) note='loan repayment to GF'; if(/Rescue Plan Transition/i.test(src)) note='GF-funded reserve account (money GF put in during 2021-23)'; if(/balance in the account/i.test(rest)) note='sweep of remaining balance'; if(/continue this policy/i.test(rest)) note='recurring sweep (legislative intent to continue)'; if(/^Air Operating Permit/.test(src)&&o.bi==='2025-27') note='VETOED by Governor (2026 partial veto of sec. 802); not law'; if(f1+f2!==o.total) note=(note?note+'; ':'')+'FY split unclear in text'; rows.push({bi:o.bi,law:o.law,sec:o.sec,src,f1:f1/1000,f2:f2/1000,tot:o.total/1000,dir:toGF?'toGF':'fromGF',cat,quote:(src+': '+rest.trim()).slice(0,400),note,dest: fromGF?(rest.match(/for transfer to the ([^,$]*?)(,| \$| for| pursuant| under| solely|$)/i)||[])[1]:'state general fund'}); } const q=s=>'"'+String(s).replace(/"/g,'""')+'"'; let csv='biennium,sessionLaw,section,sourceAccount,fy1Thousands,fy2Thousands,totalThousands,direction,category,quote,note\n'; for(const r of rows) csv+=[r.bi,q(r.law),q(r.sec),q(r.src),r.f1,r.f2,r.tot,r.dir,r.cat,q(r.quote),q(r.note)].join(',')+'\n'; fs.writeFileSync('C:/dev/FactHub-Tester/millionaires-tax/data/wa_fund_transfers_to_gf.csv',csv); fs.writeFileSync('rows.json',JSON.stringify(rows,null,1)); // counts const groups={}; const vetoed=r=>/VETOED/.test(r.note); for(const r of rows){ if(vetoed(r)) continue; const k=r.bi; groups[k]=groups[k]||{to:[],from:[]}; (r.dir==='toGF'?groups[k].to:groups[k].from).push(r);} const sum=a=>Math.round(a.reduce((s,r)=>s+r.tot,0)); const uniq=a=>new Set(a.map(r=>r.src.replace(/Marijuana/,'Cannabis').replace(/Rental and Lease/,'Rent and Lease'))).size; let T={to:[],from:[]}; for(const k of Object.keys(groups)){const g=groups[k]; T.to.push(...g.to); T.from.push(...g.from); const d=g.to.filter(r=>r.cat==='dedicated'); const d2=d.filter(r=>!r.note.match(/repayment|reserve/)); console.log(k,'toGF items',g.to.length,'$K',sum(g.to),'| distinct src',uniq(g.to),'| excl routine+bsa',d.length,'$K',sum(d),'distinct',uniq(d),'| also excl loan repay+WRPTA',d2.length,sum(d2),'| fromGF',g.from.length,sum(g.from)); if(rows.some(r=>r.bi===k&&r.note.includes('unclear'))) console.log(' unclear:',rows.filter(r=>r.bi===k&&r.note.includes('unclear')).map(r=>r.src+' '+r.tot+' '+r.f1+'/'+r.f2)); } for(const [lab,keys] of [['2021-23..2025-27',['2021-23','2023-25','2025-27']],['all incl 2019-21',['2019-21','2021-23','2023-25','2025-27']]]){ const to=T.to.filter(r=>keys.includes(r.bi)); const d=to.filter(r=>r.cat==='dedicated'); const d2=d.filter(r=>!r.note.match(/repayment|reserve/)); console.log(lab,'toGF',to.length,sum(to),'distinct',uniq(to),'| excl',d.length,sum(d),'distinct',uniq(d),'| excl2',d2.length,sum(d2),uniq(d2),'| fromGF',T.from.filter(r=>keys.includes(r.bi)).length,sum(T.from.filter(r=>keys.includes(r.bi))));}