Files
angel 50adc8e2de
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 5s
Tests / Go tests (push) Failing after 20s
prototyped svg reports
2026-08-20 00:35:13 -06:00

260 lines
6.4 KiB
Go

package reports
import (
"context"
"fmt"
"slices"
"time"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"ruben/inventory2/consts"
"ruben/inventory2/domains/accounts"
"ruben/inventory2/logging"
)
//go:generate concurry -s Store
type (
Store struct {
log *logging.Logger
db *pgxpool.Pool
accts *accounts.Store
}
)
func NewStore(logger *logging.Logger, db *pgxpool.Pool, accts *accounts.Store) *Store {
return &Store{
log: logger,
db: db,
accts: accts,
}
}
func (db *Store) WithContext(ctx context.Context) *StoreWithContext {
return NewStoreWithContext(ctx, db)
}
// TODO: prefix all these names with 'mock' since they're all mock stuff
type (
ListingCountsOverTimeReport struct {
AccountID int64
Platform accounts.Platform
ShopID string
ListingID string
Counts []ListingCountAtTime
}
ListingCountAtTime struct {
EventTimestamp *time.Time
Count int64
}
)
func (r ListingCountsOverTimeReport) MaxCount() ListingCountAtTime {
if len(r.Counts) == 0 {
return ListingCountAtTime{}
}
return slices.MaxFunc(r.Counts, func(a, b ListingCountAtTime) int {
return int(a.Count - b.Count)
})
}
func (r ListingCountsOverTimeReport) MinCount() ListingCountAtTime {
if len(r.Counts) == 0 {
return ListingCountAtTime{}
}
return slices.MinFunc(r.Counts, func(a, b ListingCountAtTime) int {
return int(a.Count - b.Count)
})
}
func (db *Store) GetListingCountsReport(ctx context.Context, acctID int64, platform accounts.Platform, shopID, listingID string) (*ListingCountsOverTimeReport, error) {
counts, err := db.GetListingCountsOverTime(ctx, acctID, platform, shopID, listingID)
if err != nil {
return nil, err
}
return &ListingCountsOverTimeReport{
AccountID: acctID,
Platform: platform,
ShopID: shopID,
ListingID: listingID,
Counts: counts,
}, nil
}
func (db *Store) GetListingCountsOverTime(ctx context.Context, acctID int64, platform accounts.Platform, shopID, listingID string) ([]ListingCountAtTime, error) {
info, ok := getMockShopSchemaInfo(platform)
if !ok {
return nil, consts.ErrNotFound
}
var counts []ListingCountAtTime
err := pgx.BeginTxFunc(ctx, db.db, pgx.TxOptions{
AccessMode: pgx.ReadOnly,
}, func(tx pgx.Tx) error {
rows, err := tx.Query(
ctx,
fmt.Sprintf(
`
SELECT
1
FROM
%s
WHERE
shop_id = @shop_id
AND listing_id = @listing_id
`,
info.listingsTable,
),
pgx.NamedArgs{
"shop_id": shopID,
"listing_id": listingID,
},
)
if err != nil {
return fmt.Errorf("failed to perform query to check the existing of the listing: %w", err)
}
if _, err := pgx.CollectExactlyOneRow(rows, pgx.RowTo[int]); err != nil {
return fmt.Errorf("listing not found: %w", consts.ErrNotFound)
}
rows, err = tx.Query(
ctx,
fmt.Sprintf(
`
SELECT
event_timestamp AS eventTimestamp,
"count"
FROM
%s
WHERE
shop_id = @shop_id
AND listing_id = @listing_id
`,
info.listingCountsView,
),
pgx.NamedArgs{
"shop_id": shopID,
"listing_id": listingID,
},
)
if err != nil {
return fmt.Errorf("failed to perform query: %w", err)
}
if counts, err = pgx.CollectRows(rows, pgx.RowToStructByNameLax[ListingCountAtTime]); err != nil {
return fmt.Errorf("failed to scan rows: %w", err)
}
return nil
})
return counts, err
}
type mockShopSchemaInfo struct {
platform accounts.Platform
shopTable string
listingsTable string
listingCountsView string
}
func getMockShopSchemaInfo(platform accounts.Platform) (mockShopSchemaInfo, bool) {
for _, in := range getAllMockShopSchemaInfos() {
if in.platform == platform {
return in, true
}
}
return mockShopSchemaInfo{}, false
}
func getAllMockShopSchemaInfos() []mockShopSchemaInfo {
return []mockShopSchemaInfo{
{
platform: accounts.Amazon,
shopTable: "mock.shop_amazon",
listingsTable: "mock.shop_amazon_listings",
listingCountsView: "mock.shop_amazon_listing_counts",
},
{
platform: accounts.BigCartel,
shopTable: "mock.shop_big_cartel",
listingsTable: "mock.shop_big_cartel_listings",
listingCountsView: "mock.shop_big_cartel_listing_counts",
},
{
platform: accounts.Ebay,
shopTable: "mock.shop_ebay",
listingsTable: "mock.shop_ebay_listings",
listingCountsView: "mock.shop_ebay_listing_counts",
},
{
platform: accounts.Ecwid,
shopTable: "mock.shop_ecwid",
listingsTable: "mock.shop_ecwid_listings",
listingCountsView: "mock.shop_ecwid_listing_counts",
},
{
platform: accounts.Etsy,
shopTable: "mock.shop_etsy",
listingsTable: "mock.shop_etsy_listings",
listingCountsView: "mock.shop_etsy_listing_counts",
},
{
platform: accounts.Shopify,
shopTable: "mock.shop_shopify",
listingsTable: "mock.shop_shopify_listings",
listingCountsView: "mock.shop_shopify_listing_counts",
},
{
platform: accounts.SquareOnline,
shopTable: "mock.shop_square_online",
listingsTable: "mock.shop_square_online_listings",
listingCountsView: "mock.shop_square_online_listing_counts",
},
{
platform: accounts.Squarespace,
shopTable: "mock.shop_squarespace",
listingsTable: "mock.shop_squarespace_listings",
listingCountsView: "mock.shop_squarespace_listing_counts",
},
{
platform: accounts.Tiktok,
shopTable: "mock.shop_tiktok",
listingsTable: "mock.shop_tiktok_listings",
listingCountsView: "mock.shop_tiktok_listing_counts",
},
{
platform: accounts.WalmartMarketplace,
shopTable: "mock.shop_walmart_marketplace",
listingsTable: "mock.shop_walmart_marketplace_listings",
listingCountsView: "mock.shop_walmart_marketplace_listing_counts",
},
{
platform: accounts.Wix,
shopTable: "mock.shop_wix",
listingsTable: "mock.shop_wix_listings",
listingCountsView: "mock.shop_wix_listing_counts",
},
{
platform: accounts.WooCommerce,
shopTable: "mock.shop_woo_commerce",
listingsTable: "mock.shop_woo_commerce_listings",
listingCountsView: "mock.shop_woo_commerce_listing_counts",
},
{
platform: accounts.Zoho,
shopTable: "mock.shop_zoho",
listingsTable: "mock.shop_zoho_listings",
listingCountsView: "mock.shop_zoho_listing_counts",
},
}
}