godot-flashcards/scenes/autoloads/data_manager.gd
2025-12-27 00:20:33 -05:00

321 lines
8.6 KiB
GDScript

extends Node
signal decks_loaded()
signal deck_saved(deck: Deck)
signal deck_deleted(deck_id: String)
signal progress_updated(deck_id: String)
const DECKS_DIR = "user://data/decks/"
const PROGRESS_FILE = "user://data/progress/progress.json"
var decks: Array[Deck] = []
var progress_data: Dictionary = {}
var user_id: String = "local"
func _ready() -> void:
_ensure_directories_exist()
load_all_decks()
load_progress()
func _ensure_directories_exist() -> void:
DirAccess.make_dir_recursive_absolute("user://data/decks/")
DirAccess.make_dir_recursive_absolute("user://data/progress/")
func load_all_decks() -> void:
decks.clear()
var dir = DirAccess.open(DECKS_DIR)
if not dir:
push_warning("Could not open decks directory, creating it...")
_ensure_directories_exist()
decks_loaded.emit()
return
dir.list_dir_begin()
var file_name = dir.get_next()
while file_name != "":
if file_name.ends_with(".json"):
var deck = load_deck_from_file(DECKS_DIR + file_name)
if deck:
decks.append(deck)
file_name = dir.get_next()
dir.list_dir_end()
decks.sort_custom(func(a, b): return a.updated_at > b.updated_at)
print("Loaded %d deck(s)" % decks.size())
decks_loaded.emit()
func load_deck_from_file(file_path: String) -> Deck:
print("Loading Deck From File: ", file_path)
var file = FileAccess.open(file_path, FileAccess.READ)
if not file:
push_error("Failed to open deck file: " + file_path)
return null
var json_string = file.get_as_text()
file.close()
var json = JSON.new()
var error = json.parse(json_string)
if error != OK:
push_error("Failed to parse JSON in file: %s (Error: %s)" % [file_path, json.get_error_message()])
return null
var deck = Deck.new()
deck.from_dict(json.data)
return deck
func save_deck(deck: Deck) -> bool:
if not deck:
push_error("Cannot save null deck")
return false
deck.updated_at = Time.get_datetime_string_from_system(true, true) + "Z"
var file_path = DECKS_DIR + deck.id + ".json"
print("Saving Deck to: ", file_path)
var file = FileAccess.open(file_path, FileAccess.WRITE)
if not file:
push_error("Failed to create deck file: " + file_path)
return false
var deck_dict = deck.to_dict()
var json_string = JSON.stringify(deck_dict, "\t")
file.store_string(json_string)
file.close()
var found = false
for i in range(decks.size()):
if decks[i].id == deck.id:
decks[i] = deck
found = true
break
if not found:
decks.append(deck)
decks.sort_custom(func(a, b): return a.updated_at > b.updated_at)
print("Saved deck: %s (%d cards)" % [deck.name, deck.get_card_count()])
deck_saved.emit(deck)
return true
func delete_deck(deck_id: String) -> bool:
var file_path = DECKS_DIR + deck_id + ".json"
if FileAccess.file_exists(file_path):
var error = DirAccess.remove_absolute(file_path)
if error != OK:
push_error("Failed to delete deck file: " + file_path)
return false
for i in range(decks.size()):
if decks[i].id == deck_id:
decks.remove_at(i)
break
if progress_data.has(deck_id):
progress_data.erase(deck_id)
save_progress()
print("Deleted deck: " + deck_id)
deck_deleted.emit(deck_id)
return true
func get_deck_by_id(deck_id: String) -> Deck:
for deck in decks:
if deck.id == deck_id:
return deck
return null
func load_progress() -> void:
if not FileAccess.file_exists(PROGRESS_FILE):
print("No progress file found, starting fresh")
return
var file = FileAccess.open(PROGRESS_FILE, FileAccess.READ)
if not file:
push_error("Failed to open progress file")
return
var json_string = file.get_as_text()
file.close()
var json = JSON.new()
var error = json.parse(json_string)
if error != OK:
push_error("Failed to parse progress JSON")
return
var data = json.data
user_id = data.get("user_id", "local")
var deck_progress = data.get("deck_progress", {})
for deck_id in deck_progress.keys():
var progress = Progress.new()
progress.from_dict(deck_progress[deck_id])
progress_data[deck_id] = progress
print("Loaded progress for %d deck(s)" % progress_data.size())
func save_progress() -> void:
var deck_progress = {}
for deck_id in progress_data.keys():
deck_progress[deck_id] = progress_data[deck_id].to_dict()
var data = {
"version": "1.0",
"user_id": user_id,
"deck_progress": deck_progress
}
var file = FileAccess.open(PROGRESS_FILE, FileAccess.WRITE)
if not file:
push_error("Failed to save progress file")
return
var json_string = JSON.stringify(data, "\t")
file.store_string(json_string)
file.close()
print("Saved progress for %d deck(s)" % progress_data.size())
func get_or_create_progress(deck_id: String) -> Progress:
if not progress_data.has(deck_id):
var progress = Progress.new()
progress.deck_id = deck_id
progress_data[deck_id] = progress
return progress_data[deck_id]
func record_quiz_result(deck_id: String, card_id: String, is_correct: bool) -> void:
var progress = get_or_create_progress(deck_id)
progress.record_answer(card_id, is_correct)
save_progress()
progress_updated.emit(deck_id)
func get_progress(deck_id: String) -> Progress:
return progress_data.get(deck_id, null)
func download_deck_from_api(api_url: String) -> void:
print("Downloading deck from API: " + api_url)
var http_request = HTTPRequest.new()
add_child(http_request)
http_request.request_completed.connect(_on_api_deck_downloaded.bind(http_request))
var error = http_request.request(api_url)
if error != OK:
push_error("Failed to start HTTP request: " + str(error))
http_request.queue_free()
func _on_api_deck_downloaded(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, http_request: HTTPRequest) -> void:
if not http_request:
return
if result != HTTPRequest.RESULT_SUCCESS:
push_error("HTTP request failed with result: " + str(result))
http_request.queue_free()
return
if response_code != 200:
push_error("API returned error code: " + str(response_code))
http_request.queue_free()
return
var json_string = body.get_string_from_utf8()
var json = JSON.new()
var parse_error = json.parse(json_string)
if parse_error != OK:
push_error("Failed to parse API response JSON")
http_request.queue_free()
return
var deck = Deck.new()
deck.from_dict(json.data)
save_deck(deck)
print("Successfully downloaded and saved deck: " + deck.name)
http_request.queue_free()
func upload_deck_to_api(deck: Deck, api_url: String, auth_token: String = "") -> void:
print("Uploading deck to API: " + deck.name)
var http_request = HTTPRequest.new()
add_child(http_request)
http_request.request_completed.connect(_on_api_deck_uploaded.bind(http_request))
var headers = ["Content-Type: application/json"]
if auth_token != "":
headers.append("Authorization: Bearer " + auth_token)
var deck_dict = deck.to_dict()
var json_string = JSON.stringify(deck_dict)
var error = http_request.request(api_url, headers, HTTPClient.METHOD_POST, json_string)
if error != OK:
push_error("Failed to start HTTP upload request: " + str(error))
http_request.queue_free()
func _on_api_deck_uploaded(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, http_request: HTTPRequest) -> void:
if not http_request:
return
if result != HTTPRequest.RESULT_SUCCESS:
push_error("HTTP upload failed with result: " + str(result))
http_request.queue_free()
return
if response_code == 200 or response_code == 201:
print("Deck successfully uploaded to API!")
# Optionally parse response to get the deck ID from server
var json_string = body.get_string_from_utf8()
print("Server response: " + json_string)
else:
push_error("API returned error code: " + str(response_code))
http_request.queue_free()
func browse_decks_from_api(api_url: String) -> void:
print("Browsing decks from API: " + api_url)
var http_request = HTTPRequest.new()
add_child(http_request)
http_request.request_completed.connect(_on_api_decks_browsed.bind(http_request))
var error = http_request.request(api_url)
if error != OK:
push_error("Failed to start browse request: " + str(error))
http_request.queue_free()
func _on_api_decks_browsed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray, http_request: HTTPRequest) -> void:
if not http_request:
return
if result != HTTPRequest.RESULT_SUCCESS or response_code != 200:
push_error("Browse request failed")
http_request.queue_free()
return
var json_string = body.get_string_from_utf8()
var json = JSON.new()
if json.parse(json_string) == OK:
var decks_list = json.data
print("Found %d decks from API" % decks_list.size())
for deck_data in decks_list:
print(" - " + deck_data.get("deck", {}).get("name", "Unknown"))
http_request.queue_free()