File size: 8,069 Bytes
7995942
 
 
 
822a25b
7995942
83a181a
7995942
af52e3b
096864a
7995942
af52e3b
 
dd42f99
 
822a25b
bd45e00
 
93237e1
83a181a
bd45e00
83a181a
 
 
 
af52e3b
7995942
822a25b
7995942
bd45e00
7995942
 
 
822a25b
 
 
 
 
 
 
 
dd42f99
822a25b
dd42f99
822a25b
7995942
 
822a25b
af52e3b
 
 
7995942
 
822a25b
83a181a
822a25b
83a181a
 
 
 
 
822a25b
 
 
7995942
 
 
 
 
822a25b
af52e3b
 
 
822a25b
 
dd42f99
822a25b
 
 
 
dd42f99
822a25b
 
 
 
dd42f99
822a25b
 
dd42f99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
af52e3b
dd42f99
 
af52e3b
dd42f99
 
 
 
 
822a25b
 
dd42f99
 
 
7995942
 
 
dd42f99
 
 
 
 
 
 
 
 
 
 
af52e3b
dd42f99
 
af52e3b
dd42f99
 
 
 
 
 
 
 
 
 
 
 
af52e3b
822a25b
dd42f99
 
 
 
 
af52e3b
dd42f99
822a25b
 
 
7995942
af52e3b
822a25b
 
af52e3b
dd42f99
7995942
 
 
 
 
 
af52e3b
83a181a
af52e3b
83a181a
bd45e00
 
 
83a181a
 
bd45e00
83a181a
 
 
af52e3b
83a181a
 
 
 
 
 
 
 
 
af52e3b
83a181a
 
 
 
 
 
 
af52e3b
83a181a
 
 
 
af52e3b
83a181a
 
af52e3b
83a181a
 
 
 
 
 
 
 
 
 
 
 
 
93237e1
 
83a181a
 
 
 
93237e1
 
83a181a
 
 
 
93237e1
 
af52e3b
 
 
7995942
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
from fastapi import FastAPI, Query, HTTPException
import duckdb
import time
import os
import threading

app = FastAPI(title="ICMR + HiTeck + Telegram API")

# ---------- ICMR SORTED INDEX FILES ----------
HF_BASE_ICMR = "https://huggingface.co/datasets/parthislive/Ha/resolve/main"

PHONE_FILES = ", ".join([f"'{HF_BASE_ICMR}/idx_phone.{i}.parquet'" for i in range(7)])
AADHAR_FILES = ", ".join([f"'{HF_BASE_ICMR}/idx_aadhar.{i}.parquet'" for i in range(7)])

DISPLAY_COLS = "name, fathersName, phoneNumber, aadharNumber, otherNumber, address, district, pincode, state, town, source"

# ---------- HITECKGROUP DATASET ----------
HF_BASE_HITECK = "https://huggingface.co/datasets/parthislive/HiTeckGroup/resolve/main"
HITECK_PARQUET = f"'{HF_BASE_HITECK}/data/train-00000-of-00001.parquet'"
HITECK_COLS = "user_id, phone_number, country, country_code"

# ---------- TELEGRAM DATASET ----------
HF_BASE_TG = "https://huggingface.co/datasets/parthislive/TELEGRAM/resolve/main"
TG_PARQUET = f"'{HF_BASE_TG}/simple_all/merged_all.parquet'"
TG_COLS = "user_id, phone_number, country, country_code"

_con = None
_lock = threading.Lock()


def get_con():
    global _con
    if _con is None:
        with _lock:
            if _con is None:
                con = duckdb.connect()
                con.execute("SET home_directory='/tmp'")
                con.execute("SET extension_directory='/tmp/duckdb_extensions'")
                con.execute("INSTALL httpfs; LOAD httpfs;")
                con.execute("INSTALL parquet; LOAD parquet;")
                con.execute("SET enable_object_cache=true")
                con.execute("SET threads=8")
                con.execute("SET http_keep_alive=true")
                con.execute("SET http_timeout=120000")
                _con = con
    return _con


# ============================================================
# ROOT & HEALTH
# ============================================================
@app.get("/")
def root():
    return {
        "app": "ICMR + HiTeck + Telegram API",
        "endpoints": {
            "/api/phone?phone=<num>": "ICMR by phone",
            "/api/aadhar?aadhar=<num>": "ICMR by aadhar",
            "/api/name?name=<name>": "ICMR by name",
            "/api/user?user_id=<id>": "Search HiTeck + Telegram by user_id",
            "/api/lookup?phone=<num>": "Search HiTeck + Telegram by phone",
        }
    }


@app.get("/health")
def health():
    return {"status": "ok"}


# ============================================================
# ICMR ENDPOINTS
# ============================================================
@app.get("/api/phone")
def api_phone(phone: str = Query(...)):
    return _search_phone(phone.strip())


@app.get("/api/aadhar")
def api_aadhar(aadhar: str = Query(...)):
    return _search_aadhar(aadhar.strip())


@app.get("/api/name")
def api_name(name: str = Query(...)):
    return _search_generic("name", name.strip(), "contains", 20)


def _search_phone(phone: str):
    if not phone:
        raise HTTPException(400, "Empty phone")
    try:
        start = time.time()
        con = get_con()
        sql = f"""
            SELECT {DISPLAY_COLS}
            FROM read_parquet([{PHONE_FILES}])
            WHERE phoneNumber = ?
            LIMIT 10
        """
        cur = con.execute(sql, [phone])
        cols = [d[0] for d in cur.description]
        rows = cur.fetchall()
        results = [dict(zip(cols, r)) for r in rows]
        elapsed = round(time.time() - start, 2)

        if not results:
            raise HTTPException(404, f"No data for phone={phone}")

        return {"status": "success", "field": "phoneNumber", "query": phone, "count": len(results), "time": f"{elapsed}s", "data": results}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(500, str(e))


def _search_aadhar(aadhar: str):
    if not aadhar:
        raise HTTPException(400, "Empty aadhar")
    try:
        start = time.time()
        con = get_con()
        sql = f"""
            SELECT {DISPLAY_COLS}
            FROM read_parquet([{AADHAR_FILES}])
            WHERE aadharNumber = ?
            LIMIT 10
        """
        cur = con.execute(sql, [aadhar])
        cols = [d[0] for d in cur.description]
        rows = cur.fetchall()
        results = [dict(zip(cols, r)) for r in rows]
        elapsed = round(time.time() - start, 2)

        if not results:
            raise HTTPException(404, f"No data for aadhar={aadhar}")

        return {"status": "success", "field": "aadharNumber", "query": aadhar, "count": len(results), "time": f"{elapsed}s", "data": results}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(500, str(e))


def _search_generic(field, value, mode, limit):
    try:
        start = time.time()
        con = get_con()
        all_files = f"{PHONE_FILES}, {AADHAR_FILES}"

        if mode == "exact":
            sql = f"SELECT {DISPLAY_COLS} FROM read_parquet([{all_files}]) WHERE {field} = ? LIMIT {limit}"
            params = [value]
        else:
            sql = f"SELECT {DISPLAY_COLS} FROM read_parquet([{all_files}]) WHERE {field} ILIKE ? LIMIT {limit}"
            params = [f"%{value}%"]

        cur = con.execute(sql, params)
        cols = [d[0] for d in cur.description]
        rows = cur.fetchall()
        results = [dict(zip(cols, r)) for r in rows]
        elapsed = round(time.time() - start, 2)

        if not results:
            raise HTTPException(404, f"No data for {field}={value}")

        return {"status": "success", "field": field, "query": value, "count": len(results), "time": f"{elapsed}s", "data": results}
    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(500, str(e))


# ============================================================
# COMBINED: HiTeck + Telegram (single endpoint each for user_id and phone)
# ============================================================
def _combined_search(field: str, value: str):
    if not value:
        raise HTTPException(400, f"Empty {field}")

    start = time.time()
    con = get_con()

    hiteck_data = []
    tg_data = []
    errors = {}

    try:
        cur = con.execute(
            f"SELECT {HITECK_COLS} FROM read_parquet({HITECK_PARQUET}) WHERE {field} = ? LIMIT 10",
            [value],
        )
        cols = [d[0] for d in cur.description]
        hiteck_data = [dict(zip(cols, r)) for r in cur.fetchall()]
    except Exception as e:
        errors["hiteck"] = str(e)

    try:
        cur = con.execute(
            f"SELECT {TG_COLS} FROM read_parquet({TG_PARQUET}) WHERE {field} = ? LIMIT 10",
            [value],
        )
        cols = [d[0] for d in cur.description]
        tg_data = [dict(zip(cols, r)) for r in cur.fetchall()]
    except Exception as e:
        errors["telegram"] = str(e)

    elapsed = round(time.time() - start, 2)
    total = len(hiteck_data) + len(tg_data)

    if total == 0 and errors:
        raise HTTPException(500, errors)

    if total == 0:
        raise HTTPException(404, f"No data for {field}={value} in any dataset")

    return {
        "status": "success",
        "field": field,
        "query": value,
        "total_count": total,
        "time": f"{elapsed}s",
        "hiteck": {"count": len(hiteck_data), "data": hiteck_data},
        "telegram": {"count": len(tg_data), "data": tg_data},
        "errors": errors if errors else None,
    }


@app.get("/api/user")
def api_user(user_id: str = Query(...)):
    """Search by user_id across HiTeck + Telegram."""
    return _combined_search("user_id", user_id.strip())


@app.get("/api/lookup")
def api_lookup(phone: str = Query(...)):
    """Search by phone across HiTeck + Telegram."""
    return _combined_search("phone_number", phone.strip())


# ============================================================
# RUN
# ============================================================
if __name__ == "__main__":
    import uvicorn
    port = int(os.environ.get("PORT", 7860))
    uvicorn.run(app, host="0.0.0.0", port=port)