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, `
|
||||
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 {
|
||||
|
||||
@@ -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 (
|
||||
"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"`
|
||||
|
||||
+21
-5
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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,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
|
||||
}
|
||||
@@ -21,6 +21,8 @@ export interface LogResult extends Log {
|
||||
task: Task
|
||||
item: Item
|
||||
secondaryItem?: Item
|
||||
itemCounted?: boolean
|
||||
secondaryItemCounted?: boolean
|
||||
}
|
||||
|
||||
export interface LogInput {
|
||||
|
||||
Reference in New Issue
Block a user