This commit is contained in:
@@ -74,10 +74,10 @@ func (r *goalRepository) Insert(ctx context.Context, goal models.Goal) error {
|
|||||||
_, err := r.db.NamedExecContext(ctx, `
|
_, err := r.db.NamedExecContext(ctx, `
|
||||||
INSERT INTO goal (
|
INSERT INTO goal (
|
||||||
goal_id, user_id, group_id, amount, start_time, end_time, name, description,
|
goal_id, user_id, group_id, amount, start_time, end_time, name, description,
|
||||||
composition_mode, unweighted, item_id
|
composition_mode, unweighted, item_id, task_filter, item_filter
|
||||||
) VALUES (
|
) VALUES (
|
||||||
:goal_id, :user_id, :group_id, :amount, :start_time, :end_time, :name, :description,
|
:goal_id, :user_id, :group_id, :amount, :start_time, :end_time, :name, :description,
|
||||||
:composition_mode, :unweighted, :item_id
|
:composition_mode, :unweighted, :item_id, :task_filter, :item_filter
|
||||||
)
|
)
|
||||||
`, &goal)
|
`, &goal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -97,7 +97,9 @@ func (r *goalRepository) Update(ctx context.Context, goal models.Goal) error {
|
|||||||
description=:description,
|
description=:description,
|
||||||
composition_mode=:composition_mode,
|
composition_mode=:composition_mode,
|
||||||
unweighted=:unweighted,
|
unweighted=:unweighted,
|
||||||
item_id=:item_id
|
item_id=:item_id,
|
||||||
|
task_filter=:task_filter,
|
||||||
|
item_filter=:item_filter
|
||||||
WHERE goal_id=:goal_id
|
WHERE goal_id=:goal_id
|
||||||
`, &goal)
|
`, &goal)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
-- +goose Up
|
||||||
|
-- +goose StatementBegin
|
||||||
|
ALTER TABLE goal
|
||||||
|
ADD COLUMN task_filter TEXT DEFAULT NULL,
|
||||||
|
ADD COLUMN item_filter TEXT DEFAULT NULL;
|
||||||
|
-- +goose StatementEnd
|
||||||
|
|
||||||
|
-- +goose Down
|
||||||
|
-- +goose StatementBegin
|
||||||
|
ALTER TABLE goal
|
||||||
|
DROP COLUMN task_filter,
|
||||||
|
DROP COLUMN item_filter;
|
||||||
|
-- +goose StatementEnd
|
||||||
+44
-2
@@ -2,6 +2,7 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,6 +18,25 @@ type Goal struct {
|
|||||||
Name string `json:"name" db:"name"`
|
Name string `json:"name" db:"name"`
|
||||||
Description string `json:"description" db:"description"`
|
Description string `json:"description" db:"description"`
|
||||||
CompositionMode string `json:"compositionMode" db:"composition_mode"`
|
CompositionMode string `json:"compositionMode" db:"composition_mode"`
|
||||||
|
TaskFilter *string `json:"taskFilter" db:"task_filter"`
|
||||||
|
ItemFilter *string `json:"itemFilter" db:"item_filter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (goal *Goal) Accepts(item *Item, task *Task) bool {
|
||||||
|
if item.GroupID != goal.GroupID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if goal.ItemID != nil && item.ID != *goal.ItemID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if goal.TaskFilter != nil && !strings.Contains(strings.ToLower(task.Name), *goal.TaskFilter) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if goal.ItemFilter != nil && !strings.Contains(strings.ToLower(item.Name), *goal.ItemFilter) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (goal *Goal) Update(update GoalUpdate) {
|
func (goal *Goal) Update(update GoalUpdate) {
|
||||||
@@ -47,6 +67,18 @@ func (goal *Goal) Update(update GoalUpdate) {
|
|||||||
if update.ClearItemID {
|
if update.ClearItemID {
|
||||||
goal.ItemID = nil
|
goal.ItemID = nil
|
||||||
}
|
}
|
||||||
|
if update.TaskFilter != nil {
|
||||||
|
goal.TaskFilter = update.TaskFilter
|
||||||
|
}
|
||||||
|
if update.ClearTaskFilter {
|
||||||
|
goal.TaskFilter = nil
|
||||||
|
}
|
||||||
|
if update.ItemFilter != nil {
|
||||||
|
goal.ItemFilter = update.ItemFilter
|
||||||
|
}
|
||||||
|
if update.ClearItemFilter {
|
||||||
|
goal.ItemFilter = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type GoalUpdate struct {
|
type GoalUpdate struct {
|
||||||
@@ -56,19 +88,29 @@ type GoalUpdate struct {
|
|||||||
Name *string `json:"name"`
|
Name *string `json:"name"`
|
||||||
Description *string `json:"description"`
|
Description *string `json:"description"`
|
||||||
ItemID *string `json:"itemId"`
|
ItemID *string `json:"itemId"`
|
||||||
|
ClearItemID bool `json:"clearItemID"`
|
||||||
Unweighted *bool `json:"unweighted"`
|
Unweighted *bool `json:"unweighted"`
|
||||||
CompositionMode *string `json:"compositionMode"`
|
CompositionMode *string `json:"compositionMode"`
|
||||||
ClearItemID bool `json:"clearItemID"`
|
TaskFilter *string `json:"taskFilter"`
|
||||||
|
ClearTaskFilter bool `json:"clearTaskFilter"`
|
||||||
|
ItemFilter *string `json:"itemFilter"`
|
||||||
|
ClearItemFilter bool `json:"clearItemFilter"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GoalResult struct {
|
type GoalResult struct {
|
||||||
Goal
|
Goal
|
||||||
Group *Group `json:"group"`
|
Group *Group `json:"group"`
|
||||||
Items []*GoalResultItem `json:"items"`
|
Items []*GoalResultItem `json:"items"`
|
||||||
Logs []*LogResult `json:"logs"`
|
Logs []*GoalResultLog `json:"logs"`
|
||||||
CompletedAmount int `json:"completedAmount"`
|
CompletedAmount int `json:"completedAmount"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GoalResultLog struct {
|
||||||
|
LogResult
|
||||||
|
ItemCounted bool `json:"itemCounted"`
|
||||||
|
SecondaryItemCounted bool `json:"secondaryItemCounted"`
|
||||||
|
}
|
||||||
|
|
||||||
type GoalResultItem struct {
|
type GoalResultItem struct {
|
||||||
Item
|
Item
|
||||||
CompletedAmount int `json:"completedAmount"`
|
CompletedAmount int `json:"completedAmount"`
|
||||||
|
|||||||
+21
-5
@@ -535,11 +535,12 @@ func (l *Loader) populateGoals(ctx context.Context, goal *models.Goal) (*models.
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Apply logs
|
// Apply logs
|
||||||
result.Logs = make([]*models.LogResult, 0, len(logs))
|
result.Logs = make([]*models.GoalResultLog, 0, len(logs))
|
||||||
for _, log := range logs {
|
for _, log := range logs {
|
||||||
resultLog := &models.LogResult{
|
resultLog := &models.GoalResultLog{
|
||||||
Log: *log,
|
LogResult: models.LogResult{Log: *log},
|
||||||
}
|
}
|
||||||
|
contributes := false
|
||||||
|
|
||||||
for _, task := range tasks {
|
for _, task := range tasks {
|
||||||
if task.ID == log.TaskID {
|
if task.ID == log.TaskID {
|
||||||
@@ -550,7 +551,7 @@ func (l *Loader) populateGoals(ctx context.Context, goal *models.Goal) (*models.
|
|||||||
|
|
||||||
for _, item := range result.Items {
|
for _, item := range result.Items {
|
||||||
amount := log.Amount(item.ID)
|
amount := log.Amount(item.ID)
|
||||||
if amount > 0 && (goal.ItemID == nil || *goal.ItemID == item.ID) {
|
if amount > 0 && goal.Accepts(&item.Item, resultLog.Task) {
|
||||||
item.CompletedAmount += amount
|
item.CompletedAmount += amount
|
||||||
|
|
||||||
if goal.Unweighted {
|
if goal.Unweighted {
|
||||||
@@ -558,21 +559,36 @@ func (l *Loader) populateGoals(ctx context.Context, goal *models.Goal) (*models.
|
|||||||
} else {
|
} else {
|
||||||
result.CompletedAmount += amount * item.GroupWeight
|
result.CompletedAmount += amount * item.GroupWeight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contributes = true
|
||||||
|
} else {
|
||||||
|
amount = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.ID == log.ItemID {
|
if item.ID == log.ItemID {
|
||||||
resultLog.Item = &item.Item
|
resultLog.Item = &item.Item
|
||||||
|
|
||||||
|
if amount > 0 {
|
||||||
|
resultLog.ItemCounted = true
|
||||||
|
}
|
||||||
|
|
||||||
if log.SecondaryItemID == nil {
|
if log.SecondaryItemID == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if log.SecondaryItemID != nil && item.ID == *log.SecondaryItemID {
|
if log.SecondaryItemID != nil && item.ID == *log.SecondaryItemID {
|
||||||
|
if amount > 0 {
|
||||||
|
resultLog.SecondaryItemCounted = true
|
||||||
|
}
|
||||||
|
|
||||||
resultLog.SecondaryItem = &item.Item
|
resultLog.SecondaryItem = &item.Item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Logs = append(result.Logs, resultLog)
|
if contributes {
|
||||||
|
result.Logs = append(result.Logs, resultLog)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { GoalCompositionMode } from "../models/goal";
|
import type { GoalCompositionMode, GoalLogResult } from "../models/goal";
|
||||||
import type { LogResult } from "../models/log";
|
import type { LogResult } from "../models/log";
|
||||||
import type { TaskResult } from "../models/task";
|
|
||||||
|
|
||||||
interface CompositionItem {
|
interface CompositionItem {
|
||||||
link: string
|
link: string
|
||||||
@@ -19,14 +18,16 @@ import type { TaskResult } from "../models/task";
|
|||||||
|
|
||||||
if (mode === "item") {
|
if (mode === "item") {
|
||||||
for (const log of logs) {
|
for (const log of logs) {
|
||||||
const item = log.item;
|
if (log.itemCounted !== false) {
|
||||||
if (!map[item.id]) {
|
const item = log.item;
|
||||||
map[item.id] = {name: item.name, amount: log.itemAmount, link: `/items#${item.id}`};
|
if (!map[item.id]) {
|
||||||
} else {
|
map[item.id] = {name: item.name, amount: log.itemAmount, link: `/items#${item.id}`};
|
||||||
map[item.id].amount += log.itemAmount;
|
} else {
|
||||||
|
map[item.id].amount += log.itemAmount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log.secondaryItem) {
|
if (log.secondaryItem && log.secondaryItemCounted !== false) {
|
||||||
const item = log.secondaryItem;
|
const item = log.secondaryItem;
|
||||||
if (!map[item.id]) {
|
if (!map[item.id]) {
|
||||||
map[item.id] = {name: item.name, amount: log.secondaryItemAmount, link: `/items#${item.id}`};
|
map[item.id] = {name: item.name, amount: log.secondaryItemAmount, link: `/items#${item.id}`};
|
||||||
@@ -49,7 +50,7 @@ import type { TaskResult } from "../models/task";
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list = Object.keys(map).sort((a, b) => map[b].amount - map[a].amount).map(k => map[k]);
|
list = Object.keys(map).sort((a, b) => map[b].amount - map[a].amount).map(k => map[k]).filter(e => e.amount > 0);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,9 @@ import type { GroupResult } from "../models/group";
|
|||||||
let compositionMode = goal.compositionMode;
|
let compositionMode = goal.compositionMode;
|
||||||
let startTime = formatFormTime(goal.startTime);
|
let startTime = formatFormTime(goal.startTime);
|
||||||
let endTime = formatFormTime(goal.endTime);
|
let endTime = formatFormTime(goal.endTime);
|
||||||
|
let taskFilter = goal.taskFilter || "";
|
||||||
|
let itemFilter = goal.itemFilter || "";
|
||||||
|
|
||||||
let error = null;
|
let error = null;
|
||||||
let loading = false;
|
let loading = false;
|
||||||
let selectedGroup: GroupResult = null;
|
let selectedGroup: GroupResult = null;
|
||||||
@@ -62,6 +64,8 @@ import type { GroupResult } from "../models/group";
|
|||||||
startTime: new Date(startTime),
|
startTime: new Date(startTime),
|
||||||
endTime: new Date(endTime),
|
endTime: new Date(endTime),
|
||||||
itemId: itemId || null,
|
itemId: itemId || null,
|
||||||
|
taskFilter: taskFilter.toLowerCase() || null,
|
||||||
|
itemFilter: itemFilter.toLowerCase() || null,
|
||||||
groupId, name, description, amount, unweighted, compositionMode
|
groupId, name, description, amount, unweighted, compositionMode
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
markStale("goal");
|
markStale("goal");
|
||||||
@@ -86,6 +90,10 @@ import type { GroupResult } from "../models/group";
|
|||||||
endTime: new Date(endTime),
|
endTime: new Date(endTime),
|
||||||
itemId: itemId || null,
|
itemId: itemId || null,
|
||||||
clearItemId: itemId === "",
|
clearItemId: itemId === "",
|
||||||
|
taskFilter: taskFilter.toLowerCase() || null,
|
||||||
|
clearTaskFilter: taskFilter === "",
|
||||||
|
itemFilter: itemFilter.toLowerCase() || null,
|
||||||
|
clearItemFilter: itemFilter === "",
|
||||||
name, description, amount, compositionMode, unweighted,
|
name, description, amount, compositionMode, unweighted,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
markStale("goal");
|
markStale("goal");
|
||||||
@@ -128,6 +136,10 @@ import type { GroupResult } from "../models/group";
|
|||||||
<input disabled={deletion} name="startTime" type="datetime-local" bind:value={startTime} />
|
<input disabled={deletion} name="startTime" type="datetime-local" bind:value={startTime} />
|
||||||
<label for="endTime">End Time</label>
|
<label for="endTime">End Time</label>
|
||||||
<input disabled={deletion} name="endTime" type="datetime-local" bind:value={endTime} />
|
<input disabled={deletion} name="endTime" type="datetime-local" bind:value={endTime} />
|
||||||
|
<label for="taskFilter">Task Filter (Optional)</label>
|
||||||
|
<input disabled={deletion} name="taskFilter" type="text" bind:value={taskFilter} />
|
||||||
|
<label for="itemFilter">Item Filter (Optional)</label>
|
||||||
|
<input disabled={deletion} name="itemFilter" type="text" bind:value={itemFilter} />
|
||||||
<Checkbox bind:checked={unweighted} label="Unweighted (All items count as 1)" />
|
<Checkbox bind:checked={unweighted} label="Unweighted (All items count as 1)" />
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ export default interface Goal {
|
|||||||
description: string
|
description: string
|
||||||
unweighted: boolean
|
unweighted: boolean
|
||||||
compositionMode: GoalCompositionMode
|
compositionMode: GoalCompositionMode
|
||||||
|
taskFilter?: string
|
||||||
|
itemFilter?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GoalFilter {
|
export interface GoalFilter {
|
||||||
@@ -26,10 +28,15 @@ export interface GoalFilter {
|
|||||||
export interface GoalResult extends Goal {
|
export interface GoalResult extends Goal {
|
||||||
group: Group
|
group: Group
|
||||||
items: GoalResultItem[]
|
items: GoalResultItem[]
|
||||||
logs: LogResult[]
|
logs: GoalLogResult[]
|
||||||
completedAmount: number
|
completedAmount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GoalLogResult extends LogResult {
|
||||||
|
itemCounted: boolean
|
||||||
|
secondaryItemCounted: boolean
|
||||||
|
}
|
||||||
|
|
||||||
interface GoalResultItem extends Item {
|
interface GoalResultItem extends Item {
|
||||||
completedAmount: number
|
completedAmount: number
|
||||||
}
|
}
|
||||||
@@ -44,6 +51,8 @@ export interface GoalInput {
|
|||||||
description: string
|
description: string
|
||||||
unweighted: boolean
|
unweighted: boolean
|
||||||
compositionMode: string
|
compositionMode: string
|
||||||
|
taskFilter?: string
|
||||||
|
itemFilter?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GoalUpdate {
|
export interface GoalUpdate {
|
||||||
@@ -56,4 +65,8 @@ export interface GoalUpdate {
|
|||||||
unweighted?: boolean
|
unweighted?: boolean
|
||||||
compositionMode?: string
|
compositionMode?: string
|
||||||
clearItemId?: boolean
|
clearItemId?: boolean
|
||||||
|
taskFilter?: string
|
||||||
|
clearTaskFilter: boolean
|
||||||
|
itemFilter?: string
|
||||||
|
clearItemFilter: boolean
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,8 @@ export interface LogResult extends Log {
|
|||||||
task: Task
|
task: Task
|
||||||
item: Item
|
item: Item
|
||||||
secondaryItem?: Item
|
secondaryItem?: Item
|
||||||
|
itemCounted?: boolean
|
||||||
|
secondaryItemCounted?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LogInput {
|
export interface LogInput {
|
||||||
|
|||||||
Reference in New Issue
Block a user