
    %jL              	         U d Z ddlmZ ddlZddlZddlZddlZddlm	Z	  e	e
      j                         j                  Zedz  Zedz  Zg dZdZd	Zd
dddddddddddddddddddddZded<   ddddd d!Zd4d"Zd5d#Zd6d$Zd7d%Zd8d&Zd9d'Zd:d(Z	 	 	 	 	 	 	 	 d;d)Zd:d*Zd:d+Zd<d,Z 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 d=d-Z!	 	 	 	 	 	 	 	 d>d.Z"	 	 	 	 	 	 d?d/Z#	 	 	 	 	 	 d@d0Z$eef	 	 	 	 	 dAd1Z%dBd2Z&e'd3k(  r e&        yy)Ca  Oracle forecast-vintage extractor.

Reads source_manifest.csv (document_type, period, expected_section,
extractable_metrics), fetches primary SEC/IR HTML, and parses capex actuals,
lease commitments, and written guidance. Earnings-call capex guidance uses
verbatim snippets keyed by manifest source_id (repeatable source family:
company-hosted webcast replay linked from IR release pages; Oracle does not
file call transcripts on SEC).
    )annotationsN)Pathzsource_manifest.csvzforecast_vintage.csv)companytickeras_of_periodtarget_periodmetric_name	raw_valuenormalized_gwunitforecast_or_actual
is_derived
source_urlsource_locationsource_snippetsnippet_support_status
confidencecaveatzOracle does not disclose data-center MW/GW. Row tracks USD capex or lease-commitment proxy only; normalized_gw intentionally blank.zCORCL-forecast-vintage-extractor/1.0 (research; contact@example.com)25z5Q4 FY2025 earnings call prepared remarks (Safra Catz)zjI expect that FY 2026 CapEx will be higher at over $25 billion as we work to meet demand from our backlog.0highzvInitial FY2026 capex guide vintage (> $25B floor) from earnings call; not in written Exhibit 99.1. Not MW/GW capacity.)r
   r   r   r   r   r   35z5Q1 FY2026 earnings call prepared remarks (Safra Catz)zUGiven our RPO growth, I now expect fiscal year 2026 CapEx will be around $35 billion.zHFY2026 capex guide raised to ~$35B on earnings call; not MW/GW capacity.50z7Q2 FY2026 earnings call prepared remarks (Doug Kehring)zHowever, given the added RPO this quarter, can be monetized quickly starting next year, we now expect fiscal 2026 CapEx will be about $15 billion higher than we forecasted after Q1.1zTFY2026 capex guide ~$50B derived: post-Q1 ~$35B + ~$15B on call. Not MW/GW capacity.)orcl_fy2025_q4_earnings_webcastorcl_fy2026_q1_earnings_webcastorcl_fy2026_q2_earnings_webcastzdict[str, dict[str, str]]CALL_GUIDANCE            )sec_10ksec_10qsec_8k_exhibit_99_1official_ir_releaseearnings_call_webcastc                    | j                  dd      5 }t        t        j                  |            cd d d        S # 1 sw Y   y xY w)N utf-8newlineencoding)openlistcsv
DictReader)manifest_pathhandles     e/home/dev/research_loop/runs/gw-forecast-production-001-loop01/artifacts/production/ORCL/extractor.pyload_manifestr5   j   s=    			B		9 ,VCNN6*+, , ,s	   <Ac                    t         j                  j                  | dt        i      }t         j                  j	                  |d      5 }|j                         j                  dd      cd d d        S # 1 sw Y   y xY w)Nz
User-Agent)headersZ   )timeoutr*   replace)errors)urllibrequestRequest
USER_AGENTurlopenreaddecode)urlr=   responses      r4   
fetch_htmlrE   o   sk    nn$$S<2L$MG					4 A}}%%gi%@A A As   	!A44A=c                >   t        j                  dd| t         j                  t         j                  z        }t        j                  dd|t         j                  t         j                  z        }t        j                  dd|      }t        j                  dd|      S )Nz<script[^>]*>.*?</script> flagsz<style[^>]*>.*?</style><[^>]+>\s+)resubIS)htmltexts     r4   html_to_textrR   u   si    66.TMD66,c4rttbdd{KD66*c4(D66&#t$$    c                   g }t        j                  d|       D ]"  }|j                  |j                  d             $ |sKt        j                  d| t         j                        D ]"  }|j                  |j                  d             $ |S )Nz;PaymentsToAcquirePropertyPlantAndEquipment"[^>]*>([0-9,]+)<r   $Capital expenditures\s*\(([0-9,]+)\)rH   )rL   finditerappendgrouprN   )rP   valuesmatchs      r4   parse_capex_valuesr[   |   s    FF & 	ekk!n%	&
 [[3$$
 	*E
 MM%++a.)	* MrS   c                    t        | j                  dd            }|dz  dj                  d      j                  d      S )N,r)   i  z.3fr   .)intr:   rstrip)rawvalues     r4   billions_from_millionsrc      s<    C$%Edl3'',33C88rS   c                P   t        |       }|dk(  r|r|d   }t        |      d| dfS |dk(  r$t        |      dk\  r|d   }t        |      d| dfS t        |       }t	        j
                  d|t        j                  	      }|r"|j                  d      }t        |      d| dfS y )
NFY2025r   Capital expenditures ()FY2024r    r   rU   rH   )r[   rc   lenrR   rL   searchrN   rX   )rP   fiscal_year_labelrY   ra   rQ   rZ   s         r4   extract_annual_capexrl      s    %FH$Qi%c*.DSE,KKKH$V)9Qi%c*.DSE,KKKDII/ddE
 kk!n%c*.DSE,KKKrS   c                J    t        |       }|sy |d   }t        |      d| dfS )Nr   rf   rg   )r[   rc   )rP   rY   ra   s      r4   extract_ytd_capexrn      s5    %F
)C!#&*@Q(GGGrS   c                    t        |       }|sy t        |d   j                  dd            }|s|d   }t        |      d| dd|fS |y ||z
  }|d}t        |      d| dd|fS )Nr   r]   r)   rf   rg   r   r   )r[   r_   r:   rc   )rP   derivedprior_ytd_millionsrY   current_ytdra   quarters          r4   extract_quarterly_capexrt      s    
  %FfQi''R01KQi"3'$SE+	
 	
 !..GQKCs#
 Q'	 rS   c                   t        |       }g d}|D ]\  }t        j                  ||t        j                        }|s,|j	                  d      |j	                  d      j                         fc S  t        j                  d| t        j                        }|sy | t        d|j                         dz
        |j                         dz    }t        j                  d|      }|sy |d	   }t        j                  d
d|      }t        j                  dd|      j                         }t        j                  dd|      }t        j                  d|t        j                        }	|	r!||	j	                  d      j                         fS |d| dfS )N)zTAs of [^,]+, we had \$([0-9.]+) billion of additional lease commitments[^.]{0,160}\.zUAs of [^,]+, we have \$([0-9.]+) billion of additional lease commitments[^.]{0,160}\.z<we had \$([0-9.]+) billion of additional lease commitments\.rH   r   r   zadditional lease commitmentsi      z>([0-9]+(?:\.[0-9]+)?)<rJ   rG   rK   z\$\s+([0-9])z$\1zAs of [^,]+, we (?:have|had) \$[0-9.]+\s+billion of additional lease commitments[^.]*(?:\.|,\s+primarily for data centers[^.]*\.)z&As of the balance-sheet date, we had $z) billion of additional lease commitments.)rR   rL   rj   rN   rX   stripmaxstartendfindallrM   )
rP   rQ   patternspatternrZ   segmentnumber_matchesr
   prosesentences
             r4   extract_lease_commitmentsr      sc   DH
  :		'4rtt4;;q>5;;q>#7#7#999: II5t244HE3q%++-#-.s1BCGZZ :GDNr"IFF:sG,EFF63&,,.EFF?FE2Eyy 	MddH
 (..+11333
0;de rS   c                f   t        |       }t        j                  d|t        j                        }|rd|j	                  d      fS t        j                  d|t        j                        }|rCt        d|j                         dz
        }d|||j                         dz    j                         fS y )NzFor fiscal year 2026, we expect revenue of \$67 billion and capital expenditures of \$50 billion\. This is unchanged from our most recent previous guidance\.rH   r   r   z$capital expenditures of \$50 billionP   (   )	rR   rL   rj   rN   rX   ry   rz   r{   rx   )rP   rQ   rZ   rz   s       r4   %extract_written_fy2026_capex_guidancer      s    DII	+ 	ddE U[[^##II/ddE
 Au{{}r)*T%%))+"2399;;;rS   c                    | d   | d   | d   fS Nr   r   r	    )rows    r4   row_keyr     s    _!5s=7IJJrS   c       
        ~    i d| d   d| d   d| d   d|d|d|dd	d
dd|d|d| d   d|d|d|d|	d|
S )Nr   r   r   periodr   r	   r
   r   r)   r   USD_billionsr   r   r   r   r   r   r   r   r   )manifest_rowr   r	   r
   r   r   r   r   r   r   r   s              r4   base_rowr   	  s    <	*,x( 	X. 		
 	{ 	Y 	 	 	0 	j 	l<0 	? 	. 	!"8 	j  	&! rS   c                d   | d   j                  d      D ch c]  }|j                          }}| d   }| d   }g }|dk(  rXd|vr|S t        j                  |      }|s|S |j	                  t        | dd|d	   d
|d   |d   |d   d|d   |d                |S | d   }	|j                  |	      }
|
t        |	      }
|
||	<   d|v r|dk(  r| d   dk(  r<t        |
d      }|r.|\  }}|j	                  t        | dd|ddd|ddt                     | d   dk(  r<t        |
d      }|r.|\  }}|j	                  t        | dd|ddd|ddt                     d |v r:t        |
      }|r-|\  }}|j	                  t        | | d   d |d
dd!|ddd"             d#|v rYt        |
      }|rL|\  }}| d   }d$d%d&d'j                  ||      }|j	                  t        | |d#|dd| d(   |ddt                     d)|v sd*|v rwd*|v }| d   j                  d+      d,   }|j                  |      }t        |
||-      }|r;|\  }}}}|||<   |j	                  t        | | d   d|d|| d(   |ddt                     d|v r;|d.v r7t        |
      }|r*|\  }}|j	                  t        | dd|d
dd/|ddd0             |S c c}w )1Nextractable_metrics;document_type	source_idr'   capex_guidance_fy2026FY2026capex_guidancer
   forecastr   r   r   verified_primaryr   r   )
r   r	   r
   r   r   r   r   r   r   r   r   capex_actual_fyr#   r   z
FY2024-FQ4rh   capex_actualactualr   zIConsolidated Statements of Cash Flows; FY2024 column Capital expendituresr   z
FY2025-FQ4re   zIConsolidated Statements of Cash Flows; FY2025 column Capital expendituresadditional_lease_commitmentsu?   Note — Leases; additional lease commitments not yet commencedzOLease commitments are forward data-center supply obligations, not operating GW.capex_actual_ytd
FY2026-FQ1z	FY2026-H1z	FY2026-9M)r   z
FY2026-FQ2z
FY2026-FQ3expected_sectioncapex_actual_quartercapex_actual_quarter_derived-r   )rp   rq   >   r&   r%   z'Guidance for Fiscal Years 2026 and 2027zOManagement FY2026 capex guidance; unchanged vintage at FQ3. Not MW/GW capacity.)splitrx   r   getrW   r   rE   rl   GW_PROXY_CAVEATr   rn   rt   r   )r   
html_cache	ytd_statemmetricsdoc_typer   rowscallrC   rP   parsedrb   snippetr   	ytd_labelrp   fiscal_year	prior_ytdr   ytd_millionss                        r4   extract_from_manifest_rowr   +  s   
 #//D"E"K"KC"PQQqwwyQGQO,H[)I!#D**"'1K  +K&,{+#-- $%6 7#$45'9-H~	
 
|
$C>>#D|#
3G#I(=!\1)$9F!'w$&.$2"'+3#&(s'./A#). !\1)$9F!'w$&.$2"'+3#&(s'./A#).  &0*40#NE7KK ".x"8 >#'1"$e#*+=%l  W$"4(#NE7!(+F*)) c&&!	 
 KK "+ 2#'/"$01C$D#*+=%*  (,Jg,U0G;"8,2237:MM+.	(wS\]7=4E7J%1Ik"KK ".x"8 .#'/)$01C$D#*+=%*  ')h ; / 7t<#NE7KK "* 0#'1"$M#*+=%l  KO Rs   J-c                    | D ci c]  }|d   t         j                  |d   d      ! }}i }|D ]=  }t        |      }|j                  |      }||||<   '||d      ||d      k  s9|||<   ? t        |j	                         d       S c c}w )Nr   r   c   c                    | d   | d   | d   fS r   r   )rs    r4   <lambda>zdedupe_rows.<locals>.<lambda>  s    q(!O*<a>NO rS   )key)SOURCE_PRIORITYr   r   sortedrY   )manifest_rows	extractedr   priority_by_urlbestr   existings          r4   dedupe_rowsr     s     ! 	L?..s?/CRHHO  8:D cl88C=DI3|,-@V0WWDI O s   $Bc           	     p   |D ch c]  }|d   	 }}t        t              }t               }t        | d      D ]  \  }}||j                         z
  }|rt	        d| dt        |             |d   j                  d      st	        d| d      |d   |vrt	        d| d|d          |j                  |d	           t        |      d
k  rt	        dt        |             | D cg c]  }|d   dk(  s|d   dk(  s| }	}t        |	      dk  rt	        dt        |	             y c c}w c c}w )Nr   r   )rz   zRow z missing columns: zhttps://z source_url must be httpsz source_url not in manifest: r      z$Need >=8 as_of_period vintages, got r	   r   r   r   r"   z-Need >=4 FY2026 capex_guidance vintages, got )	setCANONICAL_FIELDS	enumeratekeys
ValueErrorr   
startswithaddri   )
r   r   r   manifest_urlsrequiredas_of_periodsidxmissingr   guidances
             r4   validate_rowsr     sj    3@@3S&@M@#$H!eMd!, /SSXXZ'tC5(:6'?:KLMM< ++J7tC5(ABCC|M1tC5(Ec,FWEXYZZ#n-./ =A?M@R?STUUka1]#37G#GAoL^bjLjkHk
8}qHXXYY  A ls   D.+D39D3D3c                   t        |       }i }i }g }|D ]  }|j                  t        |||               t        ||      }t	        ||       |j
                  j                  dd       |j                  ddd      5 }t        j                  |t              }	|	j                          |	j                  |       d d d        |S # 1 sw Y   |S xY w)NT)parentsexist_okwr)   r*   r+   )
fieldnames)r5   extendr   r   r   parentmkdirr.   r0   
DictWriterr   writeheader	writerows)
r2   output_pathr   r   r   r   r   r   r3   writers
             r4   	emit_rowsr     s     "-0M!#J "I&(I% Y2<YWXY}i0D$&TD9			#rG		< 3CD K	 Ks   >=CCc                 H   t        j                  d      } | j                  dt        t               | j                  dt        t
               | j                         }t        |j                  |j                        }t        dt        |       d|j                          y )Nz Emit Oracle forecast_vintage.csv)descriptionz
--manifest)typedefaultz--outputzWrote z	 rows to )argparseArgumentParseradd_argumentr   DEFAULT_MANIFESTDEFAULT_OUTPUT
parse_argsr   manifestoutputprintri   )parserargsr   s      r4   mainr     s}    $$1STF
49IJ

~FDT]]DKK0D	F3t9+Yt{{m
45rS   __main__)r2   r   returnlist[dict[str, str]])rC   strr   r   )rP   r   r   r   )rP   r   r   z	list[str])ra   r   r   r   )rP   r   rk   r   r   tuple[str, str] | None)rP   r   r   r   )rP   r   rp   boolrq   z
int | Noner   z tuple[str, str, str, int] | None)r   dict[str, str]r   ztuple[str, str, str])r   r   r   r   r	   r   r
   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )r   r   r   r   r   zdict[str, int]r   r   )r   r   r   r   r   r   )r   r   r   r   r   None)r2   r   r   r   r   r   )r   r   )(__doc__
__future__r   r   r0   rL   urllib.requestr<   pathlibr   __file__resolver   BASE_DIRr   r   r   r   r?   r   __annotations__r   r5   rE   rR   r[   rc   rl   rn   rt   r   r   r   r   r   r   r   r   r   __name__r   rS   r4   <module>r     sy   #  
 	   >!!#**33 22 (F 
 S
 R: ?( R" \
( T> "(5),( )Z ,
A%"9
(H
 # &	:!H,K   	
          Dl ll l 	l^'# .Z
Z'Z 
Z0 +& (6 zF rS   