diff --git a/database.uxf b/database.uxf index 215a626..43681aa 100644 --- a/database.uxf +++ b/database.uxf @@ -1,13 +1,13 @@ - 9 + 12 UMLClass - 441 - 306 - 180 - 162 + 396 + 240 + 240 + 216 visitor -- @@ -27,10 +27,10 @@ bg=MAGENTA Relation - 558 - 216 - 36 - 108 + 552 + 120 + 48 + 144 lt=- m1=n @@ -41,10 +41,10 @@ m2=1 UMLClass - 702 - 306 - 171 - 153 + 744 + 240 + 228 + 204 request -- @@ -64,10 +64,10 @@ bg=CYAN Relation - 612 - 333 - 108 - 45 + 624 + 276 + 144 + 60 lt=- m1=1 @@ -78,10 +78,10 @@ m2=n UMLClass - 333 - 135 - 180 - 90 + 252 + 12 + 240 + 120 platform -- @@ -97,38 +97,19 @@ bg=MAGENTA UMLClass - 702 - 549 - 171 - 117 + 744 + 564 + 228 + 156 city -- <<PK>> - city_id: INTEGER -- -- country_id: INTEGER - name: TEXT -- region: TEXT -style=autoresize -bg=ORANGE - - - - UMLClass - - 945 - 549 - 171 - 99 - - country --- -<<PK>> - country_id: INTEGER --- -- name: TEXT UNIQUE -- code: TEXT UNIQUE +- region_id: INTEGER style=autoresize bg=ORANGE @@ -136,10 +117,10 @@ bg=ORANGE Relation - 864 - 567 - 99 - 45 + 960 + 588 + 132 + 60 lt=- m1=1 @@ -150,10 +131,10 @@ m2=n Relation - 612 - 567 - 108 - 45 + 624 + 588 + 144 + 60 lt=- m1=1 @@ -164,10 +145,10 @@ m2=n UMLClass - 441 - 549 - 180 - 117 + 396 + 564 + 240 + 156 ip_range -- @@ -184,10 +165,10 @@ bg=ORANGE Relation - 522 - 459 - 36 - 108 + 504 + 444 + 48 + 144 lt=- m1=1 @@ -198,10 +179,10 @@ m2=n UMLClass - 945 - 306 - 162 - 90 + 1068 + 240 + 216 + 120 route -- @@ -217,10 +198,10 @@ bg=CYAN UMLClass - 549 - 135 - 171 - 90 + 540 + 12 + 228 + 120 browser -- @@ -236,10 +217,10 @@ bg=MAGENTA Relation - 486 - 216 - 36 - 108 + 456 + 120 + 48 + 144 lt=- m1=n @@ -250,10 +231,10 @@ m2=1 UMLClass - 756 - 135 - 171 - 90 + 816 + 12 + 228 + 120 referer -- @@ -269,10 +250,10 @@ bg=CYAN Relation - 783 - 216 - 36 - 108 + 852 + 120 + 48 + 144 lt=- m1=n @@ -283,10 +264,10 @@ m2=1 Relation - 864 - 333 - 99 - 45 + 960 + 276 + 132 + 60 lt=- m1=n @@ -294,4 +275,70 @@ m2=1 10.0;20.0;90.0;20.0 + + UMLClass + + 1068 + 564 + 228 + 132 + + country +-- +<<PK>> +- country_id: INTEGER +-- +- name: TEXT UNIQUE +- code: TEXT UNIQUE +style=autoresize +bg=ORANGE + + + + Relation + + 936 + 708 + 48 + 120 + + lt=- +m1=1 +m2=n + + 10.0;80.0;10.0;10.0 + + + Relation + + 1080 + 684 + 48 + 144 + + lt=- +m1=1 +m2=n + + 10.0;10.0;10.0;100.0 + + + UMLClass + + 912 + 804 + 228 + 132 + + region +-- +<<PK>> +- region_id: INTEGER +-- +- name: TEXT +- country_id: INTEGER +style=autoresize +bg=ORANGE + + diff --git a/regina/database.py b/regina/database.py index 0d4ac00..a2112fc 100644 --- a/regina/database.py +++ b/regina/database.py @@ -283,29 +283,42 @@ class Database: assert(type(country_id_val) == int) return country_id_val - def get_city_id(self, name, region, country_id) -> int: + def get_region_id(self, name: str, country_id: int) -> int: + """ + get the id of region of name + if not present, insert and return id + """ name = sanitize(name) - region = sanitize(region) - if not sql_exists(self.cur, "city", [("name", name), ("region", region), ("country_id", country_id)], do_sanitize=False): - self.cur.execute(f"INSERT INTO city (name, region, country_id) VALUES ('{name}', '{region}', '{country_id}')") - cities = sql_select(self.cur, "city", [("name", name), ("region", region), ("country_id", country_id)], do_sanitize=False) + if not sql_exists(self.cur, "region", [("name", name), ("country_id", country_id)], do_sanitize=False): + self.cur.execute(f"INSERT INTO region (name, country_id) VALUES ('{name}', '{country_id}')") + regions = self(f"SELECT region_id FROM region WHERE name = '{name}' AND country_id = '{country_id}'") + if len(regions) > 0: + region_id_val = regions[0][0] + else: + warning(f"get_region_id: Could not get region_id for name='{name}'.") + return 0 + assert(type(region_id_val) == int) + return region_id_val + + def get_city_id(self, name, region_id: int, country_id:int) -> int: + name = sanitize(name) + if not sql_exists(self.cur, "city", [("name", name), ("region_id", region_id), ("country_id", country_id)], do_sanitize=False): + self.cur.execute(f"INSERT INTO city (name, region_id, country_id) VALUES ('{name}', '{region_id}', '{country_id}')") + cities = sql_select(self.cur, "city", [("name", name), ("region_id", region_id), ("country_id", country_id)], do_sanitize=False) if len(cities) > 0: city_id_val = cities[0][0] else: - warning(f"get_city_id: Could not get city_id for name='{name}', region='{region}' and country_id='{country_id}'.") + warning(f"get_city_id: Could not get city_id for name='{name}', region_id='{region_id}' and country_id='{country_id}'.") return 0 assert(type(city_id_val) == int) return city_id_val - def update_geoip_tables(self, geoip_city_csv_path: str): """ update the geoip data with the contents of the geoip_city_csv file Make sure to update the visitor.ip_range_id column for all visitors. In case something changed, they might point to a different city. - - TODO: update teh visitor.ip_range_id column to match (potentially) new city ip range """ # indices for the csv FROM = 0; TO = 1; CODE = 2; COUNTRY = 3; REGION = 4; CITY = 5 @@ -333,24 +346,26 @@ class Database: self.cur.execute(f"DELETE FROM ip_range") self.cur.execute(f"DELETE FROM city") self.cur.execute(f"DELETE FROM country") + self.cur.execute(f"DELETE FROM region") self.conn.commit() self.cur.execute(f"VACUUM") # guarantees that unkown city/country will have id 0 self.cur.execute(f"INSERT INTO country (country_id, name, code) VALUES (0, 'Unknown', 'XX') ") - self.cur.execute(f"INSERT INTO city (city_id, name, region) VALUES (0, 'Unknown', 'Unkown') ") + self.cur.execute(f"INSERT INTO region (region_id, name, country_id) VALUES (0, 'Unknown', 0) ") + self.cur.execute(f"INSERT INTO city (city_id, name, region_id, country_id) VALUES (0, 'Unknown', 0, 0) ") # for combining city ranges into a 'City in ' range # country_id for the range that was last added (for combining multiple csv rows in one ip_range) RANGE_DONE = -1 - combine_range_country_id = RANGE_DONE - combine_range_country_name = "" + combine_range_city_id = RANGE_DONE combine_range_low = RANGE_DONE combine_range_high = RANGE_DONE - def add_range(low, high, city_name, region, country_id): - city_id = self.get_city_id(city_name, region, country_id) - pdebug(f"update_ip_range_id: Adding range for city={city_name:20}, country_id={country_id:3}, low={low:16}, high={high:16}", lvl=2) + get_all = "all" in settings["data-collection"]["get_cities_for_countries"] + + def add_range(low, high, city_id): + pdebug(f"update_ip_range_id: Adding range for city={city_id:20}, low={low:16}, high={high:16}", lvl=3) self.cur.execute(f"INSERT INTO ip_range (low, high, city_id) VALUES ({low}, {high}, {city_id})") for i, row in enumerate(csv, 1): # if i % 100 == 0: @@ -365,25 +380,27 @@ class Database: # make sure country exists country_id = self.get_country_id(row[COUNTRY], row[CODE]) # only add cities for countries the user is interested in - if row[CODE] in settings["data-collection"]["get_cities_for_countries"]: - add_range(row[FROM], row[TO], row[CITY], row[REGION], country_id) + if get_all or row[CODE] in settings["data-collection"]["get_cities_for_countries"]: + region_id = self.get_region_id(row[REGION], country_id) + city_id = self.get_city_id(row[CITY], region_id, country_id) else: - # if continuing - if combine_range_country_id != RANGE_DONE: - # if continuing previous range, extend the upper range limit - if combine_range_country_id == country_id: - combine_range_high = row[TO] - else: # new range for country, append - add_range(combine_range_low, combine_range_high, f"City in {combine_range_country_name}", f"Region in {combine_range_country_name}", combine_range_country_id) - combine_range_country_id = RANGE_DONE - # not elif, this has to be executed if previous else was executed - if combine_range_country_id == RANGE_DONE : # currently in new range, combine with later ranges - combine_range_country_id = country_id - combine_range_country_name = row[COUNTRY] - combine_range_low = row[FROM] + region_id = self.get_region_id(f"Region in {row[COUNTRY]}", country_id) + city_id = self.get_city_id(f"City in {row[COUNTRY]}", region_id, country_id) + # if continuing + if combine_range_city_id != RANGE_DONE: + # if continuing previous range, extend the upper range limit + if combine_range_city_id == city_id: combine_range_high = row[TO] - if combine_range_country_id >= 0: # last range , append - add_range(combine_range_low, combine_range_high, f"City in {combine_range_country_name}", f"Region in {combine_range_country_name}", combine_range_country_id) + else: # new range for city, append + add_range(combine_range_low, combine_range_high, combine_range_city_id) + combine_range_city_id = RANGE_DONE + # not elif, this has to be executed if previous else was executed + if combine_range_city_id == RANGE_DONE : # currently in new range, combine with later ranges + combine_range_city_id = city_id + combine_range_low = row[FROM] + combine_range_high = row[TO] + if combine_range_city_id != RANGE_DONE: # last range , append + add_range(combine_range_low, combine_range_high, combine_range_city_id) # diff --git a/regina/sql/create_db.sql b/regina/sql/create_db.sql index 973c609..1341838 100644 --- a/regina/sql/create_db.sql +++ b/regina/sql/create_db.sql @@ -59,7 +59,15 @@ CREATE TABLE IF NOT EXISTS ip_range( CREATE TABLE IF NOT EXISTS city( city_id INTEGER PRIMARY KEY, name TEXT, - region TEXT, + region_id INTEGER, + country_id INTEGER, + FOREIGN KEY(region_id) REFERENCES region(region_id), + FOREIGN KEY(country_id) REFERENCES country(country_id) +) STRICT; + +CREATE TABLE IF NOT EXISTS region( + region_id INTEGER PRIMARY KEY, + name TEXT, country_id INTEGER, FOREIGN KEY(country_id) REFERENCES country(country_id) ) STRICT;