|
|
|
@ -1,15 +1,17 @@
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.Networking;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Supabase; // Make sure SupabaseManager initializes this correctly
|
|
|
|
|
using Postgrest.Models;
|
|
|
|
|
using Postgrest.Attributes;
|
|
|
|
|
|
|
|
|
|
public class SupabaseEventLogger : MonoBehaviour
|
|
|
|
|
{
|
|
|
|
|
public static SupabaseEventLogger Instance;
|
|
|
|
|
|
|
|
|
|
[Header("Supabase")]
|
|
|
|
|
public string supabaseUrl = "https://vihjspljbslozbjzxutl.supabase.co";
|
|
|
|
|
public string supabaseAnonKey = "YOUR_ANON_KEY_HERE";
|
|
|
|
|
|
|
|
|
|
private DateTime sessionStartTime;
|
|
|
|
|
|
|
|
|
|
private void Awake()
|
|
|
|
@ -20,161 +22,539 @@ public class SupabaseEventLogger : MonoBehaviour
|
|
|
|
|
Destroy(gameObject);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Call this at the start of the game session.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public async void StartSession()
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class GameEventPayload
|
|
|
|
|
{
|
|
|
|
|
public string event_key;
|
|
|
|
|
public string timestamp;
|
|
|
|
|
public string user_id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class GameAttemptPayload
|
|
|
|
|
{
|
|
|
|
|
public string game_id;
|
|
|
|
|
public string scenario_id;
|
|
|
|
|
public string user_id;
|
|
|
|
|
public int attempt_number;
|
|
|
|
|
public string start_timestamp;
|
|
|
|
|
public string end_timestamp;
|
|
|
|
|
public int duration_seconds;
|
|
|
|
|
public float final_score_percentage;
|
|
|
|
|
public bool pass_fail_status;
|
|
|
|
|
public int optimal_decisions_count;
|
|
|
|
|
public int suboptimal_decisions_count;
|
|
|
|
|
public string key_decisions_log;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class Decision
|
|
|
|
|
{
|
|
|
|
|
public string decisionId;
|
|
|
|
|
public string timestamp;
|
|
|
|
|
public bool optimal;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class DecisionLogWrapper
|
|
|
|
|
{
|
|
|
|
|
public List<Decision> decisions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StartSession()
|
|
|
|
|
{
|
|
|
|
|
sessionStartTime = DateTime.UtcNow;
|
|
|
|
|
|
|
|
|
|
var gameEvent = new GameEvent
|
|
|
|
|
GameEventPayload payload = new GameEventPayload
|
|
|
|
|
{
|
|
|
|
|
Id = Guid.NewGuid(), // <== Ensure this is explicitly set
|
|
|
|
|
EventKey = "game_session_started",
|
|
|
|
|
Timestamp = sessionStartTime,
|
|
|
|
|
UserId = "user123"
|
|
|
|
|
event_key = "game_session_started",
|
|
|
|
|
timestamp = sessionStartTime.ToString("o"),
|
|
|
|
|
user_id = "user123"
|
|
|
|
|
};
|
|
|
|
|
await Client.Instance.From<GameEvent>().Insert(gameEvent);
|
|
|
|
|
Debug.Log("✅ Supabase Event: game_session_started");
|
|
|
|
|
|
|
|
|
|
StartCoroutine(PostToSupabase("game_events", JsonUtility.ToJson(payload)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Logs optimal/suboptimal decisions at runtime.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public async void LogDecisionEvent(bool isOptimal)
|
|
|
|
|
public void LogDecisionEvent(bool isOptimal)
|
|
|
|
|
{
|
|
|
|
|
string eventKey = isOptimal ? "game_optimal_decision_made" : "game_suboptimal_decision_made";
|
|
|
|
|
|
|
|
|
|
var gameEvent = new GameEvent
|
|
|
|
|
GameEventPayload payload = new GameEventPayload
|
|
|
|
|
{
|
|
|
|
|
Id = Guid.NewGuid(),
|
|
|
|
|
EventKey = eventKey,
|
|
|
|
|
Timestamp = DateTime.UtcNow,
|
|
|
|
|
UserId = "user123"
|
|
|
|
|
event_key = eventKey,
|
|
|
|
|
timestamp = DateTime.UtcNow.ToString("o"),
|
|
|
|
|
user_id = "user123"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
await Client.Instance.From<GameEvent>().Insert(gameEvent);
|
|
|
|
|
Debug.Log($"✅ Supabase Event: {eventKey}");
|
|
|
|
|
StartCoroutine(PostToSupabase("game_events", JsonUtility.ToJson(payload)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Completes the session and submits full results to phishing_game_attempts table.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public async void CompleteSessionAndSubmitResult(string userId, bool passed, int optimal, int suboptimal, string scenarioId, List<Decision> decisionLog = null)
|
|
|
|
|
public void CompleteSessionAndSubmitResult(string userId, bool passed, int optimal, int suboptimal, string scenarioId, List<Decision> decisionLog = null)
|
|
|
|
|
{
|
|
|
|
|
var endTime = DateTime.UtcNow;
|
|
|
|
|
int duration = (int)(endTime - sessionStartTime).TotalSeconds;
|
|
|
|
|
|
|
|
|
|
// Log completion events
|
|
|
|
|
await Client.Instance.From<GameEvent>().Insert(new GameEvent
|
|
|
|
|
// Submit game_session_completed event
|
|
|
|
|
GameEventPayload completedEvent = new GameEventPayload
|
|
|
|
|
{
|
|
|
|
|
Id = Guid.NewGuid(),
|
|
|
|
|
EventKey = "game_session_completed",
|
|
|
|
|
Timestamp = endTime,
|
|
|
|
|
UserId = userId
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await Client.Instance.From<GameEvent>().Insert(new GameEvent
|
|
|
|
|
{Id = Guid.NewGuid(),
|
|
|
|
|
EventKey = "game_score_recorded",
|
|
|
|
|
Timestamp = endTime,
|
|
|
|
|
UserId = userId
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Insert session result
|
|
|
|
|
var gameAttempt = new GameAttempt
|
|
|
|
|
event_key = "game_session_completed",
|
|
|
|
|
timestamp = endTime.ToString("o"),
|
|
|
|
|
user_id = userId
|
|
|
|
|
};
|
|
|
|
|
StartCoroutine(PostToSupabase("game_events", JsonUtility.ToJson(completedEvent)));
|
|
|
|
|
|
|
|
|
|
// Submit game_score_recorded event
|
|
|
|
|
GameEventPayload scoreEvent = new GameEventPayload
|
|
|
|
|
{
|
|
|
|
|
GameId = "phishing-awareness-1",
|
|
|
|
|
ScenarioId = scenarioId,
|
|
|
|
|
UserId = userId,
|
|
|
|
|
AttemptNumber = 1,
|
|
|
|
|
StartTime = sessionStartTime,
|
|
|
|
|
EndTime = endTime,
|
|
|
|
|
DurationSeconds = duration,
|
|
|
|
|
Score = passed ? 100 : 50,
|
|
|
|
|
Passed = passed,
|
|
|
|
|
Optimal = optimal,
|
|
|
|
|
Suboptimal = suboptimal,
|
|
|
|
|
KeyDecisionsLogJson = decisionLog != null ? JsonUtility.ToJson(new DecisionLogWrapper { decisions = decisionLog }) : "[]"
|
|
|
|
|
event_key = "game_score_recorded",
|
|
|
|
|
timestamp = endTime.ToString("o"),
|
|
|
|
|
user_id = userId
|
|
|
|
|
};
|
|
|
|
|
StartCoroutine(PostToSupabase("game_events", JsonUtility.ToJson(scoreEvent)));
|
|
|
|
|
|
|
|
|
|
await Client.Instance.From<GameAttempt>().Insert(gameAttempt);
|
|
|
|
|
Debug.Log("✅ Supabase Game Result Submitted");
|
|
|
|
|
}
|
|
|
|
|
// Submit final game attempt data
|
|
|
|
|
GameAttemptPayload attempt = new GameAttemptPayload
|
|
|
|
|
{
|
|
|
|
|
game_id = "phishing-awareness-1",
|
|
|
|
|
scenario_id = scenarioId,
|
|
|
|
|
user_id = userId,
|
|
|
|
|
attempt_number = 1,
|
|
|
|
|
start_timestamp = sessionStartTime.ToString("o"),
|
|
|
|
|
end_timestamp = endTime.ToString("o"),
|
|
|
|
|
duration_seconds = duration,
|
|
|
|
|
final_score_percentage = passed ? 100 : 50,
|
|
|
|
|
pass_fail_status = passed,
|
|
|
|
|
optimal_decisions_count = optimal,
|
|
|
|
|
suboptimal_decisions_count = suboptimal,
|
|
|
|
|
key_decisions_log = decisionLog != null ? JsonUtility.ToJson(new DecisionLogWrapper { decisions = decisionLog }) : "[]"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class Decision
|
|
|
|
|
{
|
|
|
|
|
public string decisionId;
|
|
|
|
|
public string timestamp;
|
|
|
|
|
public bool optimal;
|
|
|
|
|
StartCoroutine(PostToSupabase("phishing_game_attempts", JsonUtility.ToJson(attempt)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class DecisionLogWrapper
|
|
|
|
|
private IEnumerator PostToSupabase(string table, string jsonBody)
|
|
|
|
|
{
|
|
|
|
|
public List<Decision> decisions;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Existing SupabaseEventLogger class here...
|
|
|
|
|
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
|
|
|
|
|
|
|
|
|
|
[Table("game_events")]
|
|
|
|
|
public class GameEvent : BaseModel
|
|
|
|
|
{
|
|
|
|
|
[PrimaryKey("id", false)]
|
|
|
|
|
public Guid Id { get; set; }
|
|
|
|
|
string url = $"{supabaseUrl}/rest/v1/{table}";
|
|
|
|
|
UnityWebRequest request = new UnityWebRequest(url, "POST");
|
|
|
|
|
byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonBody);
|
|
|
|
|
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
|
|
|
|
|
request.downloadHandler = new DownloadHandlerBuffer();
|
|
|
|
|
|
|
|
|
|
[Column("event_key")]
|
|
|
|
|
public string EventKey { get; set; }
|
|
|
|
|
request.SetRequestHeader("Content-Type", "application/json");
|
|
|
|
|
request.SetRequestHeader("apikey", supabaseAnonKey);
|
|
|
|
|
request.SetRequestHeader("Authorization", "Bearer " + supabaseAnonKey);
|
|
|
|
|
request.SetRequestHeader("Prefer", "return=representation");
|
|
|
|
|
|
|
|
|
|
[Column("timestamp")]
|
|
|
|
|
public DateTime Timestamp { get; set; }
|
|
|
|
|
yield return request.SendWebRequest();
|
|
|
|
|
|
|
|
|
|
[Column("user_id")]
|
|
|
|
|
public string UserId { get; set; }
|
|
|
|
|
if (request.result == UnityWebRequest.Result.Success)
|
|
|
|
|
{
|
|
|
|
|
Debug.Log($"✅ Supabase POST to {table}: " + request.downloadHandler.text);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError($"❌ Supabase POST Failed ({table}): {request.responseCode}\n{request.error}\n{request.downloadHandler.text}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Table("phishing_game_attempts")]
|
|
|
|
|
public class GameAttempt : BaseModel
|
|
|
|
|
{
|
|
|
|
|
[PrimaryKey("id", false)]
|
|
|
|
|
public Guid Id { get; set; } = Guid.NewGuid();
|
|
|
|
|
|
|
|
|
|
[Column("game_id")]
|
|
|
|
|
public string GameId { get; set; }
|
|
|
|
|
|
|
|
|
|
[Column("scenario_id")]
|
|
|
|
|
public string ScenarioId { get; set; }
|
|
|
|
|
|
|
|
|
|
[Column("user_id")]
|
|
|
|
|
public string UserId { get; set; }
|
|
|
|
|
//using UnityEngine;
|
|
|
|
|
//using System;
|
|
|
|
|
//using System.Collections;
|
|
|
|
|
//using System.Collections.Generic;
|
|
|
|
|
//using System.Threading.Tasks;
|
|
|
|
|
//using Supabase;
|
|
|
|
|
//using Postgrest.Models;
|
|
|
|
|
//using Postgrest.Attributes;
|
|
|
|
|
//using static SupabaseTestInsert;
|
|
|
|
|
|
|
|
|
|
[Column("attempt_number")]
|
|
|
|
|
public int AttemptNumber { get; set; }
|
|
|
|
|
//[Table("game_events")]
|
|
|
|
|
//public class GameEvent : BaseModel
|
|
|
|
|
//{
|
|
|
|
|
// [PrimaryKey("id", false)]
|
|
|
|
|
// public Guid Id { get; set; }
|
|
|
|
|
|
|
|
|
|
[Column("start_timestamp")]
|
|
|
|
|
public DateTime StartTime { get; set; }
|
|
|
|
|
// [Column("event_key")]
|
|
|
|
|
// public string EventKey { get; set; }
|
|
|
|
|
|
|
|
|
|
[Column("end_timestamp")]
|
|
|
|
|
public DateTime EndTime { get; set; }
|
|
|
|
|
// [Column("timestamp")]
|
|
|
|
|
// public DateTime Timestamp { get; set; }
|
|
|
|
|
|
|
|
|
|
[Column("duration_seconds")]
|
|
|
|
|
public int DurationSeconds { get; set; }
|
|
|
|
|
// [Column("user_id")]
|
|
|
|
|
// public string UserId { get; set; }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
[Column("final_score_percentage")]
|
|
|
|
|
public float Score { get; set; }
|
|
|
|
|
//[Table("phishing_game_attempts")]
|
|
|
|
|
//public class GameAttempt : BaseModel
|
|
|
|
|
//{
|
|
|
|
|
// [PrimaryKey("id", false)]
|
|
|
|
|
// public Guid Id { get; set; } = Guid.NewGuid();
|
|
|
|
|
|
|
|
|
|
[Column("pass_fail_status")]
|
|
|
|
|
public bool Passed { get; set; }
|
|
|
|
|
// [Column("game_id")]
|
|
|
|
|
// public string GameId { get; set; }
|
|
|
|
|
|
|
|
|
|
[Column("optimal_decisions_count")]
|
|
|
|
|
public int Optimal { get; set; }
|
|
|
|
|
|
|
|
|
|
[Column("suboptimal_decisions_count")]
|
|
|
|
|
public int Suboptimal { get; set; }
|
|
|
|
|
|
|
|
|
|
[Column("key_decisions_log")]
|
|
|
|
|
public string KeyDecisionsLogJson { get; set; }
|
|
|
|
|
}
|
|
|
|
|
// [Column("scenario_id")]
|
|
|
|
|
// public string ScenarioId { get; set; }
|
|
|
|
|
|
|
|
|
|
// [Column("user_id")]
|
|
|
|
|
// public string UserId { get; set; }
|
|
|
|
|
|
|
|
|
|
// [Column("attempt_number")]
|
|
|
|
|
// public int AttemptNumber { get; set; }
|
|
|
|
|
|
|
|
|
|
// [Column("start_timestamp")]
|
|
|
|
|
// public DateTime StartTime { get; set; }
|
|
|
|
|
|
|
|
|
|
// [Column("end_timestamp")]
|
|
|
|
|
// public DateTime EndTime { get; set; }
|
|
|
|
|
|
|
|
|
|
// [Column("duration_seconds")]
|
|
|
|
|
// public int DurationSeconds { get; set; }
|
|
|
|
|
|
|
|
|
|
// [Column("final_score_percentage")]
|
|
|
|
|
// public float Score { get; set; }
|
|
|
|
|
|
|
|
|
|
// [Column("pass_fail_status")]
|
|
|
|
|
// public bool Passed { get; set; }
|
|
|
|
|
|
|
|
|
|
// [Column("optimal_decisions_count")]
|
|
|
|
|
// public int Optimal { get; set; }
|
|
|
|
|
|
|
|
|
|
// [Column("suboptimal_decisions_count")]
|
|
|
|
|
// public int Suboptimal { get; set; }
|
|
|
|
|
|
|
|
|
|
// [Column("key_decisions_log")]
|
|
|
|
|
// public string KeyDecisionsLogJson { get; set; }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
//public class SupabaseEventLogger : MonoBehaviour
|
|
|
|
|
//{
|
|
|
|
|
// public static SupabaseEventLogger Instance;
|
|
|
|
|
|
|
|
|
|
// private DateTime sessionStartTime;
|
|
|
|
|
|
|
|
|
|
// private void Awake()
|
|
|
|
|
// {
|
|
|
|
|
// if (Instance == null)
|
|
|
|
|
// Instance = this;
|
|
|
|
|
// else
|
|
|
|
|
// Destroy(gameObject);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// public void StartSession()
|
|
|
|
|
// {
|
|
|
|
|
// StartCoroutine(StartSessionCoroutine());
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// private IEnumerator StartSessionCoroutine()
|
|
|
|
|
// {
|
|
|
|
|
// var task = StartSessionAsync();
|
|
|
|
|
// while (!task.IsCompleted)
|
|
|
|
|
// yield return null;
|
|
|
|
|
|
|
|
|
|
// if (task.Exception != null)
|
|
|
|
|
// Debug.LogError("❌ Supabase Error: " + task.Exception.InnerException?.Message);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// private async Task StartSessionAsync()
|
|
|
|
|
// {
|
|
|
|
|
// sessionStartTime = DateTime.UtcNow;
|
|
|
|
|
|
|
|
|
|
// var gameEvent = new GameEvent
|
|
|
|
|
// {
|
|
|
|
|
// Id = Guid.NewGuid(),
|
|
|
|
|
// EventKey = "game_session_started",
|
|
|
|
|
// Timestamp = sessionStartTime,
|
|
|
|
|
// UserId = "user123"
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
// await Client.Instance.From<GameEvent>().Insert(gameEvent);
|
|
|
|
|
// Debug.Log("✅ Supabase Event: game_session_started");
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// public void LogDecisionEvent(bool isOptimal)
|
|
|
|
|
// {
|
|
|
|
|
// StartCoroutine(LogDecisionCoroutine(isOptimal));
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// private IEnumerator LogDecisionCoroutine(bool isOptimal)
|
|
|
|
|
// {
|
|
|
|
|
// var task = LogDecisionAsync(isOptimal);
|
|
|
|
|
// while (!task.IsCompleted)
|
|
|
|
|
// yield return null;
|
|
|
|
|
|
|
|
|
|
// if (task.Exception != null)
|
|
|
|
|
// Debug.LogError("❌ Supabase Error: " + task.Exception.InnerException?.Message);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// private async Task LogDecisionAsync(bool isOptimal)
|
|
|
|
|
// {
|
|
|
|
|
// string eventKey = isOptimal ? "game_optimal_decision_made" : "game_suboptimal_decision_made";
|
|
|
|
|
|
|
|
|
|
// var gameEvent = new GameEvent
|
|
|
|
|
// {
|
|
|
|
|
// Id = Guid.NewGuid(),
|
|
|
|
|
// EventKey = eventKey,
|
|
|
|
|
// Timestamp = DateTime.UtcNow,
|
|
|
|
|
// UserId = "user123"
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
// await Client.Instance.From<GameEvent>().Insert(gameEvent);
|
|
|
|
|
// Debug.Log($"✅ Supabase Event: {eventKey}");
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// public void CompleteSessionAndSubmitResult(string userId, bool passed, int optimal, int suboptimal, string scenarioId, List<Decision> decisionLog = null)
|
|
|
|
|
// {
|
|
|
|
|
// StartCoroutine(CompleteSessionCoroutine(userId, passed, optimal, suboptimal, scenarioId, decisionLog));
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// private IEnumerator CompleteSessionCoroutine(string userId, bool passed, int optimal, int suboptimal, string scenarioId, List<Decision> decisionLog)
|
|
|
|
|
// {
|
|
|
|
|
// var task = CompleteSessionAsync(userId, passed, optimal, suboptimal, scenarioId, decisionLog);
|
|
|
|
|
// while (!task.IsCompleted)
|
|
|
|
|
// yield return null;
|
|
|
|
|
|
|
|
|
|
// if (task.Exception != null)
|
|
|
|
|
// Debug.LogError("❌ Supabase Error: " + task.Exception.InnerException?.Message);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// private async Task CompleteSessionAsync(string userId, bool passed, int optimal, int suboptimal, string scenarioId, List<Decision> decisionLog)
|
|
|
|
|
// {
|
|
|
|
|
// var endTime = DateTime.UtcNow;
|
|
|
|
|
// int duration = (int)(endTime - sessionStartTime).TotalSeconds;
|
|
|
|
|
|
|
|
|
|
// await Client.Instance.From<GameEvent>().Insert(new GameEvent
|
|
|
|
|
// {
|
|
|
|
|
// Id = Guid.NewGuid(),
|
|
|
|
|
// EventKey = "game_session_completed",
|
|
|
|
|
// Timestamp = endTime,
|
|
|
|
|
// UserId = userId
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
// await Client.Instance.From<GameEvent>().Insert(new GameEvent
|
|
|
|
|
// {
|
|
|
|
|
// Id = Guid.NewGuid(),
|
|
|
|
|
// EventKey = "game_score_recorded",
|
|
|
|
|
// Timestamp = endTime,
|
|
|
|
|
// UserId = userId
|
|
|
|
|
// });
|
|
|
|
|
|
|
|
|
|
// var gameAttempt = new GameAttempt
|
|
|
|
|
// {
|
|
|
|
|
// Id = Guid.NewGuid(),
|
|
|
|
|
// GameId = "phishing-awareness-1",
|
|
|
|
|
// ScenarioId = scenarioId,
|
|
|
|
|
// UserId = userId,
|
|
|
|
|
// AttemptNumber = 1,
|
|
|
|
|
// StartTime = sessionStartTime,
|
|
|
|
|
// EndTime = endTime,
|
|
|
|
|
// DurationSeconds = duration,
|
|
|
|
|
// Score = passed ? 100 : 50,
|
|
|
|
|
// Passed = passed,
|
|
|
|
|
// Optimal = optimal,
|
|
|
|
|
// Suboptimal = suboptimal,
|
|
|
|
|
// KeyDecisionsLogJson = decisionLog != null ? JsonUtility.ToJson(new DecisionLogWrapper { decisions = decisionLog }) : "[]"
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
// await Client.Instance.From<GameAttempt>().Insert(gameAttempt);
|
|
|
|
|
// Debug.Log("✅ Supabase Game Result Submitted");
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// [Serializable]
|
|
|
|
|
// public class Decision
|
|
|
|
|
// {
|
|
|
|
|
// public string decisionId;
|
|
|
|
|
// public string timestamp;
|
|
|
|
|
// public bool optimal;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// [Serializable]
|
|
|
|
|
// public class DecisionLogWrapper
|
|
|
|
|
// {
|
|
|
|
|
// public List<Decision> decisions;
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////using UnityEngine;
|
|
|
|
|
////using System;
|
|
|
|
|
////using System.Collections.Generic;
|
|
|
|
|
////using System.Threading.Tasks;
|
|
|
|
|
////using Supabase; // Make sure SupabaseManager initializes this correctly
|
|
|
|
|
////using Postgrest.Models;
|
|
|
|
|
////using Postgrest.Attributes;
|
|
|
|
|
|
|
|
|
|
////public class SupabaseEventLogger : MonoBehaviour
|
|
|
|
|
////{
|
|
|
|
|
//// public static SupabaseEventLogger Instance;
|
|
|
|
|
|
|
|
|
|
//// private DateTime sessionStartTime;
|
|
|
|
|
|
|
|
|
|
//// private void Awake()
|
|
|
|
|
//// {
|
|
|
|
|
//// if (Instance == null)
|
|
|
|
|
//// Instance = this;
|
|
|
|
|
//// else
|
|
|
|
|
//// Destroy(gameObject);
|
|
|
|
|
//// }
|
|
|
|
|
|
|
|
|
|
//// /// <summary>
|
|
|
|
|
//// /// Call this at the start of the game session.
|
|
|
|
|
//// /// </summary>
|
|
|
|
|
//// public async void StartSession()
|
|
|
|
|
//// {
|
|
|
|
|
//// sessionStartTime = DateTime.UtcNow;
|
|
|
|
|
|
|
|
|
|
//// var gameEvent = new GameEvent
|
|
|
|
|
//// {
|
|
|
|
|
//// Id = Guid.NewGuid(), // <== Ensure this is explicitly set
|
|
|
|
|
//// EventKey = "game_session_started",
|
|
|
|
|
//// Timestamp = sessionStartTime,
|
|
|
|
|
//// UserId = "user123"
|
|
|
|
|
//// };
|
|
|
|
|
//// await Client.Instance.From<GameEvent>().Insert(gameEvent);
|
|
|
|
|
//// Debug.Log("✅ Supabase Event: game_session_started");
|
|
|
|
|
//// }
|
|
|
|
|
|
|
|
|
|
//// /// <summary>
|
|
|
|
|
//// /// Logs optimal/suboptimal decisions at runtime.
|
|
|
|
|
//// /// </summary>
|
|
|
|
|
//// public async void LogDecisionEvent(bool isOptimal)
|
|
|
|
|
//// {
|
|
|
|
|
//// string eventKey = isOptimal ? "game_optimal_decision_made" : "game_suboptimal_decision_made";
|
|
|
|
|
|
|
|
|
|
//// var gameEvent = new GameEvent
|
|
|
|
|
//// {
|
|
|
|
|
//// Id = Guid.NewGuid(),
|
|
|
|
|
//// EventKey = eventKey,
|
|
|
|
|
//// Timestamp = DateTime.UtcNow,
|
|
|
|
|
//// UserId = "user123"
|
|
|
|
|
//// };
|
|
|
|
|
|
|
|
|
|
//// await Client.Instance.From<GameEvent>().Insert(gameEvent);
|
|
|
|
|
//// Debug.Log($"✅ Supabase Event: {eventKey}");
|
|
|
|
|
//// }
|
|
|
|
|
|
|
|
|
|
//// /// <summary>
|
|
|
|
|
//// /// Completes the session and submits full results to phishing_game_attempts table.
|
|
|
|
|
//// /// </summary>
|
|
|
|
|
//// public async void CompleteSessionAndSubmitResult(string userId, bool passed, int optimal, int suboptimal, string scenarioId, List<Decision> decisionLog = null)
|
|
|
|
|
//// {
|
|
|
|
|
//// var endTime = DateTime.UtcNow;
|
|
|
|
|
//// int duration = (int)(endTime - sessionStartTime).TotalSeconds;
|
|
|
|
|
|
|
|
|
|
//// // Log completion events
|
|
|
|
|
//// await Client.Instance.From<GameEvent>().Insert(new GameEvent
|
|
|
|
|
//// {
|
|
|
|
|
//// Id = Guid.NewGuid(),
|
|
|
|
|
//// EventKey = "game_session_completed",
|
|
|
|
|
//// Timestamp = endTime,
|
|
|
|
|
//// UserId = userId
|
|
|
|
|
//// });
|
|
|
|
|
|
|
|
|
|
//// await Client.Instance.From<GameEvent>().Insert(new GameEvent
|
|
|
|
|
//// {Id = Guid.NewGuid(),
|
|
|
|
|
//// EventKey = "game_score_recorded",
|
|
|
|
|
//// Timestamp = endTime,
|
|
|
|
|
//// UserId = userId
|
|
|
|
|
//// });
|
|
|
|
|
|
|
|
|
|
//// // Insert session result
|
|
|
|
|
//// var gameAttempt = new GameAttempt
|
|
|
|
|
//// {
|
|
|
|
|
//// GameId = "phishing-awareness-1",
|
|
|
|
|
//// ScenarioId = scenarioId,
|
|
|
|
|
//// UserId = userId,
|
|
|
|
|
//// AttemptNumber = 1,
|
|
|
|
|
//// StartTime = sessionStartTime,
|
|
|
|
|
//// EndTime = endTime,
|
|
|
|
|
//// DurationSeconds = duration,
|
|
|
|
|
//// Score = passed ? 100 : 50,
|
|
|
|
|
//// Passed = passed,
|
|
|
|
|
//// Optimal = optimal,
|
|
|
|
|
//// Suboptimal = suboptimal,
|
|
|
|
|
//// KeyDecisionsLogJson = decisionLog != null ? JsonUtility.ToJson(new DecisionLogWrapper { decisions = decisionLog }) : "[]"
|
|
|
|
|
//// };
|
|
|
|
|
|
|
|
|
|
//// await Client.Instance.From<GameAttempt>().Insert(gameAttempt);
|
|
|
|
|
//// Debug.Log("✅ Supabase Game Result Submitted");
|
|
|
|
|
//// }
|
|
|
|
|
|
|
|
|
|
//// [Serializable]
|
|
|
|
|
//// public class Decision
|
|
|
|
|
//// {
|
|
|
|
|
//// public string decisionId;
|
|
|
|
|
//// public string timestamp;
|
|
|
|
|
//// public bool optimal;
|
|
|
|
|
//// }
|
|
|
|
|
|
|
|
|
|
//// [Serializable]
|
|
|
|
|
//// public class DecisionLogWrapper
|
|
|
|
|
//// {
|
|
|
|
|
//// public List<Decision> decisions;
|
|
|
|
|
//// }
|
|
|
|
|
////}
|
|
|
|
|
|
|
|
|
|
////// Existing SupabaseEventLogger class here...
|
|
|
|
|
////// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
|
|
|
|
|
|
|
|
|
|
////[Table("game_events")]
|
|
|
|
|
////public class GameEvent : BaseModel
|
|
|
|
|
////{
|
|
|
|
|
//// [PrimaryKey("id", false)]
|
|
|
|
|
//// public Guid Id { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("event_key")]
|
|
|
|
|
//// public string EventKey { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("timestamp")]
|
|
|
|
|
//// public DateTime Timestamp { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("user_id")]
|
|
|
|
|
//// public string UserId { get; set; }
|
|
|
|
|
////}
|
|
|
|
|
|
|
|
|
|
////[Table("phishing_game_attempts")]
|
|
|
|
|
////public class GameAttempt : BaseModel
|
|
|
|
|
////{
|
|
|
|
|
//// [PrimaryKey("id", false)]
|
|
|
|
|
//// public Guid Id { get; set; } = Guid.NewGuid();
|
|
|
|
|
|
|
|
|
|
//// [Column("game_id")]
|
|
|
|
|
//// public string GameId { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("scenario_id")]
|
|
|
|
|
//// public string ScenarioId { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("user_id")]
|
|
|
|
|
//// public string UserId { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("attempt_number")]
|
|
|
|
|
//// public int AttemptNumber { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("start_timestamp")]
|
|
|
|
|
//// public DateTime StartTime { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("end_timestamp")]
|
|
|
|
|
//// public DateTime EndTime { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("duration_seconds")]
|
|
|
|
|
//// public int DurationSeconds { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("final_score_percentage")]
|
|
|
|
|
//// public float Score { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("pass_fail_status")]
|
|
|
|
|
//// public bool Passed { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("optimal_decisions_count")]
|
|
|
|
|
//// public int Optimal { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("suboptimal_decisions_count")]
|
|
|
|
|
//// public int Suboptimal { get; set; }
|
|
|
|
|
|
|
|
|
|
//// [Column("key_decisions_log")]
|
|
|
|
|
//// public string KeyDecisionsLogJson { get; set; }
|
|
|
|
|
////}
|
|
|
|
|