327 lines
12 KiB
Python
327 lines
12 KiB
Python
from flask import Flask, render_template, request, redirect, url_for, jsonify
|
|
import json
|
|
import requests
|
|
import logging
|
|
import os
|
|
import traceback
|
|
import time
|
|
import sys
|
|
|
|
app = Flask(__name__)
|
|
|
|
# Determine logging level
|
|
if '--debug' in sys.argv:
|
|
logging_level = logging.DEBUG
|
|
else:
|
|
logging_level = logging.INFO
|
|
|
|
# Setup logging to file
|
|
logging.basicConfig(filename='log.log', level=logging_level, format='%(asctime)s %(levelname)s:%(message)s')
|
|
|
|
# File paths
|
|
ASSORT_FILE_PATH = 'assort.json'
|
|
QUEST_ASSORT_FILE_PATH = 'questassort.json'
|
|
CACHE_FILE_PATH = 'item_cache.json'
|
|
RUBLE_TPL_ID = '5449016a4bdc2d6f028b456f'
|
|
|
|
# Load assort data
|
|
try:
|
|
with open(ASSORT_FILE_PATH) as f:
|
|
assort_data = json.load(f)
|
|
logging.info("Assort data loaded successfully")
|
|
except Exception as e:
|
|
logging.error(f"Error loading assort data: {e}")
|
|
logging.error(traceback.format_exc())
|
|
|
|
try:
|
|
with open(QUEST_ASSORT_FILE_PATH) as f:
|
|
quest_assort_data = json.load(f)
|
|
logging.info("Quest assort data loaded successfully")
|
|
except json.JSONDecodeError as e:
|
|
logging.error(f"Error loading quest assort data (malformed JSON): {e}")
|
|
logging.error(traceback.format_exc())
|
|
quest_assort_data = {}
|
|
except Exception as e:
|
|
logging.error(f"Error loading quest assort data: {e}")
|
|
logging.error(traceback.format_exc())
|
|
quest_assort_data = {}
|
|
|
|
# Load cache or initialize an empty dictionary
|
|
def load_item_cache():
|
|
global item_cache
|
|
if os.path.exists(CACHE_FILE_PATH):
|
|
try:
|
|
with open(CACHE_FILE_PATH) as f:
|
|
item_cache = json.load(f)
|
|
logging.info("Item cache loaded successfully")
|
|
except Exception as e:
|
|
logging.error(f"Error loading item cache: {e}")
|
|
logging.error(traceback.format_exc())
|
|
item_cache = {}
|
|
else:
|
|
item_cache = {}
|
|
logging.info("Initialized empty item cache")
|
|
|
|
load_item_cache()
|
|
|
|
# Tarkov.dev API URL
|
|
TARKOV_API_URL = "https://api.tarkov.dev/graphql"
|
|
|
|
def get_ruble_image():
|
|
return get_item_details_cached(RUBLE_TPL_ID)
|
|
|
|
@app.route('/')
|
|
def index():
|
|
if not is_cache_complete():
|
|
return redirect(url_for('build_cache'))
|
|
|
|
try:
|
|
items = get_main_items_with_details(assort_data)
|
|
ruble_image = get_ruble_image()
|
|
return render_template('index.html', items=items, ruble_image=ruble_image)
|
|
except Exception as e:
|
|
logging.error(f"Error in index route: {e}")
|
|
logging.error(traceback.format_exc())
|
|
return "Error loading items", 500
|
|
|
|
def get_main_items_with_details(assort_data):
|
|
items = []
|
|
for item in assort_data['items']:
|
|
if item['parentId'] == 'hideout':
|
|
item_copy = item.copy() # Create a copy of the item to avoid modifying the original
|
|
item_copy['details'] = get_item_details_cached(item_copy['_tpl'])
|
|
item_copy['parts'] = get_item_parts_with_details(item_copy['_id'], assort_data)
|
|
item_copy['barter_scheme'] = get_barter_scheme_with_details(item_copy['_id'], assort_data)
|
|
item_copy['quest_requirement'] = get_quest_requirement(item_copy['_id'])
|
|
item_copy['offer_name'] = item_copy['_id'] # Add offer name
|
|
items.append(item_copy)
|
|
logging.info(f"Main items with details: {items}")
|
|
return items
|
|
|
|
def get_item_details_cached(tpl):
|
|
if tpl in item_cache:
|
|
logging.info(f"Cache hit for tpl {tpl}")
|
|
return item_cache[tpl]
|
|
else:
|
|
logging.info(f"Cache miss for tpl {tpl}, fetching from API")
|
|
item_details = get_items_details([tpl])
|
|
if tpl in item_details:
|
|
item_cache[tpl] = item_details[tpl]
|
|
save_item_cache()
|
|
return item_details.get(tpl, {})
|
|
|
|
def get_items_details(tpls):
|
|
queries = "\n".join([
|
|
f'item{index}: item(id: "{tpl}") {{ id name shortName description basePrice image512pxLink wikiLink }}'
|
|
for index, tpl in enumerate(tpls)
|
|
])
|
|
query = f"{{ {queries} }}"
|
|
try:
|
|
response = requests.post(TARKOV_API_URL, json={'query': query})
|
|
data = response.json()
|
|
logging.info(f"Item details for tpls {tpls}: {data}")
|
|
return {tpl: data['data'][f'item{index}'] for index, tpl in enumerate(tpls)}
|
|
except Exception as e:
|
|
logging.error(f"Error fetching item details for tpls {tpls}: {e}")
|
|
logging.error(traceback.format_exc())
|
|
return {}
|
|
|
|
def get_item_parts_with_details(parent_id, assort_data):
|
|
parts = []
|
|
def fetch_parts(parent_id):
|
|
sub_parts = []
|
|
for item in assort_data['items']:
|
|
if item['parentId'] == parent_id:
|
|
part_copy = item.copy() # Create a copy of the part to avoid modifying the original
|
|
part_copy['details'] = get_item_details_cached(part_copy['_tpl'])
|
|
part_copy['parts'] = fetch_parts(part_copy['_id']) # Fetch sub-parts recursively
|
|
sub_parts.append(part_copy)
|
|
return sub_parts
|
|
parts = fetch_parts(parent_id)
|
|
logging.info(f"Parts for parent_id {parent_id}: {parts}")
|
|
return parts
|
|
|
|
def get_barter_scheme_with_details(item_id, assort_data):
|
|
barter_scheme = []
|
|
if 'barter_scheme' in assort_data:
|
|
for scheme in assort_data['barter_scheme'].get(item_id, []):
|
|
scheme_details = []
|
|
for req in scheme:
|
|
req_copy = req.copy() # Create a copy of the req to avoid modifying the original
|
|
req_details = get_item_details_cached(req_copy['_tpl'])
|
|
req_copy['details'] = req_details
|
|
scheme_details.append(req_copy)
|
|
barter_scheme.append(scheme_details)
|
|
logging.info(f"Barter scheme for item_id {item_id}: {barter_scheme}")
|
|
return barter_scheme
|
|
|
|
def get_quest_requirement(item_id):
|
|
for quest_type, quests in quest_assort_data.items():
|
|
if item_id in quests:
|
|
return {
|
|
"type": quest_type,
|
|
"quest": quests[item_id]
|
|
}
|
|
return None
|
|
|
|
def get_all_item_ids(assort_data):
|
|
ids = set()
|
|
for item in assort_data['items']:
|
|
ids.add(item['_tpl'])
|
|
for part in item.get('parts', []):
|
|
ids.add(part['_tpl'])
|
|
for schemes in assort_data.get('barter_scheme', {}).values():
|
|
for scheme in schemes:
|
|
for req in scheme:
|
|
ids.add(req['_tpl'])
|
|
return list(ids)
|
|
|
|
def is_cache_complete():
|
|
item_ids = get_all_item_ids(assort_data)
|
|
for item_id in item_ids:
|
|
if item_id not in item_cache:
|
|
return False
|
|
return True
|
|
|
|
def build_item_cache():
|
|
try:
|
|
item_ids = get_all_item_ids(assort_data)
|
|
item_details = get_items_details(item_ids)
|
|
for tpl, details in item_details.items():
|
|
item_cache[tpl] = details
|
|
save_item_cache()
|
|
return True
|
|
except Exception as e:
|
|
logging.error(f"Error building item cache: {e}")
|
|
logging.error(traceback.format_exc())
|
|
return False
|
|
|
|
@app.route('/edit/<item_id>', methods=['GET', 'POST'])
|
|
def edit_item(item_id):
|
|
try:
|
|
if request.method == 'POST':
|
|
# Handle form submission
|
|
new_barter_scheme = []
|
|
tpl_list = request.form.getlist('barter_tpl')
|
|
count_list = request.form.getlist('barter_count')
|
|
scheme = []
|
|
for i in range(len(tpl_list)):
|
|
scheme.append({
|
|
'_tpl': tpl_list[i],
|
|
'count': int(count_list[i])
|
|
})
|
|
new_barter_scheme.append(scheme)
|
|
update_barter_scheme(item_id, new_barter_scheme)
|
|
return redirect(url_for('index'))
|
|
|
|
item = next((item for item in assort_data['items'] if item['_id'] == item_id), None)
|
|
if item:
|
|
item_copy = item.copy() # Create a copy of the item to avoid modifying the original
|
|
item_copy['details'] = get_item_details_cached(item_copy['_tpl'])
|
|
item_copy['parts'] = get_item_parts_with_details(item_id, assort_data)
|
|
item_copy['barter_scheme'] = get_barter_scheme_with_details(item_id, assort_data)
|
|
item_copy['quest_requirement'] = get_quest_requirement(item_id)
|
|
ruble_image = get_ruble_image()
|
|
return render_template('edit.html', item=item_copy, ruble_image=ruble_image)
|
|
else:
|
|
return "Item not found", 404
|
|
except Exception as e:
|
|
logging.error(f"Error in edit_item route: {e}")
|
|
logging.error(traceback.format_exc())
|
|
return "Error editing item", 500
|
|
|
|
def update_barter_scheme(item_id, new_barter_scheme):
|
|
try:
|
|
assort_data['barter_scheme'][item_id] = new_barter_scheme
|
|
save_assort_data()
|
|
except Exception as e:
|
|
logging.error(f"Error updating barter scheme for item {item_id}: {e}")
|
|
logging.error(traceback.format_exc())
|
|
|
|
def save_assort_data():
|
|
try:
|
|
# Deep copy the assort data and remove 'details' key before saving
|
|
cleaned_assort_data = json.loads(json.dumps(assort_data)) # Deep copy
|
|
|
|
# Clean items
|
|
for item in cleaned_assort_data['items']:
|
|
if 'details' in item:
|
|
del item['details']
|
|
|
|
# Clean barter_scheme
|
|
for item_id, schemes in cleaned_assort_data.get('barter_scheme', {}).items():
|
|
for scheme in schemes:
|
|
for part in scheme:
|
|
if 'details' in part:
|
|
del part['details']
|
|
|
|
# Backup the existing assort.json
|
|
if not os.path.exists('./bkp'):
|
|
os.makedirs('./bkp')
|
|
backup_file_path = os.path.join('./bkp', f"assort_backup_{int(time.time())}.json")
|
|
if os.path.exists(ASSORT_FILE_PATH):
|
|
os.rename(ASSORT_FILE_PATH, backup_file_path)
|
|
logging.info(f"Backup of assort data created at {backup_file_path}")
|
|
|
|
with open(ASSORT_FILE_PATH, 'w') as f:
|
|
json.dump(cleaned_assort_data, f, indent=2) # Use 2 spaces for indentation
|
|
logging.info("Assort data saved successfully")
|
|
except Exception as e:
|
|
logging.error(f"Error saving assort data: {e}")
|
|
logging.error(traceback.format_exc())
|
|
|
|
def save_item_cache():
|
|
try:
|
|
with open(CACHE_FILE_PATH, 'w') as f:
|
|
json.dump(item_cache, f, indent=2) # Use 2 spaces for indentation
|
|
logging.info("Item cache saved successfully")
|
|
except Exception as e:
|
|
logging.error(f"Error saving item cache: {e}")
|
|
logging.error(traceback.format_exc())
|
|
|
|
@app.route('/item_image/<tpl>')
|
|
def item_image(tpl):
|
|
details = get_item_details_cached(tpl)
|
|
return jsonify({'image512pxLink': details.get('image512pxLink', ''), 'name': details.get('name', '')})
|
|
|
|
@app.route('/search_items', methods=['GET'])
|
|
def search_items():
|
|
query = request.args.get('query', '')
|
|
if not query:
|
|
return jsonify([])
|
|
|
|
search_query = f'''
|
|
{{
|
|
items(name: "{query}") {{
|
|
id
|
|
name
|
|
gridImageLink
|
|
}}
|
|
}}
|
|
'''
|
|
|
|
try:
|
|
response = requests.post(TARKOV_API_URL, json={'query': search_query})
|
|
data = response.json()
|
|
items = data['data'].get('items', [])
|
|
return jsonify(items)
|
|
except Exception as e:
|
|
logging.error(f"Error searching for items with query '{query}': {e}")
|
|
logging.error(traceback.format_exc())
|
|
return jsonify([]), 500
|
|
|
|
@app.route('/build_cache', methods=['GET'])
|
|
def build_cache():
|
|
return render_template('build_cache.html')
|
|
|
|
@app.route('/start_build_cache', methods=['POST'])
|
|
def start_build_cache():
|
|
success = build_item_cache()
|
|
if success:
|
|
return jsonify({"status": "success"})
|
|
else:
|
|
return jsonify({"status": "error"}), 500
|
|
|
|
if __name__ == '__main__':
|
|
print("Webapp is hosted at http://127.0.0.1:5000")
|
|
app.run(debug='--debug' in sys.argv) |