Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Shared completion for Group Dailies #11179

Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a023eb3
Add single shared completion logic for dailies
MeanderingCode May 18, 2019
88a4013
Show shared completion option in taskModal for group dailies
MeanderingCode May 18, 2019
aec9efb
Add uncompletion logic for single shared completion todos and dailies
MeanderingCode May 18, 2019
8307285
Fix task list filter for due and notDue to check task.completed
MeanderingCode May 18, 2019
b16ad71
Improve use of awaiting Promises and forEach in syncing group task co…
MeanderingCode May 20, 2019
61114e7
Comments and TODOs for Group Task work
MeanderingCode May 24, 2019
1d2ac37
Linting
MeanderingCode May 26, 2019
2527604
Single Completion Shared Tasks complete or delete for other users aft…
MeanderingCode May 27, 2019
c90b988
Fix counting bug in shared completion all approval required tasks
MeanderingCode May 27, 2019
abd136c
Separate syncTask and linkTask methods on Group schema to use syncTas…
MeanderingCode May 28, 2019
550f400
Add mongo document version key to omitAttrs for Task syncableAttrs to…
MeanderingCode May 28, 2019
503181a
Clean up structure for async calls, await/Promise
MeanderingCode May 28, 2019
468ebd7
Update Group Tasks test to differentiate linkTask() and syncTask()
MeanderingCode May 28, 2019
5aaa5c4
Add tests for syncing group shared completion todos and dailies
MeanderingCode May 29, 2019
5166f80
Use mongoose updateMany() for syncing group shared dailies completion…
MeanderingCode May 29, 2019
59c0f6a
linting
MeanderingCode May 29, 2019
2cbffc8
Add filter for task column "all" label to return all tasks
MeanderingCode May 29, 2019
4f35c2d
Shared Dailies: Update completion for users in the "same day", record…
MeanderingCode May 31, 2019
71fdc88
API: Add optional "yesterdaily" segment to the end of task scoring en…
MeanderingCode Jun 9, 2019
c240c80
Mark completion of assigned users shared dailies based on timezone an…
MeanderingCode Jun 9, 2019
ee9fc52
Fix bad timezone and CDS math
MeanderingCode Jun 9, 2019
4b978e8
Shared Daily Master Task history management improvement
MeanderingCode Jun 9, 2019
c601f55
Complete shared dailies for assigned user only after cron
MeanderingCode Jun 9, 2019
d910e39
Dissalow unchecking single completion shared dailies completed by ano…
MeanderingCode Jun 9, 2019
bb1f494
Use cron day start functions for checks in groupTasks
MeanderingCode Jun 9, 2019
ee598e8
Better async calling of groupTaskCompleted in cron dailies iteration
MeanderingCode Jun 10, 2019
34c2704
linting
MeanderingCode Jun 10, 2019
3b5e630
Proper import of transpiled common scripts and add missed startOfDay …
MeanderingCode Jun 12, 2019
e35f83b
Update cron tests to reflect the function changing to async
MeanderingCode Jun 12, 2019
051207f
Update group tasks sync test to use scoreTask for required behaviors
MeanderingCode Jun 12, 2019
e0977e3
Improve logic for checking history removal on daily score down
MeanderingCode Jun 12, 2019
bf87d66
Comments clarifying yesterDaily scoring from RYA
MeanderingCode Jul 9, 2019
335d44d
fix(group-tasks): fix server TypeError in _updateAssignedUsersTasks
SabreCat Aug 16, 2019
46b07f2
Merge branch 'develop' into feature/group-dailies-shared-completion
SabreCat Sep 26, 2019
14d68e0
feat(group-tasks): uncomplete master task if needed at any member cron
SabreCat Sep 26, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions website/client/components/tasks/column.vue
Expand Up @@ -609,8 +609,8 @@ export default {
if (!taskList) return [];
return taskList.filter(task => {
if (filter === 'complete2') return task.completed;
if (filter === 'due') return task.isDue;
if (filter === 'notDue') return !task.isDue;
if (filter === 'due') return !task.completed && task.isDue;
if (filter === 'notDue') return task.completed || !task.isDue;
return !task.completed;
});
},
Expand Down
2 changes: 1 addition & 1 deletion website/client/components/tasks/taskModal.vue
Expand Up @@ -159,7 +159,7 @@
| {{ $t(frequency) }}

.option.group-options(v-if='groupId')
.form-group(v-if="task.type === 'todo'")
.form-group(v-if="task.type === 'todo' || task.type === 'daily'")
label(v-once) {{ $t('sharedCompletion') }}
b-dropdown.inline-dropdown(:text="$t(sharedCompletion)")
b-dropdown-item(
Expand Down
74 changes: 58 additions & 16 deletions website/server/libs/groupTasks.js
@@ -1,24 +1,68 @@
import * as Tasks from '../models/task';
import {model as Groups} from '../models/group';
import {model as Users} from '../models/user/index';

const SHARED_COMPLETION = {
default: 'recurringCompletion',
single: 'singleCompletion',
every: 'allAssignedCompletion',
};

async function _completeMasterTask (masterTask) {
masterTask.completed = true;
async function _completeOrUncompleteMasterTask (masterTask, completed) {
masterTask.completed = completed;
await masterTask.save();
}

async function _deleteUnfinishedTasks (groupMemberTask) {
await Tasks.Task.deleteMany({
'group.taskId': groupMemberTask.group.taskId,
$and: [
{userId: {$exists: true}},
{userId: {$ne: groupMemberTask.userId}},
],
}).exec();
async function _updateAssignedUsersTasks (masterTask, groupMemberTask) {
if (groupMemberTask.type == 'todo') {
if (groupMemberTask.completed) {
// The task was done by one person and is removed from others' lists
await Tasks.Task.deleteMany({
'group.taskId': groupMemberTask.group.taskId,
$and: [
{userId: {$exists: true}},
{userId: {$ne: groupMemberTask.userId}},
],
}).exec();
} else {
// The task was uncompleted by the group member and should be recreated for assignedUsers
let group = await Groups.findById(masterTask.group.id).exec();
let userList = [];
masterTask.group.assignedUsers.forEach(userId => {
let query = {_id: userId};
userList.push(query);
});
await Users.find({
$or: userList
}).then(async assignedUsers => {
for (const assignedUser of assignedUsers) {
MeanderingCode marked this conversation as resolved.
Show resolved Hide resolved
let promises = [];
promises.push(group.syncTask(masterTask, assignedUser));
promises.push(group.save());
await Promise.all(promises);
MeanderingCode marked this conversation as resolved.
Show resolved Hide resolved
}
});
}
} else {
// Complete or uncomplete the task on other users' lists
await Tasks.Task.find({
'group.taskId': groupMemberTask.group.taskId,
$and: [
{userId: {$exists: true}},
{userId: {$ne: groupMemberTask.userId}}
]}).then(tasks => {
MeanderingCode marked this conversation as resolved.
Show resolved Hide resolved
MeanderingCode marked this conversation as resolved.
Show resolved Hide resolved
tasks.forEach (task => {
// Ajdust the task's completion to match the groupMemberTask
// @REVIEW Completed or notDue tasks have no effect at cron
// This maintain's the user's streak without scoring the task if someone else completed the task
// If no assignedUser completes the due daily, all users lose their streaks at their cron
// An alternative is to set the other assignedUsers' tasks to a later startDate
// Should we break their streaks to encourage competition for the daily?
task.completed = groupMemberTask.completed;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main point of feature discussion.
I based my implimentation (complete other users' dailies) on cron: https://github.com/HabitRPG/habitica/blob/develop/website/server/libs/cron.js#L310

The implications of completing the task are that:

  • The user who completes the Daily will score the task
  • Other users will see the Daily is completed, will not score the task, will not lose their streak or take damage
  • Other users will have their MP replenishment math include the done daily
  • Other users "uncompleting" the daily has a bug in scoring (see [WIP] Shared completion for Group Dailies #11179 (comment) and following)

More discussion on how the feature should work may be found in #10690

task.save();
MeanderingCode marked this conversation as resolved.
Show resolved Hide resolved
});
});
}
}

async function _evaluateAllAssignedCompletion (masterTask) {
Expand All @@ -35,21 +79,19 @@ async function _evaluateAllAssignedCompletion (masterTask) {
completed: true,
}).exec();
}
if (completions >= masterTask.group.assignedUsers.length) {
await _completeMasterTask(masterTask);
}
await _completeOrUncompleteMasterTask(masterTask, (completions >= masterTask.group.assignedUsers.length));
}

async function handleSharedCompletion (groupMemberTask) {
let masterTask = await Tasks.Task.findOne({
_id: groupMemberTask.group.taskId,
}).exec();

if (!masterTask || !masterTask.group || masterTask.type !== 'todo') return;
if (!masterTask || !masterTask.group || masterTask.type == 'habit') return;

if (masterTask.group.sharedCompletion === SHARED_COMPLETION.single) {
await _deleteUnfinishedTasks(groupMemberTask);
await _completeMasterTask(masterTask);
await _updateAssignedUsersTasks(masterTask, groupMemberTask);
await _completeOrUncompleteMasterTask(masterTask, groupMemberTask.completed);
} else if (masterTask.group.sharedCompletion === SHARED_COMPLETION.every) {
await _evaluateAllAssignedCompletion(masterTask);
}
Expand Down