# Backup to NEW prefix only; refuses if prefix non-empty at first run; never overwrites (same-ETag skip, else -vN). import boto3,hashlib,os B='analyticsdata123'; P='options-ml-platform/data/knowledge/popular-strategies/2026-10-27_expirycheap/'; L='/workspace/research/popular-strategies' s3=boto3.client('s3',region_name='ap-south-2') print('existing objects under prefix before upload:',s3.list_objects_v2(Bucket=B,Prefix=P,MaxKeys=5).get('KeyCount',0)) files=['index.html', 'PREREG.md', 'PROGRESS.md', 'scripts/gen_page.py', 'scripts/ec_section.py', 'scripts/s15_ec_cache.py', 'scripts/s16_ec.py', 'scripts/s17_ec_stats.py', 'scripts/backup_s3_expirycheap.py', 'results/ec_cache.log', 'results/ec_fills.csv', 'results/ec_freq.csv', 'results/ec_freq_day_year.csv', 'results/ec_freq_quarter.csv', 'results/ec_primary.csv', 'results/ec_random.csv', 'results/ec_regime.csv', 'results/ec_run.log', 'results/ec_sens.csv', 'results/ec_signals.csv'] def etag(k): try: return s3.head_object(Bucket=B,Key=k)['ETag'].strip('"') except s3.exceptions.ClientError: return None st={'new':0,'skip':0,'versioned':0} for f in sorted(files): p=f'{L}/{f}'; md=hashlib.md5(open(p,'rb').read()).hexdigest(); base,ext=os.path.splitext(f); v=1 while True: k=P+(f if v==1 else f'{base}-v{v}{ext}'); e=etag(k) if e is None: s3.upload_file(p,B,k); st['new' if v==1 else 'versioned']+=1; break if e==md: st['skip']+=1; break v+=1 print(st,'files',len(files))