package meteo import ( "bytes" "crypto/sha256" "encoding/hex" "fmt" "io" "log/slog" "net/http" "pkg" "servicea/internal/app" "time" ) type Handler struct { pkg.BaseHandler s *Service } func NewHandler(service *Service) *Handler { return &Handler{ s: service, } } func (h *Handler) IngestCSV(w http.ResponseWriter, r *http.Request) { err := r.ParseMultipartForm(10 << 20) if err != nil { slog.Error(ErrParsingForm.Error(), "error", err) h.ToJSON(w, http.StatusBadRequest, app.H{"error": ErrParsingForm}) return } file, header, err := r.FormFile("file") if err != nil { slog.Error(ErrRetrievingFile.Error(), "error", err) h.ToJSON(w, http.StatusBadRequest, app.H{"error": ErrRetrievingFile}) return } defer file.Close() content, err := io.ReadAll(file) if err != nil { slog.Error(ErrReadingFile.Error(), "error", err) h.ToJSON(w, http.StatusInternalServerError, app.H{"error": ErrReadingFile}) return } hash := sha256.Sum256(content) checksum := hex.EncodeToString(hash[:]) fileStats := &FileStats{ FileChecksum: checksum, } start := time.Now() err = h.s.IngestCSV(r.Context(), bytes.NewReader(content), fileStats) if err != nil { slog.Error(ErrCannotParseFile.Error(), "filename", header.Filename, "error", err) h.ToJSON(w, http.StatusConflict, app.H{"error": err}) return } fileStats.ElapsedMS = int(time.Since(start).Milliseconds()) h.s.UpdateElapsedMS(r.Context(), fileStats.BatchID, fileStats.ElapsedMS) slog.Info("csv file processed", "filename", header.Filename, "rows_inserted", fileStats.RowsInserted, "rows_rejected", fileStats.RowsRejected, "elapsed_ms", fileStats.ElapsedMS, "file_checksum", fileStats.FileChecksum, ) h.ToJSON(w, http.StatusOK, app.H{"stats": fileStats}) } func (h *Handler) IngestExcel(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello from excel") } func (h *Handler) GetCities(w http.ResponseWriter, r *http.Request) { cities := h.s.GetCities(r.Context()) slog.Info("cities retrieved", "count", len(cities)) h.ToJSON(w, http.StatusOK, app.H{"cities": cities}) } func (h *Handler) GetMeteoData(w http.ResponseWriter, r *http.Request) { queryParams := r.URL.Query() params := GetMeteoData{ Location: queryParams.Get("city"), From: queryParams.Get("from"), To: queryParams.Get("to"), Page: h.ParamToInt(queryParams.Get("page"), 1), Limit: h.ParamToInt(queryParams.Get("limit"), 10), } if err := params.Validate(); err != nil { slog.Error("error validating struct", "error", err) h.ToJSON(w, http.StatusBadRequest, app.H{"error": err.Error()}) return } meteoData, err := h.s.GetMeteoData(r.Context(), params) if err != nil { slog.Error(ErrReadingData.Error(), "error", err) h.ToJSON(w, http.StatusNotFound, app.H{"error": ErrReadingData.Error()}) return } slog.Info("data retrieved", "location", params.Location) h.ToJSON(w, http.StatusOK, app.H{"meteo_data": meteoData}) }