AP-45 #57
| @@ -361,10 +361,6 @@ func TestGetTaskWhenNoTasks(t *testing.T) { | ||||
| 	result.Length().IsEqual(0) | ||||
| } | ||||
|  | ||||
| func createTestTask(e *httpexpect.Expect) int { | ||||
| 	return createTestTaskWithAmount(e, 100) | ||||
| } | ||||
|  | ||||
| func createTestTaskWithAmount(e *httpexpect.Expect, amount int) int { | ||||
| 	requestBody := map[string]interface{}{ | ||||
| 		"name":   "Test Task", | ||||
| @@ -567,7 +563,8 @@ func TestCompleteTask(t *testing.T) { | ||||
|  | ||||
| 	// Verify the task is marked as completed | ||||
| 	e.GET("/task/" + strconv.Itoa(taskId)).Expect().Status(404) | ||||
| 	// Verify the allowances are updated | ||||
|  | ||||
| 	// Verify the allowances are updated for user 1 | ||||
| 	allowances := e.GET("/user/1/allowance").Expect().Status(200).JSON().Array() | ||||
| 	allowances.Length().IsEqual(3) | ||||
| 	allowances.Value(0).Object().Value("id").Number().IsEqual(0) | ||||
| @@ -576,6 +573,153 @@ func TestCompleteTask(t *testing.T) { | ||||
| 	allowances.Value(1).Object().Value("progress").Number().IsEqual(50) | ||||
| 	allowances.Value(2).Object().Value("id").Number().IsEqual(2) | ||||
| 	allowances.Value(2).Object().Value("progress").Number().IsEqual(25) | ||||
|  | ||||
| 	// And also for user 2 | ||||
| 	allowances = e.GET("/user/2/allowance").Expect().Status(200).JSON().Array() | ||||
| 	allowances.Length().IsEqual(1) | ||||
| 	allowances.Value(0).Object().Value("id").Number().IsEqual(0) | ||||
| 	allowances.Value(0).Object().Value("progress").Number().IsEqual(101) | ||||
|  | ||||
| 	for userId := 1; userId <= 2; userId++ { | ||||
| 		userIdStr := strconv.Itoa(userId) | ||||
| 		// Ensure the history got updated | ||||
| 		history := e.GET("/user/" + userIdStr + "/history").Expect().Status(200).JSON().Array() | ||||
| 		history.Length().IsEqual(1) | ||||
| 		history.Value(0).Object().Value("allowance").Number().IsEqual(101) | ||||
| 		history.Value(0).Object().Value("timestamp").String().AsDateTime().InRange(getDelta(time.Now(), 2.0)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestCompleteTaskAllowanceWeightsSumTo0(t *testing.T) { | ||||
| 	e := startServer(t) | ||||
| 	taskId := createTestTaskWithAmount(e, 101) | ||||
|  | ||||
| 	e.GET("/tasks").Expect().Status(200).JSON().Array().Length().IsEqual(1) | ||||
|  | ||||
| 	// Update rest allowance | ||||
| 	e.PUT("/user/1/allowance/0").WithJSON(UpdateAllowanceRequest{ | ||||
| 		Weight: 0, | ||||
| 	}).Expect().Status(200) | ||||
| 	// Create an allowance goal | ||||
| 	createTestAllowance(e, "Test Allowance 1", 1000, 0) | ||||
|  | ||||
| 	// Complete the task | ||||
| 	e.POST("/task/" + strconv.Itoa(taskId) + "/complete").Expect().Status(200) | ||||
|  | ||||
| 	// Verify the task is marked as completed | ||||
| 	e.GET("/task/" + strconv.Itoa(taskId)).Expect().Status(404) | ||||
|  | ||||
| 	// Verify the allowances are updated for user 1 | ||||
| 	allowances := e.GET("/user/1/allowance").Expect().Status(200).JSON().Array() | ||||
| 	allowances.Length().IsEqual(2) | ||||
| 	allowances.Value(0).Object().Value("id").Number().IsEqual(0) | ||||
| 	allowances.Value(0).Object().Value("progress").Number().IsEqual(101) | ||||
| 	allowances.Value(1).Object().Value("id").Number().IsEqual(1) | ||||
| 	allowances.Value(1).Object().Value("progress").Number().IsEqual(0) | ||||
| } | ||||
|  | ||||
| func TestCompleteTaskInvalidId(t *testing.T) { | ||||
| 	e := startServer(t) | ||||
| 	e.POST("/task/999/complete").Expect().Status(404) | ||||
| } | ||||
|  | ||||
| func TestCompleteTaskAllowanceWeightsSumTo0(t *testing.T) { | ||||
| 	e := startServer(t) | ||||
| 	taskId := createTestTaskWithAmount(e, 101) | ||||
|  | ||||
| 	e.GET("/tasks").Expect().Status(200).JSON().Array().Length().IsEqual(1) | ||||
|  | ||||
| 	// Update rest allowance | ||||
| 	e.PUT("/user/1/allowance/0").WithJSON(UpdateAllowanceRequest{ | ||||
| 		Weight: 0, | ||||
| 	}).Expect().Status(200) | ||||
| 	// Create two allowance goals | ||||
| 	e.POST("/user/1/allowance").WithJSON(CreateAllowanceRequest{ | ||||
| 		Name:   "Test Allowance 1", | ||||
| 		Target: 1000, | ||||
| 		Weight: 0, | ||||
| 	}).Expect().Status(201) | ||||
|  | ||||
| 	// Complete the task | ||||
| 	e.POST("/task/" + strconv.Itoa(taskId) + "/complete").Expect().Status(200) | ||||
|  | ||||
| 	// Verify the task is marked as completed | ||||
| 	e.GET("/task/" + strconv.Itoa(taskId)).Expect().Status(404) | ||||
|  | ||||
| 	// Verify the allowances are updated for user 1 | ||||
| 	allowances := e.GET("/user/1/allowance").Expect().Status(200).JSON().Array() | ||||
| 	allowances.Length().IsEqual(2) | ||||
| 	allowances.Value(0).Object().Value("id").Number().IsEqual(0) | ||||
| 	allowances.Value(0).Object().Value("progress").Number().IsEqual(101) | ||||
| 	allowances.Value(1).Object().Value("id").Number().IsEqual(1) | ||||
| 	allowances.Value(1).Object().Value("progress").Number().IsEqual(0) | ||||
| } | ||||
|  | ||||
| func TestCompleteAllowance(t *testing.T) { | ||||
| 	e := startServer(t) | ||||
| 	createTestTaskWithAmount(e, 100) | ||||
| 	createTestAllowance(e, "Test Allowance 1", 100, 50) | ||||
|  | ||||
| 	// Complete the task | ||||
| 	e.POST("/task/1/complete").Expect().Status(200) | ||||
|  | ||||
| 	// Complete allowance goal | ||||
| 	e.POST("/user/1/allowance/1/complete").Expect().Status(200) | ||||
|  | ||||
| 	// Verify the allowance no longer exists | ||||
| 	e.GET("/user/1/allowance/1").Expect().Status(404) | ||||
|  | ||||
| 	// Verify history is updated | ||||
| 	history := e.GET("/user/1/history").Expect().Status(200).JSON().Array() | ||||
| 	history.Length().IsEqual(2) | ||||
| 	history.Value(0).Object().Value("allowance").Number().IsEqual(100) | ||||
| 	history.Value(0).Object().Value("timestamp").String().AsDateTime().InRange(getDelta(time.Now(), 2.0)) | ||||
| 	history.Value(1).Object().Value("allowance").Number().IsEqual(-100) | ||||
| 	history.Value(1).Object().Value("timestamp").String().AsDateTime().InRange(getDelta(time.Now(), 2.0)) | ||||
| } | ||||
|  | ||||
| func TestCompleteAllowanceInvalidUserId(t *testing.T) { | ||||
| 	e := startServer(t) | ||||
| 	e.POST("/user/999/allowance/1/complete").Expect().Status(404) | ||||
| } | ||||
|  | ||||
| func TestCompleteAllowanceInvalidAllowanceId(t *testing.T) { | ||||
| 	e := startServer(t) | ||||
| 	e.POST("/user/1/allowance/999/complete").Expect().Status(404) | ||||
| } | ||||
|  | ||||
| func TestPutBulkAllowance(t *testing.T) { | ||||
| 	e := startServer(t) | ||||
|  | ||||
| 	createTestAllowance(e, "Test Allowance 1", 1000, 1) | ||||
| 	createTestAllowance(e, "Test Allowance 2", 1000, 2) | ||||
|  | ||||
| 	// Bulk edit | ||||
| 	request := []map[string]interface{}{ | ||||
| 		{ | ||||
| 			"id":     1, | ||||
| 			"weight": 5, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"id":     0, | ||||
| 			"weight": 99, | ||||
| 		}, | ||||
| 		{ | ||||
| 			"id":     2, | ||||
| 			"weight": 10, | ||||
| 		}, | ||||
| 	} | ||||
| 	e.PUT("/user/1/allowance").WithJSON(request).Expect().Status(200) | ||||
|  | ||||
| 	// Verify the allowances are updated | ||||
| 	allowances := e.GET("/user/1/allowance").Expect().Status(200).JSON().Array() | ||||
| 	allowances.Length().IsEqual(3) | ||||
| 	allowances.Value(0).Object().Value("id").Number().IsEqual(0) | ||||
| 	allowances.Value(0).Object().Value("weight").Number().IsEqual(99) | ||||
| 	allowances.Value(1).Object().Value("id").Number().IsEqual(1) | ||||
| 	allowances.Value(1).Object().Value("weight").Number().IsEqual(5) | ||||
| 	allowances.Value(2).Object().Value("id").Number().IsEqual(2) | ||||
| 	allowances.Value(2).Object().Value("weight").Number().IsEqual(10) | ||||
| } | ||||
|  | ||||
| func getDelta(base time.Time, delta float64) (time.Time, time.Time) { | ||||
| @@ -583,3 +727,15 @@ func getDelta(base time.Time, delta float64) (time.Time, time.Time) { | ||||
| 	end := base.Add(time.Duration(delta) * time.Second) | ||||
| 	return start, end | ||||
| } | ||||
|  | ||||
| func createTestAllowance(e *httpexpect.Expect, name string, target int, weight float64) { | ||||
| 	e.POST("/user/1/allowance").WithJSON(CreateAllowanceRequest{ | ||||
| 		Name:   name, | ||||
| 		Target: target, | ||||
| 		Weight: weight, | ||||
| 	}).Expect().Status(201) | ||||
| } | ||||
|  | ||||
| func createTestTask(e *httpexpect.Expect) int { | ||||
| 	return createTestTaskWithAmount(e, 100) | ||||
| } | ||||
|   | ||||
							
								
								
									
										104
									
								
								backend/db.go
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								backend/db.go
									
									
									
									
									
								
							| @@ -175,6 +175,39 @@ func (db *Db) DeleteAllowance(userId int, allowanceId int) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (db *Db) CompleteAllowance(userId int, allowanceId int) error { | ||||
| 	tx, err := db.db.Begin() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer tx.MustRollback() | ||||
|  | ||||
| 	// Get the cost of the allowance | ||||
| 	var cost int | ||||
| 	err = tx.Query("select balance from allowances where id = ? and user_id = ?"). | ||||
| 		Bind(allowanceId, userId).ScanSingle(&cost) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Delete the allowance | ||||
| 	err = tx.Query("delete from allowances where id = ? and user_id = ?"). | ||||
| 		Bind(allowanceId, userId).Exec() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Add a history entry | ||||
| 	err = tx.Query("insert into history (user_id, timestamp, amount) values (?, ?, ?)"). | ||||
| 		Bind(userId, time.Now().Unix(), -cost). | ||||
| 		Exec() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return tx.Commit() | ||||
| } | ||||
|  | ||||
| func (db *Db) UpdateUserAllowance(userId int, allowance *UpdateAllowanceRequest) error { | ||||
| 	tx, err := db.db.Begin() | ||||
| 	if err != nil { | ||||
| @@ -218,6 +251,31 @@ func (db *Db) UpdateAllowance(userId int, allowanceId int, allowance *UpdateAllo | ||||
| 	return tx.Commit() | ||||
| } | ||||
|  | ||||
| func (db *Db) BulkUpdateAllowance(userId int, allowances []BulkUpdateAllowanceRequest) error { | ||||
| 	tx, err := db.db.Begin() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	defer tx.MustRollback() | ||||
|  | ||||
| 	for _, allowance := range allowances { | ||||
| 		if allowance.ID == 0 { | ||||
| 			err = tx.Query("update users set weight=? where id = ?"). | ||||
| 				Bind(allowance.Weight, userId). | ||||
| 				Exec() | ||||
| 		} else { | ||||
| 			err = tx.Query("update allowances set weight=? where id = ? and user_id = ?"). | ||||
| 				Bind(allowance.Weight, allowance.ID, userId). | ||||
| 				Exec() | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return tx.Commit() | ||||
| } | ||||
|  | ||||
| func (db *Db) CreateTask(task *CreateTaskRequest) (int, error) { | ||||
| 	tx, err := db.db.Begin() | ||||
| 	if err != nil { | ||||
| @@ -341,32 +399,46 @@ func (db *Db) CompleteTask(taskId int) error { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// Add the history entry | ||||
| 		err = tx.Query("insert into history (user_id, timestamp, amount) values (?, ?, ?)"). | ||||
| 			Bind(userId, time.Now().Unix(), reward). | ||||
| 			Exec() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// Calculate the sums of all weights | ||||
| 		var sumOfWeights float64 | ||||
| 		err = tx.Query("select sum(weight) from allowances where user_id = ? and weight > 0").Bind(userId).ScanSingle(&sumOfWeights) | ||||
| 		sumOfWeights += userWeight | ||||
|  | ||||
| 		for allowanceRow := range tx.Query("select id, weight from allowances where user_id = ? and weight > 0").Bind(userId).Range(&err) { | ||||
| 			var allowanceId int | ||||
| 			var allowanceWeight float64 | ||||
| 			err = allowanceRow.Scan(&allowanceId, &allowanceWeight) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		remainingReward := reward | ||||
|  | ||||
| 			// Calculate the amount to add to the allowance | ||||
| 			amount := int((allowanceWeight / sumOfWeights) * float64(reward)) | ||||
| 			sumOfWeights -= allowanceWeight | ||||
| 			err = tx.Query("update allowances set balance = balance + ? where id = ? and user_id = ?"). | ||||
| 				Bind(amount, allowanceId, userId).Exec() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 		if sumOfWeights > 0 { | ||||
| 			// Distribute the reward to the allowances | ||||
| 			for allowanceRow := range tx.Query("select id, weight from allowances where user_id = ? and weight > 0").Bind(userId).Range(&err) { | ||||
| 				var allowanceId int | ||||
| 				var allowanceWeight float64 | ||||
| 				err = allowanceRow.Scan(&allowanceId, &allowanceWeight) | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
|  | ||||
| 				// Calculate the amount to add to the allowance | ||||
| 				amount := int((allowanceWeight / sumOfWeights) * float64(remainingReward)) | ||||
| 				sumOfWeights -= allowanceWeight | ||||
| 				err = tx.Query("update allowances set balance = balance + ? where id = ? and user_id = ?"). | ||||
| 					Bind(amount, allowanceId, userId).Exec() | ||||
| 				if err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 				remainingReward -= amount | ||||
| 			} | ||||
| 			reward -= amount | ||||
| 		} | ||||
|  | ||||
| 		// Add the remaining reward to the user | ||||
| 		err = tx.Query("update users set balance = balance + ? where id = ?"). | ||||
| 			Bind(reward, userId).Exec() | ||||
| 			Bind(remainingReward, userId).Exec() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|   | ||||
| @@ -31,23 +31,28 @@ type Task struct { | ||||
| } | ||||
|  | ||||
| type Allowance struct { | ||||
| 	ID       int    `json:"id"` | ||||
| 	Name     string `json:"name"` | ||||
| 	Target   int    `json:"target"` | ||||
| 	Progress int    `json:"progress"` | ||||
| 	Weight   int    `json:"weight"` | ||||
| 	ID       int     `json:"id"` | ||||
| 	Name     string  `json:"name"` | ||||
| 	Target   int     `json:"target"` | ||||
| 	Progress int     `json:"progress"` | ||||
| 	Weight   float64 `json:"weight"` | ||||
| } | ||||
|  | ||||
| type CreateAllowanceRequest struct { | ||||
| 	Name   string `json:"name"` | ||||
| 	Target int    `json:"target"` | ||||
| 	Weight int    `json:"weight"` | ||||
| 	Name   string  `json:"name"` | ||||
| 	Target int     `json:"target"` | ||||
| 	Weight float64 `json:"weight"` | ||||
| } | ||||
|  | ||||
| type UpdateAllowanceRequest struct { | ||||
| 	Name   string `json:"name"` | ||||
| 	Target int    `json:"target"` | ||||
| 	Weight int    `json:"weight"` | ||||
| 	Name   string  `json:"name"` | ||||
| 	Target int     `json:"target"` | ||||
| 	Weight float64 `json:"weight"` | ||||
| } | ||||
|  | ||||
| type BulkUpdateAllowanceRequest struct { | ||||
| 	ID     int     `json:"id"` | ||||
| 	Weight float64 `json:"weight"` | ||||
| } | ||||
|  | ||||
| type CreateGoalResponse struct { | ||||
|   | ||||
| @@ -189,6 +189,44 @@ func createUserAllowance(c *gin.Context) { | ||||
| 	c.IndentedJSON(http.StatusCreated, response) | ||||
| } | ||||
|  | ||||
| func bulkPutUserAllowance(c *gin.Context) { | ||||
| 	userIdStr := c.Param("userId") | ||||
|  | ||||
| 	userId, err := strconv.Atoi(userIdStr) | ||||
| 	if err != nil { | ||||
| 		log.Printf(ErrInvalidUserID+": %v", err) | ||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": ErrInvalidUserID}) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	exists, err := db.UserExists(userId) | ||||
| 	if err != nil { | ||||
| 		log.Printf(ErrCheckingUserExist, err) | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError}) | ||||
| 		return | ||||
| 	} | ||||
| 	if !exists { | ||||
| 		c.JSON(http.StatusNotFound, gin.H{"error": ErrUserNotFound}) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var allowanceRequest []BulkUpdateAllowanceRequest | ||||
| 	if err := c.ShouldBindJSON(&allowanceRequest); err != nil { | ||||
| 		log.Printf("Error parsing request body: %v", err) | ||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body"}) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	err = db.BulkUpdateAllowance(userId, allowanceRequest) | ||||
| 	if err != nil { | ||||
| 		log.Printf("Error updating allowance: %v", err) | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError}) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	c.IndentedJSON(http.StatusOK, gin.H{"message": "Allowance updated successfully"}) | ||||
| } | ||||
|  | ||||
| func deleteUserAllowance(c *gin.Context) { | ||||
| 	userIdStr := c.Param("userId") | ||||
| 	allowanceIdStr := c.Param("allowanceId") | ||||
| @@ -287,6 +325,49 @@ func putUserAllowance(c *gin.Context) { | ||||
| 	c.IndentedJSON(http.StatusOK, gin.H{"message": "Allowance updated successfully"}) | ||||
| } | ||||
|  | ||||
| func completeAllowance(c *gin.Context) { | ||||
| 	userIdStr := c.Param("userId") | ||||
| 	allowanceIdStr := c.Param("allowanceId") | ||||
|  | ||||
| 	userId, err := strconv.Atoi(userIdStr) | ||||
| 	if err != nil { | ||||
| 		log.Printf(ErrInvalidUserID+": %v", err) | ||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": ErrInvalidUserID}) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	allowanceId, err := strconv.Atoi(allowanceIdStr) | ||||
| 	if err != nil { | ||||
| 		log.Printf("Invalid allowance ID: %v", err) | ||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid allowance ID"}) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	exists, err := db.UserExists(userId) | ||||
| 	if err != nil { | ||||
| 		log.Printf(ErrCheckingUserExist, err) | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError}) | ||||
| 		return | ||||
| 	} | ||||
| 	if !exists { | ||||
| 		c.JSON(http.StatusNotFound, gin.H{"error": ErrUserNotFound}) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	err = db.CompleteAllowance(userId, allowanceId) | ||||
| 	if errors.Is(err, mysqlite.ErrNoRows) { | ||||
| 		c.JSON(http.StatusNotFound, gin.H{"error": "Allowance not found"}) | ||||
| 		return | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		log.Printf("Error completing allowance: %v", err) | ||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": ErrInternalServerError}) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	c.IndentedJSON(http.StatusOK, gin.H{"message": "Allowance completed successfully"}) | ||||
| } | ||||
|  | ||||
| func createTask(c *gin.Context) { | ||||
| 	var taskRequest CreateTaskRequest | ||||
| 	if err := c.ShouldBindJSON(&taskRequest); err != nil { | ||||
| @@ -517,9 +598,11 @@ func start(ctx context.Context, config *ServerConfig) { | ||||
| 	router.GET("/api/user/:userId/history", getHistory) | ||||
| 	router.GET("/api/user/:userId/allowance", getUserAllowance) | ||||
| 	router.POST("/api/user/:userId/allowance", createUserAllowance) | ||||
| 	router.PUT("/api/user/:userId/allowance", bulkPutUserAllowance) | ||||
| 	router.GET("/api/user/:userId/allowance/:allowanceId", getUserAllowanceById) | ||||
| 	router.DELETE("/api/user/:userId/allowance/:allowanceId", deleteUserAllowance) | ||||
| 	router.PUT("/api/user/:userId/allowance/:allowanceId", putUserAllowance) | ||||
| 	router.POST("/api/user/:userId/allowance/:allowanceId/complete", completeAllowance) | ||||
| 	router.POST("/api/tasks", createTask) | ||||
| 	router.GET("/api/tasks", getTasks) | ||||
| 	router.GET("/api/task/:taskId", getTask) | ||||
| @@ -560,5 +643,9 @@ func main() { | ||||
| 		Datasource: os.Getenv("DB_PATH"), | ||||
| 		Addr:       ":8080", | ||||
| 	} | ||||
| 	if config.Datasource == "" { | ||||
| 		config.Datasource = "allowance_planner.db3" | ||||
| 		log.Printf("Warning: No DB_PATH set, using default of %s", config.Datasource) | ||||
| 	} | ||||
| 	start(context.Background(), &config) | ||||
| } | ||||
|   | ||||
| @@ -2,7 +2,7 @@ create table users | ||||
| ( | ||||
|     id   integer primary key, | ||||
|     name text not null, | ||||
|     weight real not null default 1.0, | ||||
|     weight real not null default 0.0, | ||||
|     balance integer not null default 0 | ||||
| ) strict; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user