picnic-cli/index.ts

140 lines
3.7 KiB
TypeScript

import PicnicClient from "picnic-api"
import type {
ApiConfig,
Decorator,
PriceDecorator,
Delivery,
} from "picnic-api/lib/types/picnic-api.js"
import type { ParsedArgs } from 'minimist'
import { DateTime } from "luxon"
import fs from "node:fs"
import { jsonOut, csvOut, sum, runCommands, getXdgConfigHome } from "./util.js"
import path from "node:path"
type QuantityDecorator = {
type: "QUANTITY"
quantity: number
}
function getDecorator(
thing: { decorators?: Decorator[] },
name: "QUANTITY",
fallback?: number
): number
function getDecorator(
thing: { decorators?: Decorator[] },
name: "PRICE",
fallback?: number
): number
function getDecorator<R>(
thing: { decorators?: Decorator[] },
name: string,
fallback?: R
): R | undefined {
if (!thing.decorators) return fallback
const l = thing.decorators.filter(x => x.type == name)
if (l.length) {
switch (name) {
case "QUANTITY":
return l[0].quantity
case "PRICE":
return l[0].display_price
}
}
return fallback
}
function deliveryToCSV(delv: Delivery) {
const delivered = DateTime.fromISO(delv.slot.window_start).toFormat(
"yyyy-MM-dd HH:mm"
)
for (const order of delv.orders) {
const ordered = DateTime.fromISO(order.creation_time).toFormat(
"yyyy-MM-dd HH:mm"
)
for (const orderline of order.items) {
const totalqty = sum(
orderline.items.map(x => getDecorator(x, "QUANTITY"))
)
// todo orderline decorators
const real_price = getDecorator(
orderline as any,
"PRICE",
orderline.display_price
)
const display_price = orderline.display_price
const totalprice = sum(orderline.items.map(x => x.price))
const promo = real_price / totalprice
for (const orderarticle of orderline.items) {
csvOut({
id: orderarticle.id,
name: orderarticle.name,
quantity: getDecorator(orderarticle, "QUANTITY"),
price: Math.round(orderarticle.price * promo),
promo: (1 - real_price / orderline.display_price).toFixed(2),
unit_quantity: orderarticle.unit_quantity,
ordered,
delivered,
delivery_id: delv.delivery_id,
})
}
}
}
}
const commands = (argv: ParsedArgs) => {
const LOGIN_FILE = path.join(getXdgConfigHome(), "picnic-cli.json")
const config: ApiConfig = fs.existsSync(LOGIN_FILE)
? JSON.parse(fs.readFileSync(LOGIN_FILE, "utf8"))
: {}
const p = new PicnicClient(config)
return {
async login(user: string, password: string) {
await p.login(user, password)
fs.writeFileSync(
LOGIN_FILE,
JSON.stringify({
countryCode: p.countryCode,
authKey: p.authKey,
} as ApiConfig)
)
},
async getUserDetails() {
jsonOut(await p.getUserDetails())
},
async deliveries() {
const deliveries = await p.getDeliveries()
if (argv.json) {
jsonOut(deliveries)
} else {
for (const delv of deliveries) {
if (delv.status == "CANCELLED") continue
const dt = DateTime.fromISO(
delv.delivery_time ? delv.delivery_time.start : delv.slot.window_start
)
csvOut({
id: delv.delivery_id,
delivered: dt.toFormat("yyyy-MM-dd HH:mm"),
status: delv.status,
price: sum(delv.orders.map(x => x.total_price)),
})
}
}
},
async delivery(...ids: string[]) {
for (const deliveryId of ids) {
const delv = await p.getDelivery(deliveryId)
if (argv.json) {
jsonOut(delv)
} else {
deliveryToCSV(delv)
}
}
},
}
}
runCommands(commands)