Message Boards Message Boards

Pass a variable to Wolfram code when using Cloud Deploy?

GROUPS:

We're new to Wolfram but in a very short time using the Web Development Platform have been able to develop a solution that presents business stats from system events logged in a Data Drop databin. This databin collects events and "tags" the system from which it came by adding a system ID (sID) as one of the variables logged. Our development solution hard codes the sID in the initialization section. What we would like to do is to pass the sID into the code as a variable in the Cloud Deploy URL which in turn we can pull out of the URL and use to "dynamically" set sID. The APIFunction seems to head in the right direction but I've not been able to figure out how to encapsulate all our code (initialization > creation of datasets > computation of datasets > presentation of stats) in the APIFunction body. In short, we're stuck so am hoping the Community can give some guidance.

THANKS!

POSTED BY: Randy Schultz
Answer
10 days ago

I find that it helps myself to come up with a simple, toy example of the problem I'm trying to solve. That often at least helps clear any misconceptions. Let me try to do that for your example:

You have a databin with some data and each entry is labeled with a specific site ID:

myBin = CreateDatabin[
  "Interpretation" -> <|"SiteID" -> "String", "Reading" -> "String"|>];

DatabinAdd[myBin, <|"SiteID" -> "a", "Reading" -> "11"|>];
DatabinAdd[myBin, <|"SiteID" -> "b", "Reading" -> "24"|>];
DatabinAdd[myBin, <|"SiteID" -> "a", "Reading" -> "36"|>];
DatabinAdd[myBin, <|"SiteID" -> "b", "Reading" -> "41"|>];

We might want to extract data from a specific SiteID. We can write a function to do this:

getSiteFromBin[bin_, site_] := 
 Map[Lookup["Reading"]]@
  Select[Lookup[#, "SiteID"] == site &]@Normal[bin]

There are different ways of writing this function. But you can test that it works:

getSiteFromBin[myBin, "a"]
{"11", "36"}

We can now build an API that runs this function on our databin:

CloudDeploy[
 APIFunction[{"SiteID" -> "String"}, 
  getSiteFromBin[myBin, #SiteID] &], "myAPI"]

CloudObject["https://www.wolframcloud.com/objects/me/myAPI"]

And it works:

URLExecute["https://www.wolframcloud.com/objects/me/myAPI?SiteID=a"]

Is this the kind of thing you're trying to do?

POSTED BY: Sean Clarke
Answer
10 days ago

Hi. Can we try making a small example, so I can better understand your problem? I've tried to read this several times, but I can't quiet get a grasp on what you're doing. Very often the best way to get help with a problem like this is to make the smallest possible example which demonstrates your problem.

In particular I'm confused about the relation of the Databin and the API. Does the API store data into the Databin after processing it in some way? Or, does the API get fed data from the Databin by something?

The url you give CloudDeploy is the base URL for your API. You can think of that as the name of your API. There's a syntax for passing a value to an API. For example, if you're using an API with the URL "www.example.com/api/exampleAPI" you pass values to the API with the syntax "www.example.com/api/exampleAPI?x=1" and not by changing the base URL. How are you passing values to the API?

POSTED BY: Sean Clarke
Answer
10 days ago

There was a request for clarification so let me try to provide that here...

We are logging events from multiple devices located in multiple sites into a databin. As part of this logging we are capturing the site from which the log event is made by adding a site ID (sID) to the event; my original post said system ID but a more accurate description would be site ID. All of this is working fine.

This done we began developing code using the Wolfram Development Platform that accesses the databin and uses the sID to create a site-specific dataset; it basically extracts site-specific events from a databin that contains events from multiple sites. This site-specific dataset is then used to generate site-specific stats. All of this too is working fine but at present the sID is hard-coded...

sID="Site01"

The problem is that we have to change the code every time we want to show a different site; for example...

sID="Site02"

Etc.

Ideally, we'd like to pass the sID in a URL that calls our code; for example...

https://www.wolframcloud.com/objects/879e8689-dddf-4e33-a908-37773e9f73df?sID="Site03"

This would enable us to support multiple sites from one code base rather than having to customize the code with hard-coded sID for each site.

Hope this provides the clarification requested.

POSTED BY: Randy Schultz
Answer
10 days ago

YES!

This is exactly what I'm looking to do. That said, I think I'm struggling with the function block. In short, my code is organized as follows...

INITIALIZATION SECTION (95 lines of code including whitespace & comments):

(* Declare variables. For example *) sID="Site01" bID="abc123"

(* Using bID, generate dataset of entire databin *) bin=Databin[bID] dataset=Dataset[bin]

(* Using sID, generate three different site-specific datasets A, B, A+B *)

CODE BODY (2 lines of code, a MANIPULATE line and INSERT line):

(* Using datasets generated in the INITIALIZATION SECTION, draw bar chart than can be manipulated by selecting A, B or A+B *)

(* Using datasets generated in the INITIALIZATION SECTION, draw table showing A, B and A+B data *)

I saw you use the function symbol (:=). Can I encapsulate ALL of my code (initialization & body) in one function and if so how do you do this (again, am new to the Wolfram language)? Your example showed what appeared to be a single expression behind the := symbol. If I can encapsulate all my code then it should be straightforward to generate the APIFunction.

Look forward to your feedback!

POSTED BY: Randy Schultz
Answer
10 days ago

Good code is broke up into reasonable sized functions, each of which performs a coherent task.

I'm not sure what your background is with programming, but knowing how to make functions is important. I'll try to keep this definition flexible and simple.

To create a function use the following syntax:

(* Comment saying what "myFunction" does *)
myFunction[argument1_, argument2_,argument3_] :=
    Module[{},
        something;
        something;
        something;
        (* The value to be returned by the function is the last value and doesn't have a semicolon after it *)
          valueToBeReturned
    ];

As an aside, the first argument to Module shown here is an empty list. You can put the names of variables in that list and those variables will be localized to the Module. Localizing variables is a good idea, if you're familiar with the concept. Otherwise, this is the general idea of how you make a function in the Wolfram Language.

POSTED BY: Sean Clarke
Answer
10 days ago

Hi Sean,

Thanks for sticking with me on this! I understand the concept & value of breaking your code into functional blocks. My issue is more with understanding the syntax & capability of Wolfram and if its even possible to use it the way in which I'd like.

My goal is to create a web "application" built on Wolfram. What I'd like to do is provide my customers a custom URL to launch the application. For example...

https://www.wolframcloud.com/objects/me/myAPI?binID=abc123&siteID=site03

This would be given to a customer who was responsible for site03.

In passing these variables to my code...

  • binID
  • siteID

The code would use them to extract & generate three datasets - A, B, A+B - from a databin. These datasets would be used to display a bar chart that can be manipulated by selecting the desired view - A, B, A+B - followed by a data table showing the raw data for A, B, A+B.

The APIFunction seems to be the way to go but I'm not looking to return anything. I'm looking to end with the bar chart & data table displayed. Is this possible? Let me know as I'm beginning to wonder if I should write this in PHP / MySQL. It would be no where near as tight or elegant as Wolfram but it would give me a level of control I haven't been able to figure out in Wolfram.

Look forward to your feedback!

POSTED BY: Randy Schultz
Answer
9 days ago

I think I have a simple example to explain what I'm trying to do...

y=1;
Manipulate[Plot[Sin[x (y + a x)], {x, 0, 6}], {a, 0, 2}]

Assume this is my entire code. I set the value of y and call a Manipulate function which in turn draws a sine plot that can be manipulated over x. Is it possible to create a Cloud Deploy URL that passes y as a URL variable and ends up drawing the associated sine plot?

POSTED BY: Randy Schultz
Answer
9 days ago

I followed the recommended steps.

Created function...

plotSineFunction[y_] := Manipulate[Plot[Sin[x (y + a x)], {x, 0, 6}], {a, 0, 2}]

Created APIFunction...

CloudDeploy[
 APIFunction[{"yVar" -> "Number"}, 
  plotSineFunction[#yVar] &], "spAPI"]

Called API with a yVar value of 2...

[https://www.wolframcloud.com/app/objects/user-2147c9f2-d003-455e-b130-9c3bc9a7b9b5/spAPI?yVar=2]

Output was not the manipulable sine plot but rather...

Manipulate[Plot[Sin[x(2 + ax)], {x, 0, 6}], {a, 0, 2}]

Is there a way to output the manipulable sine plot rather than the text output listed above?

POSTED BY: Randy Schultz
Answer
9 days ago

So, APIs generally just return data. That's probably their smartest use-case. You build a website and then have something that queries an API to do the actual number crunching and then that website displays the result.

You can have an APIFunction return something more complicated like a webpage, or in this case, something as complicated as a CDF document. CDF is the WolframLanguage's web format for interactive documents:

CloudDeploy[
 APIFunction[{"yVar" -> "Number"}, 
  ExportForm[plotSineFunction[#yVar], "CloudCDF"] &], "spAPI"]
POSTED BY: Sean Clarke
Answer
9 days ago

Thanks Sean!

This example is EXACTLY what I was looking for!!!

POSTED BY: Randy Schultz
Answer
9 days ago

Group Abstract Group Abstract