/
DemienMedich
/
BodyweightBase
Обзор
Документация
Войти
/
DemienMedich
/
BodyweightBase
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
core/src/state_importer.cpp
217 строк
9 KB
DemienMedich
Add Nastya profile course and polish mobile UI
13 июл 2026, 22:49
13 июл 2026, 22:49
e5a49c4
Код
Авторство
О чём код?
#include "bodyweight/state_importer.h" #include <algorithm> #include <QFile> #include <QJsonArray> #include <QJsonDocument> #include <QJsonObject> #include <QJsonParseError> #include <QSet> namespace bodyweight { namespace { std::optional<int> optionalInt(const QJsonObject &object, const char *name) { const QJsonValue value = object.value(QLatin1String(name)); if (!value.isDouble()) { return std::nullopt; } return value.toInt(); } WorkoutStep parseStep(const QJsonObject &object) { WorkoutStep step; step.exerciseId = object.value("ExerciseId").toString(); step.targetOverride = optionalInt(object, "TargetOverride"); step.restSecondsOverride = optionalInt(object, "RestSecondsOverride"); step.coachNote = object.value("CoachNote").toString(); step.sets = std::max(1, object.value("Sets").toInt(1)); step.isWarmup = object.value("IsWarmup").toBool(); step.isCooldown = object.value("IsCooldown").toBool(); return step; } WorkoutPlan parsePlan(const QJsonObject &object, QStringList &warnings) { WorkoutPlan plan; plan.id = object.value("Id").toString(); plan.profileId = object.value("ProfileId").toString(); plan.basePlanId = object.value("BasePlanId").toString(); plan.name = object.value("Name").toString(); plan.goal = object.value("Goal").toString(); plan.description = object.value("Description").toString(); plan.variantId = object.value("VariantId").toString("full"); plan.roundCount = object.value("RoundCount").toInt(1); plan.warmupIncluded = object.value("WarmupIncluded").toBool(); plan.warmupStepCount = object.value("WarmupStepCount").toInt(); plan.cooldownIncluded = object.value("CooldownIncluded").toBool(); plan.cooldownStepCount = object.value("CooldownStepCount").toInt(); plan.roundStepCount = object.value("RoundStepCount").toInt(); plan.recommendedRoundsText = object.value("RecommendedRoundsText").toString(); const QJsonValue stepsValue = object.value("Steps"); if (!stepsValue.isArray()) { warnings.append(QStringLiteral("Plan '%1' has no valid Steps array.").arg(plan.id)); return plan; } for (const QJsonValue &stepValue : stepsValue.toArray()) { if (!stepValue.isObject()) { warnings.append(QStringLiteral("Plan '%1' contains a non-object step.").arg(plan.id)); continue; } WorkoutStep step = parseStep(stepValue.toObject()); if (step.exerciseId.isEmpty()) { warnings.append(QStringLiteral("Plan '%1' contains a step without ExerciseId.").arg(plan.id)); } plan.steps.append(std::move(step)); } return plan; } } // namespace ImportResult importState(const QByteArray &json) { ImportResult result; QJsonParseError parseError; const QJsonDocument document = QJsonDocument::fromJson(json, &parseError); if (parseError.error != QJsonParseError::NoError) { result.error = QStringLiteral("Invalid JSON at offset %1: %2") .arg(parseError.offset) .arg(parseError.errorString()); return result; } if (!document.isObject()) { result.error = QStringLiteral("State root must be a JSON object."); return result; } const QJsonObject root = document.object(); result.state.libraryVersion = root.value("LibraryVersion").toString(); result.state.selectedPlanId = root.value("SelectedPlanId").toString(); result.state.selectedPlanVariantId = root.value("SelectedPlanVariantId").toString("full"); result.state.selectedRoundCount = root.value("SelectedRoundCount").toInt(1); result.state.currentBodyWeightKg = root.value("CurrentBodyWeightKg").toDouble(75.0); result.state.hasPendingSessionDraft = root.contains("PendingSessionDraft") && !root.value("PendingSessionDraft").isNull(); const QJsonValue sessions = root.value("SessionHistory"); if (sessions.isArray()) { result.state.sessionCount = sessions.toArray().size(); } else { result.warnings.append(QStringLiteral("SessionHistory is not an array.")); } const QJsonValue plansValue = root.value("WorkoutPlans"); if (!plansValue.isArray()) { result.error = QStringLiteral("WorkoutPlans must be an array."); return result; } QSet<QString> planIds; for (const QJsonValue &planValue : plansValue.toArray()) { if (!planValue.isObject()) { result.warnings.append(QStringLiteral("WorkoutPlans contains a non-object entry.")); continue; } WorkoutPlan plan = parsePlan(planValue.toObject(), result.warnings); if (plan.id.isEmpty()) { result.warnings.append(QStringLiteral("A workout plan has no Id.")); } else if (planIds.contains(plan.id.toLower())) { result.warnings.append(QStringLiteral("Duplicate workout plan Id: %1").arg(plan.id)); } else { planIds.insert(plan.id.toLower()); } result.state.workoutPlans.append(std::move(plan)); } if (!result.state.selectedPlanId.isEmpty() && !planIds.contains(result.state.selectedPlanId.toLower())) { result.warnings.append(QStringLiteral("SelectedPlanId does not reference an imported plan.")); } if (result.state.selectedRoundCount < 1 || result.state.selectedRoundCount > 3) { result.warnings.append(QStringLiteral("SelectedRoundCount is outside the supported 1..3 range.")); } result.ok = true; return result; } ImportResult importStateFile(const QString &path) { QFile file(path); if (!file.open(QIODevice::ReadOnly)) { ImportResult result; result.error = QStringLiteral("Could not open '%1': %2").arg(path, file.errorString()); return result; } return importState(file.readAll()); } ImportResult importNativeState(const QByteArray &json) { QJsonParseError parseError; const QJsonDocument document = QJsonDocument::fromJson(json, &parseError); ImportResult result; if (parseError.error != QJsonParseError::NoError || !document.isObject()) { result.error = QStringLiteral("Invalid native state."); return result; } const QJsonObject root = document.object(); if (root.value("schemaVersion").toInt() != 1 || !root.value("plans").isArray()) { result.error = QStringLiteral("Unsupported native state schema."); return result; } result.state.libraryVersion = root.value("libraryVersion").toString(); result.state.selectedPlanId = root.value("selectedPlanId").toString(); result.state.selectedPlanVariantId = root.value("selectedPlanVariantId").toString("full"); result.state.selectedRoundCount = root.value("selectedRoundCount").toInt(1); result.state.currentBodyWeightKg = root.value("currentBodyWeightKg").toDouble(75); result.state.sessionCount = root.value("sessions").toArray().size(); result.state.hasPendingSessionDraft = root.value("pendingDraft").isObject(); for (const QJsonValue &value : root.value("plans").toArray()) { if (!value.isObject()) continue; const QJsonObject object = value.toObject(); WorkoutPlan plan; plan.id = object.value("id").toString(); plan.profileId = object.value("profileId").toString(); plan.basePlanId = object.value("basePlanId").toString(); plan.name = object.value("name").toString(); plan.goal = object.value("goal").toString(); plan.description = object.value("description").toString(); plan.variantId = object.value("variantId").toString("full"); plan.roundCount = object.value("roundCount").toInt(1); plan.warmupIncluded = object.value("warmupIncluded").toBool(); plan.warmupStepCount = object.value("warmupStepCount").toInt(); plan.cooldownIncluded = object.value("cooldownIncluded").toBool(); plan.cooldownStepCount = object.value("cooldownStepCount").toInt(); plan.dayOfWeek = object.value("dayOfWeek").toInt(0); plan.roundStepCount = object.value("roundStepCount").toInt(); plan.recommendedRoundsText = object.value("recommendedRoundsText").toString(); for (const QJsonValue &stepValue : object.value("steps").toArray()) { if (!stepValue.isObject()) continue; const QJsonObject stepObject = stepValue.toObject(); WorkoutStep step; step.exerciseId = stepObject.value("exerciseId").toString(); step.coachNote = stepObject.value("coachNote").toString(); if (stepObject.value("targetOverride").isDouble()) step.targetOverride = stepObject.value("targetOverride").toInt(); if (stepObject.value("restSecondsOverride").isDouble()) step.restSecondsOverride = stepObject.value("restSecondsOverride").toInt(); step.sets = std::max(1, stepObject.value("sets").toInt(1)); step.isWarmup = stepObject.value("isWarmup").toBool(); step.isCooldown = stepObject.value("isCooldown").toBool(); plan.steps.append(std::move(step)); } result.state.workoutPlans.append(std::move(plan)); } result.ok = true; return result; } } // namespace bodyweight