SupaBase entry done

dev-ali-supabase
Ali Sharoz 2 weeks ago
parent 8db95e6d40
commit ec8726cf94

@ -22176,7 +22176,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!114 &292487262
MonoBehaviour:
m_ObjectHideFlags: 0

@ -47,14 +47,14 @@ public class BodyLinkHandler : MonoBehaviour, IPointerClickHandler, IPointerExit
EmailData email = emailPanel.Email;
if (email != null)
{
SceneOutcomeManager.Instance?.Clicked(email);
//UserActionLogger.Instance?.Log($"Clicked link '{linkID}' in email from '{email.senderName}'");
string englishLog = $"Clicked link '{linkID}' in email from '{email.senderName}'";
string arabicLog = $"تم الضغط على الرابط '{linkID}' في البريد من '{email.senderName}'";
UserActionLogger.Instance?.Log(englishLog, arabicLog);
bool isOptimal = !email.isPhishing;
SupabaseEventLogger.Instance?.LogDecisionEvent(isOptimal);
SceneOutcomeManager.Instance?.Clicked(email);
}
else

@ -65,21 +65,42 @@ public class UserActionLogger : MonoBehaviour
[ContextMenu("ShowSummary")]
public void ShowSummary()
{
if (summaryText != null)
{
if (summaryText == null)
return;
string logs = GetFullLog();
// Default to showing original logs
summaryText.text = logs;
bool isArabic = LanguageManager.Instance != null &&
LanguageManager.Instance.currentLanguage == "Arabic";
string logs = GetFullLog();
if (isArabic)
{
summaryText.text = ArabicFixer.Fix(logs);
summaryText.font = LanguageManager.Instance.fontArabic;
summaryText.ForceMeshUpdate();
}
// If Arabic language is active, fix and apply Arabic font
if (LanguageManager.Instance != null &&
LanguageManager.Instance.currentLanguage == "Arabic")
{
summaryText.text = ArabicFixer.Fix(logs);
summaryText.font = LanguageManager.Instance.fontArabic;
}
summaryText.ForceMeshUpdate();
}
//public void ShowSummary()
//{
// if (summaryText != null)
// {
// bool isArabic = LanguageManager.Instance != null &&
// LanguageManager.Instance.currentLanguage == "Arabic";
// string logs = GetFullLog();
// if (isArabic)
// {
// summaryText.text = ArabicFixer.Fix(logs);
// summaryText.font = LanguageManager.Instance.fontArabic;
// summaryText.ForceMeshUpdate();
// }
// }
//}
public void ClearLog()
{
logs.Clear();

@ -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; }
////}

Binary file not shown.

@ -816,14 +816,14 @@ PlayerSettings:
webGLExceptionSupport: 1
webGLNameFilesAsHashes: 0
webGLShowDiagnostics: 0
webGLDataCaching: 1
webGLDataCaching: 0
webGLDebugSymbols: 0
webGLEmscriptenArgs:
webGLModulesDirectory:
webGLTemplate: APPLICATION:Default
webGLAnalyzeBuildSize: 0
webGLUseEmbeddedResources: 0
webGLCompressionFormat: 1
webGLCompressionFormat: 2
webGLWasmArithmeticExceptions: 0
webGLLinkerTarget: 1
webGLThreadsSupport: 0

Loading…
Cancel
Save