4.12.0: Добавлены записи за день на экране статистики
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m28s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m28s
This commit is contained in:
@@ -177,6 +177,19 @@ type FullStatisticsItem struct {
|
||||
MaxGoalScore float64 `json:"max_goal_score"`
|
||||
}
|
||||
|
||||
type TodayEntryNode struct {
|
||||
ProjectName string `json:"project_name"`
|
||||
Score float64 `json:"score"`
|
||||
Index int `json:"index"`
|
||||
}
|
||||
|
||||
type TodayEntry struct {
|
||||
ID int `json:"id"`
|
||||
Text string `json:"text"`
|
||||
CreatedDate string `json:"created_date"`
|
||||
Nodes []TodayEntryNode `json:"nodes"`
|
||||
}
|
||||
|
||||
type TodoistWebhook struct {
|
||||
EventName string `json:"event_name"`
|
||||
EventData map[string]interface{} `json:"event_data"`
|
||||
@@ -3740,6 +3753,7 @@ func main() {
|
||||
protected.HandleFunc("/project/delete", app.deleteProjectHandler).Methods("POST", "OPTIONS")
|
||||
protected.HandleFunc("/project/create", app.createProjectHandler).Methods("POST", "OPTIONS")
|
||||
protected.HandleFunc("/d2dc349a-0d13-49b2-a8f0-1ab094bfba9b", app.getFullStatisticsHandler).Methods("GET", "OPTIONS")
|
||||
protected.HandleFunc("/api/today-entries", app.getTodayEntriesHandler).Methods("GET", "OPTIONS")
|
||||
|
||||
// Integrations
|
||||
protected.HandleFunc("/api/integrations/telegram", app.getTelegramIntegrationHandler).Methods("GET", "OPTIONS")
|
||||
@@ -6185,6 +6199,128 @@ func (a *App) getFullStatisticsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
json.NewEncoder(w).Encode(statistics)
|
||||
}
|
||||
|
||||
// getTodayEntriesHandler возвращает entries с nodes за сегодняшний день
|
||||
func (a *App) getTodayEntriesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
setCORSHeaders(w)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
return
|
||||
}
|
||||
setCORSHeaders(w)
|
||||
|
||||
userID, ok := getUserIDFromContext(r)
|
||||
if !ok {
|
||||
sendErrorWithCORS(w, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Получаем опциональные параметры из query string
|
||||
projectName := r.URL.Query().Get("project")
|
||||
var projectFilter *string
|
||||
if projectName != "" {
|
||||
projectFilter = &projectName
|
||||
}
|
||||
|
||||
// Получаем дату из query string (формат: YYYY-MM-DD), если не указана - используем сегодня
|
||||
dateParam := r.URL.Query().Get("date")
|
||||
var targetDate time.Time
|
||||
if dateParam != "" {
|
||||
parsedDate, err := time.Parse("2006-01-02", dateParam)
|
||||
if err != nil {
|
||||
log.Printf("Error parsing date parameter: %v", err)
|
||||
sendErrorWithCORS(w, "Invalid date format. Use YYYY-MM-DD", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
targetDate = parsedDate
|
||||
} else {
|
||||
targetDate = time.Now()
|
||||
}
|
||||
|
||||
// Запрос для получения entries с nodes за указанный день
|
||||
// Используем подзапрос для получения nodes с правильными индексами
|
||||
query := `
|
||||
WITH entry_nodes AS (
|
||||
SELECT
|
||||
e.id as entry_id,
|
||||
e.text,
|
||||
e.created_date,
|
||||
p.name as project_name,
|
||||
n.score,
|
||||
ROW_NUMBER() OVER (PARTITION BY e.id ORDER BY n.id) - 1 as node_index
|
||||
FROM entries e
|
||||
JOIN nodes n ON n.entry_id = e.id
|
||||
JOIN projects p ON n.project_id = p.id
|
||||
WHERE DATE(n.created_date) = DATE($3)
|
||||
AND e.user_id = $1
|
||||
AND n.user_id = $1
|
||||
AND p.user_id = $1
|
||||
AND p.deleted = FALSE
|
||||
AND ($2::text IS NULL OR p.name = $2)
|
||||
)
|
||||
SELECT
|
||||
entry_id,
|
||||
text,
|
||||
created_date,
|
||||
json_agg(
|
||||
json_build_object(
|
||||
'project_name', project_name,
|
||||
'score', score,
|
||||
'index', node_index
|
||||
) ORDER BY node_index
|
||||
) as nodes
|
||||
FROM entry_nodes
|
||||
GROUP BY entry_id, text, created_date
|
||||
ORDER BY created_date DESC
|
||||
`
|
||||
|
||||
rows, err := a.DB.Query(query, userID, projectFilter, targetDate)
|
||||
if err != nil {
|
||||
log.Printf("Error querying today entries: %v", err)
|
||||
sendErrorWithCORS(w, fmt.Sprintf("Error querying today entries: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
entries := make([]TodayEntry, 0)
|
||||
|
||||
for rows.Next() {
|
||||
var entry TodayEntry
|
||||
var createdDate time.Time
|
||||
var nodesJSON string
|
||||
|
||||
err := rows.Scan(
|
||||
&entry.ID,
|
||||
&entry.Text,
|
||||
&createdDate,
|
||||
&nodesJSON,
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("Error scanning today entry row: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Парсим JSON с nodes
|
||||
if err := json.Unmarshal([]byte(nodesJSON), &entry.Nodes); err != nil {
|
||||
log.Printf("Error unmarshaling nodes JSON: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Форматируем дату в ISO 8601
|
||||
entry.CreatedDate = createdDate.Format(time.RFC3339)
|
||||
|
||||
entries = append(entries, entry)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
log.Printf("Error iterating today entries rows: %v", err)
|
||||
sendErrorWithCORS(w, fmt.Sprintf("Error iterating rows: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(entries)
|
||||
}
|
||||
|
||||
// getTelegramIntegrationHandler возвращает текущую telegram интеграцию с deep link
|
||||
func (a *App) getTelegramIntegrationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == "OPTIONS" {
|
||||
|
||||
Reference in New Issue
Block a user