Browse Source

add task and item filters to goals.

main
Gisle Aune 3 years ago
parent
commit
a187e91c3c
  1. 8
      database/postgres/goals.go
  2. 13
      migrations/postgres/20210119211214_add_goal_columns_filters.sql
  3. 46
      models/goal.go
  4. 26
      services/loader.go
  5. 19
      svelte-ui/src/components/Composition.svelte
  6. 14
      svelte-ui/src/forms/GoalForm.svelte
  7. 15
      svelte-ui/src/models/goal.ts
  8. 2
      svelte-ui/src/models/log.ts

8
database/postgres/goals.go

@ -74,10 +74,10 @@ func (r *goalRepository) Insert(ctx context.Context, goal models.Goal) error {
_, err := r.db.NamedExecContext(ctx, `
INSERT INTO goal (
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 (
: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)
if err != nil {
@ -97,7 +97,9 @@ func (r *goalRepository) Update(ctx context.Context, goal models.Goal) error {
description=:description,
composition_mode=:composition_mode,
unweighted=:unweighted,
item_id=:item_id
item_id=:item_id,
task_filter=:task_filter,
item_filter=:item_filter
WHERE goal_id=:goal_id
`, &goal)
if err != nil {

13
migrations/postgres/20210119211214_add_goal_columns_filters.sql

@ -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

46
models/goal.go

@ -2,6 +2,7 @@ package models
import (
"context"
"strings"
"time"
)
@ -17,6 +18,25 @@ type Goal struct {
Name string `json:"name" db:"name"`
Description string `json:"description" db:"description"`
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) {
@ -47,6 +67,18 @@ func (goal *Goal) Update(update GoalUpdate) {
if update.ClearItemID {
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 {
@ -56,19 +88,29 @@ type GoalUpdate struct {
Name *string `json:"name"`
Description *string `json:"description"`
ItemID *string `json:"itemId"`
ClearItemID bool `json:"clearItemID"`
Unweighted *bool `json:"unweighted"`
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 {
Goal
Group *Group `json:"group"`
Items []*GoalResultItem `json:"items"`
Logs []*LogResult `json:"logs"`
Logs []*GoalResultLog `json:"logs"`
CompletedAmount int `json:"completedAmount"`
}
type GoalResultLog struct {
LogResult
ItemCounted bool `json:"itemCounted"`
SecondaryItemCounted bool `json:"secondaryItemCounted"`
}
type GoalResultItem struct {
Item
CompletedAmount int `json:"completedAmount"`

26
services/loader.go

@ -535,11 +535,12 @@ func (l *Loader) populateGoals(ctx context.Context, goal *models.Goal) (*models.
}
// Apply logs
result.Logs = make([]*models.LogResult, 0, len(logs))
result.Logs = make([]*models.GoalResultLog, 0, len(logs))
for _, log := range logs {
resultLog := &models.LogResult{
Log: *log,
resultLog := &models.GoalResultLog{
LogResult: models.LogResult{Log: *log},
}
contributes := false
for _, task := range tasks {
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 {
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
if goal.Unweighted {
@ -558,21 +559,36 @@ func (l *Loader) populateGoals(ctx context.Context, goal *models.Goal) (*models.
} else {
result.CompletedAmount += amount * item.GroupWeight
}
contributes = true
} else {
amount = 0
}
if item.ID == log.ItemID {
resultLog.Item = &item.Item
if amount > 0 {
resultLog.ItemCounted = true
}
if log.SecondaryItemID == nil {
break
}
}
if log.SecondaryItemID != nil && item.ID == *log.SecondaryItemID {
if amount > 0 {
resultLog.SecondaryItemCounted = true
}
resultLog.SecondaryItem = &item.Item
}
}
result.Logs = append(result.Logs, resultLog)
if contributes {
result.Logs = append(result.Logs, resultLog)
}
}
}

19
svelte-ui/src/components/Composition.svelte

@ -1,7 +1,6 @@
<script lang="ts">
import type { GoalCompositionMode } from "../models/goal";
import type { GoalCompositionMode, GoalLogResult } from "../models/goal";
import type { LogResult } from "../models/log";
import type { TaskResult } from "../models/task";
interface CompositionItem {
link: string
@ -19,14 +18,16 @@ import type { TaskResult } from "../models/task";
if (mode === "item") {
for (const log of logs) {
const item = log.item;
if (!map[item.id]) {
map[item.id] = {name: item.name, amount: log.itemAmount, link: `/items#${item.id}`};
} else {
map[item.id].amount += log.itemAmount;
if (log.itemCounted !== false) {
const item = log.item;
if (!map[item.id]) {
map[item.id] = {name: item.name, amount: log.itemAmount, link: `/items#${item.id}`};
} else {
map[item.id].amount += log.itemAmount;
}
}
if (log.secondaryItem) {
if (log.secondaryItem && log.secondaryItemCounted !== false) {
const item = log.secondaryItem;
if (!map[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>

14
svelte-ui/src/forms/GoalForm.svelte

@ -49,7 +49,9 @@ import type { GroupResult } from "../models/group";
let compositionMode = goal.compositionMode;
let startTime = formatFormTime(goal.startTime);
let endTime = formatFormTime(goal.endTime);
let taskFilter = goal.taskFilter || "";
let itemFilter = goal.itemFilter || "";
let error = null;
let loading = false;
let selectedGroup: GroupResult = null;
@ -62,6 +64,8 @@ import type { GroupResult } from "../models/group";
startTime: new Date(startTime),
endTime: new Date(endTime),
itemId: itemId || null,
taskFilter: taskFilter.toLowerCase() || null,
itemFilter: itemFilter.toLowerCase() || null,
groupId, name, description, amount, unweighted, compositionMode
}).then(() => {
markStale("goal");
@ -86,6 +90,10 @@ import type { GroupResult } from "../models/group";
endTime: new Date(endTime),
itemId: itemId || null,
clearItemId: itemId === "",
taskFilter: taskFilter.toLowerCase() || null,
clearTaskFilter: taskFilter === "",
itemFilter: itemFilter.toLowerCase() || null,
clearItemFilter: itemFilter === "",
name, description, amount, compositionMode, unweighted,
}).then(() => {
markStale("goal");
@ -128,6 +136,10 @@ import type { GroupResult } from "../models/group";
<input disabled={deletion} name="startTime" type="datetime-local" bind:value={startTime} />
<label for="endTime">End Time</label>
<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)" />
<hr />

15
svelte-ui/src/models/goal.ts

@ -15,6 +15,8 @@ export default interface Goal {
description: string
unweighted: boolean
compositionMode: GoalCompositionMode
taskFilter?: string
itemFilter?: string
}
export interface GoalFilter {
@ -26,10 +28,15 @@ export interface GoalFilter {
export interface GoalResult extends Goal {
group: Group
items: GoalResultItem[]
logs: LogResult[]
logs: GoalLogResult[]
completedAmount: number
}
export interface GoalLogResult extends LogResult {
itemCounted: boolean
secondaryItemCounted: boolean
}
interface GoalResultItem extends Item {
completedAmount: number
}
@ -44,6 +51,8 @@ export interface GoalInput {
description: string
unweighted: boolean
compositionMode: string
taskFilter?: string
itemFilter?: string
}
export interface GoalUpdate {
@ -56,4 +65,8 @@ export interface GoalUpdate {
unweighted?: boolean
compositionMode?: string
clearItemId?: boolean
taskFilter?: string
clearTaskFilter: boolean
itemFilter?: string
clearItemFilter: boolean
}

2
svelte-ui/src/models/log.ts

@ -21,6 +21,8 @@ export interface LogResult extends Log {
task: Task
item: Item
secondaryItem?: Item
itemCounted?: boolean
secondaryItemCounted?: boolean
}
export interface LogInput {

Loading…
Cancel
Save