cdb_ui/lib/api.dart
2024-10-19 21:15:02 +02:00

595 lines
14 KiB
Dart

import 'dart:convert';
import 'package:cdb_ui/pages/supply.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
// todo : api errors
class API {
SharedPreferences? pref;
static final API _instance = API._internal();
// cache
List<Item>? items;
Map<String, Location>? locations;
Map<String, FlowInfo>? flowInfos;
factory API() {
return _instance;
}
API._internal();
Future<void> prefetch() async {
// todo : prefetch
// fetch items
var resp = jsonDecode(await getRequest("$instance/items"));
var lst = resp["items"] as List<dynamic>;
items = lst.map((x) => Item(x)).toList();
// fetch locations
var locResp = jsonDecode(await getRequest("$instance/locations"))
as Map<String, dynamic>;
locations = locResp.map((key, value) => MapEntry(key, Location(value)));
// fetch flowInfos
var flowResp =
jsonDecode(await getRequest("$instance/flows")) as Map<String, dynamic>;
flowInfos = flowResp.map((key, value) => MapEntry(key, FlowInfo(value)));
}
Future<void> init(Function refresh) async {
pref = await SharedPreferences.getInstance();
instance = pref!.getString("instance") ?? "";
refresh();
}
bool isInit() {
if (pref == null) {
return false;
}
return pref!.containsKey("token") && pref!.containsKey("instance");
}
bool isPrefetched() {
return items != null && locations != null && flowInfos != null;
}
void save(String instance, String token) {
pref!.setString("instance", instance);
pref!.setString("token", token);
this.instance = instance;
}
String instance = "";
Future<String> getRequest(String url) async {
var resp = await http.get(Uri.parse(url), headers: <String, String>{
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json; charset=UTF-8',
'Token': pref!.getString("token")!
});
return utf8.decode(resp.bodyBytes);
}
Future<String> postRequest(String url, Map<String, dynamic> data) async {
var resp = await http.post(Uri.parse(url),
headers: <String, String>{
'Access-Control-Allow-Origin': '*',
'Content-Type': 'application/json; charset=UTF-8',
'Token': pref!.getString("token")!
},
body: jsonEncode(data));
return utf8.decode(resp.bodyBytes);
}
// /items
List<Item> getItems() {
return items!;
}
Future<GlobalItemStat> getGlobalItemStat() async {
var resp = jsonDecode(await getRequest("$instance/items/stat"));
return GlobalItemStat(resp);
}
// /item/<item>/<variant>/unique?<field>
Future<List<String>> getUniqueField(
String item, String variant, String field) async {
var resp = jsonDecode(
await getRequest("$instance/item/$item/$variant/unique?field=$field"));
List<String> ret = [];
for (var e in resp as List<dynamic>) {
ret.add(e);
}
return ret;
}
Map<String, Location> getLocations() {
return locations!;
}
Item getItem(String item) {
return items!.firstWhere((x) => x.id == item);
}
Future<Transaction> getTransaction(String id) async {
return Transaction(
jsonDecode(await getRequest("$instance/transaction/$id")));
}
Future<List<Transaction>> getTransactionsOfLocation(String location,
{bool recursive = true}) async {
var url = "$instance/location/$location/inventory";
if (recursive) {
url += "?recursive=true";
}
var resp = jsonDecode(await getRequest(url)) as List<dynamic>;
return resp.map((x) => Transaction(x)).toList();
}
Future<List<Transaction>> getConsumedItems(String item, String variant,
{String? destination}) async {
var url = "$instance/item/$item/$variant/demand";
if (destination != null) {
url += "?destination=$destination";
}
var resp = jsonDecode(await getRequest(url)) as List<dynamic>;
return resp.map((x) => Transaction(x)).toList();
}
// /item/<item_id>/inventory?<origin>
Future<List<Transaction>> getInventory(String item, {String? origin}) async {
var url = "$instance/item/$item/inventory";
if (origin != null) {
url += "?origin=$origin";
}
var resp = jsonDecode(await getRequest(url)) as List<dynamic>;
return resp.map((x) => Transaction(x)).toList();
}
// /item/<item_id>/<variant_id>/inventory
Future<List<Transaction>> getInventoryOfVariant(
String item, String variant) async {
var resp =
jsonDecode(await getRequest("$instance/item/$item/$variant/inventory"))
as List<dynamic>;
return resp.map((x) => Transaction(x)).toList();
}
// /supply
Future<String> supplyItem(String item, String variant, double price,
String? origin, String? location, String? note) async {
if (origin!.isEmpty) {
origin = null;
}
if (location!.isEmpty) {
location = null;
}
if (note!.isEmpty) {
note = null;
}
var req = await postRequest("$instance/supply", {
"item": item,
"variant": variant,
"price": price,
"origin": origin,
"location": location,
"note": note
});
var resp = jsonDecode(req);
return resp["uuid"];
}
// /demand
Future<void> consumeItem(
String transaction, String destination, double price) async {
await postRequest("$instance/demand",
{"uuid": transaction, "destination": destination, "price": price});
}
// /item/<item_id>/<variant_id>/stat
Future<dynamic> getStat(String item, String variant,
{bool full = false}) async {
if (full) {
return FullItemVariantStat(jsonDecode(
await getRequest("$instance/item/$item/$variant/stat?full=true")));
}
return ItemVariantStat(
jsonDecode(await getRequest("$instance/item/$item/$variant/stat")));
}
String getImageURL(String item) {
return "$instance/$item/image";
}
// /item/<item_id>/<variant_id>/price_history?<origin>
Future<List<double>> getPriceHistory(String item, String variant,
{String? origin}) async {
var url = "$instance/item/$item/$variant/price_history";
if (origin != null) {
url += "?origin=$origin";
}
var resp = jsonDecode(await getRequest(url)) as List<dynamic>;
return resp.map((x) => x as double).toList();
}
Future<double> getLatestPrice(String item, String variant,
{String? origin}) async {
var url = "$instance/item/$item/$variant/price_latest";
if (origin != null) {
url += "?origin=$origin";
}
var resp = jsonDecode(await getRequest(url)) as Map<String, dynamic>;
return resp as double;
}
// Flows
// /flows
Map<String, FlowInfo> getFlows() {
return flowInfos!;
}
// /flow/<id>/info
FlowInfo getFlowInfo(String id) {
return flowInfos![id]!;
}
Future<Flow> getFlow(String id) async {
return Flow(jsonDecode(await getRequest("$instance/flow/$id")));
}
Future<List<Flow>> getActiveFlowsOf(String id) async {
var res = jsonDecode(await getRequest("$instance/flow/$id/active"))
as List<dynamic>;
return res.map((x) => Flow(x)).toList();
}
// /flow/<id>
Future<String> startFlow(String id, {List<String>? input}) async {
var resp =
jsonDecode(await postRequest("$instance/flow/$id", {"input": input}));
return resp["uuid"];
}
// /flow/<id>/end
Future<Map<String, List<String>>?> endFlow(String id,
{List<SupplyForm>? produced}) async {
var resp = jsonDecode(await postRequest("$instance/flow/$id/end",
{"produced": produced?.map((x) => x.json()).toList()}));
if (produced != null) {
var produced = resp["produced"] as Map<String, dynamic>;
return produced.map(
(key, value) {
return MapEntry(key, (value as List<dynamic>).cast<String>());
},
);
}
return null;
}
// /flow/<id>/continue
Future<String> continueFlow(String id, {List<String>? input}) async {
var resp = jsonDecode(
await postRequest("$instance/flow/$id/continue", {"input": input}));
return resp["uuid"];
}
Future<List<Transaction>> getExpiredItems() async {
var resp = jsonDecode(await getRequest("$instance/items/expired"))
as List<dynamic>;
return resp.map((x) => Transaction(x)).toList();
}
Future<List<MinItem>> getItemsUnderMin() async {
var resp =
jsonDecode(await getRequest("$instance/items/min")) as List<dynamic>;
return resp.map((x) => MinItem(x)).toList();
}
Future<void> moveTransaction(String id, String newLocation) async {
jsonDecode(await postRequest(
"$instance/transaction/$id/move", {"to": newLocation}));
}
Location getLocation(String id) {
return locations![id]!;
}
Future<String> addNoteToFlow(String flowID, String content) async {
var res = jsonDecode(
await postRequest("$instance/flow/$flowID/note", {"content": content}));
return res["uuid"];
}
Future<List<FlowNote>> getNotesOfFlow(String flowID) async {
var resp = jsonDecode(await getRequest("$instance/flow/$flowID/notes"))
as List<dynamic>;
return resp.map((x) => FlowNote(x)).toList();
}
}
class FlowInfo {
late String id;
late String name;
late List<String> depends;
late String? next;
late List<String>? produces;
FlowInfo(Map<String, dynamic> json) {
id = json["id"];
name = json["name"];
depends = (json["depends"] as List<dynamic>).cast<String>();
next = json["next"];
produces = json["produces"] != null
? (json["produces"] as List<dynamic>).cast<String>()
: null;
}
}
class Item {
late String id;
late String? image;
late String name;
late String? category;
late Map<String, ItemVariant> variants;
Item(Map<String, dynamic> json) {
id = json["uuid"];
image = json["image"];
name = json["name"];
category = json["category"];
variants = <String, ItemVariant>{};
json["variants"].forEach((key, value) {
variants[key] = ItemVariant(value);
});
}
}
class ItemVariant {
late String item;
late String variant;
late String name;
int? min;
int? expiry;
List<int>? barcodes;
ItemVariant(Map<String, dynamic> json) {
item = json["item"];
variant = json["variant"];
name = json["name"];
min = json["min"];
expiry = json["expiry"];
barcodes = json["barcodes"] != null
? (json["barcodes"] as List<dynamic>).cast<int>()
: null;
}
}
class Transaction {
late String uuid;
late String item;
late String variant;
late double price;
String? origin;
late int timestamp;
ConsumeInfo? consumed;
late bool expired;
String? note;
Location? location;
Transaction(Map<String, dynamic> json) {
uuid = json["uuid"];
item = json["item"];
variant = json["variant"];
price = json["price"];
origin = json["origin"];
timestamp = json["timestamp"];
expired = json["expired"];
note = json["note"];
consumed = json["consumed"] != null ? ConsumeInfo(json["consumed"]) : null;
location = json["location"] != null ? Location(json["location"]) : null;
}
Transaction.inMemory(String itemID, String variantID, this.price,
String? origin, Location? location, String? note) {
uuid = "";
item = itemID;
variant = variantID;
origin = origin;
timestamp = 0;
consumed = null;
expired = false;
note = note;
location = location;
}
}
class ConsumeInfo {
late String destination;
late double price;
late int timestamp;
ConsumeInfo(Map<String, dynamic> json) {
destination = json["destination"];
price = json["price"];
timestamp = json["timestamp"];
}
}
class ItemVariantStat {
late int amount;
late double totalPrice;
ItemVariantStat(Map<String, dynamic> json) {
amount = json["amount"];
totalPrice = json["total_price"];
}
}
class Location {
late String id;
late String name;
late String? parent;
late LocationCondition? conditions;
Location.zero() {
id = "";
name = "";
parent = null;
conditions = null;
}
Location(Map<String, dynamic> json) {
id = json["id"];
name = json["name"];
parent = json["parent"];
conditions = json["conditions"] != null ? (json["conditions"]) : null;
}
String fullNamePath(Map<String, Location> locations) {
var name = this.name;
if (parent != null) {
name = "${locations[parent!]!.fullNamePath(locations)} / $name";
}
return name;
}
}
class LocationCondition {
late String temperature;
LocationCondition(Map<String, dynamic> json) {
temperature = json["temperature"];
}
}
class MinItem {
late String itemVariant;
late int need;
MinItem(Map<String, dynamic> json) {
itemVariant = json["item_variant"];
need = json["need"];
}
}
class Flow {
late String id;
late int started;
late String kind;
late List<String>? input;
late FlowDone? done;
Flow(Map<String, dynamic> json) {
id = json["id"];
started = json["started"];
kind = json["kind"];
input = json["input"] != null
? (json["input"] as List<dynamic>).cast<String>()
: null;
done = (json["done"] != null) ? FlowDone(json["done"]) : null;
}
}
class FlowDone {
late int ended;
late String? next;
late List<String>? produced;
FlowDone(Map<String, dynamic> json) {
ended = json["ended"];
next = json["next"];
produced = json["produced"];
}
}
class FlowNote {
late String uuid;
late int timestamp;
late String content;
late String onFlow;
FlowNote(Map<String, dynamic> json) {
uuid = json["uuid"];
timestamp = json["timestamp"];
content = json["content"];
onFlow = json["on_flow"];
}
}
class GlobalItemStat {
late int itemCount;
late int totalTransactions;
late double totalPrice;
GlobalItemStat(Map<String, dynamic> json) {
itemCount = json["item_count"];
totalTransactions = json["total_transactions"];
totalPrice = json["total_price"];
}
}
(String, String) itemVariant(String iv) {
var split = iv.split("::");
return (split[0], split[1]);
}
class FullItemVariantStat {
late int amount;
late double totalPrice;
late double expiryRate;
late Map<String, OriginStat> origins;
FullItemVariantStat(Map<String, dynamic> json) {
amount = json["amount"];
totalPrice = json["total_price"];
expiryRate = json["expiry_rate"];
origins = (json["origins"] as Map<String, dynamic>)
.map((key, value) => MapEntry(key, OriginStat(value)));
}
}
class OriginStat {
late double averagePrice;
late int inventory;
OriginStat(Map<String, dynamic> json) {
averagePrice = json["average_price"];
inventory = json["inventory"];
}
}