我想在KoboToolbox(数据收集)和Google表格之间进行同步,但代码不起作用:我需要自动更新数据,但它没有。
//See ReadMe file for more information which can be found at
//https://github.com/kobotoolbox/kobocat-googleapps-scripts
/* Declare global varibles that are used multiple times*/
var app = UiApp.createApplication();//This creates the application which is used for setup
var current_sheet = SpreadsheetApp.getActiveSpreadsheet(); //This is the current spreadsheet
var checked_URL = ''; //This is a placeholder for the URL of the kobocat form the user has selected during setup
/***** SECTION 1 - Everything from here to the next section is part of the Setup menu item *****/
function setup(){
//This function gets information from the user including authentication token and which form updates this sheet.
var description = app.createLabel('You must get your API Token from your user account by visiting http://kc.kobotoolbox.org/YOUR-USERNAME/api-token', true);
var grid = app.createGrid(3, 3);
grid.setWidget(0, 0, app.createLabel('API Token: '));
grid.setWidget(0, 1, app.createTextBox().setName('token'));
// Create a vertical panel
var panel = app.createVerticalPanel();
// Add the description and grid to the panel
panel.add(description);
panel.add(grid);
// Create a button and click handler; pass in the grid object as a callback element and the handler as a click handler
// Identify the function as the server click handler
var button = app.createButton('Submit');
var handler = app.createServerClickHandler('saveToken');
handler.addCallbackElement(grid);
button.addClickHandler(handler);
// Add the button to the panel and the panel to the application, then display the application app in the Spreadsheet doc
panel.add(button);
app.add(panel);
current_sheet.show(app);
}
function saveToken(e){
var app = UiApp.getActiveApplication();
//Clear the all properties first
ScriptProperties.deleteAllProperties();
//Save the token and let the user know by message box
ScriptProperties.setProperty('token', e.parameter.token);
Browser.msgBox('Token Saved');
//Now, get the form list from kobocat and ask the user which form is meant to update this sheet
setupScriptProperties();//Get the URLs of each of the users forms
askUser();//Now that the token is saved, ask the user which form is meant to update this sheet
}
function askUser() {
//Load the script properties into an array to see how many forms there are
var num_forms = parseInt(ScriptProperties.getProperty('num_forms')) + 1;//we use parseInt to make sure an integer is returned, not a string
//Create a FlexTable to hold values
var table = app.createFlexTable().setId('table').setBorderWidth(1).setCellPadding(1)
//Set Headers of the table
table.setWidget(0, 0, app.createLabel('Form Name'));
table.setWidget(0, 1, app.createLabel('Update this sheet'));
//Create a server handler that handles the checkbox when it's changed
var check = app.createServerHandler('check');
check.addCallbackElement(table);
//The ScriptProperties stores everything as strings. We have to convert true and false strings into boolean properties that can be read by the checkbox
for(r=0;r<num_forms-1; r++){
var formname= 'form'+r+'name';//Define the formname to pull from getProperty
var this_sheet_property_name = 'form'+r+'this_sheet';//Define the property name
var this_sheet_val = ScriptProperties.getProperty(this_sheet_property_name);
if(this_sheet_val == 'true') {
this_sheet_val = Boolean(true);
}else{
this_sheet_val = Boolean(false);
}
//Set the labels and checkboxes in the grid
table.setWidget(r+1, 0, app.createLabel(ScriptProperties.getProperty(formname)));
table.setWidget(r+1,1,app.createCheckBox().addValueChangeHandler(check).setName('check'+r).setValue(this_sheet_val));// create the checkBox once every row if condition is true and give it a name
}
// Create a vertical panel
var panel = app.createVerticalPanel();
// Create a button and click handler that closes the app because the action of checking the box kicks off the check(e) function that saves the scriptproperty
var button = app.createButton('Submit');
var handler = app.createServerHandler('closeApp')
button.addClickHandler(handler);
// ...and add the table to the panel
panel.add(table);
panel.add(button);
app.add(panel);
current_sheet.show(app);
}
function check(e) {
//This function takes the user's input from the form and writes preferences to setProperty
var num_forms = parseInt(ScriptProperties.getProperty('num_forms'));//we use parseInt to make sure an integer is returned, not a string
for(var n=0; n < num_forms;n++){
var formname= 'form'+n+'name';//Define the formname to pull from getProperty
var this_sheet_property_name = 'form'+n+'this_sheet';//Set the property name
//Set the values of the checked box in the ScriptProperties
ScriptProperties.setProperty(this_sheet_property_name, e.parameter['check'+n]);
}
}
function setupScriptProperties(){
var json_array = getkobocatData('https://kc.kobotoolbox.org/api/v1/forms');
//Get the username from the json_array URL
var kobocat_users = JSON.parse(UrlFetchApp.fetch('https://kc.kobotoolbox.org/api/v1/users',getUrlFetchOptions()).getContentText());
var kobocat_username = kobocat_users[0]["username"];
//Assign the script properties
for (var j=0; j<json_array.length; j++)
{
ScriptProperties.setProperty('form'+j+'name',json_array[j]["id_string"]);//This value saves the name of the form
ScriptProperties.setProperty('form'+j+'URL', json_array[j]["url"]);//This value saves the URL of the form
ScriptProperties.setProperty('form'+j+'this_sheet', 'false');//This value will determine if this form is the one that updates the sheet
ScriptProperties.setProperty('form'+j+'num_responses', 0)//This tracks the number of form responses per form allowing us to know if there has been an update
}
//Save the number of forms
ScriptProperties.setProperty('num_forms', j);
ScriptProperties.setProperty('kobocat_username', kobocat_username);
//Logger.log(ScriptProperties.getProperty('num_forms'));
}
function closeApp(){
app.close();
Browser.msgBox("Setup Complete");
}
/***** SECTION 2 - Everything from here to the next section is part of the 'Import Data' menu item *****/
function ImportData(){
//This function imports data from kobocat to the existing spreadsheet
//First check to see if the setup has been run by evaluating the first form's name. If it's null run setup
if(ScriptProperties.getProperty('form0name')== null){
setup();
}else{
//See if an update is needed
if(UpdateNeeded()){
//if needed, get the data from kobocat
//Call the function getkobocatData but first, change the URL from /forms/ to /data/
var form_data_JSON = getkobocatData(checked_URL.replace('/forms/','/data/'));//Note this presently returns all data
//Use the setRowsData function (SECTION 5) to write the data to the sheet
setRowsData(current_sheet.getActiveSheet(), form_data_JSON);
Logger.log(form_data_JSON);
Browser.msgBox('Update Complete');
}else{
//Tell the user that the form is up to date
Browser.msgBox('This sheet is up to date')
}
}
}
function UpdateNeeded(){
// Check to see which form has a true value for "this_sheet" property
var num_forms = parseInt(ScriptProperties.getProperty('num_forms'));//we use parseInt to make sure an integer is returned, not a string
for(var n=0; n < num_forms;n++){
var formname= 'form'+n+'name';//Define the formname to pull from getProperty
var this_sheet_property_name = 'form'+n+'this_sheet';//Set the property name
if(ScriptProperties.getProperty(this_sheet_property_name) =='true'){
var checked_formname = ScriptProperties.getProperty('form'+n+'name');
checked_URL = ScriptProperties.getProperty('form'+n+'URL');
}
}
//Connect to kobocat and get the form list
var list_of_forms_JSON = getkobocatData('https://kc.kobotoolbox.org/api/v1/forms');
//Match the checked_formname to the form in the list_of_forms_JSON and get the number of submissions
for (var i=0; i < list_of_forms_JSON.length; i++){
if(list_of_forms_JSON[i]['id_string'] == checked_formname) {
var latest_num_of_submissions = list_of_forms_JSON[i]['num_of_submissions'];
}
}
//Check to see if number of submissions is different than the number of rows in the spreadsheet
var lastsheetrow = current_sheet.getActiveSheet().getLastRow() - 1; //set the last row of the current sheet (Assuming 1 row header)
var Update_Needed = true; //assume an update is needed unless the number of rows in the sheet is different from the latest num of submissions
if (latest_num_of_submissions == lastsheetrow) {
Update_Needed = false;
}
return Update_Needed;
}
/***** SECTION 3 - Communicating with the kobocat server (used more than once) getkobocatData and Token Authorization Parameters *****/
function getkobocatData(getDataURL){
//First, we have to translate the
var list_of_forms = UrlFetchApp.fetch(getDataURL,getUrlFetchOptions()).getContentText(); //provides a TEXT list of form names and URLs
var json_array = JSON.parse(list_of_forms); //parse to JSON so we can work with the data in an array
//Logger.log(json_array);
return json_array;
}
function getUrlFetchOptions() {
//This function returns the Authorization headers with token information to kobocat
return {
"headers" : {
"Authorization" : "Token " + ScriptProperties.getProperty('token'),
}
};
}
/***** SECTION 4 - Working with JSON object and entering into spreadsheet *****/
//The following Google Developers site was instrumental in writing this code: https://developers.google.com/apps-script/guides/sheets#reading
// setRowsData fills in one row of data per object defined in the objects Array.
// For every Column, it checks if data objects define a value for it.
// Arguments:
// - sheet: the Sheet Object where the data will be written
// - objects: an Array of Objects, each of which contains data for a row
// - optHeadersRange: a Range of cells where the column headers are defined. This
// defaults to the entire first row in sheet.
// - optFirstDataRowIndex: index of the first row where data should be written. This
// defaults to the row immediately below the headers.
function setRowsData(sheet, objects, optHeadersRange, optFirstDataRowIndex) {
var headersRange = optHeadersRange || sheet.getRange(1, 1, 1, sheet.getMaxColumns());
var firstDataRowIndex = optFirstDataRowIndex || headersRange.getRowIndex() + 1;
var headers = headersRange.getValues()[0];
var data = [];
for (var i = 0; i < objects.length; ++i) {
var values = []
for (j = 0; j < headers.length; ++j) {
var header = headers[j];
// If the header is non-empty and the object value is 0...
if ((header.length > 0) && (objects[i][header] == 0)) {
values.push(0);
}
// If the header is empty or the object value is empty...
else if ((!(header.length > 0)) || (objects[i][header]=='')) {
values.push('');
}
else {
values.push(objects[i][header]);
}
}
data.push(values);
}
var destinationRange = sheet.getRange(firstDataRowIndex, headersRange.getColumnIndex(),
objects.length, headers.length);
destinationRange.setValues(data);
}
/***** SECTION 5 - Setting up custom menu *****/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Setup",
functionName : "setup"
},
{ name : "Import Data",
functionName : "ImportData"
}];
sheet.addMenu("Update from kobocat", entries);
};
&#13;
谢谢