Update Readme

Support Archive and unarchive
Fix bug with the scheduler  #53
This commit is contained in:
Mo Tarbin 2024-12-22 14:23:32 -05:00
commit 52af6652d8
4 changed files with 195 additions and 50 deletions

162
README.md
View file

@ -1,29 +1,62 @@
![Donetick Logo](assets/logo.png)
![Logo](assets/logo.png) # Donetick
## What is Donetick
An open-source, user-friendly app for managing tasks and chores, featuring customizable options to help you and others stay organized
## Features Highlights > **Simplify Tasks & Chores, Together!**
- Task and Chore Management: Easily create, edit, and manage tasks and chores for yourself or your group.
- Build with sharing in mind: you give people access to you group and you can assign each other tasks. only those assigned to task or chore can see it Donetick is an open-source, user-friendly app designed to help you organize tasks and chores effectively.featuring customizable options to help you and others stay organized
- Assignee Assignment: Assign tasks to specific individuals with ability rotate them automatically using customizable strategies like randomly, least completed,etc..
- Recurring Tasks: Schedule tasks to repeat daily, weekly, monthly, or yearly or something to trigger on specific day of month or day of the week. if you are not sure you can have adaptive recurring task where it does figure it out base on historical completion ---
- Progress Tracking: Track the completion status of tasks and view historical data.
- API Integration ## ✨ Features
## Selfhosted : 🏰 Group-Based Task Management: Create and manage tasks for solo or with your family or friends in shared circles.
Release binary included everything needed to be up and running, as even the frontend file served
### Using Docker run : 🔄 Smart Assignments: Assign tasks to individuals with rotation strategies like random or least completed.
1. pull the latest image using: `docker pull donetick/donetick`
2. run the container and replace `/path/to/host/data` with where you want to place attach the volumne for the sqlite db `DT_ENV=selfhosted DT_SQLITE_PATH=/donetick-data/donetick.db docker run -v /path/to/host/data:/donetick-data -p 2021:2021 donetick/donetick` ⏰ Recurring Tasks: Automate task scheduling with flexible recurrence options:
Daily, weekly, monthly, or yearly.
Custom triggers based on specific days or adaptive patterns using historical completion data.
📈 Progress Tracking: Monitor completion rates and view historical data to track trends.
📢 NFC Tag Support: Write NFC tags to trigger tasks instantly by scanning.
📧 Notifications: Stay on top of tasks with reminders sent via Telegram or Pushover.
📦 Integration:
Trigger tasks using external systems through API.
View tasks directly in supported platforms (e.g., Home Assistant with a custom component).
💿 Labels for Organization: Group and organize tasks with labels. Labels can also be shared within the same group for better collaboration.
🛠️ "Things" Integration: Use entities (numbers, strings, booleans) to trigger tasks, track values, or connect with external systems via webhooks.
---
## 🚀 Quick Start
make sure you update the `config/selfhosted.yaml`
### Using Docker
1. **Pull the latest image:**
```bash
docker pull donetick/donetick
```
2. **Run the container:** Replace `/path/to/host/data` with your preferred data directory:
```bash
docker run -v /path/to/host/data:/donetick-data -p 2021:2021 \
-e DT_ENV=selfhosted \
-e DT_SQLITE_PATH=/donetick-data/donetick.db \
donetick/donetick
```
### Using Docker Compose
### Using Docker Compose: Use this template to set up Donetick with Docker Compose:
You can use the following template
```yaml ```yaml
services: services:
donetick: donetick:
@ -31,40 +64,85 @@ services:
container_name: donetick container_name: donetick
restart: unless-stopped restart: unless-stopped
ports: ports:
- 2021:2021 # needed for serving backend and frontend - 2021:2021
volumes: volumes:
- ./data:/donetick-data # database file stored (sqlite database) - ./data:/donetick-data
- ./config:/config # configration file like selfhosted.yaml - ./config:/config
environment: environment:
- DT_ENV=selfhosted # this tell donetick to load ./config/selfhosted.yaml for the configuration file - DT_ENV=selfhosted
- DT_SQLITE_PATH=/donetick-data/donetick.db - DT_SQLITE_PATH=/donetick-data/donetick.db
``` ```
### Using binary:
1. Navigate to [Release] ### Using the Binary
2. Download the appropiate file for you arch/os 1. **Download the latest release** from the [Releases](https://github.com/donetick/donetick/releases) page.
3. cd into the extracted folder 2. **Extract the file** and navigate to the folder:
4. run `./donetick` and you can specify the env as `DT_ENV=selfhosted` ```bash
cd path/to/extracted-folder
```
3. **Run Donetick:**
```bash
DT_ENV=selfhosted ./donetick
```
---
## Development Environment
## 🛠️ Development Environment
1. Clone the repository: 1. Clone the repository:
2. Navigate to the project directory: `cd donetick` ```bash
3. Download dependency `go mod download` git clone https://github.com/donetick/donetick.git
4. Run locally `go run .` ```
2. Navigate to the project directory:
```bash
cd donetick
```
3. Install dependencies:
```bash
go mod download
```
4. Run the app locally:
```bash
go run .
```
---
## Contributing ## 🤝 Contributing
Contributions are welcome! If you would like to contribute to Donetick, please follow these steps:
1. Fork the repository
2. Create a new branch: `git checkout -b feature/your-feature-name`
3. Make your changes and commit them: `git commit -m 'Add some feature'`
4. Push to the branch: `git push origin feature/your-feature-name`
5. Submit a pull request
Contributions are welcome! If you want to work on something that is not listed as an issue, please open a [Discussion](https://github.com/donetick/donetick/discussions) first to ensure it aligns with our goals and to avoid any unnecessary effort!
## License if you have an idea also feel free to use the [Discussion](https://github.com/donetick/donetick/discussions)
This project is licensed under the AGPLv3. See the [LICENSE](LICENSE) file for more details. I might consider changing it later to something else 1. Pick an issue or open discuss about the contribution
2. Fork the repository.
3. Create a new branch:
```bash
git checkout -b feature/your-feature-name
```
4. Make your changes and commit them:
```bash
git commit -m 'Add a new feature'
```
5. Push your branch:
```bash
git push origin feature/your-feature-name
```
6. Submit a pull request.
---
## 🔒 License
This project is licensed under the **AGPLv3**. See the [LICENSE](LICENSE) file for more details.
---
## 💡 Support Donetick
If you find it helpful, consider supporting us by starring the repository, contributing code, or sharing feedback!
---

View file

@ -841,6 +841,70 @@ func (h *Handler) updateDueDate(c *gin.Context) {
"res": chore, "res": chore,
}) })
} }
func (h *Handler) archiveChore(c *gin.Context) {
rawID := c.Param("id")
id, err := strconv.Atoi(rawID)
if err != nil {
c.JSON(400, gin.H{
"error": "Invalid ID",
})
return
}
currentUser, ok := auth.CurrentUser(c)
if !ok {
c.JSON(500, gin.H{
"error": "Error getting current user",
})
return
}
err = h.choreRepo.ArchiveChore(c, id, currentUser.ID)
if err != nil {
c.JSON(500, gin.H{
"error": "Error archiving chore",
})
return
}
c.JSON(200, gin.H{
"message": "Chore archived successfully",
})
}
func (h *Handler) UnarchiveChore(c *gin.Context) {
rawID := c.Param("id")
id, err := strconv.Atoi(rawID)
if err != nil {
c.JSON(400, gin.H{
"error": "Invalid ID",
})
return
}
currentUser, ok := auth.CurrentUser(c)
if !ok {
c.JSON(500, gin.H{
"error": "Error getting current user",
})
return
}
err = h.choreRepo.UnarchiveChore(c, id, currentUser.ID)
if err != nil {
c.JSON(500, gin.H{
"error": "Error unarchiving chore",
})
return
}
c.JSON(200, gin.H{
"message": "Chore archived successfully",
})
}
func (h *Handler) completeChore(c *gin.Context) { func (h *Handler) completeChore(c *gin.Context) {
type CompleteChoreReq struct { type CompleteChoreReq struct {
Note string `json:"note"` Note string `json:"note"`
@ -1297,6 +1361,7 @@ func Routes(router *gin.Engine, h *Handler, auth *jwt.GinJWTMiddleware) {
{ {
choresRoutes.GET("/", h.getChores) choresRoutes.GET("/", h.getChores)
choresRoutes.GET("/archived", h.getArchivedChores) choresRoutes.GET("/archived", h.getArchivedChores)
choresRoutes.PUT("/", h.editChore) choresRoutes.PUT("/", h.editChore)
choresRoutes.PUT("/:id/priority", h.updatePriority) choresRoutes.PUT("/:id/priority", h.updatePriority)
choresRoutes.POST("/", h.createChore) choresRoutes.POST("/", h.createChore)
@ -1309,6 +1374,8 @@ func Routes(router *gin.Engine, h *Handler, auth *jwt.GinJWTMiddleware) {
choresRoutes.POST("/:id/skip", h.skipChore) choresRoutes.POST("/:id/skip", h.skipChore)
choresRoutes.PUT("/:id/assignee", h.updateAssignee) choresRoutes.PUT("/:id/assignee", h.updateAssignee)
choresRoutes.PUT("/:id/dueDate", h.updateDueDate) choresRoutes.PUT("/:id/dueDate", h.updateDueDate)
choresRoutes.PUT("/:id/archive", h.archiveChore)
choresRoutes.PUT("/:id/unarchive", h.UnarchiveChore)
choresRoutes.DELETE("/:id", h.deleteChore) choresRoutes.DELETE("/:id", h.deleteChore)
} }

View file

@ -293,3 +293,11 @@ func (r *ChoreRepository) GetChoreDetailByID(c context.Context, choreID int, cir
} }
return &choreDetail, nil return &choreDetail, nil
} }
func (r *ChoreRepository) ArchiveChore(c context.Context, choreID int, userID int) error {
return r.db.WithContext(c).Model(&chModel.Chore{}).Where("id = ? and created_by = ?", choreID, userID).Update("is_active", false).Error
}
func (r *ChoreRepository) UnarchiveChore(c context.Context, choreID int, userID int) error {
return r.db.WithContext(c).Model(&chModel.Chore{}).Where("id = ? and created_by = ?", choreID, userID).Update("is_active", true).Error
}

View file

@ -42,14 +42,6 @@ func scheduleNextDueDate(chore *chModel.Chore, completedDate time.Time) (*time.T
baseDate = time.Date(baseDate.Year(), baseDate.Month(), baseDate.Day(), t.Hour(), t.Minute(), 0, 0, t.Location()) baseDate = time.Date(baseDate.Year(), baseDate.Month(), baseDate.Day(), t.Hour(), t.Minute(), 0, 0, t.Location())
} }
if chore.IsRolling && chore.NextDueDate.Before(completedDate) {
// we need to check if chore due date is before the completed date to handle this senario:
// if user trying to complete chore due in future (multiple time for insance) due date will be calculated
// from the last completed date and due date change only in seconds.
// this make sure that the due date is always in future if the chore is rolling
baseDate = completedDate.UTC()
}
if chore.FrequencyType == "daily" { if chore.FrequencyType == "daily" {
nextDueDate = baseDate.AddDate(0, 0, 1) nextDueDate = baseDate.AddDate(0, 0, 1)