Events working
This commit is contained in:
parent
ff8803ba68
commit
8db95e6d40
@ -22160,6 +22160,50 @@ Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 4270791331071568, guid: f0bfb58546547264682d6066f7947e72, type: 3}
|
||||
m_PrefabInstance: {fileID: 71862592}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1 &292487261
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 292487263}
|
||||
- component: {fileID: 292487262}
|
||||
m_Layer: 0
|
||||
m_Name: SupabaseTestInsert
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &292487262
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 292487261}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 619706b2e2fd4fe4eb36567a686c7da0, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!4 &292487263
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 292487261}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -0.0981338, y: 2.3009996, z: 3.8460662}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!4 &292591182 stripped
|
||||
Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 4940683387464080, guid: 1941338505201ed4b86d0fc32a45bdcc, type: 3}
|
||||
@ -154096,6 +154140,65 @@ Transform:
|
||||
m_CorrespondingSourceObject: {fileID: 4829453017703304, guid: 07751b6a4092629438c147af65e877b3, type: 3}
|
||||
m_PrefabInstance: {fileID: 1574431602}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1 &2143540272
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2143540275}
|
||||
- component: {fileID: 2143540274}
|
||||
- component: {fileID: 2143540273}
|
||||
m_Layer: 0
|
||||
m_Name: SupabaseManager
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &2143540273
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2143540272}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 50b27839ecef84443a10112beb860a7a, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!114 &2143540274
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2143540272}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ef01b27a4a6f3724a981d178a963c567, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
supabaseUrl: https://vihjspljbslozbjzxutl.supabase.co
|
||||
supabaseKey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZpaGpzcGxqYnNsb3pianp4dXRsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDk1NDc4OTMsImV4cCI6MjA2NTEyMzg5M30.IYPvShgu5j3NnE5PHn-aFLCBJl1QQaVQvAjzxFt8tlA
|
||||
--- !u!4 &2143540275
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2143540272}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -0.0981338, y: 2.3009996, z: 3.8460662}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1001 &2143858061
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -160336,3 +160439,5 @@ SceneRoots:
|
||||
- {fileID: 401603027}
|
||||
- {fileID: 72663021}
|
||||
- {fileID: 1568526664}
|
||||
- {fileID: 2143540275}
|
||||
- {fileID: 292487263}
|
||||
|
@ -53,6 +53,8 @@ public class BodyLinkHandler : MonoBehaviour, IPointerClickHandler, IPointerExit
|
||||
string arabicLog = $"تم الضغط على الرابط '{linkID}' في البريد من '{email.senderName}'";
|
||||
|
||||
UserActionLogger.Instance?.Log(englishLog, arabicLog);
|
||||
bool isOptimal = !email.isPhishing;
|
||||
SupabaseEventLogger.Instance?.LogDecisionEvent(isOptimal);
|
||||
|
||||
}
|
||||
else
|
||||
|
@ -54,6 +54,7 @@ public class CharacterMovement : MonoBehaviour
|
||||
{
|
||||
animator.SetTrigger("StartWalking");
|
||||
isStarted = true;
|
||||
SupabaseEventLogger.Instance?.StartSession();
|
||||
InstructionManager.Instance?.ShowScreenInstruction("mission_intro");
|
||||
}
|
||||
void Update()
|
||||
|
@ -129,6 +129,14 @@ public class EmailOpenPanel : MonoBehaviour
|
||||
SceneOutcomeManager.Instance.Ignored(emailData);
|
||||
break;
|
||||
}
|
||||
|
||||
SupabaseEventLogger.Instance?.CompleteSessionAndSubmitResult(
|
||||
userId: "user123", // replace with real user ID if available
|
||||
passed: isCorrect,
|
||||
optimal: isCorrect ? 1 : 0,
|
||||
suboptimal: isCorrect ? 0 : 1,
|
||||
scenarioId: "scene_1"
|
||||
);
|
||||
}
|
||||
|
||||
void LocalizeTMP(TextMeshProUGUI tmp, string english, string arabic)
|
||||
|
@ -61,6 +61,9 @@ public class MiniQuizManager : MonoBehaviour
|
||||
|
||||
// Prepend a ✅ to the selected label
|
||||
answerLabels[selectedIndex].text = "✅ " + answerLabels[selectedIndex].text;
|
||||
|
||||
bool isCorrect = (selectedIndex == correctIndex);
|
||||
SupabaseEventLogger.Instance?.LogDecisionEvent(isCorrect);
|
||||
}
|
||||
}
|
||||
|
||||
|
180
Assets/SupabaseEventLogger.cs
Normal file
180
Assets/SupabaseEventLogger.cs
Normal file
@ -0,0 +1,180 @@
|
||||
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; }
|
||||
}
|
11
Assets/SupabaseEventLogger.cs.meta
Normal file
11
Assets/SupabaseEventLogger.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50b27839ecef84443a10112beb860a7a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
31
Assets/SupabaseManager.cs
Normal file
31
Assets/SupabaseManager.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using Supabase;
|
||||
using UnityEngine;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class SupabaseManager : MonoBehaviour
|
||||
{
|
||||
public static Supabase.Client Client => Supabase.Client.Instance;
|
||||
|
||||
[Header("Supabase Settings")]
|
||||
public string supabaseUrl = "https://vihjspljbslozbjzxutl.supabase.co";
|
||||
public string supabaseKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZpaGpzcGxqYnNsb3pianp4dXRsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDk1NDc4OTMsImV4cCI6MjA2NTEyMzg5M30.IYPvShgu5j3NnE5PHn-aFLCBJl1QQaVQvAjzxFt8tlA";
|
||||
|
||||
private async void Awake()
|
||||
{
|
||||
if (Supabase.Client.Instance != null)
|
||||
{
|
||||
Debug.Log("✅ Supabase already initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
var options = new SupabaseOptions
|
||||
{
|
||||
AutoConnectRealtime = false,
|
||||
ShouldInitializeRealtime = false
|
||||
};
|
||||
|
||||
await Supabase.Client.InitializeAsync(supabaseUrl, supabaseKey, options);
|
||||
|
||||
Debug.Log("✅ Supabase Initialized via static method");
|
||||
}
|
||||
}
|
11
Assets/SupabaseManager.cs.meta
Normal file
11
Assets/SupabaseManager.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef01b27a4a6f3724a981d178a963c567
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
39
Assets/SupabaseTestInsert.cs
Normal file
39
Assets/SupabaseTestInsert.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using UnityEngine;
|
||||
using Supabase;
|
||||
using System;
|
||||
using Postgrest.Models;
|
||||
using Postgrest.Attributes;
|
||||
|
||||
public class SupabaseTestInsert : MonoBehaviour
|
||||
{
|
||||
async void Start()
|
||||
{
|
||||
var gameEvent = new GameEvent
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
EventKey = "manual_test_event",
|
||||
Timestamp = DateTime.UtcNow,
|
||||
UserId = "test_user_1"
|
||||
};
|
||||
|
||||
Debug.Log("🔍 Trying test insert...");
|
||||
await Client.Instance.From<GameEvent>().Insert(gameEvent);
|
||||
Debug.Log("✅ Test insert complete");
|
||||
}
|
||||
|
||||
[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; }
|
||||
}
|
||||
}
|
11
Assets/SupabaseTestInsert.cs.meta
Normal file
11
Assets/SupabaseTestInsert.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 619706b2e2fd4fe4eb36567a686c7da0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
x
Reference in New Issue
Block a user