| import os |
| import duckdb |
| import time |
| import psutil |
| import platform |
| import logging |
| import sys |
| from flask import Flask, request, jsonify, render_template_string |
| from huggingface_hub import hf_hub_download |
|
|
| |
| |
| |
|
|
| sys.stdout.reconfigure(line_buffering=True) |
|
|
| logging.basicConfig( |
| level=logging.INFO, |
| format="%(asctime)s | %(message)s", |
| force=True |
| ) |
|
|
| log = logging.info |
| log("🔥 APP STARTED") |
|
|
| |
| |
| |
|
|
| FILES = [ |
| ("Oiasx/Datx", "chunk_020.parquet"), |
| ("Oiasx/Datx", "chunk_021.parquet") |
| ] |
|
|
| app = Flask(__name__) |
|
|
| |
| |
| |
|
|
| HF_TOKEN = os.environ.get("Oiasx_TOKEN") |
|
|
| if not HF_TOKEN: |
| raise RuntimeError("Oiasx_TOKEN missing") |
|
|
| log("🔐 Token loaded") |
|
|
| |
| |
| |
|
|
| log("🚀 Starting system") |
| log(f"CPU: {os.cpu_count()}") |
|
|
| ram = psutil.virtual_memory() |
| log(f"RAM: {round(ram.total/1e9,2)} GB") |
|
|
| |
| |
| |
|
|
| parquet_files = [] |
|
|
| log("📥 Downloading parquet files...") |
|
|
| for repo, fname in FILES: |
|
|
| try: |
| log(f"⬇️ {fname}") |
|
|
| path = hf_hub_download( |
| repo_id=repo, |
| filename=fname, |
| repo_type="dataset", |
| token=HF_TOKEN |
| ) |
|
|
| parquet_files.append(path) |
|
|
| log(f"✅ Done: {fname}") |
|
|
| except Exception as e: |
| log(f"❌ Failed: {fname} | {e}") |
|
|
| log(f"📊 Total files: {len(parquet_files)}") |
|
|
| |
| |
| |
|
|
| con = duckdb.connect() |
|
|
| con.execute(f"PRAGMA threads={os.cpu_count()}") |
| con.execute("PRAGMA enable_object_cache") |
|
|
| log("⚡ DuckDB ready") |
|
|
| |
| |
| |
|
|
| parquet_list_sql = ",".join([f"'{p}'" for p in parquet_files]) |
|
|
| log("🔍 Reading schema...") |
|
|
| cols = con.execute(f""" |
| DESCRIBE SELECT * FROM read_parquet([{parquet_list_sql}]) |
| """).fetchall() |
|
|
| columns = [c[0] for c in cols] |
|
|
| log(f"Columns: {columns}") |
|
|
| |
| |
| |
|
|
| def run_search(column, value): |
|
|
| log(f"🔎 Search: {column} = {value}") |
|
|
| if column not in columns: |
| return {"error": "column not found"} |
|
|
| value = str(value).strip() |
|
|
| base = f"SELECT * FROM read_parquet([{parquet_list_sql}])" |
|
|
| if column == "mobile": |
|
|
| query = base + f" WHERE mobile = ? LIMIT 100" |
| param = value |
| log("⚡ Mode: EXACT mobile") |
|
|
| elif column in ["circle","email","id","alt"]: |
|
|
| query = base + f" WHERE {column} = ? LIMIT 100" |
| param = value |
| log("⚡ Mode: EXACT") |
|
|
| else: |
|
|
| query = base + f" WHERE {column} LIKE ? LIMIT 100" |
| param = value + "%" |
| log("🔍 Mode: PREFIX") |
|
|
| start = time.time() |
|
|
| rows = con.execute(query, [param]).fetchall() |
|
|
| query_time = (time.time() - start) * 1000 |
|
|
| log(f"⏱ {round(query_time,3)} ms | Rows: {len(rows)}") |
|
|
| result = [dict(zip(columns, r)) for r in rows] |
|
|
| return { |
| "rows": result, |
| "returned": len(result), |
| "query_ms": round(query_time, 3) |
| } |
|
|
| |
| |
| |
|
|
| HTML = """<!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Telecom Search</title> |
| <style> |
| *{margin:0;padding:0;box-sizing:border-box} |
| :root{ |
| --bg:#07070f; |
| --bg2:#0d0d1c; |
| --bg3:#12122a; |
| --surface:#161630; |
| --s2:#1c1c3a; |
| --pu:#7c3aed; |
| --pu2:#a855f7; |
| --pu3:#c084fc; |
| --cy:#06b6d4; |
| --cy2:#22d3ee; |
| --pk:#ec4899; |
| --gn:#10b981; |
| --am:#f59e0b; |
| --tx:#e2e8f0; |
| --tx2:#94a3b8; |
| --tx3:#475569; |
| --bd:rgba(124,58,237,0.18); |
| --bd2:rgba(124,58,237,0.35); |
| } |
| body{ |
| font-family:'Segoe UI',system-ui,sans-serif; |
| background:var(--bg); |
| color:var(--tx); |
| min-height:100vh; |
| overflow-x:hidden; |
| } |
| /* ─── LOADING ─── */ |
| #ld{ |
| position:fixed;inset:0; |
| background:var(--bg); |
| display:flex;flex-direction:column; |
| align-items:center;justify-content:center; |
| gap:44px;z-index:999; |
| transition:opacity .6s ease; |
| } |
| #ld.out{opacity:0;pointer-events:none} |
| /* Orb */ |
| .orb{position:relative;width:148px;height:148px} |
| .ring{ |
| position:absolute;inset:0;border-radius:50%; |
| border:1.5px solid transparent; |
| animation:spin linear infinite; |
| } |
| .ring:nth-child(1){border-top-color:var(--pu);border-right-color:var(--pu);animation-duration:2.2s;inset:0} |
| .ring:nth-child(2){border-top-color:var(--cy);animation-duration:3.4s;animation-direction:reverse;inset:16px} |
| .ring:nth-child(3){border-top-color:var(--pk);border-left-color:var(--pk);animation-duration:1.8s;inset:30px} |
| .ring:nth-child(4){border-bottom-color:var(--am);animation-duration:4.1s;animation-direction:reverse;inset:44px} |
| .core{ |
| position:absolute;inset:58px;border-radius:50%; |
| background:radial-gradient(circle at 33% 33%,var(--pu2),var(--pu) 55%,#2e1065); |
| display:flex;align-items:center;justify-content:center; |
| } |
| .core::before{ |
| content:'';position:absolute;inset:0;border-radius:50%; |
| background:radial-gradient(circle at 30% 25%,rgba(255,255,255,.3) 0%,transparent 55%); |
| } |
| .pulse{ |
| width:7px;height:7px;border-radius:50%; |
| background:#fff;opacity:.85; |
| animation:pulse 1.5s ease-in-out infinite; |
| } |
| /* Progress */ |
| .prog-wrap{width:300px;display:flex;flex-direction:column;align-items:center;gap:10px} |
| .bar-track{ |
| width:100%;height:3px; |
| background:rgba(124,58,237,.1);border-radius:99px;overflow:hidden; |
| } |
| .bar-fill{ |
| height:100%;border-radius:99px;width:0%; |
| background:linear-gradient(90deg,var(--pu),var(--cy2)); |
| transition:width .7s cubic-bezier(.4,0,.2,1); |
| position:relative; |
| } |
| .bar-fill::after{ |
| content:'';position:absolute;right:-1px;top:-2px; |
| width:7px;height:7px;border-radius:50%; |
| background:var(--cy2); |
| box-shadow:0 0 10px var(--cy),0 0 20px var(--cy); |
| } |
| .segs{display:flex;gap:5px} |
| .seg{ |
| width:30px;height:3px;border-radius:99px; |
| background:rgba(124,58,237,.1); |
| transition:background .4s,box-shadow .4s; |
| } |
| .seg.on{background:var(--pu);box-shadow:0 0 8px rgba(124,58,237,.7)} |
| .seg.done{background:var(--pu2)} |
| .prog-txt{ |
| font-size:11px;letter-spacing:.1em;text-transform:uppercase; |
| color:var(--tx3);font-weight:500;min-height:15px; |
| transition:color .3s; |
| } |
| /* Steps */ |
| .steps{display:flex;flex-direction:column;gap:9px;width:290px} |
| .step{ |
| display:flex;align-items:center;gap:10px; |
| font-size:12px;letter-spacing:.03em;color:var(--tx3); |
| opacity:0;transform:translateY(8px); |
| transition:opacity .4s,transform .4s,color .3s; |
| } |
| .step.show{opacity:1;transform:translateY(0)} |
| .step.done{color:var(--tx2)} |
| .dot{ |
| width:7px;height:7px;border-radius:50%; |
| background:var(--tx3);flex-shrink:0; |
| transition:background .3s,box-shadow .3s; |
| } |
| .step.active .dot{ |
| background:var(--cy2); |
| box-shadow:0 0 6px var(--cy),0 0 12px var(--cy); |
| animation:blink .9s ease-in-out infinite; |
| } |
| .step.done .dot{background:var(--gn);box-shadow:0 0 6px var(--gn);animation:none} |
| /* Clock */ |
| .clk-wrap{display:flex;flex-direction:column;align-items:center;gap:5px} |
| .clk-lbl{font-size:10px;letter-spacing:.14em;text-transform:uppercase;color:var(--tx3);font-weight:500} |
| .clk{ |
| font-size:30px;font-weight:300;letter-spacing:.06em; |
| color:var(--tx);font-variant-numeric:tabular-nums;line-height:1; |
| } |
| .clk .sep{color:var(--pu2);animation:blink2 1s step-end infinite} |
| /* ─── APP ─── */ |
| #app{display:none;min-height:100vh} |
| .hdr{ |
| background:var(--bg2); |
| border-bottom:1px solid var(--bd); |
| padding:16px 28px; |
| display:flex;align-items:center;gap:14px; |
| } |
| .hdr-ico{ |
| width:38px;height:38px;border-radius:10px; |
| background:linear-gradient(135deg,var(--pu),#2e1065); |
| display:flex;align-items:center;justify-content:center;font-size:17px; |
| } |
| .hdr-t{font-size:15px;font-weight:600;letter-spacing:.02em} |
| .hdr-s{font-size:11px;color:var(--tx3);margin-top:2px;letter-spacing:.04em} |
| .body{max-width:860px;margin:0 auto;padding:32px 20px} |
| .card{ |
| background:var(--surface); |
| border:1px solid var(--bd);border-radius:14px; |
| padding:22px;margin-bottom:20px; |
| } |
| .row{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap} |
| .fg{display:flex;flex-direction:column;gap:6px} |
| .fl{font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--tx3);font-weight:500} |
| select,input[type=text]{ |
| background:var(--bg3);border:1px solid var(--bd);border-radius:8px; |
| color:var(--tx);font-size:13.5px;padding:9px 13px;outline:none; |
| transition:border-color .2s;font-family:inherit; |
| } |
| select{min-width:130px;cursor:pointer} |
| input[type=text]{min-width:220px} |
| select:focus,input[type=text]:focus{border-color:var(--pu2)} |
| .btn{ |
| padding:9px 22px;background:var(--pu);color:#fff;border:none; |
| border-radius:8px;font-size:13.5px;font-weight:500;cursor:pointer; |
| letter-spacing:.03em;transition:background .2s,transform .1s; |
| font-family:inherit; |
| } |
| .btn:hover{background:var(--pu2)} |
| .btn:active{transform:scale(.97)} |
| .stats{ |
| display:flex;gap:18px;margin-top:12px; |
| font-size:12px;color:var(--tx3);letter-spacing:.03em; |
| } |
| .sc{display:flex;align-items:center;gap:5px} |
| .sc .v{color:var(--cy2);font-weight:500;font-variant-numeric:tabular-nums} |
| .res{background:var(--surface);border:1px solid var(--bd);border-radius:14px;overflow:hidden} |
| .res-h{ |
| padding:13px 18px;border-bottom:1px solid var(--bd); |
| font-size:11px;color:var(--tx3);letter-spacing:.07em;text-transform:uppercase;font-weight:500; |
| } |
| pre#out{ |
| padding:18px;font-size:12px;line-height:1.75;color:var(--tx2); |
| font-family:'Cascadia Code','Fira Code',Consolas,monospace; |
| overflow-x:auto;max-height:500px;overflow-y:auto; |
| white-space:pre-wrap;word-break:break-all; |
| } |
| pre#out::-webkit-scrollbar{width:5px;height:5px} |
| pre#out::-webkit-scrollbar-track{background:transparent} |
| pre#out::-webkit-scrollbar-thumb{background:var(--bd2);border-radius:99px} |
| @keyframes spin{to{transform:rotate(360deg)}} |
| @keyframes pulse{0%,100%{opacity:.4;transform:scale(.7)}50%{opacity:1;transform:scale(1.3)}} |
| @keyframes blink{0%,100%{opacity:1}50%{opacity:.2}} |
| @keyframes blink2{0%,100%{opacity:1}50%{opacity:0}} |
| </style> |
| </head> |
| <body> |
| <div id="ld"> |
| <div class="orb"> |
| <div class="ring"></div> |
| <div class="ring"></div> |
| <div class="ring"></div> |
| <div class="ring"></div> |
| <div class="core"><div class="pulse"></div></div> |
| </div> |
| <div class="prog-wrap"> |
| <div class="bar-track"><div class="bar-fill" id="bf"></div></div> |
| <div class="segs" id="segs"> |
| <div class="seg"></div><div class="seg"></div><div class="seg"></div> |
| <div class="seg"></div><div class="seg"></div><div class="seg"></div> |
| </div> |
| <div class="prog-txt" id="ptxt">Initializing…</div> |
| </div> |
| <div class="steps" id="steps"> |
| <div class="step" id="st0"><div class="dot"></div>Loading token</div> |
| <div class="step" id="st1"><div class="dot"></div>Downloading parquet files</div> |
| <div class="step" id="st2"><div class="dot"></div>Connecting DuckDB</div> |
| <div class="step" id="st3"><div class="dot"></div>Reading schema</div> |
| <div class="step" id="st4"><div class="dot"></div>Ready</div> |
| </div> |
| <div class="clk-wrap"> |
| <div class="clk-lbl">Clock</div> |
| <div class="clk" id="clk">00<span class="sep">:</span>00<span class="sep">:</span>00</div> |
| </div> |
| </div> |
| <div id="app"> |
| <div class="hdr"> |
| <div class="hdr-ico">⚡</div> |
| <div> |
| <div class="hdr-t">Telecom Search</div> |
| <div class="hdr-s">DuckDB · Parquet · Real-time query</div> |
| </div> |
| </div> |
| <div class="body"> |
| <div class="card"> |
| <div class="row"> |
| <div class="fg"> |
| <div class="fl">Column</div> |
| <select id="column"> |
| {% for c in columns %} |
| <option value="{{c}}">{{c}}</option> |
| {% endfor %} |
| </select> |
| </div> |
| <div class="fg"> |
| <div class="fl">Value</div> |
| <input type="text" id="value" placeholder="Search value…"> |
| </div> |
| <button class="btn" onclick="go()">Search</button> |
| </div> |
| <div class="stats" id="stats" style="display:none"> |
| <div class="sc">Rows <span class="v" id="sr">—</span></div> |
| <div class="sc">Query <span class="v" id="sq">—</span> ms</div> |
| </div> |
| </div> |
| <div class="res"> |
| <div class="res-h">Results</div> |
| <pre id="out">Run a search to see results.</pre> |
| </div> |
| </div> |
| </div> |
| <script> |
| /* Clock */ |
| function tick(){ |
| var n=new Date(); |
| var p=function(v){return String(v).padStart(2,'0');}; |
| document.getElementById('clk').innerHTML= |
| p(n.getHours())+'<span class="sep">:</span>'+ |
| p(n.getMinutes())+'<span class="sep">:</span>'+ |
| p(n.getSeconds()); |
| } |
| tick();setInterval(tick,1000); |
| /* Loading sequence */ |
| var STEPS=[ |
| {id:'st0',txt:'Token verified',pct:16}, |
| {id:'st1',txt:'Downloading parquet…',pct:42}, |
| {id:'st2',txt:'DuckDB connected',pct:64}, |
| {id:'st3',txt:'Schema ready',pct:83}, |
| {id:'st4',txt:'All systems go',pct:100}, |
| ]; |
| var DELAYS=[350,1000,1900,2700,3400]; |
| var segs=document.querySelectorAll('.seg'); |
| var bf=document.getElementById('bf'); |
| var ptxt=document.getElementById('ptxt'); |
| function setProg(pct){ |
| bf.style.width=pct+'%'; |
| var n=Math.round(pct/100*segs.length); |
| segs.forEach(function(s,i){ |
| s.classList.remove('on','done'); |
| if(i<n-1)s.classList.add('done'); |
| else if(i===n-1)s.classList.add('on'); |
| }); |
| } |
| function setStep(idx){ |
| STEPS.forEach(function(s,i){ |
| var el=document.getElementById(s.id); |
| el.classList.remove('active','done','show'); |
| if(i<idx){el.classList.add('show','done');} |
| else if(i===idx){el.classList.add('show','active');} |
| }); |
| } |
| STEPS.forEach(function(s,i){ |
| setTimeout(function(){ |
| setStep(i);setProg(s.pct);ptxt.textContent=s.txt; |
| },DELAYS[i]); |
| }); |
| setTimeout(function(){ |
| var ld=document.getElementById('ld'); |
| ld.classList.add('out'); |
| setTimeout(function(){ |
| ld.style.display='none'; |
| document.getElementById('app').style.display='block'; |
| },650); |
| },4200); |
| /* Search */ |
| async function go(){ |
| var column=document.getElementById('column').value; |
| var value=document.getElementById('value').value; |
| var out=document.getElementById('out'); |
| out.textContent='Searching…'; |
| try{ |
| var r=await fetch('/search?column='+encodeURIComponent(column)+'&value='+encodeURIComponent(value)); |
| var d=await r.json(); |
| document.getElementById('stats').style.display='flex'; |
| document.getElementById('sr').textContent=d.returned; |
| document.getElementById('sq').textContent=d.query_ms; |
| out.textContent=JSON.stringify(d.rows,null,2); |
| }catch(e){out.textContent='Error: '+e.message;} |
| } |
| document.getElementById('value').addEventListener('keydown',function(e){if(e.key==='Enter')go();}); |
| </script> |
| </body> |
| </html> |
| """ |
|
|
| |
| |
| |
|
|
| @app.route("/") |
| def home(): |
| return render_template_string(HTML, columns=columns) |
|
|
| @app.route("/search") |
| def search(): |
| column = request.args.get("column") |
| value = request.args.get("value") |
| return jsonify(run_search(column, value)) |
|
|
| |
| |
| |
|
|
| if __name__ == "__main__": |
| log("🌐 Server starting...") |
| app.run(host="0.0.0.0", port=7860) |