2025-08-25 13:46:37 +03:00

147 lines
3.8 KiB
Go

package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strconv"
"strings"
)
type TimesheetPayload struct {
Date string `json:"date"`
Description string `json:"description"`
GiteaUsername string `json:"gitea_username"`
HourSpent float64 `json:"hour_spent"`
TaskID int64 `json:"task_id"`
}
// parseOdooTicketID extracts the numeric ticket id from a ref formatted like
// "<prefix>/ticket-<number>". It scans path segments and picks the first segment
// that starts with "ticket-" (case-insensitive), then reads leading digits.
func parseOdooTicketID(ref string) (int64, error) {
if strings.TrimSpace(ref) == "" {
return 0, fmt.Errorf("empty ref")
}
parts := strings.Split(ref, "/")
for _, seg := range parts {
if seg == "" {
continue
}
lower := strings.ToLower(seg)
const pfx = "ticket-"
if strings.HasPrefix(lower, pfx) {
numPart := seg[len(pfx):]
// collect leading digits
var digits strings.Builder
for _, r := range numPart {
if r >= '0' && r <= '9' {
digits.WriteRune(r)
} else {
break
}
}
if digits.Len() == 0 {
return 0, fmt.Errorf("no digits after ticket- in segment: %s", seg)
}
id, err := strconv.ParseInt(digits.String(), 10, 64)
if err != nil {
return 0, err
}
return id, nil
}
}
return 0, fmt.Errorf("no ticket-<number> segment found in ref: %s", ref)
}
func main() {
baseURL := os.Getenv("BASE_URL")
branchRef := os.Getenv("BRANCH_REF")
sender := os.Getenv("SENDER_LOGIN")
title := os.Getenv("ISSUE_TITLE")
trackedSeconds := os.Getenv("TRACKED_SECONDS")
trackedCreated := os.Getenv("TRACKED_CREATED")
action := os.Getenv("EVENT_ACTION")
if baseURL == "" || branchRef == "" || sender == "" || trackedSeconds == "" || trackedCreated == "" {
fmt.Println("Some required environment variables are missing.")
os.Exit(1)
}
if action != "add_time" && action != "edited" && action != "time_tracked" && action != "time_added" {
// Best-effort: only proceed for time-related actions; allow edited if your instance sends edited
fmt.Printf("Skipping action: %s\n", action)
return
}
// Logger
logFile, err := os.OpenFile("action.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
fmt.Printf("Error creating log file: %v\n", err)
os.Exit(1)
}
defer logFile.Close()
logger := log.New(io.MultiWriter(os.Stdout, logFile), "", log.LstdFlags)
// Extract task ID from branch ref
taskID, err := parseOdooTicketID(branchRef)
if err != nil {
logger.Printf("Failed to parse Odoo ticket from BRANCH_REF '%s': %v\n", branchRef, err)
os.Exit(1)
}
seconds, err := strconv.ParseInt(trackedSeconds, 10, 64)
if err != nil {
logger.Printf("Invalid TRACKED_SECONDS: %v\n", err)
os.Exit(1)
}
hours := float64(seconds) / 3600.0
desc := title
if strings.TrimSpace(desc) == "" {
desc = "/"
}
date := trackedCreated
if idx := strings.Index(date, "T"); idx > 0 {
date = date[:idx]
}
payload := TimesheetPayload{
Date: date,
Description: desc,
GiteaUsername: sender,
HourSpent: hours,
TaskID: taskID,
}
body, err := json.Marshal(payload)
if err != nil {
logger.Printf("Error marshalling payload: %v\n", err)
os.Exit(1)
}
endpoint := strings.TrimRight(baseURL, "/") + "/api/v1/account_analytic_gitea_odoo/"
req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewReader(body))
if err != nil {
logger.Printf("Error creating POST request: %v\n", err)
os.Exit(1)
}
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
logger.Printf("Error calling FastAPI: %v\n", err)
os.Exit(1)
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
logger.Printf("FastAPI status: %s\nResponse: %s\n", resp.Status, string(respBody))
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
os.Exit(1)
}
}