Skip to content

feat: change names of groups from Priority Field and change the sort of order groups same as task sorting order #1966

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

Closed
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d7fc0dc
test: document current group sorting of PriorityField
ilandikov May 20, 2023
1acce88
refactor: move localeCompare to groupSorter
ilandikov May 20, 2023
1e7e92c
refactor: move groupSorter to Field
ilandikov May 20, 2023
edca28f
refactor: rename sorter to comparator
ilandikov May 20, 2023
3f1cb45
refactor: move groupComparator() to the bottom
ilandikov May 20, 2023
5e827e4
refactor: custom groupComparator() in PriorityField
ilandikov May 20, 2023
e9355f2
refactor: add Priority.toNumber()
ilandikov May 20, 2023
d0a078f
feat: make Priority group names shorter
ilandikov May 20, 2023
090c195
refactor: rename types
ilandikov May 20, 2023
f61c819
fix: toNumber() default priority is 3
ilandikov May 20, 2023
1d29d1d
refactor: remove temp variable
ilandikov May 20, 2023
bf597ea
refactor: shorter import type
ilandikov May 21, 2023
fefd7b2
test: fix test description
ilandikov May 21, 2023
3180630
refactor: compare first tasks instead of group names
ilandikov May 21, 2023
11c12be
docs: update group names in docs
ilandikov May 22, 2023
b7f9f20
test: test priority group sorting
ilandikov May 22, 2023
67a5baf
docs: document updated urgency grouping behaviour
ilandikov May 22, 2023
65d132c
refactor: add DescriptionLengthGroupingfield for testing
ilandikov May 22, 2023
8c2adc3
fix: add defalt group comparator if Field does not support sorting
ilandikov May 22, 2023
75e6cf7
test: test default group comparator
ilandikov May 22, 2023
a5c389c
test: fix date to fix urgency calculation
ilandikov May 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/Queries/Grouping.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ For more information, including adding your own customised statuses, see [[Statu

1. `priority`
- The priority of the task, namely one of:
- `Priority 1: High`
- `Priority 2: Medium`
- `Priority 3: None`
- `Priority 4: Low`
- `High`
- `Medium`
- `None`
- `Low`
1. `urgency` ([[Urgency|urgency]])
- Currently, the groups run from the lowest urgency to highest.
- You can reverse this with `group by urgency reverse`.
Expand Down
2 changes: 1 addition & 1 deletion src/Query/Filter/Field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ export abstract class Field {
* @param reverse - false for normal group order, true for reverse group order.
*/
public createGrouper(reverse: boolean): Grouper {
return new Grouper(this.fieldNameSingular(), this.grouper(), reverse);
return new Grouper(this.fieldNameSingular(), this.grouper(), reverse, this.comparator());
Copy link
Collaborator

Choose a reason for hiding this comment

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

I thing this will change (improve) the sort order for urgency grouping.

Which will just require confirmation and then a change the docs of group by urgency please.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I checked the docs of urgency here and here but didn't find it related to the changes in this PR (Priority group names...).

Do you mean that now sorting of groups from urgency field can use this code? I agree with that, but I guess this should be a different PR.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It’s this text:

  • Currently, the groups run from the lowest urgency to highest.
  • You can reverse this with group by urgency reverse.
  • In a future release, the default group order will become from the highest urgency to lowest.

I am reasonably confident that this PR has done point 3 and the above text should be updated.

Copy link
Collaborator

Choose a reason for hiding this comment

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

didn't find it related to the changes in this PR (Priority group names...).

To be more specific, this PR does several things:

  1. change the naming of groups for the Priority field
  2. change the sort of order of grouping for all fields to sort by task sorting order instead of by group name alphabetical order

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

You are right! I wrote a test and tried it before and after all the changes and it does change the behaviour indeed. Test and docs.

Now I'm thinking that I should've probably split the PR in 2 - one for refactoring with addition of the comparator to the grouper and the other one to fix the Priority group names. May be a third for the Urgency =)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Lmk if you wish to have a separate issue for the reversed urgency order =)

}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Query/Filter/MultiTextField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export abstract class MultiTextField extends TextField {
* This overloads {@link Field.createGrouper} to put a plural field name in the {@link Grouper.property}.
*/
public createGrouper(reverse: boolean): Grouper {
return new Grouper(this.fieldNamePlural(), this.grouper(), reverse);
return new Grouper(this.fieldNamePlural(), this.grouper(), reverse, this.comparator());
Copy link
Collaborator

@claremacrae claremacrae May 22, 2023

Choose a reason for hiding this comment

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

What happens with this code if the field does not implement sorting?

I expect that calling this.comparator() will then throw some kind of exception.

If you look at the Quick Reference table you will find a few such cases - such as recurrence or recurring (I forget which).

And certainly the new group by function will create a grouper that does not sort.

So... you could write a failing test using recurrence, but if it is taught to sort in future, that test will become useless.

So the testing trick here is to write a new test and have it use a simple implementation for Field that does grouping but not sorting. Perhaps DescriptionLengthGroupingfield. A field that is written purely to make testing easy and self-explanatory.

Then line 67 can be changed to only use this.comparator() if supportsSorting() is true, and if not, fall back on the old behaviour of sorting by group name.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What happens with this code if the field does not implement sorting?

I expect that calling this.comparator() will then throw some kind of exception.

Very good point, thanks.

So the testing trick here is to write a new test and have it use a simple implementation for Field that does grouping but not sorting. Perhaps DescriptionLengthGroupingfield. A field that is written purely to make testing easy and self-explanatory.

Yup, I did that now, this seems to be a good trade-off.

Then line 67 can be changed to only use this.comparator() if supportsSorting() is true, and if not, fall back on the old behaviour of sorting by group name.

On Field level I don't have access to the group names so I can't compare them in the fall back case. I'm not really sure how to solve this.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok, I think I got this, but not sure whether it is a good solution. Something smells here, can't tell what exactly. Probably the whole direction of the development ㅠㅠ

}

protected grouperRegExp(): RegExp {
Expand Down
16 changes: 6 additions & 10 deletions src/Query/Filter/PriorityField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,22 +87,18 @@ export class PriorityField extends Field {

public grouper(): GrouperFunction {
return (task: Task) => {
let priorityName = 'ERROR';
switch (task.priority) {
case Priority.High:
priorityName = 'High';
break;
return ['High'];
case Priority.Medium:
priorityName = 'Medium';
break;
return ['Medium'];
case Priority.None:
priorityName = 'None';
break;
return ['None'];
case Priority.Low:
priorityName = 'Low';
break;
return ['Low'];
default:
return ['ERROR'];
}
return [`Priority ${task.priority}: ${priorityName}`];
};
}
}
6 changes: 5 additions & 1 deletion src/Query/Grouper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Task } from '../Task';
import type { Comparator } from './Sorter';

/**
* A group-naming function, that takes a Task object and returns zero or more
Expand Down Expand Up @@ -36,9 +37,12 @@ export class Grouper {

public readonly reverse: boolean;

constructor(property: string, grouper: GrouperFunction, reverse: boolean) {
public readonly groupComparator: Comparator;

constructor(property: string, grouper: GrouperFunction, reverse: boolean, groupComparator: Comparator) {
this.property = property;
this.grouper = grouper;
this.reverse = reverse;
this.groupComparator = groupComparator;
}
}
14 changes: 3 additions & 11 deletions src/Query/TaskGroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,10 @@ export class TaskGroups {

private sortTaskGroups() {
const compareFn = (group1: TaskGroup, group2: TaskGroup) => {
// Compare two TaskGroup objects, sorting them by the group names at each grouping level.
const groupNames1 = group1.groups;
const groupNames2 = group2.groups;
// The containers are guaranteed to be identical sizes,
// they have one value for each 'group by' line in the query.
for (let i = 0; i < groupNames1.length; i++) {
// For now, we only have one sort option: sort by the names of the groups.
// In future, we will add control over the sorting of group headings,
// which will likely involve adjusting this code to sort by applying a Comparator
// to the first Task in each group.
// Compare two TaskGroup objects, sorting them by first task in each group.
for (let i = 0; i < this._groupers.length; i++) {
const grouper = this._groupers[i];
const result = groupNames1[i].localeCompare(groupNames2[i], undefined, { numeric: true });
const result = grouper.groupComparator(group1.tasks[0], group2.tasks[0]);
if (result !== 0) {
return grouper.reverse ? -result : result;
}
Expand Down
56 changes: 51 additions & 5 deletions tests/Query/Filter/PriorityField.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { TaskBuilder } from '../../TestingTools/TaskBuilder';
import { testFilter } from '../../TestingTools/FilterTestHelpers';
import { PriorityField } from '../../../src/Query/Filter/PriorityField';
import { fromLine } from '../../TestHelpers';

import { TaskGroups } from '../../../src/Query/TaskGroups';
import {
expectTaskComparesAfter,
expectTaskComparesBefore,
Expand Down Expand Up @@ -163,15 +163,61 @@ describe('grouping by priority', () => {
});

it.each([
['- [ ] a ⏫', ['Priority 1: High']],
['- [ ] a 🔼', ['Priority 2: Medium']],
['- [ ] a', ['Priority 3: None']],
['- [ ] a 🔽', ['Priority 4: Low']],
['- [ ] a ⏫', ['High']],
['- [ ] a 🔼', ['Medium']],
['- [ ] a', ['None']],
['- [ ] a 🔽', ['Low']],
])('task "%s" should have groups: %s', (taskLine: string, groups: string[]) => {
// Arrange
const grouper = new PriorityField().createNormalGrouper().grouper;

// Assert
expect(grouper(fromLine({ line: taskLine }))).toEqual(groups);
});

it('should sort groups according to priority meaning', () => {
// Arrange
const tasks = [
fromLine({ line: '- [ ] a 🔽' }),
fromLine({ line: '- [ ] a ⏫' }),
fromLine({ line: '- [ ] a' }),
fromLine({ line: '- [ ] a 🔼' }),
];

const grouper = [new PriorityField().createNormalGrouper()];
const groups = new TaskGroups(grouper, tasks);

// Assert
expect(groups.toString()).toMatchInlineSnapshot(`
"Groupers (if any):
- priority

Group names: [High]
#### [priority] High
- [ ] a ⏫

---

Group names: [Medium]
#### [priority] Medium
- [ ] a 🔼

---

Group names: [None]
#### [priority] None
- [ ] a

---

Group names: [Low]
#### [priority] Low
- [ ] a 🔽

---

4 tasks
"
`);
});
});