mirror of
https://github.com/go-vikunja/vikunja.git
synced 2026-02-01 22:47:40 +00:00
fix: lint
This commit is contained in:
@@ -19,6 +19,7 @@ package csv
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"errors"
|
||||
"io"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -46,29 +47,29 @@ var SupportedQuoteChars = []string{"\"", "'"}
|
||||
|
||||
// SupportedDateFormats contains common date formats for parsing
|
||||
var SupportedDateFormats = []string{
|
||||
"2006-01-02", // ISO date
|
||||
"2006-01-02T15:04:05", // ISO datetime
|
||||
"2006-01-02T15:04:05Z07:00", // RFC3339
|
||||
"2006-01-02T15:04:05-0700", // ISO with timezone
|
||||
"02/01/2006", // DD/MM/YYYY
|
||||
"01/02/2006", // MM/DD/YYYY
|
||||
"02-01-2006", // DD-MM-YYYY
|
||||
"01-02-2006", // MM-DD-YYYY
|
||||
"Jan 2, 2006", // Month D, YYYY
|
||||
"2 Jan 2006", // D Month YYYY
|
||||
"02/01/2006 15:04", // DD/MM/YYYY HH:MM
|
||||
"01/02/2006 15:04", // MM/DD/YYYY HH:MM
|
||||
"2006-01-02 15:04:05", // MySQL datetime
|
||||
"2006/01/02", // YYYY/MM/DD
|
||||
"02.01.2006", // DD.MM.YYYY (European)
|
||||
"02.01.2006 15:04", // DD.MM.YYYY HH:MM (European)
|
||||
time.RFC1123, // RFC1123
|
||||
time.RFC1123Z, // RFC1123 with numeric zone
|
||||
time.RFC822, // RFC822
|
||||
time.RFC822Z, // RFC822 with numeric zone
|
||||
time.RFC850, // RFC850
|
||||
time.ANSIC, // ANSIC
|
||||
time.UnixDate, // Unix date
|
||||
"2006-01-02", // ISO date
|
||||
"2006-01-02T15:04:05", // ISO datetime
|
||||
"2006-01-02T15:04:05Z07:00", // RFC3339
|
||||
"2006-01-02T15:04:05-0700", // ISO with timezone
|
||||
"02/01/2006", // DD/MM/YYYY
|
||||
"01/02/2006", // MM/DD/YYYY
|
||||
"02-01-2006", // DD-MM-YYYY
|
||||
"01-02-2006", // MM-DD-YYYY
|
||||
"Jan 2, 2006", // Month D, YYYY
|
||||
"2 Jan 2006", // D Month YYYY
|
||||
"02/01/2006 15:04", // DD/MM/YYYY HH:MM
|
||||
"01/02/2006 15:04", // MM/DD/YYYY HH:MM
|
||||
"2006-01-02 15:04:05", // MySQL datetime
|
||||
"2006/01/02", // YYYY/MM/DD
|
||||
"02.01.2006", // DD.MM.YYYY (European)
|
||||
"02.01.2006 15:04", // DD.MM.YYYY HH:MM (European)
|
||||
time.RFC1123, // RFC1123
|
||||
time.RFC1123Z, // RFC1123 with numeric zone
|
||||
time.RFC822, // RFC822
|
||||
time.RFC822Z, // RFC822 with numeric zone
|
||||
time.RFC850, // RFC850
|
||||
time.ANSIC, // ANSIC
|
||||
time.UnixDate, // Unix date
|
||||
}
|
||||
|
||||
// TaskAttribute represents a task attribute that can be mapped from CSV
|
||||
@@ -112,20 +113,20 @@ type ColumnMapping struct {
|
||||
|
||||
// DetectionResult contains the auto-detected CSV structure
|
||||
type DetectionResult struct {
|
||||
Columns []string `json:"columns"`
|
||||
Delimiter string `json:"delimiter"`
|
||||
QuoteChar string `json:"quote_char"`
|
||||
DateFormat string `json:"date_format"`
|
||||
Columns []string `json:"columns"`
|
||||
Delimiter string `json:"delimiter"`
|
||||
QuoteChar string `json:"quote_char"`
|
||||
DateFormat string `json:"date_format"`
|
||||
SuggestedMapping []ColumnMapping `json:"suggested_mapping"`
|
||||
PreviewRows [][]string `json:"preview_rows"`
|
||||
PreviewRows [][]string `json:"preview_rows"`
|
||||
}
|
||||
|
||||
// ImportConfig contains the configuration for CSV import
|
||||
type ImportConfig struct {
|
||||
Delimiter string `json:"delimiter"`
|
||||
QuoteChar string `json:"quote_char"`
|
||||
DateFormat string `json:"date_format"`
|
||||
Mapping []ColumnMapping `json:"mapping"`
|
||||
Delimiter string `json:"delimiter"`
|
||||
QuoteChar string `json:"quote_char"`
|
||||
DateFormat string `json:"date_format"`
|
||||
Mapping []ColumnMapping `json:"mapping"`
|
||||
}
|
||||
|
||||
// PreviewTask represents a task preview before import
|
||||
@@ -170,7 +171,7 @@ func detectDelimiter(data []byte) string {
|
||||
delimiterCounts := make(map[string]int)
|
||||
for _, delim := range SupportedDelimiters {
|
||||
count := 0
|
||||
for _, line := range lines[:min(3, len(lines))] {
|
||||
for _, line := range lines[:minInt(3, len(lines))] {
|
||||
count += strings.Count(line, delim)
|
||||
}
|
||||
delimiterCounts[delim] = count
|
||||
@@ -278,7 +279,7 @@ func suggestMapping(columns []string) []ColumnMapping {
|
||||
}
|
||||
|
||||
// parseCSV parses CSV data with the given configuration
|
||||
func parseCSV(data []byte, delimiter, quoteChar string) ([]string, [][]string, error) {
|
||||
func parseCSV(data []byte, delimiter, _ string) ([]string, [][]string, error) {
|
||||
data = stripBOM(data)
|
||||
reader := csv.NewReader(bytes.NewReader(data))
|
||||
|
||||
@@ -316,7 +317,7 @@ func DetectCSVStructure(file io.ReaderAt, size int64) (*DetectionResult, error)
|
||||
// Read the entire file
|
||||
data := make([]byte, size)
|
||||
_, err := file.ReadAt(data, 0)
|
||||
if err != nil && err != io.EOF {
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -374,7 +375,7 @@ func PreviewImport(file io.ReaderAt, size int64, config ImportConfig) (*PreviewR
|
||||
|
||||
data := make([]byte, size)
|
||||
_, err := file.ReadAt(data, 0)
|
||||
if err != nil && err != io.EOF {
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -384,18 +385,13 @@ func PreviewImport(file io.ReaderAt, size int64, config ImportConfig) (*PreviewR
|
||||
}
|
||||
|
||||
result := &PreviewResult{
|
||||
Tasks: make([]PreviewTask, 0, min(5, len(rows))),
|
||||
Tasks: make([]PreviewTask, 0, minInt(5, len(rows))),
|
||||
TotalRows: len(rows),
|
||||
}
|
||||
|
||||
previewCount := min(5, len(rows))
|
||||
previewCount := minInt(5, len(rows))
|
||||
for i := 0; i < previewCount; i++ {
|
||||
task, err := rowToPreviewTask(rows[i], config)
|
||||
if err != nil {
|
||||
result.ErrorCount++
|
||||
result.Errors = append(result.Errors, err.Error())
|
||||
continue
|
||||
}
|
||||
task := rowToPreviewTask(rows[i], config)
|
||||
result.Tasks = append(result.Tasks, task)
|
||||
}
|
||||
|
||||
@@ -403,7 +399,7 @@ func PreviewImport(file io.ReaderAt, size int64, config ImportConfig) (*PreviewR
|
||||
}
|
||||
|
||||
// rowToPreviewTask converts a CSV row to a preview task
|
||||
func rowToPreviewTask(row []string, config ImportConfig) (PreviewTask, error) {
|
||||
func rowToPreviewTask(row []string, config ImportConfig) PreviewTask {
|
||||
task := PreviewTask{}
|
||||
|
||||
for _, mapping := range config.Mapping {
|
||||
@@ -435,10 +431,14 @@ func rowToPreviewTask(row []string, config ImportConfig) (PreviewTask, error) {
|
||||
task.Labels = parseLabels(value)
|
||||
case AttrProject:
|
||||
task.Project = value
|
||||
case AttrReminder:
|
||||
// Reminders are not supported in preview tasks
|
||||
case AttrIgnore:
|
||||
// Ignored attributes are not processed
|
||||
}
|
||||
}
|
||||
|
||||
return task, nil
|
||||
return task
|
||||
}
|
||||
|
||||
// parseBool parses various boolean representations
|
||||
@@ -470,10 +470,10 @@ func parsePriority(value string) int {
|
||||
return 4
|
||||
case strings.Contains(lower, "medium") || strings.Contains(lower, "normal"):
|
||||
return 3
|
||||
case strings.Contains(lower, "low"):
|
||||
return 2
|
||||
case strings.Contains(lower, "lowest"):
|
||||
return 1
|
||||
case strings.Contains(lower, "low"):
|
||||
return 2
|
||||
}
|
||||
|
||||
return 0
|
||||
@@ -526,7 +526,7 @@ func parseDate(value, format string) time.Time {
|
||||
// @Failure 400 {object} models.Message "Invalid CSV file or configuration"
|
||||
// @Failure 500 {object} models.Message "Internal server error"
|
||||
// @Router /migration/csv/migrate [put]
|
||||
func (m *Migrator) Migrate(u *user.User, file io.ReaderAt, size int64) error {
|
||||
func (m *Migrator) Migrate(_ *user.User, _ io.ReaderAt, _ int64) error {
|
||||
// This will be called with the standard file migrator handler
|
||||
// The actual configuration will come through the handler
|
||||
return &migration.ErrNotACSVFile{} // Need config, use MigrateWithConfig instead
|
||||
@@ -540,7 +540,7 @@ func MigrateWithConfig(u *user.User, file io.ReaderAt, size int64, config Import
|
||||
|
||||
data := make([]byte, size)
|
||||
_, err := file.ReadAt(data, 0)
|
||||
if err != nil && err != io.EOF {
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -664,6 +664,10 @@ func rowToTask(row []string, config ImportConfig, taskID int64) models.Task {
|
||||
},
|
||||
}
|
||||
}
|
||||
case AttrProject:
|
||||
// Project attribute is handled separately for task creation
|
||||
case AttrIgnore:
|
||||
// Ignored attributes are not processed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -675,7 +679,7 @@ func rowToTask(row []string, config ImportConfig, taskID int64) models.Task {
|
||||
return task
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
func minInt(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ import (
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
// CSVMigratorWeb handles CSV migration HTTP routes
|
||||
type CSVMigratorWeb struct{}
|
||||
// MigratorWeb handles CSV migration HTTP routes
|
||||
type MigratorWeb struct{}
|
||||
|
||||
// RegisterRoutes registers all CSV migration routes
|
||||
func (c *CSVMigratorWeb) RegisterRoutes(g *echo.Group) {
|
||||
func (c *MigratorWeb) RegisterRoutes(g *echo.Group) {
|
||||
g.GET("/csv/status", c.Status)
|
||||
g.PUT("/csv/detect", c.Detect)
|
||||
g.PUT("/csv/preview", c.Preview)
|
||||
@@ -47,7 +47,7 @@ func (c *CSVMigratorWeb) RegisterRoutes(g *echo.Group) {
|
||||
// @Success 200 {object} migration.Status "The migration status"
|
||||
// @Failure 500 {object} models.Message "Internal server error"
|
||||
// @Router /migration/csv/status [get]
|
||||
func (c *CSVMigratorWeb) Status(ctx echo.Context) error {
|
||||
func (c *MigratorWeb) Status(ctx echo.Context) error {
|
||||
u, err := user2.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err)
|
||||
@@ -74,7 +74,7 @@ func (c *CSVMigratorWeb) Status(ctx echo.Context) error {
|
||||
// @Failure 400 {object} models.Message "Invalid CSV file"
|
||||
// @Failure 500 {object} models.Message "Internal server error"
|
||||
// @Router /migration/csv/detect [put]
|
||||
func (c *CSVMigratorWeb) Detect(ctx echo.Context) error {
|
||||
func (c *MigratorWeb) Detect(ctx echo.Context) error {
|
||||
_, err := user2.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err)
|
||||
@@ -112,7 +112,7 @@ func (c *CSVMigratorWeb) Detect(ctx echo.Context) error {
|
||||
// @Failure 400 {object} models.Message "Invalid CSV file or configuration"
|
||||
// @Failure 500 {object} models.Message "Internal server error"
|
||||
// @Router /migration/csv/preview [put]
|
||||
func (c *CSVMigratorWeb) Preview(ctx echo.Context) error {
|
||||
func (c *MigratorWeb) Preview(ctx echo.Context) error {
|
||||
_, err := user2.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err)
|
||||
@@ -160,7 +160,7 @@ func (c *CSVMigratorWeb) Preview(ctx echo.Context) error {
|
||||
// @Failure 400 {object} models.Message "Invalid CSV file or configuration"
|
||||
// @Failure 500 {object} models.Message "Internal server error"
|
||||
// @Router /migration/csv/migrate [put]
|
||||
func (c *CSVMigratorWeb) Migrate(ctx echo.Context) error {
|
||||
func (c *MigratorWeb) Migrate(ctx echo.Context) error {
|
||||
u, err := user2.GetCurrentUser(ctx)
|
||||
if err != nil {
|
||||
return handler.HandleHTTPError(err)
|
||||
|
||||
@@ -740,7 +740,7 @@ func registerMigrations(m *echo.Group) {
|
||||
tickTickFileMigrator.RegisterRoutes(m)
|
||||
|
||||
// CSV File Migrator (always enabled - generic import)
|
||||
csvFileMigrator := &csvmigrator.CSVMigratorWeb{}
|
||||
csvFileMigrator := &csvmigrator.MigratorWeb{}
|
||||
csvFileMigrator.RegisterRoutes(m)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user