-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathgeolocation_db.py
122 lines (92 loc) · 3.91 KB
/
geolocation_db.py
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
from typing import Optional
from contextlib import contextmanager
import sqlite3
from logger import logger
DBCursor = sqlite3.Connection.cursor
class GeolocationDB:
"""SQLite database to keep latitude, longitude for IP
Raises RuntimeError if database connection is not established.
"""
def __init__(self) -> None:
self._create_db_table()
def save_geolocation(
self, ip_address: str, latitude: str, longitude: str, country_code: str
) -> None:
"""Save IP, latitude, longitude to database
Raises RuntimeError if an error occurs during the save operation.
:type ip_address: str
:param ip_address: IP address
:type latitude: str
:param latitude: Latitude for IP
:type longitude: str
:param longitude: Longitude for IP
:type country_code: str
:param country_code: Country code for IP
"""
try:
with self._geolocation_db() as geolocation_db_cursor:
geolocation_db_cursor.execute(
"SELECT ip_address FROM geolocation WHERE ip_address=?", (ip_address,)
)
found = geolocation_db_cursor.fetchone()
if not found:
geolocation_db_cursor.execute(
"INSERT INTO geolocation VALUES (?, ?, ?, ?)",
(ip_address, latitude, longitude, country_code),
)
logger.debug(
f"[{ip_address}: ({latitude}, {longitude}, {country_code})] matching added to database."
)
else:
logger.debug(
f"[{ip_address}: ({latitude}, {longitude}, {country_code})] already exists. Skipping..."
)
except sqlite3.Error as exp:
raise RuntimeError(exp) from exp
def query_geolocation(self, ip_address: str) -> Optional[tuple[str, str, str]]:
"""Query given IP address in database and return latitude and longitude if exists
:type ip_address: str
:param ip_address: IP address
:rtype: tuple
:returns: (latitude, longitude, country_code) tuple for the given IP
"""
try:
with self._geolocation_db() as geolocation_db_cursor:
geolocation_db_cursor.execute(
"SELECT ip_address, latitude, longitude, country_code FROM geolocation WHERE ip_address=?",
(ip_address,),
)
found = geolocation_db_cursor.fetchone()
if not found:
logger.debug(f"Couldn't found {ip_address} in database!")
return None
else:
return (found[1], found[2], found[3])
except sqlite3.Error as exp:
raise RuntimeError(exp) from exp
def _create_db_table(self) -> None:
"""Create table to store latitude, longitude for IP"""
with self._geolocation_db() as geolocation_db_cursor:
geolocation_db_cursor.execute(
"""CREATE TABLE IF NOT EXISTS geolocation (
ip_address TEXT PRIMARY KEY NOT NULL,
latitude TEXT NOT NULL,
longitude TEXT NOT NULL,
country_code TEXT NOT NULL
);"""
)
@contextmanager
def _geolocation_db(self) -> DBCursor:
"""Context manager that returns geolocation db cursor
:rtype: sqlite3.Connection.cursor
:returns: Database connection cursor
"""
try:
geolocation_db = sqlite3.connect("geolocation.db")
yield geolocation_db.cursor()
except sqlite3.Error as exp:
logger.error(exp)
raise RuntimeError("Failed to connect to geolocation database!") from exp
finally:
geolocation_db.commit()
geolocation_db.close()