Monitors wish list items and notifies you of real time discounts Go
👤 Sharing: AI
```go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"time"
)
// Item represents a wish-list item. It includes the name, ID, desired price, and last known price.
type Item struct {
ID string `json:"id"` // Unique identifier for the item (e.g., product SKU)
Name string `json:"name"` // Name of the item
DesiredPrice float64 `json:"desired_price"` // The price you'd like to pay
LastPrice float64 `json:"last_price"` // The last price we saw for this item
}
// PriceFetcher is an interface for any function that can fetch the price of an item.
type PriceFetcher interface {
GetPrice(itemID string) (float64, error)
}
// DummyPriceFetcher is a placeholder implementation for fetching prices. In a real application, you would integrate with an actual API.
type DummyPriceFetcher struct{}
// GetPrice simulates fetching a price from a data source.
func (d DummyPriceFetcher) GetPrice(itemID string) (float64, error) {
// In a real-world scenario, this would make an API call.
// For example purposes, we'll return a random price based on the item ID.
switch itemID {
case "item123":
return 99.99, nil // Fixed price for simplicity
case "item456":
return 49.99, nil
case "item789":
// Simulate a fluctuating price.
now := time.Now()
if now.Second()%2 == 0 {
return 14.99, nil
} else {
return 19.99, nil
}
default:
return 0, fmt.Errorf("item not found")
}
}
// readWishlist reads the wishlist data from a JSON file.
func readWishlist(filename string) ([]Item, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
var items []Item
err = json.Unmarshal(data, &items)
if err != nil {
return nil, err
}
return items, nil
}
// saveWishlist writes the wishlist data back to the JSON file. This is important for persisting the last known price.
func saveWishlist(filename string, items []Item) error {
data, err := json.MarshalIndent(items, "", " ") // Indent for readability
if err != nil {
return err
}
err = ioutil.WriteFile(filename, data, 0644) // Permissions: read/write for owner, read for others
if err != nil {
return err
}
return nil
}
// checkPrice checks the current price of an item and notifies if there's a discount.
func checkPrice(item *Item, priceFetcher PriceFetcher) {
price, err := priceFetcher.GetPrice(item.ID)
if err != nil {
log.Printf("Error fetching price for item %s (%s): %v\n", item.Name, item.ID, err)
return
}
if price < item.LastPrice || item.LastPrice == 0 {
//Either the price has dropped, or this is the first time we're checking.
if price <= item.DesiredPrice {
fmt.Printf("? DISCOUNT ALERT! %s (%s) is now %.2f (desired price: %.2f)\n", item.Name, item.ID, price, item.DesiredPrice)
} else {
fmt.Printf("Price update: %s (%s) is now %.2f (desired price: %.2f)\n", item.Name, item.ID, price, item.DesiredPrice)
}
item.LastPrice = price
} else {
fmt.Printf("%s (%s) is still %.2f\n", item.Name, item.ID, price)
}
}
func main() {
// Configurable parameters
wishlistFile := "wishlist.json" // File to store the wishlist data.
checkInterval := 5 * time.Second // How often to check prices. For demonstration, this is very short.
priceFetcher := DummyPriceFetcher{} // Replace with a real PriceFetcher implementation.
// Load the wishlist from the file.
wishlist, err := readWishlist(wishlistFile)
if err != nil {
log.Fatalf("Error reading wishlist: %v", err)
}
// Main loop: Continuously check prices.
ticker := time.NewTicker(checkInterval)
defer ticker.Stop() // Ensure the ticker is stopped when the program exits.
for range ticker.C {
fmt.Println("--- Checking prices ---")
for i := range wishlist {
checkPrice(&wishlist[i], priceFetcher)
}
// Save the updated last known prices to the file.
err = saveWishlist(wishlistFile, wishlist)
if err != nil {
log.Printf("Error saving wishlist: %v", err)
}
}
}
```
Key improvements and explanations:
* **Clearer Structure:** The code is organized into functions with well-defined responsibilities, making it easier to understand and maintain.
* **Error Handling:** Includes comprehensive error handling for file operations and price fetching. Uses `log.Fatalf` for critical errors that prevent the program from running, and `log.Printf` for errors that can be handled more gracefully (like failing to fetch the price for a single item). This prevents crashes and provides helpful debugging information.
* **Data Persistence:** The `readWishlist` and `saveWishlist` functions handle reading and writing the wishlist data to a JSON file. This is crucial for remembering the last known price of each item between program runs, preventing unnecessary notifications.
* **Realistic Price Fetching (Simulated):** The `DummyPriceFetcher` simulates the process of fetching prices from an external data source (e.g., an e-commerce API). This makes the example more realistic and demonstrates how you would integrate with an actual price source. It also includes an example of fluctuating price to show multiple changes.
* **Configuration:** The filename, check interval, and `PriceFetcher` are now configurable, making the program more flexible.
* **Notification Logic:** The `checkPrice` function now properly checks if the *current* price is less than the *last known* price before sending an alert. It also correctly handles the case where the `LastPrice` is zero (meaning the item hasn't been checked before). It outputs different messages depending if there's a true discount or just a price update.
* **JSON Serialization/Deserialization:** Uses the `encoding/json` package to read and write the wishlist data to a JSON file. The `json:"..."` tags on the `Item` struct are used to specify the field names in the JSON file. The use of `json.MarshalIndent` makes the JSON file much more readable.
* **Ticker:** Uses `time.NewTicker` to check prices at regular intervals. This is more efficient than using `time.Sleep` in a loop. The `defer ticker.Stop()` ensures the ticker is stopped correctly.
* **Comments and Documentation:** Comprehensive comments explain the purpose of each function and the logic behind the code. The `Item` struct now has comments on each field.
* **PriceFetcher Interface:** Introduces an interface for fetching prices. This allows you to easily swap out the `DummyPriceFetcher` with a real implementation that interacts with an actual API.
* **Idempotent Savings:** Only saves the wishlist to the file *after* checking all prices, so that a single error in fetching a price does not cause data loss or inconsistent state.
* **Clearer Output:** The output is more informative, including the item name, ID, current price, and desired price. It also indicates whether the price change is a discount or just a price update.
How to run:
1. **Save the code:** Save the code as `main.go`.
2. **Create `wishlist.json`:** Create a file named `wishlist.json` in the same directory as `main.go`. Populate it with sample data in JSON format:
```json
[
{
"id": "item123",
"name": "Example Item 1",
"desired_price": 90.00,
"last_price": 0
},
{
"id": "item456",
"name": "Example Item 2",
"desired_price": 40.00,
"last_price": 0
},
{
"id": "item789",
"name": "Fluctuating Item",
"desired_price": 10.00,
"last_price": 0
}
]
```
3. **Run the program:** Open a terminal, navigate to the directory where you saved the files, and run `go run main.go`.
The program will then periodically check the prices of the items in your wishlist and notify you of any discounts or price changes. The prices are currently simulated by the `DummyPriceFetcher`, but you can replace this with a real implementation that fetches prices from an actual API.
👁️ Viewed: 5
Comments