ó
    }¬j6S  ã                   óâ  • S r SSKrSSKrSSKrSSKJr  SSKJr  \R                  " S5      r
\R                  " S5      r/ SQrS	 rS
 rS rS rS rS!S jrS rS r\R                  " S5      rS rS rS r\R                  " S5      rS rS"S jrS#S jrSr\" / SQS5       V Vs0 s H  u  pUR?                  5       U _M     snn r \R                  " S5      r!S r"S r#S$S jr$S%S  jr%gs  snn f )&a<  
Auction4Cars reader.

Reads the logged in Auction4Cars live auctions list at auction4cars.com and
turns each card into a Car. The list card gives make, model, derivative,
year, mileage, transmission, fuel, location and price, but not the
registration, owners, or a guide value, those are only on each car's own
page, so every candidate needs a detail visit (the same shape as Carwow).

The real reserve IS available (found 2026-08-20), just never rendered as
visible text: each detail page carries priceCon.data('reserve', N) in an
inline script, read by _real_reserve(). Guide Clean (the platform's own
lower guide value) is kept as a fallback for the rare page that script is
not found on. This corrects the original 2026-08-19 assumption that no
reserve is ever disclosed on this platform, it just took reading the page
source to find it.

This platform still has no 1 to 5 condition grade, only a defect map and
tyre depths (Mark 2026-08-19). The grade gate is skipped for this source
(pricing.assess(..., assume_grade_ok=True)), condition is judged by eye
from the photos on the card instead.
Distance is a depot name (for example A4C-Castleford), not a mile figure, so
the distance gate is skipped too (assume_distance_ok=True) and the depot name
is carried in car.location for display.

Nothing here bids. It reads the screen only.
é    Né   )ÚCar)Úbrowseru   Â£\s*([\d,]+)z^(19[89]\d|20[0-4]\d)$)z
LAND ROVERzMERCEDES-BENZzMERCEDES BENZz
ALFA ROMEOzASTON MARTINzMG MOTOR UKz
GREAT WALLc                 ó    • U c  g [        U 5      R                  SS5      R                  5       n U R                  5       (       a  [	        U 5      $ S $ )NÚ,Ú )ÚstrÚreplaceÚstripÚisdigitÚint©Úss    Ú>/Users/stevendouglas/BidBrain/bidbrain/readers/auction4cars.pyÚ_numr   /   s@   € Ø�yØÜˆA‹�‰�s˜BÓ×%Ñ%Ó'€AØ—Y‘Y—[‘[Œ3ˆq‹6Ð* dÐ*ó    c                 ó‚   • U (       d  g [         R                  U 5      nU(       a  [        UR                  S5      5      $ S $ )Né   )ÚMONEYÚsearchr   Úgroup)r   Úms     r   Ú_moneyr   6   s/   € ÞØÜ�‰�Q‹€AÞ Œ4�—‘˜“
ÓÐ* dÐ*r   c                 óN   • SR                  S U R                  5        5       5      $ )NÚ c              3   óŒ   #   • U  H:  oR                  5       (       a  [        U5      S :”  a  UR                  5       OUv •  M<     g7f)é   N)ÚisupperÚlenÚ
capitalize)Ú.0Úws     r   Ú	<genexpr>Ú_title_case.<locals>.<genexpr>@   s/   é € Ð^ÒT]Èq¯)©)¯+©+¼¸Q»À!»�A—L‘L”NÈÔJÒT]ùs   ‚AA)ÚjoinÚsplitr   s    r   Ú_title_caser'   =   s#   € ð �8‰8Ñ^ÐTU×T[ÑT[ÔT]Ó^Ó^Ð^r   c                 óÎ  • U =(       d    SR                  5       R                  5       n[         H[  nUR                  U5      (       d  M  U R                  5       [	        U5      S  R                  5       n[        U5      [        U5      4s  $    U R                  5       R                  SS5      nU(       a  US   OSn[	        U5      S:”  a  US   OSn[        U5      [        U5      4$ )Nr   r   r   r   )r   ÚupperÚ_MULTI_WORD_MAKESÚ
startswithr   r'   r&   )ÚtitleÚupÚmkÚrestÚpartsÚmakeÚmodels          r   Ú_split_make_modelr3   C   sº   € Ø
�+�2×	Ñ	Ó	×	$Ñ	$Ó	&€BßˆØ�=‰=˜×ÓØ—;‘;“=¤ R£ Ð*×0Ñ0Ó2ˆDÜ˜r“?¤K°Ó$5Ð5Ò5ñ  ð �K‰K‹M×Ñ  QÓ'€EÞˆ5�Š8 "€DÜ˜E›
 Q›ˆE�!ŠH¨B€EÜ�tÓœk¨%Ó0Ð0Ð0r   c                 ó*   • U R                  S5      SS  $ )Nz$<div class="vehicle-card-container">r   )r&   )Ú	page_htmls    r   Ú_card_blocksr6   O   s   € Ø�?‰?ÐAÓBÀ1À2ÐFÐFr   c                 óÀ  • U=(       d    [         R                  R                  5       nU[         R                  " US9-   n/ n[	        U 5       GHˆ  n[
        R                  " SU5      nU(       a  UR                  S5      OSnU(       d  M>  [
        R                  " SU5      nU(       d  M^  [         R                   R                  [        UR                  S5      5      S-  5      n	XR                  5       s=::  a  U::  d  O  M¶  [
        R                  " SU5      n
U
(       a3  [        R                  " U
R                  S5      5      R                  5       OSn
[        U
5      u  p¼[
        R                  " SU5      nU(       a3  [        R                  " UR                  S5      5      R                  5       OSn[
        R                  " S	U5      nU(       a3  [        R                  " UR                  S5      5      R                  5       OSn[
        R                  " S
U5      nS=nnS=nnU(       aé  [        R                  " UR                  S5      5      R                  S5       Vs/ s H  nUR                  5       PM     nnU H‘  n[        R!                  U5      (       a  [#        U5      nM*  SUR%                  5       ;   a#  [#        [
        R&                  " SSU5      5      nMa  UR%                  5       S;   a  UnMy  UR%                  5       S;   d  M�  UnM“     [
        R                  " SU5      nU(       a  [)        UR                  S5      5      OSn[
        R                  " SU5      nU(       a  UR                  S5      OSnUR+                  [-        X¼UUUUUUU(       a  SU-   OS-   R                  5       UUUU	R/                  5       SU 3SS95        GM‹     U$ s  snf )aî  Parse one page of the live auctions list into partial Cars (no reg,
owners or guide value yet, those need enrich_from_detail). Every card on
this page is a live auction, there is no other listing type mixed in.

Unlike Motorway and Carwow, an Auction4Cars listing does not sell the next
day, auctions run 3, 5 or 7 days (Mark 2026-08-19). Originally this kept
only auctions ending today or tomorrow, matching how far ahead Motorway
and Carwow ever look; widened 2026-08-21 (Mark: add a cockpit filter for
how many days ahead to show, which needs the days actually captured to
filter across) to the full days_ahead window, default 7, the longest a
real Auction4Cars auction runs, so nothing is dropped before the cockpit
even gets a chance to filter it. A car whose auction-end-date is further
out than that is still silently skipped here, the same way Carwow
silently skips a non auction listing state. today defaults to the real
date, pass it explicitly to keep a run's cars consistent with its
sale_date.)Údayszdata-auctionid="(\d+)"r   r   zdata-auction-end-date="(\d+)"iè  z<h3 class="title">([^<]*)</h3>z(<p class="sub-title"[^>]*title="([^"]*)"zS<div class="location">\s*<span class="icon-location"></span>\s*<span>([^<]*)</span>z"<div class="details">([^<]*)</div>NÚ|Úmilez\D)ÚmanualÚ	automaticzsemi-automaticzsemi automatic)ÚpetrolÚdieselÚhybridÚelectricz"<span class="price">([^<]*)</span>z%<img class="lozad" data-src="([^"]*)"r   ú8https://www.auction4cars.com/VehicleAuction/AuctionPage/ÚAuction4Cars)r1   r2   Ú
derivativeÚyearÚmileageÚtransmissionÚfuelÚengineÚlocationÚreserveÚ	photo_urlÚauction_ends_atÚlisting_urlÚsource)ÚdatetimeÚdateÚtodayÚ	timedeltar6   Úrer   r   Úfromtimestampr   ÚhtmllibÚunescaper   r3   r&   ÚYEARÚmatchr   ÚlowerÚsubr   Úappendr   Ú	isoformat)r5   rQ   Ú
days_aheadÚ
window_endÚcarsÚblockÚaidÚ
auction_idÚendÚends_atr,   r1   r2   rZ   rC   ÚlocrI   ÚdetailsrD   rE   rF   rG   Úpr0   ÚpriceÚphotorK   s                              r   Úparse_listingrj   S   sü  € ð" ×*”X—]‘]×(Ñ(Ó*€Eð
 œ×+Ò+°Ñ<Ñ<€JØ€DÜ˜i×(ˆÜ�iŠiÐ1°5Ó9ˆÞ%(�S—Y‘Y˜q”\¨bˆ
ÞÙä�iŠiÐ8¸%Ó@ˆÞÙÜ×#Ñ#×1Ñ1´#°c·i±iÀ³lÓ2CÀdÑ2JÓKˆØŸ™›Õ5¨:Õ5Ùä—	’	Ð;¸UÓCˆÞ<A”× Ò  §¡¨Q£Ó0×6Ñ6Ô8ÀrˆÜ'¨Ó.‰ˆä�iŠiÐCÀUÓKˆÞ?B”W×%Ò% c§i¡i°£lÓ3×9Ñ9Ô;Èˆ
ä�iŠiÐnØóˆæ=@”7×#Ò# C§I¡I¨a£LÓ1×7Ñ7Ô9Àbˆä—)’)ÐAÀ5ÓIˆØÐˆˆwØ Ð ˆ�tÞÜ(/×(8Ò(8¸¿¹ÀqÓ9IÓ(J×(PÑ(PÐQTÔ(UÓVÒ(U 1�Q—W‘W–YÑ(UˆEÐVÛ�Ü—:‘:˜a—=‘=Ü ›7’DØ˜qŸw™w›yÓ(Ü"¤2§6¢6¨%°°QÓ#7Ó8’GØ—W‘W“YÐ"]Ó]Ø#$’LØ—W‘W“YÐ"LÕLØ’Dñ ô —	’	Ð?ÀÓGˆÞ*/”�u—{‘{ 1“~Ô&°Tˆä—	’	ÐBÀEÓJˆÞ&+�E—K‘K ”N°ˆ	à�‰”CØ¨zØ˜w°\Èð ¶ #¨¢*¸"Ñ=×DÑDÓFØ u¸	Ø#×-Ñ-Ó/ØRÐS]ÐR^Ð_Ø!ñ
÷ 	ñ[ )ðx €KùòA Ws   É$Oc                 ót  • 0 n[         R                  " SU 5       Hš  n[        R                  " UR	                  S5      5      R                  SS5      R                  5       R                  S5      R                  5       n[        R                  " UR	                  S5      5      R                  5       nXAU'   Mœ     U$ )z†Every label: value pair in the Vehicle Details block, keyed by the
label text with the trailing colon and non breaking space stripped.zb<div class="car-detail-title">([^<]*)</div>\s*<div class="car-detail-content">\s*([^<]*?)\s*</div>r   ô   Â r   Ú:r   )rS   ÚfinditerrU   rV   r   r
   r   Úrstrip)r5   Úoutr   ÚlabelÚvalues        r   Ú_detail_fieldsrs   ª   s–   € ð €CÜ�[Š[ØmØöˆô × Ò  §¡¨£Ó,×4Ñ4°V¸SÓA×GÑGÓI×PÑPÐQTÓU×[Ñ[Ó]ˆÜ× Ò  §¡¨£Ó,×2Ñ2Ó4ˆØˆE‹
ñð €Jr   c                 óì   • [         R                  " SU 5      nU Vs/ s H)  o"R                  5       (       d  M  UR                  5       PM+     nnU(       d  g[        S U 5       5      (       a  S$ S$ s  snf )NzG<td>\s*[\d/]+\s*</td>\s*<td>\s*[\d,]*\s*</td>\s*<td>\s*([^<]*?)\s*</td>r   c              3   óF   #   • U  H  oR                  5       S :H  v •  M     g7f)zmain dealerN)rY   )r!   Úts     r   r#   Ú#_service_history.<locals>.<genexpr>¾   s   é € ÐCºU¸Ÿ™› mÖ3ºUùs   ‚!ÚfullÚpartial)rS   Úfindallr   Úall)r5   Útypesrv   s      r   Ú_service_historyr}   ¸   s^   € Ü�JŠJÐaØ ó"€Eá %Ó3¢˜1¯©¯‹YˆQ�W‰WŽY¡€EÐ3ÞØÜÑC¹UÓC×CÑCˆ6ÐRÈÐRùò 4s
   œA1¸A1z'priceCon\.data\('reserve',\s*([\d.]+)\)c                 ó¤   • [         R                  U 5      nU(       d  g [        [        UR	                  S5      5      5      $ ! [
         a     gf = f)a~  The real reserve, found in an inline script on the car's own detail
page (priceCon.data('reserve', 5000.0000);), not shown as visible text
anywhere on the page (Mark 2026-08-20, found by reading the page source).
Corrects the earlier assumption that Auction4Cars never discloses a
reserve, it does, it is just never rendered. Returns None if the pattern
is not found, never a guess.Nr   )Ú_RESERVE_REr   ÚroundÚfloatr   Ú
ValueError)r5   r   s     r   Ú_real_reserverƒ   Ä   sI   € ô 	×Ñ˜9Ó%€AÞØðÜ”U˜1Ÿ7™7 1›:Ó&Ó'Ð'øÜó Ùðús   Ÿ"A Á
AÁAc                 ó¬  • [         R                  " SU [         R                  5      nU(       d  g[         R                  " SUR	                  S5      5      nU Vs/ s H7  n[
        R                  " U5      R                  SS5      R                  5       PM9     nnU Vs/ s H  oU(       d  M  SU;  d  M  UPM     nnSR                  U5      $ s  snf s  snf )Nz4<h3>More Information</h3>(.*?)<div class="clearfix">r   z<p[^>]*>([^<]*)</p>r   rl   ÚSVAr   )
rS   r   ÚSrz   r   rU   rV   r
   r   r%   )r5   r   Úparasrg   ÚlinesÚls         r   Ú_notesrŠ   Ô   s¥   € Ü
�	Š	ÐIÈ9ÔVX×VZÑVZÓ[€AÞØÜ�JŠJÐ-¨q¯w©w°q«zÓ:€EÙFKÓLÂeÀŒW×Ò˜aÓ ×(Ñ(¨°Ó4×:Ñ:Ö<Áe€EÐLÙÓ6š�1£‹Q u°A¡~�Q™€EÐ6Ø�8‰8�E‹?Ðùò MùÚ6s   Á>CÂ
CÂ+CÂ3Cc                 óN  • U R                  UR                  SSS9  U R                  SSS9  U R                  5       n[	        U5      nUR                  SS5      R                  5       Ul        [        UR                  S	5      5      Ul	        [        U5      =(       d-    [        UR                  S
5      5      =(       d    UR                  Ul        [        U5      Ul        [        U5      Ul        UR"                  (       d  UR                  SS5      Ul        UR$                  (       d  UR                  SS5      Ul        UR&                  UR$                  (       a  SUR$                  -   OS-   R                  5       Ul        UR*                  (       d  [-        U5      SS Ul        U$ )aè  Visit the car's own page for the reg, owners, real reserve and service
history the list card does not carry. The real reserve is read from an
inline script (priceCon.data('reserve', ...), Mark 2026-08-20), not
visible as text anywhere on the page, which is why it was originally
thought Auction4Cars never discloses one. Guide Clean is kept as a
fallback for the rare page where that script is not found. The grade
gate is still skipped entirely for this source, so no grade is read
here.Údomcontentloadedé0u  ©Ú
wait_untilÚtimeoutú#car-details-containeré N  ©r�   ú
Reg Numberr   ÚOwnerszGuide CleanÚTransmissionÚFuelr   Nr   )ÚgotorM   Úwait_for_selectorÚcontentrs   Úgetr   Úregr   Úownersrƒ   r   rJ   r}   Úservice_historyrŠ   ÚnotesrF   rG   rC   rH   Ú
photo_urlsÚ_gallery_photos_from_html)ÚpageÚcarÚhtmlÚfieldss       r   Úenrich_from_detailr¦   Þ   s3  € ð 	‡I�Iˆc�o‰oÐ*<Àe€IÑLØ×ÑÐ3¸UÐÑCØ�<‰<‹>€Dä˜DÓ!€FØ�j‰j˜ rÓ*×0Ñ0Ó2€C„GÜ�f—j‘j Ó*Ó+€C„JÜ Ó%×Y¬°·
±
¸=Ó0IÓ)J×YÈcÏkÉk€C„KÜ*¨4Ó0€CÔÜ�t“€C„IØ××Ø!Ÿ:™: n°bÓ9ˆÔØ�8�8Ø—:‘:˜f bÓ)ˆŒØ—.‘.°c·h·h C¨#¯(©(¢NÀBÑG×NÑNÓP€C„Jð �>�>Ü2°4Ó8¸¸!Ð<ˆŒà€Jr   z?https://www\.auction4cars\.com/AuctionImages/\d+/Image_\d+\.jpgc                 óÐ   • 0 n[         R                  U 5       H3  nUR                  UR                  S5      UR                  S5      5        M5     [	        UR                  5       5      $ )z¸The full listing gallery from a car's own detail page HTML, deduped
to one URL per distinct photo. A standalone function purely for
testing, enrich_from_detail is the only real caller.r   )Ú_GALLERY_PHOTO_RErn   Ú
setdefaultr   ÚlistÚvalues)r¤   ÚseenÚgms      r   r¡   r¡     sM   € ð €DÜ×(Ñ(¨Ö.ˆØ�‰˜Ÿ™ › R§X¡X¨a£[Ö1ñ /ä�—‘“ÓÐr   c                 óÊ  • [        U R                  5       XS9nU R                  S5      nSnU(       ab  UR                  S5      nU V	s/ s H+  o™R	                  S5      (       d  M  U	R	                  S5      PM-     n
n	U
(       a  [        S U
 5       5      nU(       a  [        Xs5      nSn[        SUS-   5       H¢  nU R                  S5      nU(       d    U$ UR                  [        U5      5        U R                  S	5        [        U R                  5       XS9nUR                  U5        U(       a  U" U[        U5      5        U(       a  SOUS-   nUS:¼  d  M¡    U$    U$ s  sn	f )
aZ  Paginate the live auctions list from an already open, already
navigated page (the page dropdown at the foot of the list jumps straight
to any page number, proven live: selecting page 2 loads a wholly
different set of cars), keeping only cars whose auction ends within
days_ahead days. The caller owns the page and its context, so the daily
run can reuse the same page afterward for detail enrichment without
opening a second session.

The list's own default sort is soonest ending first, so once two pages in
a row have nothing in the window everything after is later still, and
reading stops early rather than working through all 31 or so pages. This
is a speed optimisation only, not a correctness one: parse_listing always
filters by the real end date regardless of sort order, so a wrong
assumption here means a slower read, never a wrongly included car.)rQ   r]   z+div.pagination-container select.form-selectr   Úoptionrr   c              3   ó8   #   • U  H  n[        U5      v •  M     g 7f©N)r   )r!   Úvs     r   r#   Ú!read_all_pages.<locals>.<genexpr>&  s   é € Ð5ªf¨œc !Ÿf˜fªfùs   ‚r   r   i  )rj   rš   Úquery_selectorÚquery_selector_allÚget_attributeÚmaxÚminÚrangeÚselect_optionr	   Úwait_for_timeoutÚextendr   )r¢   rQ   r]   Ú	max_pagesÚon_pager_   ÚselÚtotal_pagesÚoptsÚor«   Úempty_pagesÚnÚfounds                 r   Úread_all_pagesrÆ     s;  € ô ˜Ÿ™›¨uÑL€DØ
×
Ñ
ÐKÓ
L€CØ€KÞ
Ø×%Ñ% hÓ/ˆÙ48ÓU²D¨q¿O¹OÈG×<TÓ*�!—/‘/ 'Ö*±DˆÐUÞÜÑ5©fÓ5Ó5ˆKÞÜ˜+Ó1ˆà€KÜ�1�k A‘oÖ&ˆØ×!Ñ!Ð"OÓPˆÞØð €Kð 	×Ñœ#˜a›&Ô!Ø×Ñ˜dÔ#Ü˜dŸl™l›n°EÑQˆØ�‰�EÔÞÙ�A”s˜4“yÔ!Þ ‘a k°A¡oˆØ˜!ÕØØ€Kñ 'ð €Kùò) Vs   ÁE Á%E c                 óä  • [         R                  " U SUS9n UR                  (       a  UR                  S   OUR                  5       nUR	                  [         R
                  S   S   SSS9  UR                  SS	S
9  UR                  S5        [        R                  " SUR                  5       5      (       d  [        S5      e[        XbX4S9UR                  5         $ ! UR                  5         f = f)a°  Standalone convenience: opens its own context, reads the live auctions
(Cars tab) list for cars ending within days_ahead days, and closes.
Returns partial Cars, reg, owners and the guide value are not read yet,
that needs enrich_from_detail per car. The daily run does not use this,
it manages its own context so the same page can be reused for
enrichment, see daily_run._read_auction4cars. Fails loudly if the list
does not render.Úauction4cars©Úheadlessr   Ú	stock_urlrŒ   éÈ¯  rŽ   zdiv.vehicle-card-containerr�   r“   iÜ  zdata-auctionid="\d+"zNAuction4Cars list rendered but no cars were parsed. The page may have changed.)rQ   r]   r½   )r   Úopen_reader_contextÚpagesÚnew_pager˜   ÚSITESr™   r»   rS   r   rš   ÚRuntimeErrorrÆ   Úclose)Ú
playwrightrÊ   rQ   r]   r½   Úctxr¢   s          r   Ú	read_liverÕ   ;  s¿   € ô ×
%Ò
% j°.È8Ñ
T€CðØ"ŸyŸyˆs�y‰y˜Š|¨c¯l©l«nˆØ�	‰	”'—-‘- Ñ/°Ñ<ÐI[Ðejˆ	ÑkØ×ÑÐ;ÀUÐÑKØ×Ñ˜dÔ#ô �yŠyÐ0°$·,±,³.×AÑAÜÐoÓpÐpÜ˜d¸JÑ\à�	‰	�øˆ�	‰	�ús   ˜B4C ÃC/z:https://www.auction4cars.com/DealerAdmin/MyAccount/History)ÚJanuaryÚFebruaryÚMarchÚAprilÚMayÚJuneÚJulyÚAugustÚ	SeptemberÚOctoberÚNovemberÚDecemberr   z#^(\d{1,2})\s+([A-Za-z]+)\s+(\d{4})$c                 ó´  • [         R                  U =(       d    SR                  5       5      nU(       d  g[        UR	                  S5      5      UR	                  S5      R                  5       [        UR	                  S5      5      pCn[        R                  U5      nU(       d  g [        R                  " XEU5      R                  5       $ ! [         a     gf = f)a  "28 January 2026" (the History page's own real format, a full month
name, never abbreviated the way Motorway's own purchases screen is)
turned into a plain YYYY-MM-DD string. Returns "" rather than guessing
if the text does not match, never seen any other shape live.r   r   r   r   )Ú_DATE_WON_RErX   r   r   r   rY   Ú_MONTHS_FULLr›   rO   rP   r\   r‚   )Útextr   ÚdayÚ
month_namerD   Úmonths         r   Ú_parse_history_dateré   h  s¢   € ô
 	×Ñ˜DŸJ B×-Ñ-Ó/Ó0€AÞØÜ §¡¨£
›O¨Q¯W©W°Q«Z×-=Ñ-=Ó-?ÄÀQÇWÁWÈQÃZÃ�T€CÜ×Ñ˜ZÓ(€EÞØðÜ�}Š}˜T¨#Ó.×8Ñ8Ó:Ð:øÜó Ùðús   Â%$C
 Ã

CÃCc                 ó€  • / nU R                  S5      SS  GH"  n[        R                  " SU5      nU(       d  M$  UR                  S5      n[        R                  " SU5      nU(       a3  [        R
                  " UR                  S5      5      R                  5       OSn[        R                  " SU5      nU(       a  [        UR                  S5      5      OSn[        R                  " SU5      nU(       a  [        UR                  S5      5      OSnU(       d  GM  UR                  S	USUSXhS
U 3S.5        GM%     U$ )a  Parse the My Purchase History list into partial purchase dicts, one
per <div class="bnt-container bnt-history">: the auction id, vehicle
name, purchase price (this figure already includes buyer's fees, the
same real "what was actually paid" basis Motorway's and Carwow's own
purchase price fields already use, confirmed live against a real
auction: the detail page's own "Final Price" reads lower, the raw
winning bid before fees) and the date won. No reg or mileage yet, that
needs a detail page visit per row, see read_purchase_history.z'<div class="bnt-container bnt-history">r   Nz(href="/VehicleAuction/AuctionPage/(\d+)"z3class="auctionResult-table-vehicleName">([^<]*)</a>r   u6   Purchase Price:\s*</div>\s*<div>\s*Â£([\d,]+)\s*</div>z.Date Won:\s*</div>\s*<div>\s*([^<]*?)\s*</div>rB   rA   )ÚplatformÚvehicle_refrœ   ÚnameÚstatusrh   Úbought_daterM   )
r&   rS   r   r   rU   rV   r   r   ré   r[   )	r5   rp   r`   ra   rb   rí   rh   Úwonrï   s	            r   Úparse_historyrñ   z  s  € ð €CØ—‘Ð!JÓKÈAÈBÔOˆÜ�iŠiÐCÀUÓKˆÞÙØ—Y‘Y˜q“\ˆ
Ü�yŠyÐOÐQVÓWˆÞ:>Œw×Ò §
¡
¨1£Ó.×4Ñ4Ô6ÀBˆÜ—	’	ÐSÐUZÓ[ˆÞ(-”�U—[‘[ “^Ô$°4ˆÜ�iŠiÐIÈ5ÓQˆÞ;>Ô)¨#¯)©)°A«,Ô7ÀBˆÞÚØ�
‰
Ø&°zÈ"Ø B°ØUÐV`ÐUaÐbñ
÷ 	ñ Pð$ €Jr   c                 óô  • [         R                  " U SUS9n UR                  (       a  UR                  S   OUR                  5       nUR	                  [
        SSS9   UR                  SSS	9  [        UR                  5       5      nU H�  n UR	                  US   SSS9  UR                  SSS	9  [        UR                  5       5      nUR                  SS5      R                  5       US'   [        UR                  S5      5      US'   Mƒ     UUR!                  5         $ ! [         a    [        S
5      ef = f! [         a  n[        SUS    SU 35         SnAMÔ  SnAff = f! UR!                  5         f = f)aX  Read the account's own real purchase history: every car ever bought
on Auction4Cars, newest first, over the platform's own 13 month window.
One continuous session, the list read once then every entry's own
detail page visited in turn for its real registration and mileage
(never on the list page itself), the same shape purchases_run.py's own
Motorway and Carwow blocks already use for their own per car detail
reads. Fails loudly if the History screen itself does not render; one
bad detail page is skipped rather than failing the whole read, its own
reg is simply left blank. Read only, never acts.rÈ   rÉ   r   rŒ   rÌ   rŽ   z.auctionResult-table, .tab-paner�   r“   z—Auction4Cars purchase history screen did not render. The page may have changed or the login may have expired. Run: python3 login.py auction4cars manualrM   r‘   r’   r”   r   rœ   ÚMileagerE   z5Auction4Cars purchase detail read failed for auction rì   z: N)r   rÍ   rÎ   rÏ   r˜   ÚHISTORY_URLr™   Ú	ExceptionrÑ   rñ   rš   rs   r›   r   r   ÚprintrÒ   )rÓ   rÊ   rÔ   r¢   ÚrowsÚrr¥   Úes           r   Úread_purchase_historyrú   ™  sj  € ô ×
%Ò
% j°.È8Ñ
T€CðØ"ŸyŸyˆs�y‰y˜Š|¨c¯l©l«nˆØ�	‰	”+Ð*<Àeˆ	ÑLð	=Ø×"Ñ"Ð#DÈeÐ"ÑTô ˜TŸ\™\›^Ó,ˆÛˆAðgØ—	‘	˜!˜MÑ*Ð7IÐSX�	ÑYØ×&Ñ&Ð'?ÈÐ&ÑOÜ'¨¯©«Ó7�Ø!Ÿ:™: l°BÓ7×=Ñ=Ó?��%‘Ü# F§J¡J¨yÓ$9Ó:��)“ñ ð à�	‰	�øô# ó 	=Üð<ó=ð =ð	=ûô ó gÜÐMÈaÐP]ÑN^ÐM_Ð_aÐbcÐadÐe×fÒfûðgûð 	�	‰	�úsO   ˜AE% ÁD  Á.E% ÂA=D9Ä
E% Ä D6Ä6E% Ä9
E"ÅEÅE% ÅE"Å"E% Å%E7c                 ó  • SSK Jn  U R                  USSS9   U R                  SSS9  UR                  U R                  5       5      nUR                  5       US
'   X$S'   U$ ! [         a    UR                  S	U5      s $ f = f)zÌThe shortlist deep read of one Auction4Cars car page (2026-09-17):
its HTML (the Vehicle Details block, the service table, the reserve
script, every photo), handed to bidbrain.deep_read.from_auction4cars.r   )Ú	deep_readrŒ   r�   rŽ   r‘   r’   r“   zjAuction4Cars' car page did not load; the login may have expired. Run: python3 login.py auction4cars manualÚread_atÚasked_at)	Úbidbrainrü   r˜   r™   rõ   ÚfailedÚfrom_auction4carsrš   Ú	now_stamp)r¢   rM   rþ   Ú_deepÚds        r   rü   rü   ½  s›   € õ ,Ø‡I�IˆkÐ&8À%€IÑHðTØ×ÑÐ7ÀÐÑGð 	×Ñ §¡£Ó/€AØ—?‘?Ó$€A€i�LØ€j�MØ€Høô ó TØ�|‰|ð  Ið  KSó  Tò  	TðTús   ™A! Á!B Á?B )Né   )Nr  NN)TNr  N)Tr±   )&Ú__doc__rS   rO   r¤   rU   Úpricingr   r   r   Úcompiler   rW   r*   r   r   r'   r3   r6   rj   rs   r}   r   rƒ   rŠ   r¦   r¨   r¡   rÆ   rÕ   rô   Ú	enumeraterY   rä   rã   ré   rñ   rú   rü   )Úir   s   00r   Ú<module>r     s#  ðñó8 
Û Û Ý Ý à
�
Š
Ð#Ó$€Ø	‡z‚zÐ+Ó,€òÐ ò+ò+ò_ò	1òGôTònòSð �jŠjÐCÓD€òò ò"ðJ —J’JÐaÓbÐ òô(ôVðJ K€á)2ò?Ø@Aô*Cô Dò *C¡ �—‘“	˜1’ñ *Cò D€ð �zŠzÐ@ÓA€òò$ô>!õHùówDs   Â'C+