Smartsheet-API: Populating data and formulas in a sheet - smartsheet-api

UPDATE 2022-12-01
This question is now obsolete. Smartsheet API now supports the requested feature. The accepted answer contains more details on the update.
CONTEXT
A developer wishes to populate an existing smartsheet worksheet using the smartsheet API. The data to populate consists of both data and formulas.
However, according to the API documentation (http://www.smartsheet.com/developers/api-documentation) formulas cannot be added using the API.
Cells containing formulas, links to other cells, system values, or
Gantt values cannot be inserted or updated through the API.
PROBLEM
Testing verifies this. Attempting to add a simple formula using the smartsheet API causes the formula to be transformed to opaque text. Inspection reveals that the formula is modified with the single quote character, which renders it as opaque text instead of a formula.
QUESTION
Is there any way (other than through manual entry) to force smartsheet to re-evaluate the opaque text inserted, so as to transform the opaque text back into a formula?
If it is not possible (other than through manual entry) is it possible to copy an existing sheet that has formulas in place, and then populate the non-formula data into the sheet, all using the smartsheet API?
GOAL
The basic goal is to find a way to populate formula data into the smartsheet application without having to require manual entry of the formula data.

Update: Smartsheet does now support adding or updating formulas via the API which can be found in the documentation for adding a row and updating a row.
The main difference is to set the formula in the row object instead of setting the value.
Original Answer
You are correct, formulas are not currently supported via the API. Although, we do plan to add this functionality in the future.
Currently, if you try to send a formula to the API it will be handled as a string and a single quote will be added to the beginning of the formula. Then the only way to convert the string back to a formula is to manually remove the single quote when inside the Smartsheet UI.
Available Option
Your suggestion to use a template will definitely work if you will always be using the same formulas. The process will look like the following:
Setup a template with the formulas that you want to use.
Create a new sheet from the template.
Add extra data to the new sheet that the formulas will use.
Note: rows that have never been used cannot be updated since they do not exist. So, in the template you can initialize the rows by putting a word in the locations that you want to update. For example, you could put the word "PLACEHOLDER" in all of the locations that you intend to update.
I have added two examples below one using curl and the other using our Java SDK.
Curl Example
Create a new sheet from the Template. Make sure to replace YOUR_TOKEN and YOUR_TEMPLATE_OR_SHEET_ID in the below command.
curl https://api.smartsheet.com/1.1/sheets?include=data,attachments,discussions -H "Authorization: Bearer YOUR_TOKEN" -H "Content-Type: application/json" -X POST -d '{"name":"newSheetFromTemplate","fromId":"YOUR_TEMPLATE_OR_SHEET_ID"}'
Then take the sheet id from the response and issue a command to get the row id's.
curl https://api.smartsheet.com/1.1/sheet/YOUR_SHEET_ID -H "Authorization: Bearer YOUR_TOKEN"
Last, grab the row id and column id from the response and issue a command to update the appropriate cells. I'm updating two cells with the values 1 and 2 with the below command.
curl https://api.smartsheet.com/1.1/row/YOUR_ROW_ID/cells -H "Authorization: Bearer YOUR_TOKEN" -H "Content-Type: application/json" -X PUT -d '[ {"columnId": YOUR_COLUMN_ID, "value": 1}]'
curl https://api.smartsheet.com/1.1/row/YOUR_ROW_ID/cells -H "Authorization: Bearer YOUR_TOKEN" -H "Content-Type: application/json" -X PUT -d '[ {"columnId": YOUR_COLUMN_ID, "value": 2}]'
SDK Example
This example requires installing our Java SDK. There is also a C# SDK that works in a very similar fashion.
import java.util.EnumSet;
import java.util.List;
import com.smartsheet.api.Smartsheet;
import com.smartsheet.api.SmartsheetBuilder;
import com.smartsheet.api.models.Cell;
import com.smartsheet.api.models.Column;
import com.smartsheet.api.models.ObjectInclusion;
import com.smartsheet.api.models.Row;
import com.smartsheet.api.models.Sheet;
import com.smartsheet.api.SmartsheetException;
public class Test {
public static void main(String[] args) throws SmartsheetException {
// Setup a Smartsheet object
Smartsheet smartsheet = new SmartsheetBuilder().setAccessToken("YOUR_TOKEN").build();
// Create a copy of a sheet from the template
Sheet newSheet = new Sheet.CreateFromTemplateOrSheetBuilder().setFromId(YOUR_TEMPLATE_OR_SHEET_ID).setName("newSheetName").build();
newSheet = smartsheet.sheets().createSheetFromExisting(newSheet, EnumSet.allOf(ObjectInclusion.class));
// Get the columns/rows/data for the sheet we just created
newSheet = smartsheet.sheets().getSheet(newSheet.getId(), EnumSet.allOf(ObjectInclusion.class));
// Grab the column and rows that will be updated in the new sheet
Column column1 = newSheet.getColumnByIndex(0);
Row row1 = newSheet.getRowByRowNumber(1);
Row row2 = newSheet.getRowByRowNumber(2);
// Setup two cells for the the specified columns
List<Cell> newCell1 = new Cell.UpdateRowCellsBuilder().addCell(column1.getId(), 1).build();
List<Cell> newCell2 = new Cell.UpdateRowCellsBuilder().addCell(column1.getId(), 2).build();
// Update the cell for the specified row
smartsheet.rows().updateCells(row1.getId(), newCell1);
smartsheet.rows().updateCells(row2.getId(), newCell2);
}
}

Related

How to get child elements that is not deleted using nuxeo rest endpoint?

When I call http://localhost:8080/nuxeo/api/v1/id/bad6cbc5-b75f-4373-981f-6908cec66779?enrichers.document=children endpoint it returns all child elements include deleted elements. But I need to get only active elements and I think I should add isTrashed=false query to endpoint. But http://localhost:8080/nuxeo/api/v1/id/bad6cbc5-b75f-4373-981f-6908cec66779?enrichers.document=children&isTrashed=false does not any effect. How can I get only active child elemets from nuxeo server using rest api?
/nuxeo/api/v1/id endpoint with children enricher does not support this kind of filtering.
I see two options:
Implement own enricher which will support filtering of trashed documents. children enricher is implemented by org.nuxeo.ecm.core.io.marshallers.json.enrichers.ChildrenJsonEnricher class so you can inspire there how to do that.
Use different end point with page provider which supports filtering of trashed documents:
/nuxeo/api/v1/search/pp/advanced_document_content/execute?&ecm_parentId=bad6cbc5-b75f-4373-981f-6908cec66779&ecm_trashed=false
Benefits of the second option:
paging - simply add currentPageIndex=0&offset=0&pageSize=20 to the query
properties - you can define what properties do you need by adding of header: properties:dublincore,common,uid,file
enrichers - it means that you can use enrichers for each child and receive for example permission or thumbnail URL for each child. To do that add this header: enrichers-document: thumbnail, permissions
Example curl call:
curl -X GET -u Administrator:Administrator \
-H "properties:dublincore,common,uid,file" \
-H "enrichers-document: thumbnail, permissions" \
"http://localhost:8080/nuxeo/api/v1/search/pp/advanced_document_content/execute?&ecm_parentId=bad6cbc5-b75f-4373-981f-6908cec66779&ecm_trashed=false" | jq

Adding Pull Requests & Issues to a Project

Does the Github API provide an easy way to add a Pull Request or an Issue to a Project Board?
This is the programmatic equivalent of a user going to a pull request and selecting one more "Projects" from the sidebar menu
NOTE: The API does seem to provide a way to add cards to the Project, but I have to specify a specific project column. I'd love to just add the project blindly and let automation rules determine the column, similar to clicking on it through the UI.
Thanks!
I think the best way to associate an existing pull request with a project, in some kind of default column, would be to daisy-chain three separate pieces of the Github API, the Get a Single Pull Request method, the Create a Project Card method, and the List Project Columns method. The idea is as follows:
Use 'Get a Single Pull Request' to retrieve the ID
Use 'List Project Columns' to get a list of the columns
Do any conditional logic if you want to see if a certain column exists, or just use the first one, or create a certain column if it doesn't exist
Use "Create a Project Card" to add the card using the Pull request ID and Column you've selected.
Here is a simplified example in Python:
import requests, json
#get pull request
r = requests.get('https://api.github.com/repos/[myusername]/[myrepo]/pulls/2')
pull = json.loads(r.text)
#requires authentication ... create your token through Github.com
api_token = "mytoken"
#prepare dictionary of header data
h = {"Accept":"application/vnd.github.inertia-preview+json", "Authorization": "token %s" % api_token}
projects_r = requests.get('https://api.github.com/repos/[myusername]/[myrepo]/projects', headers=h)
#get projects data
projects = json.loads(projects_r.text)
#get columns url for the first project in the list projects
columns_url = projects[0]['columns_url']
columns_r = requests.get(columns_url, headers=h)
columns = json.loads(columns_r.text)
#get column url for the first column in the list of columns
column_url = columns[0]['cards_url']
#use retrieved data to build post
data = {"content_id":pull_id, "content_type":"PullRequest"}
#use post method with headers and data to create card in column
result = requests.post(column_url, headers=h, data=json.dumps(data))
#returns with status 201, created

Solr: Synonyms using the Managed Resources REST API

How do I change an initArgs value for Synonyms using the Managed Resources REST API?
In particular, I need to change the following:
"initArgs":{"ignoreCase":false}
... to true.
https://cwiki.apache.org/confluence/display/solr/Managed+Resources#ManagedResources-Synonyms
I don't see any mention in the documentation about changing initArgs.
You can edit the file directly after it has been created, but the docs explicitly say this is not the correct way to change data in this file. (it does work however).
found it. try&error style ;-)
curl -X POST -H 'Content-type:application/json' --data-binary '{"initArgs":{"ignoreCase":true}}' "http://<solr-host>/solr/<core>/schema/analysis/synonyms/german"

How to use scaffolding and RESTfulness together in Grails 2.3

Official Grails documentation says that
Version 2.0.x of the scaffolding plugin includes different scaffolding
templates that are aligned with the new REST APIs introcued in Grails
2.3 and above.
(taken from here http://grails.org/doc/latest/guide/scaffolding.html)
But I can't make (or I don't understand the concept) work RESTfulness together with scaffolding.
Let's start from scratch:
grails create-app myapp
cd myapp/
grails create-domain-class Book
grails create-scaffold-controller myapp.Book
Add a field to the domain class
class Book {
String text
static constraints = {
}
}
and run the app with grails run-app.
Surfing on the http://localhost:8080/myapp/ shows that scaffolding works great:
http://localhost:8080/myapp/book/index page shows books list
http://localhost:8080/myapp/book/show/1 page show details for the book with id = 1
http://localhost:8080/myapp/book/create page creates a book
and so force, good old scaffolding.
Let's see what about REST.
Official docs say I should use URLs like http://localhost:8080/myapp/books/... for the REST but any attempt to access the app, like this curl -i -H "Accept: application/json" localhost:8080/myapp/books/1 returns 404 with bunch of HTML.
Ok, let's read docs carefully:
The easiest way to create a RESTful API in Grails is to expose a
domain class as a REST resource. This can be done by adding the
grails.rest.Resource transformation to any domain class
No problem, now the Book class heading is
import grails.rest.*
#Resource(uri='/books') class Book {
Now surfing on the http://localhost:8080/myapp/ shows that scaffolding is broken:
http://localhost:8080/myapp/book/index page shows books list
http://localhost:8080/myapp/book/create page shows xml output <?xml version="1.0" encoding="UTF-8"?><book><text /></book>
and so force, bad new xml output.
I'd played with #Resource and "/books"(resources:"book") in URLMappings.groovy but hadn't found any working solution which makes possible scaffolding and RESTfulness work back-to-back. Indeed, I managed to make them work separately.
Update
I'd found the way how to achieve the desired goal. The way I found is:
Mark the Book class with #Resource(uri = "/books").
Remove scaffold controller BookController.
Create dedicated controller with scaffolding for the Book: class HumanBookController {static scaffold = Book}
Now scaffold GUI pages with URLs like http://localhost:8080/myapp/humanBook/index work pretty well. Either json requests are handled well with URLs like http://localhost:8080/myapp/books/1. But it's not elegant to have 2 controllers doing same things for common web and json.
You can do this:
import grails.rest.RestfulController
class BookController extends RestfulController {
static responseFormats = ['html', 'json']
BookController() {
super(Book)
}
}
And then in the UrlMappings.groovy:
"/books"(resources:"book")
"/$controller/$action?/$id?(.${format})?"{
constraints {
// apply constraints here
}
}
No need to add #Resource in the domain.
You can now have /books/1.json or /books/1.html to point to the right places. You might still need to do grails generate-view Book to have the view generated. But although you need to generate the views for html, you keep only single controller and path.
I had the same problems as yours.
That might be a trivial solution and not for every case, but try updating your Grails version.
As for me: Grails 2.3.4 -> Grails 2.3.6 worked.
Hope that help anyone.
I'm currently using Grails 2.4.0, the solution came by doing this:
Controller: BookController { static scaffold = true }
Domain: Book { .... } // WITHOUT #Resource
The result is that you can:
/book.json to get a list JSONized
/book/index to get an HTML standard scaffolding
/book/create html scaffold for a new item
/book/show/1 html scaffold edit item 1
/book/show/1.json JSON for item id: 1
I'ts wicked, i know. I found this and it get me going.
With Grails 2.4.4 I was able to get the scaffolding working with single controller using the following steps:
Added a URL to Resource mapping in UrlMappings.groovy, e.g. "/books"(resources:"book")
Inserted static scaffold = true into the generated controller
I did not verify if the following made a difference, but I also set grails.mime.disable.accept.header.userAgents = [] and grails.mime.use.accept.header = true in Config.groovy (the latter is presumably the new default value).
Both of the scaffolded REST and UI interfaces are working fine with the following tests:
GET /app//1 (passing Accept header)
GET /app//1.json (no Accept header)
POST /app/ (with payload as json or form encoded)
DELETE /app//1
PUT /app//1 (with json payload. form payload updated the object, but sent back 302 redirects)
EDIT
Removed the Resource annotation step and clarified the URL mapping setup
The URI assigned in the URL mapping is not the same as the default URI for the controller. For example, "books" instead of "book". After adding this mapping, the URI for the controller will default to the URI in UrlMapping, but the original URI will continue to work.
The generated controller is a Restful controller because it implements actions aware of requests like:
curl -i -X GET yourDomain:8080/yourApp/books.json
It returns a list of books in json format. (10 books, assuming that you created test data, did you?)
You can append a parameter like:
curl -i -X GET yourDomain:8080/yourApp/books.xml?40
By default you will get the html format. You need to append .json or .xml to get the correct data.
You can to use the Accept header too
curl -i -X GET -H "Accept: application/xml" yourDomain/books/1
returns details of book with id=1 in xml format. Finally
curl -i -X POST -H "Content-Type: application/json" -d "{name: 'Book'}" yourDomain/books
creates a new book and
curl -i -X PUT -H "Content-Type: application/json" -d "{name: 'Book'}" yourDomain/books/1
updates name of book with id=1
All resources need to be exposes through and url. The url is not generated for you, you should write it on UrlMappings file:
"/v1/books"(resources: "book")
Where the first string "/v1/books" is the uri and the second string "book" is the controller name following the grails convention. (The preceding v1 string is because I always put version number to my API URIs)
| GET | /v1/books | Action: index |
| GET | /v1/books/create | Action: create |
| POST | /v1/books | Action: save |
| GET | /v1/books/${id} | Action: show |
| GET | /v1/books/${id}/edit | Action: edit |
| PUT | /v1/books/${id} | Action: update |
| DELETE | /v1/books/${id} | Action: delete |
All that should be required is #Resource annotation with the uri on the Domain class. If you want specific formats (default format is first), also include the formats:
#Resource(uri='/books', formats=['json', 'xml'])
That should be it. If ypu are still having trouble finding your dynamic #Resource endpoint, try running:
grails url-mappings-report
That will give you a nice summary of all urls, including those backed by scaffolded controllers for #Resource domains. I have found that I tend to make silly mistakes when trying to "guess" the URL - using the report output ensures you and grails are in agreement.

Facebook Ads API: What is 'location' parameter in the call for a Reach estimate

I'm attempting to work with the reachestimation objects defined at http://developers.facebook.com/docs/reference/ads-api/reachestimate/.
Both the bid estimation and impression estimation that are part of the reach estimate contain an entry for location that is given with a simple integer.
I've searched throughout the documentation and can't find any listing of what these numbers correspond to.
Does anyone know where I can find this information and/or what the different values are for?
If they're not documented, it's probably a bad idea to rely on them, but it refers to the different locations on Facebook in which the ad will be shown.
I don't know which values correspond to which locations, but presumably one refers to News Feed and one refers to the normal right-hand-side ads bar
You can search for mapping between the location name and the integer by using the adgeolocation endpoint. Here's the curl example:
curl -G \
-d "type=adgeolocation" \
-d "q=Washington" \
-d "location_types=['city']" \
-d "access_token=XYZ" \
https://graph.facebook.com/v2.3/search