diff --git a/daml/DataManagement/Report.daml b/daml/DataManagement/Report.daml index f50c25a..1c491e8 100644 --- a/daml/DataManagement/Report.daml +++ b/daml/DataManagement/Report.daml @@ -20,9 +20,9 @@ template Report with signatory reporter, operator controller operator can Check : ContractId Publication do - exerciseByKey @Publication operator AddObserver with obs = reporter + exerciseByKey @Publication (content.species, operator) AddObserver with obs = reporter Reject : ContractId Publication do - exerciseByKey @Publication operator RemoveObserver with obs = reporter + exerciseByKey @Publication (content.species, operator) RemoveObserver with obs = reporter data PublicationData = PublicationData with targetArea : (Coordinates, Decimal) @@ -37,14 +37,15 @@ template Publication with content : PublicationData where signatory operator - key (operator): Party - maintainer key + key (content.species, operator): (Text, Party) + maintainer key._2 observer observers controller operator can AddObserver: ContractId Publication with obs : Party do create this with observers = dedup $ obs :: observers RemoveObserver: ContractId Publication with obs : Party do create this with observers = observers \\ [obs] - choice RequestQuota: ContractId FishingQuotaRequest with requestor : Party + nonconsuming choice RequestQuota: ContractId FishingQuotaRequest with requestor : Party controller requestor - do create FishingQuotaRequest with operator; requestor ; species = content.species \ No newline at end of file + do + create FishingQuotaRequest with operator; requestor ; species = content.species \ No newline at end of file diff --git a/daml/Main.daml b/daml/Main.daml index e32f2cc..a9abdbd 100644 --- a/daml/Main.daml +++ b/daml/Main.daml @@ -4,8 +4,8 @@ import Daml.Script import DA.Date import DA.Time -import FishingQuota.FishingQuota() -import DataManagement.Report (Report(..), ReportContent(..), Publication(..), PublicationData(..)) +import FishingQuota.FishingQuota(Issue(..)) +import DataManagement.Report (RequestQuota(..), Check(..), Report(..), ReportContent(..), Publication(..), PublicationData(..)) import VesselTracking.ShipTrajectory() import Data.Coordinates @@ -54,8 +54,11 @@ template User with setup: Script () setup = do let microsecondsInDay = 86400000000 + -- Create system operator commission commission <- allocatePartyWithHint "Commission" (PartyIdHint "Commission") op <- commission `submit` createCmd OperatorOrganisation with operator = commission ; participants = [] + + -- onboard countries as users us <- allocatePartyWithHint "USA" (PartyIdHint "USA") uk <- allocatePartyWithHint "UK" (PartyIdHint "UK") canada <- allocatePartyWithHint "Canada" (PartyIdHint "Canada") @@ -80,7 +83,9 @@ setup = do spUser <- spain `submit` exerciseCmd inv7 Subscribe (op8, inv8) <- commission `submit` exerciseCmd op7 $ Onboard with participant = portugal ; userName = "Portugal" portugal `submit` exerciseCmd inv8 Subscribe + t <- getTime + -- submitting fishing reports brasil `submit` exerciseCmd brasilUser $ SubmitReport with content = ReportContent with species = "ALB" @@ -88,7 +93,7 @@ setup = do area = ((Coordinates with lat = 30.0; long = -45.0), 10.0) timestamp = time (date 2020 Mar 15) 0 0 0 duration = convertMicrosecondsToRelTime (14 * microsecondsInDay) - brasil `submit` exerciseCmd brasilUser $ SubmitReport with + brReport <- brasil `submit` exerciseCmd brasilUser $ SubmitReport with content = ReportContent with species = "ALB" caughtQty = 11.95782 @@ -104,14 +109,16 @@ setup = do timestamp = time (date 2020 Jun 3) 0 0 0 duration = convertMicrosecondsToRelTime (14 * microsecondsInDay) - spain `submit` exerciseCmd spUser $ SubmitReport with + espReport <- spain `submit` exerciseCmd spUser $ SubmitReport with content = ReportContent with species = "ALB" caughtQty = 10.01248 area = ((Coordinates with lat = 40.0; long = -50.0), 10.0) timestamp = time (date 2020 Jun 3) 0 0 0 duration = convertMicrosecondsToRelTime (14 * microsecondsInDay) - commission `submit` createCmd $ Publication with + + -- commission prepares a ppublication about fishing opportunities + pub <- commission `submit` createCmd $ Publication with operator = commission observers = [] content = PublicationData with @@ -120,4 +127,14 @@ setup = do species = "ALB" timeLimit = time (date 2020 Dec 1) 0 0 0 + -- commission accepts report and by that reveals publication for the party + commission `submit` exerciseCmd espReport Check + pub2 <- commission `submit` exerciseCmd brReport Check + + -- the parties that see the opportunity request fising quota + qr <- spain `submit` exerciseCmd pub2 RequestQuota with requestor = spain + brasil `submit` exerciseCmd pub2 RequestQuota with requestor = brasil + + -- commission issues a quota + commission `submit` exerciseCmd qr Issue pure () diff --git a/ui/src/components/MainView.tsx b/ui/src/components/MainView.tsx index 4a4facb..815f0e2 100644 --- a/ui/src/components/MainView.tsx +++ b/ui/src/components/MainView.tsx @@ -1,13 +1,18 @@ -import { DataManagement, Main } from '@daml.js/odyssey'; -import { useLedger, useParty } from '@daml/react'; +import { FishingQuota, DataManagement, Main } from '@daml.js/odyssey'; +import { useLedger, useParty, useQuery } from '@daml/react'; import React, { useMemo, useState, useRef, useEffect } from 'react'; import * as ui from 'semantic-ui-react'; import MapView from './MapView'; import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'; import { LatLngExpression } from 'leaflet'; -import { ReportView, ReportModel } from './ReportView'; +import { ReportView, ReportModel, ReportViewModel } from './ReportView'; import { ReportForm } from './ReportForm'; import { formatDiagnosticsWithColorAndContext } from 'typescript'; +import { ContractId } from '@daml/types'; +import { Report } from '@daml.js/odyssey/lib/DataManagement/Report'; +import { PublicationModel, PublicationView } from './PublicationView'; +import { Tab } from 'semantic-ui-react'; +import { QuotaView, QuotaRequestModel, QuotaModel } from './QuotaView'; const MainView: React.FC = () => { @@ -16,74 +21,186 @@ const MainView: React.FC = () => { const ledger = useLedger(); - let [reports, setReports] = useState([]) + let [reports, setReports] = useState([]); - let reportModelFromReport = function (r: DataManagement.Report.Report): ReportModel { + let [publications, setPublications] = useState([]); + + let [quotaRequests, setQuotaRequsts] = useState([]); + + let [quotas, setQuotas] = useState([]); + + let quotaRequestModelFromContract = function ( + contractId: ContractId, + req: FishingQuota.FishingQuota.FishingQuotaRequest + ): QuotaRequestModel { return { - reporter: r.reporter, - caughtQty: Number.parseFloat(r.content.caughtQty), - areaCenterLong: Number.parseFloat(r.content.area._1.long), - areaCenterLat: Number.parseFloat(r.content.area._1.lat), - areaRadius: Number.parseFloat(r.content.area._2), - fish: r.content.species, - } + fish: req.species, + requestor: req.requestor, + contractId: contractId + }; + } + + let quotaModelFromContract = function ( + contractId: ContractId, + q: FishingQuota.FishingQuota.FishingQuota + ): QuotaModel { + return { + qty: Number.parseFloat(q.quantity), + fish: q.species, + owner: q.owner, + issuer: q.issuer, + }; + } + + let reportModelFromReport = function ( + contractId: ContractId, + r: DataManagement.Report.Report): ReportViewModel { + return { + report: { + reporter: r.reporter, + caughtQty: Number.parseFloat(r.content.caughtQty), + areaCenterLong: Number.parseFloat(r.content.area._1.long), + areaCenterLat: Number.parseFloat(r.content.area._1.lat), + areaRadius: Number.parseFloat(r.content.area._2), + fish: r.content.species, + }, + contractId: contractId + }; }; + let publicationModelFromContract = function ( + contractId: ContractId, + p: DataManagement.Report.Publication): PublicationModel { + return { + fish: p.content.species, + allowed: Number.parseFloat(p.content.totalAllowance), + timeLimit: p.content.timeLimit, + areaCenterLat: Number.parseFloat(p.content.targetArea._1.lat), + areaCenterLong: Number.parseFloat(p.content.targetArea._1.long), + areaRadius: Number.parseFloat(p.content.targetArea._2), + contractId: contractId + }; + } - let reportsQ = ledger.query(DataManagement.Report.Report) - .then(xs => xs.map(x => reportModelFromReport(x.payload))) - .then(setReports) + let reportQ = useQuery(DataManagement.Report.Report) + let pubQ = useQuery(DataManagement.Report.Publication) + let quotaRequestQ = useQuery(FishingQuota.FishingQuota.FishingQuotaRequest); + let quotaQ = useQuery(FishingQuota.FishingQuota.FishingQuota) + useEffect(() => { + setReports(reportQ.contracts.map(x => reportModelFromReport(x.contractId, x.payload))); + setPublications(pubQ.contracts.map(x => publicationModelFromContract(x.contractId, x.payload))); + setQuotaRequsts(quotaRequestQ.contracts.map(x => quotaRequestModelFromContract(x.contractId, x.payload))); + setQuotas(quotaQ.contracts.map(x => quotaModelFromContract(x.contractId, x.payload))); + console.log("quotas"); + console.log(quotas); + }, [ledger, reportQ, pubQ, quotaRequestQ, quotaQ]) let createReport = function (r: ReportModel) { - ledger.fetchByKey(Main.User, username).then(user => { - if (user) { - ledger.exercise(Main.User.SubmitReport, user.contractId, - { - content: { - species: r.fish, - caughtQty: r.caughtQty.toFixed(), - timestamp: (new Date()).toISOString(), - area: { - _1: { - long: r.areaCenterLong.toFixed(), - lat: r.areaCenterLat.toFixed() + try { + ledger.fetchByKey(Main.User, username).then(user => { + if (user) { + ledger.exercise(Main.User.SubmitReport, user.contractId, + { + content: { + species: r.fish, + caughtQty: r.caughtQty.toFixed(), + timestamp: (new Date()).toISOString(), + area: { + _1: { + long: r.areaCenterLong.toFixed(), + lat: r.areaCenterLat.toFixed() + }, + _2: r.areaRadius.toFixed() }, - _2: r.areaRadius.toFixed() - }, - duration: { microseconds: "0" } - } - }) - } - }); + duration: { microseconds: "0" } + } + }) + } + }).catch(e => console.log(`error report create ${e}`)); + } catch { + console.log("error report create"); + } } + + let check = function (contractId: ContractId) { + try { + ledger.exercise(DataManagement.Report.Report.Check, contractId, {}) + .catch(e => console.log(`error: reject: ${e} `)); + } catch { + console.log("error: check") + } + } + + let reject = function (contractId: ContractId) { + try { + ledger.exercise(DataManagement.Report.Report.Reject, contractId, {}) + .catch(e => console.log(`error: reject: ${e} `)); + } catch { + console.log("error: reject"); + } + } + + let requestQuota = function (contractId: ContractId) { + try { + ledger.exercise(DataManagement.Report.Publication.RequestQuota, contractId, { requestor: username }) + .catch(e => console.log(`error: request: ${e}`)); + } catch { + console.log("error: quota request"); + } + } + + let issueQuota = function(contractId: ContractId) { + if(username=="Commission") { + ledger.exercise(FishingQuota.FishingQuota.FishingQuotaRequest.Issue, contractId, {}) + .catch(e => console.log(`error quota issue: ${e}`)); + } else { + + } + } + var view = if (username == "Commission") { - view = ; + view = ; } else { view = ; } + const mapView = ( + + + {reports + .map(rvm => rvm.report) + .map(rs => { return { p: ([rs.areaCenterLat, rs.areaCenterLong] as LatLngExpression), r: rs } }) + .map(({ p, r }) => { + return ( + + + {r.fish} fishing activity from {r.reporter}: + qty: {r.caughtQty} + + ) + })} + ); + + const panes = [ + { menuItem: 'Map View', pane: (
{mapView}
) }, + { menuItem: 'Reports', pane:
{view}
}, + { + menuItem: 'Publications', pane:
+ {publications.map(p => )} +
+
+ }, + { menuItem: 'Quotas', pane:
}, + ] + return ( - - - {reports - .map(rs => { return {p : ([rs.areaCenterLat, rs.areaCenterLong] as LatLngExpression), r: rs }}) - .map(({p, r}) => { - return ( - - - {r.fish} fishing activity from {r.reporter}: - qty: {r.caughtQty } - - ) - })} - - {view} + ); } diff --git a/ui/src/components/PublicationView.tsx b/ui/src/components/PublicationView.tsx new file mode 100644 index 0000000..8c80650 --- /dev/null +++ b/ui/src/components/PublicationView.tsx @@ -0,0 +1,58 @@ + +import { Main, DataManagement } from '@daml.js/odyssey'; +import { useLedger, useParty } from '@daml/react'; +import React, { useMemo, useState, useRef, useEffect } from 'react'; +import * as ui from 'semantic-ui-react'; +import MapView from './MapView'; +import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'; +import { LatLngExpression } from 'leaflet'; +import { ContractId } from '@daml/types'; + +export type PublicationModel = { + fish: string + timeLimit: string, + allowed: number + areaCenterLong: number + areaCenterLat: number + areaRadius: number + contractId: ContractId +} + +type Props = { publication: PublicationModel + username: string + request: (contractId: ContractId) => void + } + +export const PublicationView: React.FC = ({publication, username, request}) => { + + let buttons = ( request(publication.contractId)}>Request Quota); + if(username == "Commission") { + buttons = (<>); + } + return ( + + + + {publication.fish}: ({publication.areaCenterLat}, {publication.areaCenterLong}) + + + + + + + {publication.fish} + + + + Total Allowance: {publication.allowed} + + + + + + +
{buttons}
+
+
); + +} diff --git a/ui/src/components/QuotaView.tsx b/ui/src/components/QuotaView.tsx new file mode 100644 index 0000000..47d5556 --- /dev/null +++ b/ui/src/components/QuotaView.tsx @@ -0,0 +1,127 @@ +import { Data, DataManagement, Main } from '@daml.js/odyssey'; +import { useLedger, useParty } from '@daml/react'; +import React, { useMemo, useState, useRef, useEffect } from 'react'; +import * as ui from 'semantic-ui-react'; +import MapView from './MapView'; +import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'; +import { LatLngExpression } from 'leaflet'; +import { ReportView, ReportModel, ReportViewModel } from './ReportView'; +import { ReportForm } from './ReportForm'; +import { formatDiagnosticsWithColorAndContext } from 'typescript'; +import { ContractId } from '@daml/types'; +import { Report } from '@daml.js/odyssey/lib/DataManagement/Report'; +import { PublicationModel, PublicationView } from './PublicationView'; +import { FishingQuotaRequest, Issue } from '@daml.js/odyssey/lib/FishingQuota/FishingQuota'; + +export type QuotaRequestModel = { + // qty: number + fish: string + requestor: string + contractId: ContractId + // areaLat: number + // areaLong: number +} + +export type QuotaModel = { + qty: number + fish: string + issuer: string + owner: string +}; + +type Props = { + quotas: QuotaModel[] + requests: QuotaRequestModel[] + username: string + issue: (contactId: ContractId) => void +} +export const QuotaView: React.FC = ({ quotas, requests, username, issue }) => { + if (username == "Commission") { + return ( + + + + {quotas.map(q => { + return ( + + + + Issued to: {q.owner} + + + + {q.fish}: {q.qty} + + + ) + })} + + + {requests.map(r => { + return ( + + + + + Requested: {r.requestor} + + + + {r.fish} + + +
+ issue(r.contractId)}>Issue +
+
+
+
+ ) + })} +
+
+
+ ); + } else { + return ( + + + + {quotas.map(q => { + return ( + + + + Issued to: {q.owner} + + + + {q.fish}: {q.qty} + + + ) + })} + + + {requests.map(r => { + return ( + + + + + Requested: {r.requestor} + + + + {r.fish} + + + + ) + })} + + + ) + + } +} diff --git a/ui/src/components/ReportView.tsx b/ui/src/components/ReportView.tsx index e0a8a13..27aa1dc 100644 --- a/ui/src/components/ReportView.tsx +++ b/ui/src/components/ReportView.tsx @@ -7,6 +7,7 @@ import MapView from './MapView'; import { MapContainer, TileLayer, Marker, Popup } from 'react-leaflet'; import { LatLngExpression } from 'leaflet'; import { ContractId } from '@daml/types'; +import { checkServerIdentity } from 'tls'; export type ReportModel = { reporter: string @@ -17,12 +18,19 @@ export type ReportModel = { fish: string } +export type ReportViewModel = { + report : ReportModel + contractId: ContractId +} +type Props = { + reports: ReportViewModel[] + check: (cid: ContractId) => void + reject: (cid: ContractId) => void + } -type Props = { reports: ReportModel[]} - -export const ReportView: React.FC = ({reports}) => { - +export const ReportView: React.FC = ({reports, check, reject}) => { + return ( @@ -30,18 +38,18 @@ export const ReportView: React.FC = ({reports}) => { return ( - {r.reporter}: ({r.areaCenterLat}, {r.areaCenterLong}) + {r.report.reporter}: ({r.report.areaCenterLat}, {r.report.areaCenterLong}) - {r.fish} + {r.report.fish} - Qty: {r.caughtQty} + Qty: {r.report.caughtQty} @@ -49,8 +57,8 @@ export const ReportView: React.FC = ({reports}) => {
- Check - Reject + check(r.contractId) }>Check + reject(r.contractId)}>Reject
); diff --git a/ui/yarn.lock b/ui/yarn.lock index e07d8f2..efa6757 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -1184,9 +1184,9 @@ "@daml.js/odyssey@file:../daml.js/odyssey-1.0.0": version "1.7.0" dependencies: - "@daml.js/40f452260bef3f29dede136108fc08a88d5a5250310281067087da6f0baddff7" "file:../../../.cache/yarn/v6/npm-@daml-js-odyssey-1.7.0-10caa812-542f-49e0-983c-a5d2fd08ba3b-1605387468423/node_modules/@daml.js/40f452260bef3f29dede136108fc08a88d5a5250310281067087da6f0baddff7" - "@daml.js/733e38d36a2759688a4b2c4cec69d48e7b55ecc8dedc8067b815926c917a182a" "file:../../../.cache/yarn/v6/npm-@daml-js-odyssey-1.7.0-10caa812-542f-49e0-983c-a5d2fd08ba3b-1605387468423/node_modules/@daml.js/733e38d36a2759688a4b2c4cec69d48e7b55ecc8dedc8067b815926c917a182a" - "@daml.js/d14e08374fc7197d6a0de468c968ae8ba3aadbf9315476fd39071831f5923662" "file:../../../.cache/yarn/v6/npm-@daml-js-odyssey-1.7.0-10caa812-542f-49e0-983c-a5d2fd08ba3b-1605387468423/node_modules/@daml.js/d14e08374fc7197d6a0de468c968ae8ba3aadbf9315476fd39071831f5923662" + "@daml.js/40f452260bef3f29dede136108fc08a88d5a5250310281067087da6f0baddff7" "file:../../../.cache/yarn/v6/npm-@daml-js-odyssey-1.7.0-840c1715-4a98-4b8d-9272-04304ae8c32f-1605406595642/node_modules/@daml.js/40f452260bef3f29dede136108fc08a88d5a5250310281067087da6f0baddff7" + "@daml.js/733e38d36a2759688a4b2c4cec69d48e7b55ecc8dedc8067b815926c917a182a" "file:../../../.cache/yarn/v6/npm-@daml-js-odyssey-1.7.0-840c1715-4a98-4b8d-9272-04304ae8c32f-1605406595642/node_modules/@daml.js/733e38d36a2759688a4b2c4cec69d48e7b55ecc8dedc8067b815926c917a182a" + "@daml.js/d14e08374fc7197d6a0de468c968ae8ba3aadbf9315476fd39071831f5923662" "file:../../../.cache/yarn/v6/npm-@daml-js-odyssey-1.7.0-840c1715-4a98-4b8d-9272-04304ae8c32f-1605406595642/node_modules/@daml.js/d14e08374fc7197d6a0de468c968ae8ba3aadbf9315476fd39071831f5923662" "@daml/ledger" "1.7.0" "@daml/types" "1.7.0" "@mojotech/json-type-validation" "^3.1.0"