111 lines
3.4 KiB
Go
111 lines
3.4 KiB
Go
package marketing
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// PresetID identifies a seasonal content-calendar preset.
|
||
|
|
type PresetID string
|
||
|
|
|
||
|
|
const (
|
||
|
|
PresetBlackFriday PresetID = "black_friday"
|
||
|
|
PresetChristmas PresetID = "christmas"
|
||
|
|
)
|
||
|
|
|
||
|
|
// CampaignStructureKey is stored on export_feeds.template JSON (no migration).
|
||
|
|
const CampaignStructureKey = "_campaign"
|
||
|
|
|
||
|
|
// Preset is a dated seasonal window for preparing an export feed.
|
||
|
|
type Preset struct {
|
||
|
|
ID PresetID `json:"id"`
|
||
|
|
Name string `json:"name"`
|
||
|
|
Description string `json:"description"`
|
||
|
|
StartDate string `json:"start_date"`
|
||
|
|
EndDate string `json:"end_date"`
|
||
|
|
Year int `json:"year"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// StructureMeta is persisted under template._campaign.
|
||
|
|
type StructureMeta struct {
|
||
|
|
PresetID PresetID `json:"presetId"`
|
||
|
|
Name string `json:"name"`
|
||
|
|
StartDate string `json:"startDate"`
|
||
|
|
EndDate string `json:"endDate"`
|
||
|
|
Year int `json:"year"`
|
||
|
|
PreparedAt string `json:"preparedAt"`
|
||
|
|
}
|
||
|
|
|
||
|
|
func pad2(n int) string {
|
||
|
|
return fmt.Sprintf("%02d", n)
|
||
|
|
}
|
||
|
|
|
||
|
|
func toISODate(t time.Time) string {
|
||
|
|
return fmt.Sprintf("%s-%s-%s", pad2(t.Year()), pad2(int(t.Month())), pad2(t.Day()))
|
||
|
|
}
|
||
|
|
|
||
|
|
// BlackFridayDate returns Black Friday (day after US Thanksgiving) in UTC date parts.
|
||
|
|
func BlackFridayDate(year int) time.Time {
|
||
|
|
nov1 := time.Date(year, time.November, 1, 0, 0, 0, 0, time.UTC)
|
||
|
|
dow := int(nov1.Weekday()) // Sunday=0
|
||
|
|
firstThursday := 1 + ((4 - dow + 7) % 7)
|
||
|
|
fourthThursday := firstThursday + 21
|
||
|
|
return time.Date(year, time.November, fourthThursday+1, 0, 0, 0, 0, time.UTC)
|
||
|
|
}
|
||
|
|
|
||
|
|
// ResolvePreset returns date window for a preset id and year.
|
||
|
|
func ResolvePreset(id PresetID, year int) (Preset, error) {
|
||
|
|
if year < 2000 || year > 2100 {
|
||
|
|
return Preset{}, ClientMsg("invalid year")
|
||
|
|
}
|
||
|
|
switch id {
|
||
|
|
case PresetBlackFriday:
|
||
|
|
bf := BlackFridayDate(year)
|
||
|
|
start := bf.AddDate(0, 0, -7)
|
||
|
|
end := bf.AddDate(0, 0, 3)
|
||
|
|
return Preset{
|
||
|
|
ID: id,
|
||
|
|
Name: "Black Friday",
|
||
|
|
Description: "Promo window around Black Friday — prepare a Google Shopping-style export feed.",
|
||
|
|
StartDate: toISODate(start),
|
||
|
|
EndDate: toISODate(end),
|
||
|
|
Year: year,
|
||
|
|
}, nil
|
||
|
|
case PresetChristmas:
|
||
|
|
return Preset{
|
||
|
|
ID: PresetChristmas,
|
||
|
|
Name: "Christmas",
|
||
|
|
Description: "Holiday catalog push from Dec 1 through Boxing Day.",
|
||
|
|
StartDate: toISODate(time.Date(year, time.December, 1, 0, 0, 0, 0, time.UTC)),
|
||
|
|
EndDate: toISODate(time.Date(year, time.December, 26, 0, 0, 0, 0, time.UTC)),
|
||
|
|
Year: year,
|
||
|
|
}, nil
|
||
|
|
default:
|
||
|
|
return Preset{}, ClientMsg("preset_id must be black_friday or christmas")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ListPresets returns Black Friday + Christmas for the year.
|
||
|
|
func ListPresets(year int) []Preset {
|
||
|
|
bf, _ := ResolvePreset(PresetBlackFriday, year)
|
||
|
|
xmas, _ := ResolvePreset(PresetChristmas, year)
|
||
|
|
return []Preset{bf, xmas}
|
||
|
|
}
|
||
|
|
|
||
|
|
// DefaultCampaignMappings are Google Shopping-ish CSV column → product field sources.
|
||
|
|
func DefaultCampaignMappings() map[string]string {
|
||
|
|
return map[string]string{
|
||
|
|
"id": "product_id",
|
||
|
|
"title": "processed_name",
|
||
|
|
"description": "processed_description",
|
||
|
|
"link": "attr.url",
|
||
|
|
"image_link": "attr.image",
|
||
|
|
"availability": "attr.availability",
|
||
|
|
"price": "attr.price",
|
||
|
|
"brand": "attr.brand",
|
||
|
|
"gtin": "gtin",
|
||
|
|
"google_product_category": "category",
|
||
|
|
"condition": "attr.condition",
|
||
|
|
}
|
||
|
|
}
|