1 from configparser import ConfigParser, NoOptionError
3 from logging import getLogger
5 from sqlite3 import connect
6 from typing import Any, IO, Optional
7 from zlib import decompressobj, MAX_WBITS
11 log = getLogger("loctrkd/ocid_dload")
14 "https://opencellid.org/ocid/downloads"
15 "?token={token}&type={dltype}&file={fname}.csv.gz"
18 SCHEMA = """create table if not exists cells (
34 DBINDEX = "create index if not exists cell_idx on cells (area, cell)"
39 File-like object that unzips http response body.
40 read(size) method returns chunks of binary data as bytes
41 When used as iterator, splits data to lines
42 and yelds them as strings.
45 def __init__(self, zstream: IO[bytes]) -> None:
46 self.zstream = zstream
47 self.decoder: Optional[Any] = decompressobj(16 + MAX_WBITS)
51 def read(self, n: int = 1024) -> bytes:
52 if self.decoder is None:
54 while len(self.outdata) < n:
55 raw_data = self.zstream.read(n)
56 self.outdata += self.decoder.decompress(raw_data)
61 data, self.outdata = self.outdata[:n], self.outdata[n:]
65 def __next__(self) -> str:
67 splittry = self.line.split(b"\n", maxsplit=1)
70 moredata = self.read(256)
76 return line.decode("utf-8")
78 def __iter__(self) -> "unzipped":
82 def main(conf: ConfigParser) -> None:
84 url = conf.get("opencellid", "downloadurl")
89 conf.get("opencellid", "downloadtoken"), encoding="ascii"
91 token = fl.read().strip()
92 except FileNotFoundError:
94 "Opencellid access token not configured, cannot download"
97 mcc = conf.get("opencellid", "downloadmcc")
100 fname = "cell_towers"
104 url = RURL.format(token=token, dltype="mcc", fname=mcc)
105 dbfn = conf.get("opencellid", "dbfn")
107 with requests.get(url, stream=True) as resp, connect(dbfn) as db:
108 log.debug("Requested %s, result %s", url, resp)
109 if resp.status_code != 200:
110 log.error("Error getting %s: %s", url, resp)
112 db.execute("pragma journal_mode = wal")
114 db.execute("delete from cells")
115 rows = csv.reader(unzipped(resp.raw))
119 values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
125 log.warning("Did not get any data for MCC %s, rollback", mcc)
130 "repopulated %s with %d records for MCC %s", dbfn, count, mcc
134 if __name__.endswith("__main__"):
135 main(common.init(log))