how can update only Map values in daml contract? - daml

Datatype detail:
type1: Text
type2: [detail2]
Datatype detail2:
type1: Text
template A
with
consumerName: Text
producerName : Text
details : Map Text detail
create A with consumerName,producerName,details
now need to update contract old Details(Map Text Datatypes) with new Details(Map Text Datatypes) of multiple keyvalues .how can be achieve this using merge without doing multiple contract updation or any other solution is possible ?

You can use the functions in DA.Next.Map to manipulate maps. Here is a complete working example which I hope can shed some light on syntax and simple use-cases:
module Main where
import Daml.Script
import qualified DA.Next.Map as Map
import DA.Next.Map (Map)
template Comments
with
producer: Party
consumer: Party
productComments: Map Text [Text]
where
signatory producer
observer consumer
preconsuming choice AddComment: ContractId Comments
with productName: Text, comment: Text
controller consumer
do
create this with productComments = addComment productComments productName comment
nonconsuming choice ReadComments: Map Text [Text]
with reader: Party
controller reader
do return productComments
addComment: Map Text [Text] -> Text -> Text -> Map Text [Text]
addComment prev item newComment = case Map.lookup item prev of
-- If the key is not in the map yet, we just add the comment
None -> Map.insert item [newComment] prev
-- If there are comments already, add to the end
Some existingComments -> Map.insert item (existingComments ++ [newComment]) prev
setup : Script ()
setup = script do
alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
id1 <- submit bob do
createCmd Comments
with producer = bob
consumer = alice
productComments = mempty -- start with an empty map
map1 <- submit alice do exerciseCmd id1 (ReadComments alice)
assert $ map1 == Map.fromList []
id2 <- submit alice do exerciseCmd id1 (AddComment "item1" "it was not very good")
map2 <- submit alice do exerciseCmd id2 (ReadComments alice)
assert $ map2 == Map.fromList [("item1", ["it was not very good"])]
id3 <- submit alice do exerciseCmd id2 (AddComment "item2" "this is great!")
map3 <- submit alice do exerciseCmd id3 (ReadComments alice)
assert $ map3 == Map.fromList [("item1", ["it was not very good"]),
("item2", ["this is great!"])]
id4 <- submit alice do exerciseCmd id3 (AddComment "item2" "I can't stop raving about it")
map4 <- submit alice do exerciseCmd id4 (ReadComments alice)
assert $ map4 == Map.fromList [("item1", ["it was not very good"]),
("item2", ["this is great!", "I can't stop raving about it"])]
Tested on SDK 1.6.0.
I've also recently answered a possibly related question on the DAML forum, where I have a few more examples of using the functions in DA.Next.Map. Maybe that can help, too.

Related

Altering external data like a python list

I have a problem that seems like it should be simple in my head but i'm struggling to figure out the simplest way of executing it.
Basically, I have 2 lists of names:
list1 = ['name1', 'name2', 'name3', 'name4', 'name5']
list2 = ['name6', 'name7', 'name8', 'name9', 'name10']
My ultimate goal is, when the python script is run, i will return 1 random name from each list but ideally the script would not return the same name from either list for the next 4 times it was run. Essentially, i want each run's pair/choice to be random but i want it to restart the rotation every 5 times the script is run.
I think I'll need to store the data externally. Conceptually, i think i need the script to move a name from one list to another list every time it's run and when the first list is empty, it reverses and moves the names the other way and so on.
Should i use the csv.DictReader to move names between two CSV? or store them in a JSON?
Forgive me if this doesn't make sense. I'm struggling to put my problem into words.
Why not just store it in a text file?
import random
list1 = ['name1', 'name2', 'name3', 'name4', 'name5']
list2 = ['name6', 'name7', 'name8', 'name9', 'name10']
used = open("old.txt").readlines()
l1_copy = list1[:]
l2_copy = list2[:]
for line in used:
if line := line.strip():
a, b = line.split(",")
l1_copy.remove(a)
l2_copy.remove(b)
if not l1_copy or not l2_copy:
# clear file
with open("old.txt", "w"): pass
else:
list1 = l1_copy
list2 = l2_copy
choice1, choice2 = random.choice(list1), random.choice(list2)
print("Choice 1:", choice1)
print("Choice 2:", choice2)
with open("old.txt", "a") as f:
f.write(choice1 + "," + choice2 + "\n")
Make a file named old.txt in your current directory. It will store the previous runs, comma-separated.
Example run:
$ python3.8 choose_from_list.py
Choice 1: name2
Choice 2: name9
$ python3.8 choose_from_list.py
Choice 1: name5
Choice 2: name10
$ python3.8 choose_from_list.py
Choice 1: name4
Choice 2: name6
$ python3.8 choose_from_list.py
Choice 1: name1
Choice 2: name7
$ python3.8 choose_from_list.py
Choice 1: name3
Choice 2: name8

How to extract the name of a Party?

In a DAML contract, how do I extract the name of a party from a Party field?
Currently, toText p gives me Party(Alice). I'd like to only keep the name of the party.
That you care about the precise formatting of the resulting string suggests that you are implementing a codec in DAML. As a general principle DAML excels as a modelling/contract language, but consequently has limited features to support the sort of IO-oriented work this question implies. You are generally better off returning DAML values, and implementing codecs in Java/Scala/C#/Haskell/etc interfacing with the DAML via the Ledger API.
Still, once you have a Text value you also have access to the standard List manipulation functions via unpack, so converting "Party(Alice)" to "Alice" is not too difficult:
daml 1.0 module PartyExtract where
import Base.List
def pack (cs: List Char) : Text =
foldl (fun (acc: Text) (c: Char) -> acc <> singleton c) "" cs;
def partyToText (p: Party): Text =
pack $ reverse $ drop 2 $ reverse $ drop 7 $ unpack $ toText p
test foo : Scenario {} = scenario
let p = 'Alice'
assert $ "Alice" == partyToText p
In DAML 1.2 the standard library has been expanded, so the code above can be simplified:
daml 1.2
module PartyExtract2
where
import DA.Text
traceDebug : (Show a, Show b) => b -> a -> a
traceDebug b a = trace (show b <> show a) $ a
partyToText : Party -> Text
partyToText p = dropPrefix "'" $ dropSuffix "'" $ traceDebug "show party: " $ show p
foo : Scenario ()
foo = do
p <- getParty "Alice"
assert $ "Alice" == (traceDebug "partyToText party: " $ partyToText p)
NOTE: I have left the definition and calls to traceDebug so you can see the exact strings being generated in the scenario trace output.

Haskell getLine in a function's otherwise

I'm really new to Haskell and am trying to learn some simple functions.
I made some option choices through functions like so:
displayOptions :: Int -> String
displayOptions option
| option == 0 = "1 - Shop\n2 - Fight Monsters\n3 - Inn\n4 - Monk"
| otherwise = "invalid"
I then get the user input with getLine
choice <- getLine
And then I display a second option box for example,
playerChoice :: String -> String
playerChoice option
| option == "1" = "Sword - 50gp\nShield - 100gp"
| option == "2" = "You go fight some monsters outside town."
| option == "3" = "You go to the town Inn."
| option == "4" = "You go see the holy monk."
| otherwise = "You entered invalid information...\n" ++ displayOptions(0)
What I'm confused about is how I can get the user's choice again within a function. I want my otherwise = to say invalid information, display the options, get the input again and then display the choice they made.
So my main program would look something like this:
main = do
putStrLn "The king has sent you on the journey to become a master."
putStrLn $ "What would you like to do?"
putStrLn $ displayOptions(0)
choice <- getLine
putStrLn $ playerChoice(choice)
You would have to change the return type to be IO String instead of String.
However, probably you want to return Either String String so the function indicates that it's returned the game progression text Right "You do something" or a failure with an explanation of the failure Left "Not an option".
Then in the caller you loop until you get a Right value and each time you get a Left value you print the text and ask again.
I'm sure there's a slightly better way but here's some quickly fixed up code:
module Main where
playerChoice :: String -> Either String String
playerChoice option
| option == "1" = Right "Sword - 50gp\nShield - 100gp"
| option == "2" = Right "You go fight some monsters outside town."
| option == "3" = Right "You go to the town Inn."
| option == "4" = Right "You go see the holy monk."
| otherwise = Left "You entered invalid information..."
displayOptions :: Int -> String
displayOptions option
| option == 0 = "1 - Shop\n2 - Fight Monsters\n3 - Inn\n4 - Monk\n"
| otherwise = "invalid"
main = do
let progress whathappens = do
putStrLn whathappens
let tryAsk prompt = do
putStrLn prompt
choice <- getLine
either tryAsk progress $ playerChoice(choice)
tryAsk $ displayOptions(0) ++ "What would you like to do?"
progress "The king has sent you on the journey to become a master."
if you import Data.Function then you can also write it like the following - which in this case probably isn't better but it's a nice shallow step into a fascinating part of haskell:
fix (\moreProgress whathappens -> do
putStrLn whathappens
fix (\askAgain prompt -> do
putStrLn prompt
choice <- getLine
either askAgain moreProgress $ playerChoice(choice))
$ displayOptions(0) ++ "What would you like to do?")
$ "The king has sent you on the journey to become a master."
You should change the return type to Either String String in order to provide an error message. For more details, look at the docs for the Either type.
In Haskell, we don't have traditional looping structures like for or while instead we use recursive calls. See While loop in Haskell with a condition for an example.

From list of json files to data.table: partial variable list

I have a list of more than 100,000 json files from which I want to get a data.table with only a few variables. Unfortunately the files are complex. The content of each json file looks like:
Sample 1
$id
[1] "10.1"
$title
$title$value
[1] "Why this item"
$itemsource
$itemsource$id
[1] "AA"
$date
[1] "1992-01-01"
$itemType
[1] "art"
$creators
list()
Sample 2
$id
[1] "10.2"
$title
$title$value
[1] "We need this item"
$itemsource
$itemsource$id
[1] "AY"
$date
[1] "1999-01-01"
$itemType
[1] "art"
$creators
type name firstname surname affiliationIds
1 Person Frank W. Cornell. Frank W. Cornell. a1
2 Person David A. Chen. David A. Chen. a1
$affiliations
id name
1 a1 Foreign Affairs Desk, New York Times
What I need from this set of files is a table with creator names, item ids and dates. For the two sample files above:
id date name firstname lastname creatortype
"10.1" "1992-01-01" NA NA NA NA
"10.2" "1999-01-01" Frank W. Cornell. Frank W. Cornell. Person
"10.2" "1999-01-01" David A. Chen. David A. Chen. Person
What I have done so far:
library(parallel)
library(data.table)
library(jsonlite)
library(dplyr)
filelist = list.files(pattern="*.json",recursive=TRUE,include.dirs =TRUE)
parsed = mclapply(filelist, function(x) fromJSON(x),mc.cores=24)
data = rbindlist(mclapply(1:length(parsed), function(x) {
a = data.table(item = parsed[[x]]$id, date = list(list(parsed[[x]]$date)), name = list(list(parsed[[x]]$name)), creatortype = list(list(parsed[[x]]$creatortype))) #ignoring the firstname/lastname fields here for convenience
b = data.table(id = a$item, date = unlist(a$date), name=unlist(a$name), creatortype=unlist(a$creatortype))
return(b)
},mc.cores=24))
However, on the last step, I get this error:
"Error in rbindlist(mclapply(1:length(parsed), function(x){:
Item 1 of list is not a data.frame, data.table or list"
Thanks in advance for your suggestions.
Related questions include:
Extract data from list of lists [R]
R convert json to list to data.table
I want to convert JSON file into data.table in r
How can read files from directory using R?
Convert R data table column from JSON to data table
from the error message, i suppose this basically means that one of the results from mclapply() is empty, by empty I mean either NULL or data.table with 0 row, or simply encounters an error within the parallel processing.
what you could do is:
add more checks inside the mclapply() like try-error or check the class of b and nrow of b, whether b is empty or not
when you use rbindlist, add argument fill = T
hope this solves ur problem.

Websharper, Sitelets and Forms

I've been trying to create a form using Websharper to collect user input. So far I've identified three actions for my site:
type MyAction =
| [<CompiledName "">] Index
| [<Method "POST">] GetUser of username : string
| Stats of username: string
Using Sitelet.Infer I've managed to implement basic UI, but I have no idea how to refer to the content of my input box (usernameInput):
Sitelet.Infer <| function
| Index ->
Content.PageContent <| fun ctx ->
let usernameInput= Input [Text ""]
{ Page.Default with
Title = Some "Welcome!"
Body =
[
Div [
Form
[
usernameInput-< [Name "username" ]
Input [Value "Request"] -< [Type "submit" ]
] -< [ Attr.Action (ctx.Link (* GetUser usernameInput.Content *) ); Method "POST" ]
]
]
}
| GetUser username ->
Content.Redirect <| Stats username
| Stats username ->
Content.PageContent <| fun ctx ->
{ Page.Default with
Body = [Text ("Stats for " + username)] }
I noticed usernameInput doesn't have any field like "Value" or so and I guess either it needs casting or I'm doing something wrong.
I would prefer not to use JavaScript in my code (Is it possible to mix Html.Server and Html.Client Elements in a Sitelet at all ?).
Form POST data is not passed via the URL, so you cannot pass it with ctx.Link. It is automatically passed via the request body, with a format similar to GET query arguments (for example in your case, username=myusername). This is currently not parsed by Sitelet.Infer, although we will probably add it in the future. For now you can use an action without arguments and then extract the data from the request:
type MyAction =
| [<Method "POST">] GetUser
| // ...
Sitelet.Infer <| function
| GetUser ->
Content.CustomContentAsync <| fun ctx ->
match ctx.Request.Post.["username"] with
| None -> Content.NotFound
| Some username -> Content.Redirect <| Stats username
|> Content.ToResponseAsync ctx
| // ...