286 lines
9.1 KiB
C#
286 lines
9.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using TH1_Logic.Tools;
|
|
using UnityEngine;
|
|
|
|
namespace TH1_Logic.Questionnaire
|
|
{
|
|
[Serializable]
|
|
public class QuestionnaireAnswerArchive
|
|
{
|
|
public string Schema = "th1.questionnaire-answer-archive.v2";
|
|
public int SchemaVersion = 2;
|
|
public List<QuestionnaireAnswerSheet> Submissions = new List<QuestionnaireAnswerSheet>();
|
|
|
|
[Obsolete("Use Submissions. Kept for reading v1 local archives.")]
|
|
public List<QuestionnaireAnswerSheet> Sheets = new List<QuestionnaireAnswerSheet>();
|
|
}
|
|
|
|
[Serializable]
|
|
public class QuestionnaireAnswerSheet
|
|
{
|
|
public string ResponseId;
|
|
public string QuestionnaireId;
|
|
public int QuestionnaireRevision = 1;
|
|
public long SubmittedAtUnix;
|
|
public string SubmittedAtUtc;
|
|
public string LastUploadObjectKey;
|
|
public string LastUploadedAtUtc;
|
|
public string LastUploadError;
|
|
public string ClientVersion;
|
|
public QuestionnaireDefinitionSnapshot DefinitionSnapshot;
|
|
public List<QuestionnaireAnswer> Answers = new List<QuestionnaireAnswer>();
|
|
}
|
|
|
|
[Serializable]
|
|
public class QuestionnaireAnswer
|
|
{
|
|
public string QuestionId;
|
|
public QuestionnaireQuestionType QuestionType;
|
|
public List<string> SelectedOptionIds = new List<string>();
|
|
public string OpenText;
|
|
}
|
|
|
|
[Serializable]
|
|
public class QuestionnaireDefinitionSnapshot
|
|
{
|
|
public string QuestionnaireId;
|
|
public int Revision;
|
|
public string Title;
|
|
public string Description;
|
|
public List<QuestionnaireQuestionSnapshot> Questions = new List<QuestionnaireQuestionSnapshot>();
|
|
}
|
|
|
|
[Serializable]
|
|
public class QuestionnaireQuestionSnapshot
|
|
{
|
|
public string QuestionId;
|
|
public QuestionnaireQuestionType QuestionType;
|
|
public string Title;
|
|
public string Hint;
|
|
public bool Required;
|
|
public List<QuestionnaireOptionSnapshot> Options = new List<QuestionnaireOptionSnapshot>();
|
|
}
|
|
|
|
[Serializable]
|
|
public class QuestionnaireOptionSnapshot
|
|
{
|
|
public string OptionId;
|
|
public string Text;
|
|
}
|
|
|
|
public class QuestionnaireAnswerStore
|
|
{
|
|
public static readonly QuestionnaireAnswerStore Instance = new QuestionnaireAnswerStore();
|
|
|
|
private const string FileName = "questionnaire_answers.json";
|
|
private QuestionnaireAnswerArchive _archive;
|
|
|
|
public bool HasSubmitted(string questionnaireId)
|
|
{
|
|
return GetAnswerSheet(questionnaireId) != null;
|
|
}
|
|
|
|
public QuestionnaireAnswerSheet GetAnswerSheet(string questionnaireId)
|
|
{
|
|
return GetLatestAnswerSheet(questionnaireId);
|
|
}
|
|
|
|
public QuestionnaireAnswerSheet GetLatestAnswerSheet(string questionnaireId)
|
|
{
|
|
if (string.IsNullOrEmpty(questionnaireId)) return null;
|
|
|
|
EnsureLoaded();
|
|
QuestionnaireAnswerSheet latest = null;
|
|
foreach (var sheet in _archive.Submissions)
|
|
{
|
|
if (sheet == null || sheet.QuestionnaireId != questionnaireId) continue;
|
|
if (latest == null || sheet.SubmittedAtUnix > latest.SubmittedAtUnix)
|
|
{
|
|
latest = sheet;
|
|
}
|
|
}
|
|
|
|
return latest;
|
|
}
|
|
|
|
public List<QuestionnaireAnswerSheet> GetAnswerSheets(string questionnaireId)
|
|
{
|
|
EnsureLoaded();
|
|
var result = new List<QuestionnaireAnswerSheet>();
|
|
if (string.IsNullOrEmpty(questionnaireId)) return result;
|
|
|
|
foreach (var sheet in _archive.Submissions)
|
|
{
|
|
if (sheet != null && sheet.QuestionnaireId == questionnaireId)
|
|
{
|
|
result.Add(sheet);
|
|
}
|
|
}
|
|
|
|
result.Sort((left, right) => right.SubmittedAtUnix.CompareTo(left.SubmittedAtUnix));
|
|
return result;
|
|
}
|
|
|
|
public IReadOnlyList<QuestionnaireAnswerSheet> GetAllAnswerSheets()
|
|
{
|
|
EnsureLoaded();
|
|
return _archive.Submissions;
|
|
}
|
|
|
|
public bool SaveAnswerSheet(QuestionnaireAnswerSheet sheet)
|
|
{
|
|
if (sheet == null || string.IsNullOrEmpty(sheet.QuestionnaireId)) return false;
|
|
|
|
EnsureLoaded();
|
|
var now = DateTimeOffset.UtcNow;
|
|
sheet.SubmittedAtUnix = now.ToUnixTimeSeconds();
|
|
sheet.SubmittedAtUtc = now.UtcDateTime.ToString("o");
|
|
if (string.IsNullOrEmpty(sheet.ResponseId))
|
|
{
|
|
sheet.ResponseId = Guid.NewGuid().ToString("N");
|
|
}
|
|
|
|
var index = _archive.Submissions.FindIndex(item => item != null && item.ResponseId == sheet.ResponseId);
|
|
if (index >= 0)
|
|
{
|
|
_archive.Submissions[index] = sheet;
|
|
}
|
|
else
|
|
{
|
|
_archive.Submissions.Add(sheet);
|
|
}
|
|
|
|
return SaveArchive();
|
|
}
|
|
|
|
public bool SaveUploadResult(string questionnaireId, bool uploaded, string objectKey, string error)
|
|
{
|
|
var sheet = GetLatestAnswerSheet(questionnaireId);
|
|
return sheet != null && SaveUploadResultByResponseId(sheet.ResponseId, uploaded, objectKey, error);
|
|
}
|
|
|
|
public bool SaveUploadResultByResponseId(string responseId, bool uploaded, string objectKey, string error)
|
|
{
|
|
if (string.IsNullOrEmpty(responseId)) return false;
|
|
|
|
EnsureLoaded();
|
|
QuestionnaireAnswerSheet sheet = null;
|
|
foreach (var item in _archive.Submissions)
|
|
{
|
|
if (item == null || item.ResponseId != responseId) continue;
|
|
sheet = item;
|
|
break;
|
|
}
|
|
|
|
if (sheet == null) return false;
|
|
|
|
if (uploaded)
|
|
{
|
|
sheet.LastUploadObjectKey = objectKey ?? string.Empty;
|
|
sheet.LastUploadedAtUtc = DateTime.UtcNow.ToString("o");
|
|
sheet.LastUploadError = string.Empty;
|
|
}
|
|
else
|
|
{
|
|
sheet.LastUploadError = error ?? string.Empty;
|
|
}
|
|
|
|
return SaveArchive();
|
|
}
|
|
|
|
private void EnsureLoaded()
|
|
{
|
|
if (_archive != null) return;
|
|
|
|
_archive = new QuestionnaireAnswerArchive();
|
|
var path = GetArchivePath();
|
|
if (!File.Exists(path)) return;
|
|
|
|
try
|
|
{
|
|
var json = File.ReadAllText(path, Encoding.UTF8);
|
|
var loaded = JsonUtility.FromJson<QuestionnaireAnswerArchive>(json);
|
|
if (loaded != null)
|
|
{
|
|
_archive = loaded;
|
|
NormalizeArchive();
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"[QuestionnaireAnswerStore] Failed to read {path}: {e.Message}");
|
|
}
|
|
}
|
|
|
|
private void NormalizeArchive()
|
|
{
|
|
_archive.Schema = "th1.questionnaire-answer-archive.v2";
|
|
_archive.SchemaVersion = 2;
|
|
if (_archive.Submissions == null)
|
|
{
|
|
_archive.Submissions = new List<QuestionnaireAnswerSheet>();
|
|
}
|
|
|
|
#pragma warning disable CS0618
|
|
if ((_archive.Submissions.Count == 0) && _archive.Sheets != null && _archive.Sheets.Count > 0)
|
|
{
|
|
_archive.Submissions.AddRange(_archive.Sheets);
|
|
}
|
|
|
|
_archive.Sheets = new List<QuestionnaireAnswerSheet>();
|
|
#pragma warning restore CS0618
|
|
|
|
foreach (var sheet in _archive.Submissions)
|
|
{
|
|
if (sheet == null) continue;
|
|
if (string.IsNullOrEmpty(sheet.ResponseId))
|
|
{
|
|
sheet.ResponseId = Guid.NewGuid().ToString("N");
|
|
}
|
|
|
|
if (sheet.QuestionnaireRevision <= 0)
|
|
{
|
|
sheet.QuestionnaireRevision = 1;
|
|
}
|
|
|
|
if (sheet.Answers == null)
|
|
{
|
|
sheet.Answers = new List<QuestionnaireAnswer>();
|
|
}
|
|
|
|
foreach (var answer in sheet.Answers)
|
|
{
|
|
if (answer != null && answer.SelectedOptionIds == null)
|
|
{
|
|
answer.SelectedOptionIds = new List<string>();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool SaveArchive()
|
|
{
|
|
try
|
|
{
|
|
NormalizeArchive();
|
|
var json = JsonUtility.ToJson(_archive, true);
|
|
var bytes = Encoding.UTF8.GetBytes(json);
|
|
return FileTools.SafeWriteFile(GetArchivePath(), bytes);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogError($"[QuestionnaireAnswerStore] Failed to save answers: {e.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static string GetArchivePath()
|
|
{
|
|
return Path.Combine(Application.persistentDataPath, "../Config", FileName);
|
|
}
|
|
}
|
|
}
|