Skip to content
Snippets Groups Projects
Commit f2bcee81 authored by Gomez Goiri, Aitor's avatar Gomez Goiri, Aitor
Browse files

Collecting stats for cement companies

parent 28233092
Branches
No related tags found
No related merge requests found
// COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022. // COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
const faker = require("@faker-js/faker")
const { Option } = require("commander") const { Option } = require("commander")
const { createCLIApp, HypercogApiClient } = require("@hypercog/utils") const { createCLIApp, HypercogApiClient } = require("@hypercog/utils")
...@@ -38,22 +39,22 @@ const simulateCycle = async (arg1, cmd) => { ...@@ -38,22 +39,22 @@ const simulateCycle = async (arg1, cmd) => {
await signInAs(apiClient, otherArgs.sidenorUser, password) await signInAs(apiClient, otherArgs.sidenorUser, password)
let batchId = await createAssetAndSendToCone(apiClient, "simulated asset 1") let batchId = await createAssetAndSendToCone(apiClient, "simulated asset 1")
batchId = await createAssetAndSendToCone(apiClient, "simulated asset 2") //batchId = await createAssetAndSendToCone(apiClient, "simulated asset 2")
batchId = await createAssetAndSendToCone(apiClient, "simulated asset 3") //batchId = await createAssetAndSendToCone(apiClient, "simulated asset 3")
const selectedStock = await sendToStock(apiClient, { assetId: batchId }) const selectedStock = await sendToStock(apiClient, { assetId: batchId })
await signInAs(apiClient, otherArgs.cement1User, password) await signInAs(apiClient, otherArgs.cement1User, password)
await bidAsset(apiClient, { await bidAsset(apiClient, {
assetId: selectedStock.id, assetId: selectedStock.id,
price: 2 price: faker.datatype.number({ min: 50, max: 150 })
}) })
await signInAs(apiClient, otherArgs.cement2User, password) await signInAs(apiClient, otherArgs.cement2User, password)
await bidAsset(apiClient, { await bidAsset(apiClient, {
assetId: selectedStock.id, assetId: selectedStock.id,
quantity: selectedStock.quantity - 500, quantity: selectedStock.quantity - 500,
price: 3 price: faker.datatype.number({ min: 25, max: 200 })
}) })
await signInAs(apiClient, otherArgs.sidenorUser, password) await signInAs(apiClient, otherArgs.sidenorUser, password)
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
"": { "": {
"name": "@hypercog/batch-sim", "name": "@hypercog/batch-sim",
"dependencies": { "dependencies": {
"@faker-js/faker": "^5.5.3",
"commander": "^9.0.0" "commander": "^9.0.0"
} }
}, },
...@@ -38,6 +39,11 @@ ...@@ -38,6 +39,11 @@
"inquirer": "^8.2.0" "inquirer": "^8.2.0"
} }
}, },
"node_modules/@faker-js/faker": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz",
"integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw=="
},
"node_modules/commander": { "node_modules/commander": {
"version": "9.0.0", "version": "9.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.0.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-9.0.0.tgz",
...@@ -48,6 +54,11 @@ ...@@ -48,6 +54,11 @@
} }
}, },
"dependencies": { "dependencies": {
"@faker-js/faker": {
"version": "5.5.3",
"resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz",
"integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw=="
},
"commander": { "commander": {
"version": "9.0.0", "version": "9.0.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-9.0.0.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-9.0.0.tgz",
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
"@hypercog/utils": "^0.0.1", "@hypercog/utils": "^0.0.1",
"@hypercog/sidenor-cli": "^0.0.1", "@hypercog/sidenor-cli": "^0.0.1",
"@hypercog/cement-cli": "^0.0.1", "@hypercog/cement-cli": "^0.0.1",
"@faker-js/faker": "^5.5.3",
"commander": "^9.0.0" "commander": "^9.0.0"
} }
} }
...@@ -19,7 +19,8 @@ const bidAsset = async ( ...@@ -19,7 +19,8 @@ const bidAsset = async (
{ {
type: "number", type: "number",
name: "inputQuantity", name: "inputQuantity",
message: `Choose an ammount of slag (max: ${assetToBid.quantity} ${assetToBid.units}):` message: `Choose an ammount of slag (max: ${assetToBid.quantity} ${assetToBid.units}):`,
default: Math.floor(assetToBid.quantity * 0.1) // Default: 10%
} }
]) ])
chosenQuantity = inputQuantity chosenQuantity = inputQuantity
......
...@@ -39,35 +39,45 @@ const humanizePercentage = value => { ...@@ -39,35 +39,45 @@ const humanizePercentage = value => {
if (value.percentage === undefined) { if (value.percentage === undefined) {
return value.total return value.total
} }
return `${value.total.toFixed(3)} [%${(value.percentage * 100).toFixed(2)}]` return `${value.total.toFixed(3)} [${(value.percentage * 100).toFixed(2)} %]`
} }
const humanizePrice = value => const humanizePrice = value =>
`${value.avg.toFixed(2)} [${value.min.toFixed(2)}, ${value.max.toFixed(2)}]` `${value.avg.toFixed(2)} [${value.min.toFixed(2)}, ${value.max.toFixed(2)}]`
const toLine = stat => [ const toSteelLine = stat => [
stat.total.toFixed(3), stat.total.toFixed(3),
humanizePercentage(stat.reused), humanizePercentage(stat.reused),
humanizePercentage(stat.discarded), humanizePercentage(stat.discarded),
humanizePrice(stat.price) humanizePrice(stat.price)
] ]
const toCementLine = stat => [stat.total.toFixed(3), humanizePrice(stat.price)]
const getStats = async apiClient => { const getStats = async apiClient => {
const stats = await apiClient.getStats() const stats = await apiClient.getStats()
const tableValues = [ let tableValues = [
[ [
"", "",
"Total slag\nt", "Total slag\nt",
"Reused\nt [%]", "Reused\nt [%]",
"Discarded\nt [%]", "Discarded\nt [%]",
"Price (€)\n Avg [Min, Max]" "Price (€/t)\n Avg [Min, Max]"
], ],
...Object.entries(stats.perSteelCompany || []).map(([org, val]) => [ ...Object.entries(stats.perSteelCompany || []).map(([org, val]) => [
org, org,
...toLine(val) ...toSteelLine(val)
]) ])
] ]
console.log(table(tableValues, TABLE_CONFIG))
tableValues = [
["", "Total slag\nt", "Price (€/t)\n Avg [Min, Max]"],
...Object.entries(stats.perCementCompany || []).map(([org, val]) => [
org,
...toCementLine(val)
])
]
console.log(table(tableValues, TABLE_CONFIG)) console.log(table(tableValues, TABLE_CONFIG))
} }
......
...@@ -199,7 +199,7 @@ const bidResponse = ...@@ -199,7 +199,7 @@ const bidResponse =
name: "inputBidder", name: "inputBidder",
message: `Select the bidder whose bid will be ${action}ed`, message: `Select the bidder whose bid will be ${action}ed`,
choices: assetSelected.fields.bids.map(b => ({ choices: assetSelected.fields.bids.map(b => ({
name: `${b.bidder.id} (quantity: ${b.quantity} ${assetSelected.units}, price: ${b.price})`, name: `${b.bidder.id} (quantity: ${b.quantity} ${assetSelected.units}, price: ${b.price})`,
value: b.bidder value: b.bidder
})) }))
} }
......
// controller.go /**
// COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2018. * bid_input.go
// controller.go is part of the Computer file TRACEBLOCK. Copyrighted under U.S. Copyright Office Registration Number TXu002105160, registered on 2018-06-25 by FUNDACIÓN TECNALIA RESEARCH & INNOVATION. *
// This license is effective worldwide. * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
*/
package bid package bid
...@@ -15,6 +16,8 @@ import ( ...@@ -15,6 +16,8 @@ import (
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
"git.code.tecnalia.com/blockchain/hypercog/controller/stats" "git.code.tecnalia.com/blockchain/hypercog/controller/stats"
hmodel "git.code.tecnalia.com/blockchain/hypercog/model"
errs "git.code.tecnalia.com/traceblock/sdk/constants" errs "git.code.tecnalia.com/traceblock/sdk/constants"
"git.code.tecnalia.com/traceblock/sdk/controller/base" "git.code.tecnalia.com/traceblock/sdk/controller/base"
"git.code.tecnalia.com/traceblock/sdk/controller/split" "git.code.tecnalia.com/traceblock/sdk/controller/split"
...@@ -30,28 +33,6 @@ type BidDecisionController struct { ...@@ -30,28 +33,6 @@ type BidDecisionController struct {
BidController BidController
} }
type BidStatus string
const (
Proposed = "proposed"
Accepted = "accepted"
Rejected = "rejected"
)
type Bidder struct {
Id string `json:"id"`
Role string `json:"role"`
Org string `json:"org"`
}
type Bid struct {
// Not renaming any of these fields in json to make mapstructure.decode work
Quantity uint32 `json:"quantity"`
Price float32 `json:"price"`
Bidder Bidder `json:"bidder"`
Status BidStatus `json:"status"`
}
var ( var (
errInvalidInputData = errors.New("failed to read model") errInvalidInputData = errors.New("failed to read model")
errParamsMissing = errors.New("missing split params. assetId and splitParams config must be send in split request") errParamsMissing = errors.New("missing split params. assetId and splitParams config must be send in split request")
...@@ -101,8 +82,8 @@ func (c BidController) readAsset(stub shared.LedgerBuildrStubInterface, requestA ...@@ -101,8 +82,8 @@ func (c BidController) readAsset(stub shared.LedgerBuildrStubInterface, requestA
return c.readTraceableAsset(stub, requestAsset.GetID()) return c.readTraceableAsset(stub, requestAsset.GetID())
} }
func (c BidController) getBids(biddableAsset *model.TraceableAsset) ([]Bid, error) { func (c BidController) getBids(biddableAsset *model.TraceableAsset) ([]hmodel.Bid, error) {
bidArr := []Bid{} bidArr := []hmodel.Bid{}
if _, ok := biddableAsset.ArbitraryDataFields["bids"]; ok { if _, ok := biddableAsset.ArbitraryDataFields["bids"]; ok {
// to avoid interface conversion errors ([]interface {} is not []bid.Bid) // to avoid interface conversion errors ([]interface {} is not []bid.Bid)
err := mapstructure.Decode(biddableAsset.ArbitraryDataFields["bids"], &bidArr) err := mapstructure.Decode(biddableAsset.ArbitraryDataFields["bids"], &bidArr)
...@@ -147,15 +128,15 @@ func (c BidController) BidAsset(stub shared.LedgerBuildrStubInterface, params sh ...@@ -147,15 +128,15 @@ func (c BidController) BidAsset(stub shared.LedgerBuildrStubInterface, params sh
return api.NewApiResponsePtr(fnName, err, nil).SendResponse() return api.NewApiResponsePtr(fnName, err, nil).SendResponse()
} }
newBid := Bid{ newBid := hmodel.Bid{
Quantity: bidParams.Bid.Quantity, Quantity: bidParams.Bid.Quantity,
Price: bidParams.Bid.Price, Price: bidParams.Bid.Price,
Bidder: Bidder{ Bidder: hmodel.Bidder{
Id: stub.GetUniqueUserId(), Id: stub.GetUniqueUserId(),
Org: org, Org: org,
Role: role, Role: role,
}, },
Status: Proposed, Status: hmodel.Proposed,
} }
alreadyExists := false alreadyExists := false
...@@ -177,7 +158,7 @@ func (c BidController) BidAsset(stub shared.LedgerBuildrStubInterface, params sh ...@@ -177,7 +158,7 @@ func (c BidController) BidAsset(stub shared.LedgerBuildrStubInterface, params sh
return c.SaveAbstractAsset(stub, biddableAsset) return c.SaveAbstractAsset(stub, biddableAsset)
} }
func (c BidDecisionController) bidResponse(stub shared.LedgerBuildrStubInterface, params shared.LedgerBuildrAsset, acceptBid bool) (*model.TraceableAsset, *Bid, error) { func (c BidDecisionController) bidResponse(stub shared.LedgerBuildrStubInterface, params shared.LedgerBuildrAsset, acceptBid bool) (*model.TraceableAsset, *hmodel.Bid, error) {
bidDecisionParams, ok := params.(*BidDecisionParams) bidDecisionParams, ok := params.(*BidDecisionParams)
if bidDecisionParams == nil || !ok { if bidDecisionParams == nil || !ok {
//failed when casting/fetching input data //failed when casting/fetching input data
...@@ -194,14 +175,14 @@ func (c BidDecisionController) bidResponse(stub shared.LedgerBuildrStubInterface ...@@ -194,14 +175,14 @@ func (c BidDecisionController) bidResponse(stub shared.LedgerBuildrStubInterface
return nil, nil, err return nil, nil, err
} }
var selectedBid *Bid = nil var selectedBid *hmodel.Bid = nil
for i, bid := range auxArr { for i, bid := range auxArr {
if bid.Bidder.Id == bidDecisionParams.Bidder { if bid.Bidder.Id == bidDecisionParams.Bidder {
selectedBid = &bid selectedBid = &bid
if acceptBid { if acceptBid {
auxArr[i].Status = Accepted auxArr[i].Status = hmodel.Accepted
} else { } else {
auxArr[i].Status = Rejected auxArr[i].Status = hmodel.Rejected
} }
break break
} }
...@@ -285,7 +266,7 @@ func (c BidDecisionController) _acceptBid(stub shared.LedgerBuildrStubInterface ...@@ -285,7 +266,7 @@ func (c BidDecisionController) _acceptBid(stub shared.LedgerBuildrStubInterface
transferredAsset := transfer.TransferAsset(stub, soldStock, transferParams) transferredAsset := transfer.TransferAsset(stub, soldStock, transferParams)
err = stats.RegisterSale(stub, soldStock.Quantity, bid.Price, soldStock.Units) err = stats.RegisterSale(stub, bid, soldStock.Units)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
package stats package stats
import ( import (
"git.code.tecnalia.com/blockchain/hypercog/model" "git.code.tecnalia.com/blockchain/hypercog/model/stats"
"git.code.tecnalia.com/ledgerbuilder/sdk/core/api" "git.code.tecnalia.com/ledgerbuilder/sdk/core/api"
"git.code.tecnalia.com/ledgerbuilder/sdk/core/fabric/protos" "git.code.tecnalia.com/ledgerbuilder/sdk/core/fabric/protos"
"git.code.tecnalia.com/ledgerbuilder/sdk/shared" "git.code.tecnalia.com/ledgerbuilder/sdk/shared"
...@@ -15,7 +15,8 @@ import ( ...@@ -15,7 +15,8 @@ import (
type StatsResult struct { type StatsResult struct {
PerSteelCompany map[string]model.Stats `json:"perSteelCompany,omitempty"` PerSteelCompany map[string]stats.SteelStats `json:"perSteelCompany,omitempty"`
PerCementCompany map[string]stats.CementStats `json:"perCementCompany,omitempty"`
} }
func GetStatsOperation(stub shared.LedgerBuildrStubInterface, request shared.LedgerBuildrAsset) protos.Response { func GetStatsOperation(stub shared.LedgerBuildrStubInterface, request shared.LedgerBuildrAsset) protos.Response {
...@@ -23,23 +24,41 @@ func GetStatsOperation(stub shared.LedgerBuildrStubInterface, request shared.Led ...@@ -23,23 +24,41 @@ func GetStatsOperation(stub shared.LedgerBuildrStubInterface, request shared.Led
ret := new(StatsResult) ret := new(StatsResult)
orgs, err := getOrgList(stub) orgs, err := getSteelOrgList(stub)
if err != nil { if err != nil {
return api.NewApiResponsePtr(fnName, err, nil).SendResponse() return api.NewApiResponsePtr(fnName, err, nil).SendResponse()
} }
if len(*orgs) > 0 { if len(*orgs) > 0 {
ret.PerSteelCompany = make(map[string]model.Stats) ret.PerSteelCompany = make(map[string]stats.SteelStats)
} }
for _, org := range *orgs { for _, org := range *orgs {
orgStat, err := getStats(stub, org) orgStat, err := getSteelStats(stub, org)
if err != nil { if err != nil {
return api.NewApiResponsePtr(fnName, err, nil).SendResponse() return api.NewApiResponsePtr(fnName, err, nil).SendResponse()
} }
ret.PerSteelCompany[org] = *orgStat.RefineStats() ret.PerSteelCompany[org] = *orgStat.RefineStats()
} }
orgs, err = getCementOrgList(stub)
if err != nil {
return api.NewApiResponsePtr(fnName, err, nil).SendResponse()
}
if len(*orgs) > 0 {
ret.PerCementCompany = make(map[string]stats.CementStats)
}
for _, org := range *orgs {
orgStat, err := getCementStats(stub, org)
if err != nil {
return api.NewApiResponsePtr(fnName, err, nil).SendResponse()
}
ret.PerCementCompany[org] = *orgStat.RefineStats()
}
return api.NewAPIGenericResponsePtr(fnName, nil, ret).SendResponse() return api.NewAPIGenericResponsePtr(fnName, nil, ret).SendResponse()
} }
......
...@@ -11,21 +11,41 @@ import ( ...@@ -11,21 +11,41 @@ import (
"fmt" "fmt"
"math" "math"
"git.code.tecnalia.com/blockchain/hypercog/model"
"git.code.tecnalia.com/ledgerbuilder/sdk/core/util/logging" "git.code.tecnalia.com/ledgerbuilder/sdk/core/util/logging"
"git.code.tecnalia.com/ledgerbuilder/sdk/shared" "git.code.tecnalia.com/ledgerbuilder/sdk/shared"
"git.code.tecnalia.com/blockchain/hypercog/model"
"git.code.tecnalia.com/blockchain/hypercog/model/stats"
) )
const STEEL_ORG_LIST_KEY = "steel_orgs" const STEEL_ORG_LIST_KEY = "steel_orgs"
const CEMENT_ORG_LIST_KEY = "cement_orgs"
type OrgList []string type OrgList []string
var log = logging.NewGoLogger("stats") var log = logging.NewGoLogger("stats")
func getStats(stub shared.LedgerBuildrStubInterface, key string) (st *model.RawStats, err error) { func getSteelStats(stub shared.LedgerBuildrStubInterface, key string) (st *stats.RawSteelOrgStats, err error) {
value, err := stub.GetState(key)
if err != nil || len(value) == 0 {
st = new(stats.RawSteelOrgStats)
st.MinPrice = math.MaxFloat64
} else {
err = json.Unmarshal(value, &st)
if err != nil {
return nil, err
}
}
return st, nil
}
func getCementStats(stub shared.LedgerBuildrStubInterface, key string) (st *stats.RawCementOrgStats, err error) {
value, err := stub.GetState(key) value, err := stub.GetState(key)
if err != nil || len(value) == 0 { if err != nil || len(value) == 0 {
st = new(model.RawStats) st = new(stats.RawCementOrgStats)
st.MinPrice = math.MaxFloat64 st.MinPrice = math.MaxFloat64
} else { } else {
err = json.Unmarshal(value, &st) err = json.Unmarshal(value, &st)
...@@ -37,7 +57,7 @@ func getStats(stub shared.LedgerBuildrStubInterface, key string) (st *model.RawS ...@@ -37,7 +57,7 @@ func getStats(stub shared.LedgerBuildrStubInterface, key string) (st *model.RawS
return st, nil return st, nil
} }
func putStats(stub shared.LedgerBuildrStubInterface, key string, stats *model.RawStats) (error) { func putJson(stub shared.LedgerBuildrStubInterface, key string, stats interface{}) (error) {
serialized, err := json.Marshal(stats) serialized, err := json.Marshal(stats)
if err != nil { if err != nil {
return err return err
...@@ -51,8 +71,8 @@ func putStats(stub shared.LedgerBuildrStubInterface, key string, stats *model.Ra ...@@ -51,8 +71,8 @@ func putStats(stub shared.LedgerBuildrStubInterface, key string, stats *model.Ra
return nil return nil
} }
func getOrgList(stub shared.LedgerBuildrStubInterface) (st *OrgList, err error) { func _getOrgList(stub shared.LedgerBuildrStubInterface, key string) (st *OrgList, err error) {
value, err := stub.GetState(STEEL_ORG_LIST_KEY) value, err := stub.GetState(key)
if err != nil || len(value) == 0 { if err != nil || len(value) == 0 {
st = new(OrgList) st = new(OrgList)
} else { } else {
...@@ -65,8 +85,26 @@ func getOrgList(stub shared.LedgerBuildrStubInterface) (st *OrgList, err error) ...@@ -65,8 +85,26 @@ func getOrgList(stub shared.LedgerBuildrStubInterface) (st *OrgList, err error)
return st, nil return st, nil
} }
func getSteelOrgList(stub shared.LedgerBuildrStubInterface) (st *OrgList, err error) {
return _getOrgList(stub, STEEL_ORG_LIST_KEY)
}
func getCementOrgList(stub shared.LedgerBuildrStubInterface) (st *OrgList, err error) {
return _getOrgList(stub, CEMENT_ORG_LIST_KEY)
}
func resetStats(stub shared.LedgerBuildrStubInterface) (error) { func resetStats(stub shared.LedgerBuildrStubInterface) (error) {
orgs, err := getOrgList(stub) orgs, err := getSteelOrgList(stub)
if len(*orgs) > 0 {
for _, org := range *orgs {
err = stub.DelState(org)
if err != nil {
return err
}
}
}
orgs, err = getCementOrgList(stub)
if len(*orgs) > 0 { if len(*orgs) > 0 {
for _, org := range *orgs { for _, org := range *orgs {
err = stub.DelState(org) err = stub.DelState(org)
...@@ -77,6 +115,8 @@ func resetStats(stub shared.LedgerBuildrStubInterface) (error) { ...@@ -77,6 +115,8 @@ func resetStats(stub shared.LedgerBuildrStubInterface) (error) {
} }
err = stub.DelState(STEEL_ORG_LIST_KEY) err = stub.DelState(STEEL_ORG_LIST_KEY)
err = stub.DelState(CEMENT_ORG_LIST_KEY)
return err return err
} }
...@@ -89,8 +129,8 @@ func (orgs OrgList) contains(org string) (bool) { ...@@ -89,8 +129,8 @@ func (orgs OrgList) contains(org string) (bool) {
return false return false
} }
func addOrgIfNotExist(stub shared.LedgerBuildrStubInterface, org string) (err error) { func addOrgIfNotExist(stub shared.LedgerBuildrStubInterface, key string, org string) (err error) {
orgs, err := getOrgList(stub) orgs, err := _getOrgList(stub, key)
if err != nil { if err != nil {
return err return err
} }
...@@ -103,7 +143,7 @@ func addOrgIfNotExist(stub shared.LedgerBuildrStubInterface, org string) (err er ...@@ -103,7 +143,7 @@ func addOrgIfNotExist(stub shared.LedgerBuildrStubInterface, org string) (err er
return err return err
} }
err = stub.PutState(STEEL_ORG_LIST_KEY, serialized) err = stub.PutState(key, serialized)
if err != nil { if err != nil {
return err return err
} }
...@@ -115,13 +155,13 @@ func addOrgIfNotExist(stub shared.LedgerBuildrStubInterface, org string) (err er ...@@ -115,13 +155,13 @@ func addOrgIfNotExist(stub shared.LedgerBuildrStubInterface, org string) (err er
} }
func retrieveExistingStats(stub shared.LedgerBuildrStubInterface) (*model.RawStats, error) { func retrieveExistingStats(stub shared.LedgerBuildrStubInterface) (*stats.RawSteelOrgStats, error) {
org, err := stub.GetOrganization() org, err := stub.GetOrganization()
if err != nil { if err != nil {
return nil, err return nil, err
} }
customStats, err := getStats(stub, org) customStats, err := getSteelStats(stub, org)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -129,18 +169,18 @@ func retrieveExistingStats(stub shared.LedgerBuildrStubInterface) (*model.RawSta ...@@ -129,18 +169,18 @@ func retrieveExistingStats(stub shared.LedgerBuildrStubInterface) (*model.RawSta
return customStats, nil return customStats, nil
} }
func upgradeStats(stub shared.LedgerBuildrStubInterface, customStats *model.RawStats) (error) { func upgradeSteelStats(stub shared.LedgerBuildrStubInterface, customStats *stats.RawSteelOrgStats) (error) {
org, err := stub.GetOrganization() org, err := stub.GetOrganization()
if err != nil { if err != nil {
return err return err
} }
err = putStats(stub, org, customStats) err = putJson(stub, org, customStats)
if err != nil { if err != nil {
return err return err
} }
err = addOrgIfNotExist(stub, org) err = addOrgIfNotExist(stub, STEEL_ORG_LIST_KEY, org)
if err != nil { if err != nil {
return err return err
} }
...@@ -149,6 +189,20 @@ func upgradeStats(stub shared.LedgerBuildrStubInterface, customStats *model.RawS ...@@ -149,6 +189,20 @@ func upgradeStats(stub shared.LedgerBuildrStubInterface, customStats *model.RawS
return nil return nil
} }
func upgradeCementStats(stub shared.LedgerBuildrStubInterface, customStats *stats.RawCementOrgStats, org string) (error) {
err := putJson(stub, org, customStats)
if err != nil {
return err
}
err = addOrgIfNotExist(stub, CEMENT_ORG_LIST_KEY, org)
if err != nil {
return err
}
log.Debug("Upgrading stats")
return nil
}
func RegisterSlag(stub shared.LedgerBuildrStubInterface, quantity uint32, units string) (error) { func RegisterSlag(stub shared.LedgerBuildrStubInterface, quantity uint32, units string) (error) {
orgStats, err := retrieveExistingStats(stub) orgStats, err := retrieveExistingStats(stub)
...@@ -158,7 +212,7 @@ func RegisterSlag(stub shared.LedgerBuildrStubInterface, quantity uint32, units ...@@ -158,7 +212,7 @@ func RegisterSlag(stub shared.LedgerBuildrStubInterface, quantity uint32, units
orgStats.RegisterSlag(quantity, units) orgStats.RegisterSlag(quantity, units)
err = upgradeStats(stub, orgStats) err = upgradeSteelStats(stub, orgStats)
if err != nil { if err != nil {
return err return err
} }
...@@ -166,20 +220,33 @@ func RegisterSlag(stub shared.LedgerBuildrStubInterface, quantity uint32, units ...@@ -166,20 +220,33 @@ func RegisterSlag(stub shared.LedgerBuildrStubInterface, quantity uint32, units
return nil return nil
} }
func RegisterSale(stub shared.LedgerBuildrStubInterface, quantity uint32, price float32, units string) (error) { func RegisterSale(stub shared.LedgerBuildrStubInterface, acceptedBid *model.Bid, units string) (error) {
orgStats, err := retrieveExistingStats(stub) senderOrgStats, err := retrieveExistingStats(stub)
if err != nil {
return err
}
senderOrgStats.UpgradeSale(acceptedBid.Quantity, acceptedBid.Price, units)
err = upgradeSteelStats(stub, senderOrgStats)
if err != nil { if err != nil {
return err return err
} }
orgStats.UpgradeSale(quantity, price, units)
log.Debug(fmt.Sprintf("Sale registered in stats (quantity: %d %s, price: %.2f €)", quantity, units, price)) receiverOrgStats, err := getCementStats(stub, acceptedBid.Bidder.Org)
if err != nil {
return err
}
receiverOrgStats.UpgradeSale(acceptedBid.Quantity, acceptedBid.Price, units)
err = upgradeStats(stub, orgStats) err = upgradeCementStats(stub, receiverOrgStats, acceptedBid.Bidder.Org)
if err != nil { if err != nil {
return err return err
} }
log.Debug(fmt.Sprintf("Sale registered in stats (quantity: %d %s, price: %.2f €)", acceptedBid.Quantity, units, acceptedBid.Price))
return nil return nil
} }
\ No newline at end of file
/**
* bid.go
*
* COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
*/
package model
type BidStatus string
const (
Proposed = "proposed"
Accepted = "accepted"
Rejected = "rejected"
)
type Bidder struct {
Id string `json:"id"`
Role string `json:"role"`
Org string `json:"org"`
}
type Bid struct {
// Not renaming any of these fields in json to make mapstructure.decode work
Quantity uint32 `json:"quantity"`
Price float32 `json:"price"`
Bidder Bidder `json:"bidder"`
Status BidStatus `json:"status"`
}
/**
* cement.go
*
* COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
*/
package stats
// Refined stats which will be returned
type CementStats struct {
TotalSlag float64 `json:"total"` // in tons
SoldBatch float64 `json:"sold"` // in t/sold batch
Price SummaryMetric `json:"price"` // in t/€
}
// Raw stored stats (per cement company)
type RawCementOrgStats struct {
RawStats
}
func (raw *RawCementOrgStats) UpgradeSale(quantity uint32, price float32, units string) {
soldBatch := toTons(quantity, units)
raw.TotalSlag += soldBatch
raw.TotalPrice += float64(price)
raw.SoldBatches += 1
convertedPrice := float64(price) / soldBatch
if convertedPrice < raw.MinPrice {
raw.MinPrice = convertedPrice
}
if raw.MaxPrice < convertedPrice {
raw.MaxPrice = convertedPrice
}
}
func (raw *RawCementOrgStats) RefineStats() (*CementStats) {
ret := new(CementStats)
ret.TotalSlag = raw.TotalSlag
ret.SoldBatch = ret.TotalSlag / float64(raw.SoldBatches)
ret.Price.Minimum = raw.MinPrice
ret.Price.Maximum = raw.MaxPrice
if raw.TotalSlag > 0 {
ret.Price.Average = raw.TotalPrice / raw.TotalSlag
}
return ret
}
/**
* common.go
*
* COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
*/
package stats
type PercentageMetric struct {
Total float64 `json:"total"` // in tons
Percentage float64 `json:"percentage,omitempty"` // in %
}
type SummaryMetric struct {
Minimum float64 `json:"min"`
Maximum float64 `json:"max"`
Average float64 `json:"avg"`
}
// Generic raw stats
type RawStats struct {
TotalSlag float64 `json:"total"` // in tons
TotalPrice float64 `json:"totalPrice"` // in €
SoldBatches uint32 `json:"soldBatches"` // in units
MinPrice float64 `json:"minPrice"` // in €
MaxPrice float64 `json:"maxPrice"` // in €
}
func toTons(quantity uint32, units string) (float64) {
amount := float64(quantity)
if units == "kg" {
amount /= 1000.0
}
return amount
}
/** /**
* stats.go * steel.go
* *
* COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022. * COPYRIGHT: FUNDACIÓN TECNALIA RESEARCH & INNOVATION, 2022.
*/ */
package model package stats
type PercentageMetric struct {
Total float64 `json:"total"` // in tons
Percentage float64 `json:"percentage,omitempty"` // in %
}
type PriceMetric struct {
Minimum float64 `json:"min"`
Maximum float64 `json:"max"`
Average float64 `json:"avg"`
}
// Total or per company
// Refined stats which will be returned // Refined stats which will be returned
type Stats struct { type SteelStats struct {
TotalSlag float64 `json:"total"` // in tons TotalSlag float64 `json:"total"` // in tons
Reused PercentageMetric `json:"reused"` Reused PercentageMetric `json:"reused"`
Discarded PercentageMetric `json:"discarded"` Discarded PercentageMetric `json:"discarded"`
Price PriceMetric `json:"price"` SoldBatch float64 `json:"sold"` // in t/sold batch
Price SummaryMetric `json:"price"` // in t/€
} }
// Total or per company // Raw stored stats (per steel company)
// Raw stored stats type RawSteelOrgStats struct {
type RawStats struct { RawStats
TotalSlag float64 `json:"total"` // in tons
SlagReused float64 `json:"reused"` // in tons SlagReused float64 `json:"reused"` // in tons
SlagDiscarded float64 `json:"discarded"` // in tons SlagDiscarded float64 `json:"discarded"` // in tons
TotalPrice float64 `json:"totalPrice"` // in €
BidAmount uint32 `json:"bidAmount"` // in units
MinPrice float64 `json:"minPrice"` // in €
MaxPrice float64 `json:"maxPrice"` // in €
} }
func toTons(quantity uint32, units string) (float64) {
amount := float64(quantity)
if units == "kg" {
amount /= 1000.0
}
return amount
}
func (raw *RawStats) RegisterSlag(quantity uint32, units string) { func (raw *RawSteelOrgStats) RegisterSlag(quantity uint32, units string) {
raw.TotalSlag += toTons(quantity, units) raw.TotalSlag += toTons(quantity, units)
} }
func (raw *RawStats) UpgradeSale(quantity uint32, price float32, units string) { func (raw *RawSteelOrgStats) UpgradeSale(quantity uint32, price float32, units string) {
raw.SlagReused += toTons(quantity, units) soldBatch := toTons(quantity, units)
raw.SlagReused += soldBatch
raw.TotalPrice += float64(price) raw.TotalPrice += float64(price)
raw.BidAmount += 1 raw.SoldBatches += 1
convertedPrice := float64(price) convertedPrice := float64(price) / soldBatch
if convertedPrice < raw.MinPrice { if convertedPrice < raw.MinPrice {
raw.MinPrice = convertedPrice raw.MinPrice = convertedPrice
} }
...@@ -65,8 +44,8 @@ func (raw *RawStats) UpgradeSale(quantity uint32, price float32, units string) { ...@@ -65,8 +44,8 @@ func (raw *RawStats) UpgradeSale(quantity uint32, price float32, units string) {
} }
} }
func (raw *RawStats) RefineStats() (*Stats) { func (raw *RawSteelOrgStats) RefineStats() (*SteelStats) {
ret := new(Stats) ret := new(SteelStats)
ret.TotalSlag = raw.TotalSlag ret.TotalSlag = raw.TotalSlag
ret.Reused.Total = raw.SlagReused ret.Reused.Total = raw.SlagReused
...@@ -79,10 +58,12 @@ func (raw *RawStats) RefineStats() (*Stats) { ...@@ -79,10 +58,12 @@ func (raw *RawStats) RefineStats() (*Stats) {
ret.Discarded.Percentage = ret.Discarded.Total / ret.TotalSlag ret.Discarded.Percentage = ret.Discarded.Total / ret.TotalSlag
} }
ret.SoldBatch = ret.TotalSlag / float64(raw.SoldBatches)
ret.Price.Minimum = raw.MinPrice ret.Price.Minimum = raw.MinPrice
ret.Price.Maximum = raw.MaxPrice ret.Price.Maximum = raw.MaxPrice
if raw.BidAmount > 0 { if raw.TotalSlag > 0 {
ret.Price.Average = raw.TotalPrice / float64(raw.BidAmount) ret.Price.Average = raw.TotalPrice / raw.TotalSlag
} }
return ret return ret
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment