From 3c6a816942dd26fbe950062a1135b20df1e5283a Mon Sep 17 00:00:00 2001 From: serversdwn Date: Sun, 25 Jan 2026 08:34:42 +0000 Subject: [PATCH] Initial OnlyScavs: keys, ratings, grid icons --- README.md | 14 +++ app.py | 110 ++++++++++++++++++++ import_keys.py | 125 +++++++++++++++++++++++ migrations_v1.sql | 27 +++++ tarkov.db | Bin 0 -> 122880 bytes templates/index.html | 237 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 513 insertions(+) create mode 100644 README.md create mode 100644 app.py create mode 100644 import_keys.py create mode 100644 migrations_v1.sql create mode 100644 tarkov.db create mode 100644 templates/index.html diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa1b6ec --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +##Place hoder, for this here personalized tarkov DB im building. + + +##Items to be tracked = ## +**Weight Management Mostly** +--Weapons and weapons parts +--helmets + armor and rigs +--backpacks + +**Keys** +--full list w/ locations and whats behind the lock +-- vendor price +-- My personal 0-4 priority scale. +-- Flag useless keys. \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..e13115b --- /dev/null +++ b/app.py @@ -0,0 +1,110 @@ +from flask import Flask, render_template, request, redirect, url_for +import sqlite3 + +app = Flask(__name__) +DB_PATH = "tarkov.db" + + +def get_db(): + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + return conn + + +@app.route("/") +def index(): + conn = get_db() + maps = conn.execute(""" + SELECT id, name + FROM maps + ORDER BY name + """).fetchall() + map_filter = request.args.get("map_id", type=int) + key_map_rows = conn.execute(""" + SELECT key_id, map_id + FROM key_maps + """).fetchall() + key_maps = {} + for row in key_map_rows: + key_maps.setdefault(row["key_id"], set()).add(row["map_id"]) + + key_query = """ + SELECT + k.id, + k.name, + k.icon_url, + k.grid_image_url, + k.wiki_url, + r.priority, + r.reason, + COALESCE(r.used_in_quest, 0) AS used_in_quest + FROM keys k + """ + params = [] + if map_filter: + key_query += """ + JOIN key_maps kmf + ON k.id = kmf.key_id + AND kmf.map_id = ? + """ + params.append(map_filter) + key_query += """ + LEFT JOIN key_ratings r ON k.id = r.key_id + ORDER BY + CASE WHEN r.priority IS NULL THEN 1 ELSE 0 END, + r.priority DESC, + k.name + """ + keys = conn.execute(key_query, params).fetchall() + conn.close() + + key_maps = {k: sorted(v) for k, v in key_maps.items()} + return render_template( + "index.html", + keys=keys, + maps=maps, + key_maps=key_maps, + map_filter=map_filter, + ) + + +@app.route("/rate", methods=["POST"]) +def rate_key(): + key_id = request.form["key_id"] + priority = request.form["priority"] + reason = request.form.get("reason", "") + used_in_quest = 1 if request.form.get("used_in_quest") == "on" else 0 + map_filter = request.form.get("map_id") + map_ids = [] + for value in request.form.getlist("map_ids"): + try: + map_ids.append(int(value)) + except ValueError: + continue + + conn = get_db() + conn.execute(""" + INSERT INTO key_ratings (key_id, priority, reason, used_in_quest) + VALUES (?, ?, ?, ?) + ON CONFLICT(key_id) DO UPDATE SET + priority = excluded.priority, + reason = excluded.reason, + used_in_quest = excluded.used_in_quest, + updated_at = CURRENT_TIMESTAMP + """, (key_id, priority, reason, used_in_quest)) + conn.execute("DELETE FROM key_maps WHERE key_id = ?", (key_id,)) + if map_ids: + conn.executemany( + "INSERT OR IGNORE INTO key_maps (key_id, map_id) VALUES (?, ?)", + [(key_id, map_id) for map_id in map_ids], + ) + conn.commit() + conn.close() + + if map_filter: + return redirect(url_for("index", map_id=map_filter)) + return redirect(url_for("index")) + + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=5000, debug=True) diff --git a/import_keys.py b/import_keys.py new file mode 100644 index 0000000..8f00f70 --- /dev/null +++ b/import_keys.py @@ -0,0 +1,125 @@ +import requests +import sqlite3 +import sys + +DB_PATH = "tarkov.db" +API_URL = "https://api.tarkov.dev/graphql" + +GRAPHQL_QUERY = """ +query { + items(types: [keys]) { + id + name + shortName + weight + wikiLink + gridImageLink + properties { + ... on ItemPropertiesKey { + uses + } + } + } +} +""" + +def fetch_keys(): + response = requests.post( + API_URL, + json={"query": GRAPHQL_QUERY}, + timeout=30 + ) + response.raise_for_status() + data = response.json() + + if "errors" in data: + raise RuntimeError(data["errors"]) + + return data["data"]["items"] + +def upsert_keys(conn, keys): + inserted = 0 + updated = 0 + skipped = 0 + + cursor = conn.cursor() + + for k in keys: + api_id = k.get("id") + name = k.get("name") + short_name = k.get("shortName") + weight = k.get("weight") + wiki_url = k.get("wikiLink") + grid_image_url = k.get("gridImageLink") + uses = None + + props = k.get("properties") + if props and "uses" in props: + uses = props["uses"] + + if not api_id or not name: + skipped += 1 + continue + + cursor.execute( + """ + SELECT id FROM keys WHERE api_id = ? + """, + (api_id,) + ) + row = cursor.fetchone() + + if row: + cursor.execute( + """ + UPDATE keys + SET name = ?, short_name = ?, weight_kg = ?, uses = ?, wiki_url = ?, grid_image_url = ? + WHERE api_id = ? + """, + (name, short_name, weight, uses, wiki_url, grid_image_url, api_id) + ) + updated += 1 + else: + cursor.execute( + """ + INSERT INTO keys (api_id, name, short_name, weight_kg, uses) + VALUES (?, ?, ?, ?, ?) + """, + (api_id, name, short_name, weight, uses, icon_url, wiki_url) + ) + inserted += 1 + + conn.commit() + return inserted, updated, skipped + +def main(): + print("Fetching keys from Tarkov.dev...") + try: + keys = fetch_keys() + except Exception as e: + print("ERROR: Failed to fetch keys") + print(e) + sys.exit(1) + + print(f"Fetched {len(keys)} keys") + + conn = sqlite3.connect(DB_PATH) + conn.execute("PRAGMA foreign_keys = ON") + + try: + inserted, updated, skipped = upsert_keys(conn, keys) + except Exception as e: + conn.rollback() + print("ERROR: Database operation failed") + print(e) + sys.exit(1) + finally: + conn.close() + + print("Import complete") + print(f"Inserted: {inserted}") + print(f"Updated: {updated}") + print(f"Skipped: {skipped}") + +if __name__ == "__main__": + main() diff --git a/migrations_v1.sql b/migrations_v1.sql new file mode 100644 index 0000000..4c9bfb7 --- /dev/null +++ b/migrations_v1.sql @@ -0,0 +1,27 @@ +-- V1: maps tagging + used_in_quest flag +PRAGMA foreign_keys = ON; + +CREATE TABLE IF NOT EXISTS maps ( + id INTEGER PRIMARY KEY, + name TEXT UNIQUE +); + +CREATE TABLE IF NOT EXISTS key_maps ( + key_id INTEGER NOT NULL, + map_id INTEGER NOT NULL, + PRIMARY KEY (key_id, map_id), + FOREIGN KEY (key_id) REFERENCES keys(id), + FOREIGN KEY (map_id) REFERENCES maps(id) +); + +ALTER TABLE key_ratings ADD COLUMN used_in_quest INTEGER DEFAULT 0; + +INSERT OR IGNORE INTO maps (name) VALUES + ('Customs'), + ('Factory'), + ('Shoreline'), + ('Interchange'), + ('Reserve'), + ('Woods'), + ('Labs'), + ('Streets'); diff --git a/tarkov.db b/tarkov.db new file mode 100644 index 0000000000000000000000000000000000000000..145d92837b6593d29362a8bdb2dd3cd1cceb40a6 GIT binary patch literal 122880 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
x{QAtJ3^AU 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 literal 0 HcmV?d00001 diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..bb30b50 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,237 @@ + + + + OnlyScavs – Key Ratings + + + + + +
+

OnlyScavs – Keys

+ +
+ + + +
+ +{% for key in keys %} +
+ +
+ {{ key.name }} + {% if key.wiki_url %} + wiki + {% endif %} + {% set selected_maps = key_maps.get(key.id, []) %} + {% if selected_maps %} +
+ {% for map in maps %} + {% if map.id in selected_maps %} + {{ map.name }} + {% endif %} + {% endfor %} +
+ {% endif %} +
+ +
+ + {% if map_filter %} + + {% endif %} + + + +
+ {% for map in maps %} + + {% endfor %} +
+ +
+
+{% endfor %} + +
+ +