Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 1 addition & 21 deletions internal/writer/chart.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,12 @@ package writer
import (
"fmt"
cc "github.com/cduggn/ccexplorer/internal/types"
"github.com/cduggn/ccexplorer/internal/utils"
"github.com/go-echarts/go-echarts/v2/charts"
"github.com/go-echarts/go-echarts/v2/components"
"github.com/go-echarts/go-echarts/v2/opts"

"io"
)

var (
chartFileName = "ccexplorer_chart.html"
)

func WriteToChart(p *components.Page) error {

f, err := utils.NewFile(OutputDir, chartFileName)
if err != nil {
return cc.Error{
Msg: "Failed creating chart HTML file: " + err.Error(),
}
}
defer f.Close()
return p.Render(io.MultiWriter(f))
}

func (Builder) NewCharts(r cc.InputType) (*components.Page,
error) {
func (Builder) NewCharts(r cc.InputType) (*components.Page, error) {
page := components.NewPage()
page.PageTitle = "Cost and Usage Report"

Expand Down
73 changes: 73 additions & 0 deletions internal/writer/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package writer

import (
"github.com/cduggn/ccexplorer/internal/http"
)

// Config holds configuration for writer components
type Config struct {
// OutputDir is the directory where files are written
OutputDir string

// Vector database configuration
OpenAIAPIKey string
PineconeAPIKey string
PineconeIndex string

// HTTP client for external services
HTTPClient *http.HttpRequestBuilder

// Sorting configuration
SortBy string
}

// Option is a functional option for configuring writers
type Option func(*Config)

// WithOutputDir sets the output directory for file-based writers
func WithOutputDir(dir string) Option {
return func(c *Config) {
c.OutputDir = dir
}
}

// WithVectorDBConfig sets the vector database configuration
func WithVectorDBConfig(openAIKey, pineconeKey, pineconeIndex string) Option {
return func(c *Config) {
c.OpenAIAPIKey = openAIKey
c.PineconeAPIKey = pineconeKey
c.PineconeIndex = pineconeIndex
}
}

// WithHTTPClient sets the HTTP client for external services
func WithHTTPClient(client *http.HttpRequestBuilder) Option {
return func(c *Config) {
c.HTTPClient = client
}
}

// WithSortBy sets the sorting preference
func WithSortBy(sortBy string) Option {
return func(c *Config) {
c.SortBy = sortBy
}
}

// DefaultConfig returns a Config with sensible defaults
func DefaultConfig() *Config {
return &Config{
OutputDir: "./writer",
SortBy: "amount",
HTTPClient: http.NewRequestBuilder(),
}
}

// NewConfig creates a Config with the given options
func NewConfig(opts ...Option) *Config {
config := DefaultConfig()
for _, opt := range opts {
opt(config)
}
return config
}
43 changes: 0 additions & 43 deletions internal/writer/csv.go

This file was deleted.

76 changes: 76 additions & 0 deletions internal/writer/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package writer

import (
"fmt"

"github.com/cduggn/ccexplorer/internal/types"
)

// WriterFactory provides type-safe writer creation
type WriterFactory struct {
config *Config
}

// NewWriterFactory creates a new factory with the given configuration
func NewWriterFactory(opts ...Option) *WriterFactory {
return &WriterFactory{
config: NewConfig(opts...),
}
}

// CreateCostUsageWriter creates a writer for cost and usage data
func (f *WriterFactory) CreateCostUsageWriter(format FormatType) (interface{}, error) {
switch format {
case FormatTable:
return NewCostUsageTableWriter(f.configToOptions()...), nil
case FormatCSV:
return NewCostUsageCSVWriter(f.configToOptions()...), nil
case FormatChart:
return NewCostUsageChartWriter(f.configToOptions()...), nil
case FormatVector:
return NewCostUsageVectorWriter(f.configToOptions()...), nil
default:
return nil, fmt.Errorf("unsupported format: %v", format)
}
}

// CreateForecastWriter creates a writer for forecast data
func (f *WriterFactory) CreateForecastWriter() *ForecastTableWriter {
return NewForecastTableWriter(f.configToOptions()...)
}

// WriteCostUsage is a type-safe convenience method for writing cost usage data
func (f *WriterFactory) WriteCostUsage(format FormatType, data types.CostAndUsageOutputType) error {
switch format {
case FormatTable:
writer := NewCostUsageTableWriter(f.configToOptions()...)
return writer.Write(data)
case FormatCSV:
writer := NewCostUsageCSVWriter(f.configToOptions()...)
return writer.Write(data)
case FormatChart:
writer := NewCostUsageChartWriter(f.configToOptions()...)
return writer.Write(data)
case FormatVector:
writer := NewCostUsageVectorWriter(f.configToOptions()...)
return writer.Write(data)
default:
return fmt.Errorf("unsupported format: %v", format)
}
}

// WriteForecast is a type-safe convenience method for writing forecast data
func (f *WriterFactory) WriteForecast(data types.ForecastPrintData) error {
writer := NewForecastTableWriter(f.configToOptions()...)
return writer.Write(data)
}

// configToOptions converts the factory's config to a slice of options
func (f *WriterFactory) configToOptions() []Option {
return []Option{
WithOutputDir(f.config.OutputDir),
WithSortBy(f.config.SortBy),
WithVectorDBConfig(f.config.OpenAIAPIKey, f.config.PineconeAPIKey, f.config.PineconeIndex),
WithHTTPClient(f.config.HTTPClient),
}
}
51 changes: 29 additions & 22 deletions internal/writer/renderers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"io"
"os"

"github.com/cduggn/ccexplorer/internal/http"
"github.com/cduggn/ccexplorer/internal/types"
"github.com/cduggn/ccexplorer/internal/utils"
"github.com/jedib0t/go-pretty/v6/table"
Expand Down Expand Up @@ -112,16 +111,18 @@ func (r *ForecastTableRenderer) Render(data *ForecastTableOutput) error {
}

// CSVRenderer renders CSV data to files
type CSVRenderer struct{}
type CSVRenderer struct {
config *Config
}

// NewCSVRenderer creates a new CSV renderer
func NewCSVRenderer() *CSVRenderer {
return &CSVRenderer{}
func NewCSVRenderer(config *Config) *CSVRenderer {
return &CSVRenderer{config: config}
}

// Render implements the Renderer interface for CSV files
func (r *CSVRenderer) Render(data *CSVOutput) error {
filePath := utils.BuildOutputFilePath(OutputDir, data.Filename)
filePath := utils.BuildOutputFilePath(r.config.OutputDir, data.Filename)
file, err := os.Create(filePath)
if err != nil {
return types.Error{Msg: "Error creating CSV file: " + err.Error()}
Expand All @@ -145,16 +146,18 @@ func (r *CSVRenderer) Render(data *CSVOutput) error {
}

// ChartRenderer renders chart data to HTML files
type ChartRenderer struct{}
type ChartRenderer struct {
config *Config
}

// NewChartRenderer creates a new chart renderer
func NewChartRenderer() *ChartRenderer {
return &ChartRenderer{}
func NewChartRenderer(config *Config) *ChartRenderer {
return &ChartRenderer{config: config}
}

// Render implements the Renderer interface for charts
func (r *ChartRenderer) Render(data *ChartOutput) error {
filePath := utils.BuildOutputFilePath(OutputDir, data.Filename)
filePath := utils.BuildOutputFilePath(r.config.OutputDir, data.Filename)
file, err := os.Create(filePath)
if err != nil {
return types.Error{Msg: "Failed creating chart HTML file: " + err.Error()}
Expand All @@ -165,25 +168,29 @@ func (r *ChartRenderer) Render(data *ChartOutput) error {
}

// VectorRenderer renders vector data to vector databases
type VectorRenderer struct{}
type VectorRenderer struct {
config *Config
client VectorStore
}

// NewVectorRenderer creates a new vector renderer
func NewVectorRenderer() *VectorRenderer {
return &VectorRenderer{}
func NewVectorRenderer(config *Config) *VectorRenderer {
client := NewVectorStoreClient(
config.HTTPClient,
config.OpenAIAPIKey,
config.PineconeIndex,
config.PineconeAPIKey,
)
return &VectorRenderer{
config: config,
client: client,
}
}

// Render implements the Renderer interface for vector databases
func (r *VectorRenderer) Render(data *VectorOutput) error {
// Create the vector store client - this would need to be passed in or configured
client := NewVectorStoreClient(
http.NewRequestBuilder(),
data.IndexName,
"", // API keys would need to be provided
"",
)

// Create embeddings for the vector items
vectors, err := client.CreateEmbeddings(data.Items)
vectors, err := r.client.CreateEmbeddings(data.Items)
if err != nil {
return types.Error{Msg: "Error creating embeddings: " + err.Error()}
}
Expand All @@ -198,7 +205,7 @@ func (r *VectorRenderer) Render(data *VectorOutput) error {

// Convert to Pinecone format and upsert
pineconeData := utils.ConvertToPineconeStruct(data.Items)
resp, err := client.Upsert(context.Background(), pineconeData)
resp, err := r.client.Upsert(context.Background(), pineconeData)
if err != nil {
return types.Error{Msg: "Error upserting to vector store: " + err.Error()}
}
Expand Down
Loading