Add question reporting and reference reviews
This commit is contained in:
@@ -4,6 +4,7 @@ const DEFAULT_QUESTIONS_API_URL := "https://s3q.ranaze.com/api/questions/search"
|
||||
const DEFAULT_QUESTION_ATTEMPT_API_URL := "https://s3q.ranaze.com/api/questions/attempts/first"
|
||||
const DEFAULT_QUESTION_API_BASE_URL := "https://s3q.ranaze.com/api/questions"
|
||||
const DEFAULT_RATINGS_API_URL := "https://s3q.ranaze.com/api/ratings/me"
|
||||
const DEFAULT_QUESTION_REPORTS_API_URL := "https://s3q.ranaze.com/api/question-reports"
|
||||
const RETRY_DELAYS := [2, 5, 10, 20, 30]
|
||||
|
||||
signal questions_changed
|
||||
@@ -12,17 +13,21 @@ signal choice_percentages_updated(question_id: String, percentages: Dictionary)
|
||||
signal ratings_changed
|
||||
signal question_rating_updated(question_id: String, question_elo: int, rating: Dictionary)
|
||||
signal question_skipped(question_id: String, correct_choice_id: String, question_elo: int, rating: Dictionary)
|
||||
signal question_report_submitted(question_id: String, report_id: String)
|
||||
signal question_report_failed(question_id: String, message: String)
|
||||
|
||||
var questions: Array = []
|
||||
var request: HTTPRequest
|
||||
var attempt_request: HTTPRequest
|
||||
var rating_request: HTTPRequest
|
||||
var skip_request: HTTPRequest
|
||||
var report_request: HTTPRequest
|
||||
var retry_timer: Timer
|
||||
var questions_api_url := DEFAULT_QUESTIONS_API_URL
|
||||
var question_attempt_api_url := DEFAULT_QUESTION_ATTEMPT_API_URL
|
||||
var question_api_base_url := DEFAULT_QUESTION_API_BASE_URL
|
||||
var ratings_api_url := DEFAULT_RATINGS_API_URL
|
||||
var question_reports_api_url := DEFAULT_QUESTION_REPORTS_API_URL
|
||||
var is_loading := false
|
||||
var last_error := ""
|
||||
var load_state := "idle"
|
||||
@@ -32,12 +37,14 @@ var overall_elo := 1000
|
||||
var rated_question_count := 0
|
||||
var category_elos := {}
|
||||
var last_rating_change := {}
|
||||
var pending_report_question_id := ""
|
||||
|
||||
func _ready() -> void:
|
||||
questions_api_url = str(ProjectSettings.get_setting("step3/questions_api_url", DEFAULT_QUESTIONS_API_URL))
|
||||
question_attempt_api_url = str(ProjectSettings.get_setting("step3/question_attempt_api_url", DEFAULT_QUESTION_ATTEMPT_API_URL))
|
||||
question_api_base_url = str(ProjectSettings.get_setting("step3/question_api_base_url", DEFAULT_QUESTION_API_BASE_URL))
|
||||
ratings_api_url = str(ProjectSettings.get_setting("step3/ratings_api_url", DEFAULT_RATINGS_API_URL))
|
||||
question_reports_api_url = str(ProjectSettings.get_setting("step3/question_reports_api_url", DEFAULT_QUESTION_REPORTS_API_URL))
|
||||
request = HTTPRequest.new()
|
||||
request.request_completed.connect(_on_api_questions_loaded)
|
||||
add_child(request)
|
||||
@@ -54,6 +61,10 @@ func _ready() -> void:
|
||||
skip_request.request_completed.connect(_on_question_skipped)
|
||||
add_child(skip_request)
|
||||
|
||||
report_request = HTTPRequest.new()
|
||||
report_request.request_completed.connect(_on_question_report_submitted)
|
||||
add_child(report_request)
|
||||
|
||||
retry_timer = Timer.new()
|
||||
retry_timer.one_shot = true
|
||||
retry_timer.timeout.connect(_on_retry_timer_timeout)
|
||||
@@ -135,6 +146,8 @@ func clear_questions() -> void:
|
||||
rating_request.cancel_request()
|
||||
if skip_request != null:
|
||||
skip_request.cancel_request()
|
||||
if report_request != null:
|
||||
report_request.cancel_request()
|
||||
if retry_timer != null:
|
||||
retry_timer.stop()
|
||||
questions.clear()
|
||||
@@ -147,6 +160,7 @@ func clear_questions() -> void:
|
||||
rated_question_count = 0
|
||||
category_elos = {}
|
||||
last_rating_change = {}
|
||||
pending_report_question_id = ""
|
||||
questions_changed.emit()
|
||||
ratings_changed.emit()
|
||||
|
||||
@@ -182,6 +196,37 @@ func skip_question(question_id: String) -> void:
|
||||
push_warning("Failed to record skipped question.")
|
||||
|
||||
|
||||
func submit_question_report(question_id: String, category: String, details: String) -> bool:
|
||||
if AuthState.access_token.is_empty() or question_id == "":
|
||||
question_report_failed.emit(question_id, "Login is required to submit a report.")
|
||||
return false
|
||||
if report_request.get_http_client_status() != HTTPClient.STATUS_DISCONNECTED:
|
||||
question_report_failed.emit(question_id, "Another report is still being submitted.")
|
||||
return false
|
||||
|
||||
var headers := PackedStringArray([
|
||||
"Content-Type: application/json",
|
||||
"Authorization: Bearer %s" % AuthState.access_token
|
||||
])
|
||||
var payload := {
|
||||
"questionExternalId": question_id,
|
||||
"category": category,
|
||||
"details": details
|
||||
}
|
||||
pending_report_question_id = question_id
|
||||
var err := report_request.request(
|
||||
question_reports_api_url,
|
||||
headers,
|
||||
HTTPClient.METHOD_POST,
|
||||
JSON.stringify(payload)
|
||||
)
|
||||
if err != OK:
|
||||
pending_report_question_id = ""
|
||||
question_report_failed.emit(question_id, "The report could not be submitted. Please try again.")
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func load_ratings_from_api() -> void:
|
||||
if AuthState.access_token.is_empty():
|
||||
return
|
||||
@@ -305,6 +350,35 @@ func _on_question_skipped(result: int, response_code: int, _headers: PackedStrin
|
||||
question_skipped.emit(question_id, correct_choice_id, question_elo, rating)
|
||||
|
||||
|
||||
func _on_question_report_submitted(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
var parsed: Variant = JSON.parse_string(body.get_string_from_utf8())
|
||||
var question_id := ""
|
||||
if typeof(parsed) == TYPE_DICTIONARY:
|
||||
question_id = str(parsed.get("questionExternalId", parsed.get("question_external_id", "")))
|
||||
if question_id == "":
|
||||
question_id = pending_report_question_id
|
||||
pending_report_question_id = ""
|
||||
|
||||
if result != HTTPRequest.RESULT_SUCCESS or response_code < 200 or response_code >= 300:
|
||||
var message := "The report could not be submitted. Please try again."
|
||||
if typeof(parsed) == TYPE_DICTIONARY:
|
||||
message = str(parsed.get("detail", parsed.get("title", message)))
|
||||
elif typeof(parsed) == TYPE_STRING and not str(parsed).strip_edges().is_empty():
|
||||
message = str(parsed).strip_edges()
|
||||
question_report_failed.emit(question_id, message)
|
||||
return
|
||||
|
||||
if typeof(parsed) != TYPE_DICTIONARY:
|
||||
question_report_failed.emit(question_id, "The report API returned an invalid response.")
|
||||
return
|
||||
|
||||
var report_id := str(parsed.get("id", ""))
|
||||
if question_id == "" or report_id == "":
|
||||
question_report_failed.emit(question_id, "The report API returned an incomplete response.")
|
||||
return
|
||||
question_report_submitted.emit(question_id, report_id)
|
||||
|
||||
|
||||
func _on_ratings_loaded(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
if result != HTTPRequest.RESULT_SUCCESS or response_code < 200 or response_code >= 300:
|
||||
push_warning("Ratings API request failed with status %d." % response_code)
|
||||
|
||||
Reference in New Issue
Block a user