2025-12-13 14:48:00 +00:00
|
|
|
|
import 'package:drift/drift.dart';
|
2025-12-13 23:35:14 +00:00
|
|
|
|
import 'package:drift_postgres/drift_postgres.dart';
|
2025-12-13 14:48:00 +00:00
|
|
|
|
import 'package:mnemo_cards_backend/cron/task.dart' as cron_task;
|
|
|
|
|
|
import 'package:mnemo_cards_backend/database/database.dart';
|
2025-12-13 23:35:14 +00:00
|
|
|
|
import 'package:mnemo_cards_common/mnemo_cards_common.dart';
|
2025-11-16 11:25:27 +00:00
|
|
|
|
import 'package:mnemo_cards_common_backend/mnemo_cards_common_backend.dart';
|
|
|
|
|
|
|
2025-12-13 14:48:00 +00:00
|
|
|
|
class TasksSeederTask with cron_task.Task {
|
|
|
|
|
|
final AppDatabase _db;
|
|
|
|
|
|
|
|
|
|
|
|
TasksSeederTask(this._db);
|
2025-11-16 11:25:27 +00:00
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
String get name => 'tasks_seeder';
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
Future<void> task() async {
|
|
|
|
|
|
print('Seeding initial user tasks...');
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
final now = DateTime.now();
|
|
|
|
|
|
final weekFromNow = now.add(const Duration(days: 7));
|
|
|
|
|
|
|
2025-12-13 23:35:14 +00:00
|
|
|
|
// Используем предопределенные UUID для каждой задачи
|
|
|
|
|
|
// Это позволяет избежать дубликатов при повторных запусках
|
|
|
|
|
|
final tasksWithIds = [
|
|
|
|
|
|
(
|
|
|
|
|
|
id: '00000000-0000-0000-0001-000000000001',
|
|
|
|
|
|
task: UserTaskModel(
|
|
|
|
|
|
title: 'Пройди 3 теста сегодня',
|
2025-12-20 18:26:15 +00:00
|
|
|
|
description:
|
|
|
|
|
|
'Заверши три тестовые сессии в приложении для улучшения навыков испанского.',
|
|
|
|
|
|
type: 'app_internal',
|
|
|
|
|
|
difficulty: 'easy',
|
|
|
|
|
|
rewards: [
|
|
|
|
|
|
TaskRewardModel(type: 'xp', amount: 50),
|
|
|
|
|
|
TaskRewardModel(type: 'coins', amount: 10),
|
|
|
|
|
|
],
|
|
|
|
|
|
status: 'available',
|
|
|
|
|
|
createdAt: now,
|
|
|
|
|
|
expiresAt: weekFromNow,
|
|
|
|
|
|
tags: ['tests', 'practice', 'daily'],
|
|
|
|
|
|
),
|
2025-12-13 23:35:14 +00:00
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
id: '00000000-0000-0000-0001-000000000002',
|
|
|
|
|
|
task: UserTaskModel(
|
|
|
|
|
|
title: 'Достигни 80% правильных ответов в тесте',
|
2025-12-20 18:26:15 +00:00
|
|
|
|
description:
|
|
|
|
|
|
'Пройди тест с результатом не менее 80% правильных ответов.',
|
|
|
|
|
|
type: 'app_internal',
|
|
|
|
|
|
difficulty: 'medium',
|
|
|
|
|
|
rewards: [
|
|
|
|
|
|
TaskRewardModel(type: 'xp', amount: 100),
|
|
|
|
|
|
TaskRewardModel(type: 'coins', amount: 25),
|
|
|
|
|
|
],
|
|
|
|
|
|
status: 'available',
|
|
|
|
|
|
createdAt: now,
|
|
|
|
|
|
expiresAt: weekFromNow,
|
|
|
|
|
|
tags: ['tests', 'accuracy', 'challenge'],
|
|
|
|
|
|
),
|
2025-12-13 23:35:14 +00:00
|
|
|
|
),
|
2025-11-16 11:25:27 +00:00
|
|
|
|
// External Tasks
|
2025-12-13 23:35:14 +00:00
|
|
|
|
(
|
|
|
|
|
|
id: '00000000-0000-0000-0001-000000000003',
|
|
|
|
|
|
task: UserTaskModel(
|
|
|
|
|
|
title: 'Подпишись на Telegram канал',
|
2025-12-20 18:26:15 +00:00
|
|
|
|
description:
|
|
|
|
|
|
'Подпишись на наш Telegram канал @mnemo_cards для получения обновлений и советов по изучению испанского.',
|
|
|
|
|
|
type: 'external',
|
|
|
|
|
|
difficulty: 'easy',
|
|
|
|
|
|
rewards: [
|
|
|
|
|
|
TaskRewardModel(type: 'xp', amount: 30),
|
|
|
|
|
|
TaskRewardModel(type: 'coins', amount: 5),
|
|
|
|
|
|
],
|
|
|
|
|
|
status: 'available',
|
|
|
|
|
|
createdAt: now,
|
|
|
|
|
|
expiresAt: weekFromNow,
|
|
|
|
|
|
tags: ['telegram', 'social', 'subscription'],
|
|
|
|
|
|
),
|
2025-12-13 23:35:14 +00:00
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
id: '00000000-0000-0000-0001-000000000004',
|
|
|
|
|
|
task: UserTaskModel(
|
|
|
|
|
|
title: 'Закажи еду на испанском',
|
2025-12-20 18:26:15 +00:00
|
|
|
|
description:
|
|
|
|
|
|
'Сделай заказ в ресторане или кафе, используя испанский язык. Запиши процесс на видео для подтверждения.',
|
|
|
|
|
|
type: 'external',
|
|
|
|
|
|
difficulty: 'hard',
|
|
|
|
|
|
rewards: [
|
|
|
|
|
|
TaskRewardModel(type: 'xp', amount: 200),
|
|
|
|
|
|
TaskRewardModel(type: 'coins', amount: 50),
|
|
|
|
|
|
TaskRewardModel(
|
2025-11-16 12:15:21 +00:00
|
|
|
|
type: 'achievement',
|
|
|
|
|
|
amount: 0,
|
2025-12-20 18:26:15 +00:00
|
|
|
|
achievementId: 'real_world_practice',
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
status: 'available',
|
|
|
|
|
|
createdAt: now,
|
|
|
|
|
|
expiresAt: weekFromNow.add(const Duration(days: 14)), // 3 weeks
|
|
|
|
|
|
instructions:
|
|
|
|
|
|
'Запиши видео, где ты делаешь заказ на испанском. Убедись, что официант понимает тебя.',
|
|
|
|
|
|
tags: ['speaking', 'restaurant', 'video', 'real_world'],
|
|
|
|
|
|
),
|
2025-12-13 23:35:14 +00:00
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
id: '00000000-0000-0000-0001-000000000005',
|
|
|
|
|
|
task: UserTaskModel(
|
|
|
|
|
|
title: 'Посмотри испанский фильм с субтитрами',
|
2025-12-20 18:26:15 +00:00
|
|
|
|
description:
|
|
|
|
|
|
'Посмотри испанский фильм или сериал с испанскими субтитрами в течение 30 минут.',
|
|
|
|
|
|
type: 'external',
|
|
|
|
|
|
difficulty: 'medium',
|
|
|
|
|
|
rewards: [
|
|
|
|
|
|
TaskRewardModel(type: 'xp', amount: 75),
|
|
|
|
|
|
TaskRewardModel(type: 'coins', amount: 15),
|
|
|
|
|
|
],
|
|
|
|
|
|
status: 'available',
|
|
|
|
|
|
createdAt: now,
|
|
|
|
|
|
expiresAt: weekFromNow,
|
|
|
|
|
|
tags: ['listening', 'media', 'subtitles'],
|
|
|
|
|
|
),
|
2025-12-13 23:35:14 +00:00
|
|
|
|
),
|
2025-11-16 11:25:27 +00:00
|
|
|
|
// Social Tasks
|
2025-12-13 23:35:14 +00:00
|
|
|
|
(
|
|
|
|
|
|
id: '00000000-0000-0000-0001-000000000006',
|
|
|
|
|
|
task: UserTaskModel(
|
|
|
|
|
|
title: 'Поделись прогрессом в соцсетях',
|
2025-12-20 18:26:15 +00:00
|
|
|
|
description:
|
|
|
|
|
|
'Поделись своим прогрессом в изучении испанского в социальных сетях с хэштегом #MnemoCards.',
|
|
|
|
|
|
type: 'social',
|
|
|
|
|
|
difficulty: 'easy',
|
|
|
|
|
|
rewards: [
|
|
|
|
|
|
TaskRewardModel(type: 'xp', amount: 40),
|
|
|
|
|
|
TaskRewardModel(type: 'coins', amount: 10),
|
|
|
|
|
|
],
|
|
|
|
|
|
status: 'available',
|
|
|
|
|
|
createdAt: now,
|
|
|
|
|
|
expiresAt: weekFromNow,
|
|
|
|
|
|
tags: ['social', 'sharing', 'progress'],
|
|
|
|
|
|
),
|
2025-12-13 23:35:14 +00:00
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
id: '00000000-0000-0000-0001-000000000007',
|
|
|
|
|
|
task: UserTaskModel(
|
|
|
|
|
|
title: 'Найди партнёра для практики',
|
2025-12-20 18:26:15 +00:00
|
|
|
|
description:
|
|
|
|
|
|
'Найди собеседника для практики испанского языка (друга, коллегу или через языковые приложения).',
|
|
|
|
|
|
type: 'social',
|
|
|
|
|
|
difficulty: 'medium',
|
|
|
|
|
|
rewards: [
|
|
|
|
|
|
TaskRewardModel(type: 'xp', amount: 90),
|
|
|
|
|
|
TaskRewardModel(type: 'coins', amount: 20),
|
|
|
|
|
|
],
|
|
|
|
|
|
status: 'available',
|
|
|
|
|
|
createdAt: now,
|
|
|
|
|
|
expiresAt: weekFromNow.add(const Duration(days: 7)), // 2 weeks
|
|
|
|
|
|
tags: ['conversation', 'partner', 'practice'],
|
|
|
|
|
|
),
|
2025-12-13 23:35:14 +00:00
|
|
|
|
),
|
2025-11-16 11:25:27 +00:00
|
|
|
|
// Additional App Tasks
|
2025-12-13 23:35:14 +00:00
|
|
|
|
(
|
|
|
|
|
|
id: '00000000-0000-0000-0001-000000000008',
|
|
|
|
|
|
task: UserTaskModel(
|
|
|
|
|
|
title: 'Изучай слова ежедневно 7 дней подряд',
|
2025-12-20 18:26:15 +00:00
|
|
|
|
description:
|
|
|
|
|
|
'Учи новые слова в приложении каждый день в течение недели.',
|
|
|
|
|
|
type: 'app_internal',
|
|
|
|
|
|
difficulty: 'hard',
|
|
|
|
|
|
rewards: [
|
|
|
|
|
|
TaskRewardModel(type: 'xp', amount: 150),
|
|
|
|
|
|
TaskRewardModel(type: 'coins', amount: 35),
|
|
|
|
|
|
TaskRewardModel(
|
2025-11-16 12:15:21 +00:00
|
|
|
|
type: 'achievement',
|
|
|
|
|
|
amount: 0,
|
2025-12-20 18:26:15 +00:00
|
|
|
|
achievementId: 'consistency_master',
|
|
|
|
|
|
),
|
|
|
|
|
|
],
|
|
|
|
|
|
status: 'available',
|
|
|
|
|
|
createdAt: now,
|
|
|
|
|
|
expiresAt: weekFromNow.add(const Duration(days: 21)), // 4 weeks
|
|
|
|
|
|
tags: ['vocabulary', 'daily', 'streak', 'consistency'],
|
|
|
|
|
|
),
|
2025-12-13 23:35:14 +00:00
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
id: '00000000-0000-0000-0001-000000000009',
|
|
|
|
|
|
task: UserTaskModel(
|
|
|
|
|
|
title: 'Пройди тест по грамматике',
|
2025-12-20 18:26:15 +00:00
|
|
|
|
description:
|
|
|
|
|
|
'Заверши специализированный тест по грамматике испанского языка.',
|
|
|
|
|
|
type: 'app_internal',
|
|
|
|
|
|
difficulty: 'medium',
|
|
|
|
|
|
rewards: [
|
|
|
|
|
|
TaskRewardModel(type: 'xp', amount: 80),
|
|
|
|
|
|
TaskRewardModel(type: 'coins', amount: 20),
|
|
|
|
|
|
],
|
|
|
|
|
|
status: 'available',
|
|
|
|
|
|
createdAt: now,
|
|
|
|
|
|
expiresAt: weekFromNow,
|
|
|
|
|
|
tags: ['grammar', 'tests', 'knowledge'],
|
|
|
|
|
|
),
|
2025-12-13 23:35:14 +00:00
|
|
|
|
),
|
|
|
|
|
|
(
|
|
|
|
|
|
id: '00000000-0000-0000-0001-000000000010',
|
|
|
|
|
|
task: UserTaskModel(
|
|
|
|
|
|
title: 'Практикуй произношение',
|
2025-12-20 18:26:15 +00:00
|
|
|
|
description:
|
|
|
|
|
|
'Заверши урок по произношению или запиши себя, читая текст на испанском.',
|
|
|
|
|
|
type: 'app_internal',
|
|
|
|
|
|
difficulty: 'easy',
|
|
|
|
|
|
rewards: [
|
|
|
|
|
|
TaskRewardModel(type: 'xp', amount: 60),
|
|
|
|
|
|
TaskRewardModel(type: 'coins', amount: 12),
|
|
|
|
|
|
],
|
|
|
|
|
|
status: 'available',
|
|
|
|
|
|
createdAt: now,
|
|
|
|
|
|
expiresAt: weekFromNow,
|
|
|
|
|
|
tags: ['pronunciation', 'speaking', 'practice'],
|
|
|
|
|
|
),
|
2025-12-13 23:35:14 +00:00
|
|
|
|
),
|
2025-11-16 11:25:27 +00:00
|
|
|
|
];
|
|
|
|
|
|
|
2025-12-14 00:13:49 +00:00
|
|
|
|
// Convert UserTaskModel to UserTasksCompanion and insert через DAO
|
|
|
|
|
|
// Используем предопределенные UUID для идемпотентности seeder'а
|
|
|
|
|
|
int insertedCount = 0;
|
|
|
|
|
|
int skippedCount = 0;
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-13 23:35:14 +00:00
|
|
|
|
for (final taskData in tasksWithIds) {
|
|
|
|
|
|
final taskModel = taskData.task;
|
2025-12-13 14:48:00 +00:00
|
|
|
|
final rewardsJson = taskModel.rewards.map((r) => r.toJson()).toList();
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-14 00:13:49 +00:00
|
|
|
|
final companion = UserTasksCompanion.insert(
|
2025-12-20 18:26:15 +00:00
|
|
|
|
id: Value(
|
|
|
|
|
|
taskData.id,
|
|
|
|
|
|
), // Предопределенный UUID для идемпотентности seeder'а
|
2025-12-14 00:13:49 +00:00
|
|
|
|
title: taskModel.title,
|
|
|
|
|
|
description: taskModel.description,
|
|
|
|
|
|
type: taskModel.type,
|
|
|
|
|
|
difficulty: taskModel.difficulty,
|
|
|
|
|
|
status: taskModel.status,
|
|
|
|
|
|
rewards: Value(rewardsJson),
|
|
|
|
|
|
createdAt: Value(PgDateTime(taskModel.createdAt)),
|
|
|
|
|
|
expiresAt: PgDateTime(taskModel.expiresAt),
|
|
|
|
|
|
completedAt: Value(taskModel.completedAt?.let(PgDateTime.new)),
|
|
|
|
|
|
proofUrl: Value(taskModel.proofUrl),
|
|
|
|
|
|
instructions: Value(taskModel.instructions),
|
|
|
|
|
|
tags: Value(taskModel.tags),
|
|
|
|
|
|
imageUrl: Value(taskModel.imageUrl),
|
2025-12-13 14:48:00 +00:00
|
|
|
|
);
|
2025-12-20 18:26:15 +00:00
|
|
|
|
|
2025-12-14 00:13:49 +00:00
|
|
|
|
final wasCreated = await _db.taskDao.createUserTaskOrIgnore(companion);
|
|
|
|
|
|
if (wasCreated) {
|
|
|
|
|
|
insertedCount++;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
skippedCount++;
|
|
|
|
|
|
}
|
2025-12-13 14:48:00 +00:00
|
|
|
|
}
|
2025-11-16 11:25:27 +00:00
|
|
|
|
|
2025-12-20 18:26:15 +00:00
|
|
|
|
print(
|
|
|
|
|
|
'Successfully seeded user tasks: $insertedCount inserted, $skippedCount skipped (already exist)',
|
|
|
|
|
|
);
|
2025-11-16 11:25:27 +00:00
|
|
|
|
} catch (e) {
|
|
|
|
|
|
print('Error seeding tasks: $e');
|
|
|
|
|
|
rethrow;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
|
int get intervalSeconds => 60 * 60 * 24; // Run once per day
|
|
|
|
|
|
}
|