从 API 查询到 Google 电子表格/G Apps 脚本并获取过滤结果

Querying from an API to a Google Spreadsheet / G Apps Script and obtaining filtered results

我正在尝试为 1 分钟的外汇数据构建一个基于 DataDT's 优秀 API 的电子表格。我正在尝试构建一个函数,该函数 1) 从单元格中读取值 ("Date time") 2) 从上述 API 中的给定 URL 中搜索该值 3) 打印 2同一日期的其他属性(开盘价和收盘价)。

换句话说,它将从 N 行和 O 行获取输入,并在 H 行和 I 行中输出相关值(来自 API 的 OPEN 和 CLOSE)。

(Link 到当前 GSpreadsheet)

此电子表格将 link 宏观经济新闻和历史价格,并可能为外汇用户揭示有用的见解。

我已经成功地从 API 中有效地查询数据,但我找不到一种方法来仅针对我所询问的日期时间进行过滤。不同日期的迭代要少得多!在用户@Cooper 的帮助下,我得到了以下代码,可以从 API 查询整个页面,但还不能有效地过滤。如果您能提供任何帮助,我将不胜感激。

这是 Appscript 中代码的当前状态:

(Code.gs)

function searchOnEdit(e) {
  //e.source.toast('Entry');// I use these lines for debugging
  var sh=e.range.getSheet();
  if(sh.getName()!='API') return;
  var checkedValue='TRUE';//these are the defaults if you install the checkboxes from the Insert Menu
  var uncheckedValue='FALSE';
  if(e.range.columnStart==17 && e.range.rowStart>1 && e.value==checkedValue) {
    e.range.setValue(uncheckedValue);//this was commented out it should not have been sorry for that Cooper
    //e.source.toast('flag1');
    var r=sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).getValues()[0];
    var obj={datetime:r[14],url:r[13],event:e};
    var dObj=getDataDT1(obj);
    //Logger.log(dObj);
    sh.getRange(e.range.rowStart,4).setValue(dObj.OPEN);//loading OPEN on your spreadsheet
    sh.getRange(e.range.rowStart,5).setValue(dObj.CLOSE);//loadding CLOSE on your spreadsheet
  }
}

//{datetime:'',url:'',event:e}
function getDataDT1(obj) {
  Logger.log(JSON.stringify(obj));//I need to see this
  var r=UrlFetchApp.fetch(obj.url);
  var data=JSON.parse(r.getContentText("UTF-8"));
  //Logger.log(data);
  var pair='USDJPY';
  var dat=new Date(obj.datetime);
  var dtv=new Date(dat.getFullYear(),dat.getMonth(),dat.getDate(),dat.getHours(),dat.getMinutes()).valueOf();
  for(var i=0;i<data.length;i++) {
    var dt=data[i].DATE_TIME.split(' ');
    var sd=new Date(data[i].DATE_TIME);
    var sdv=new Date(sd.getFullYear(),sd.getMonth(),sd.getDate(),sd.getHours(),sd.getMinutes()).valueOf();
    if(sdv==dtv) {
      var d=dt[0].split('-');
      var t=dt[1].split(':');
      var datestring=Utilities.formatString('%s/%s/%s',d[1],d[2],d[0]);
      var timestring=Utilities.formatString('%s:%s',t[0],t[1]);
      var rObj={DATE:datestring,TIME:timestring,PAIR:pair,OPEN:data[i].OPEN.toFixed(3),CLOSE:data[i].CLOSE.toFixed(3)};
      break;
    }
  }
  //Logger.log(rObj);
  return rObj;
}

(Appscript.json)

{
  "timeZone": "America/Caracas",
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER",
  "oauthScopes":  ["https://www.googleapis.com/auth/userinfo.email","https://www.googleapis.com/auth/script.external_request","https://www.googleapis.com/auth/script.scriptapp","https://www.googleapis.com/auth/spreadsheets"]
}

(Trigger.js)

function createSearchOnEditTrigger() {
  var ss=SpreadsheetApp.getActive();
  ScriptApp.newTrigger('searchOnEdit').forSpreadsheet(ss.getId()).onEdit().create();
}

onEdit 搜索

您需要在第 17 列中添加一列复选框,并创建一个可安装的 onEdit 触发器。您可以使用提供的代码或通过 Edit/Project 触发器菜单手动执行。使用触发器创建代码时,请检查以确保只创建了一个触发器,因为多个触发器可能会导致问题。

此外,不要将可安装触发器命名为 onEdit(e) 的错误,因为它会响应简单触发器和导致问题的可安装触发器。

我在下面有一个动画向您展示它是如何运作的,您还可以看到电子表格的布局。请注意隐藏的列。我必须这样做才能使动画尽可能小。但我没有删除你的任何专栏。

最好等待复选框重置为关闭状态,然后再选中另一个复选框。有可能检查它们的速度如此之快以至于脚本无法跟上并且可能会遗漏一些搜索。

我还必须手动添加这些范围:

"oauthScopes":["https://www.googleapis.com/auth/userinfo.email","https://www.googleapis.com/auth/script.external_request","https://www.googleapis.com/auth/spreadsheets"]

您可以将它们放入您的 appsscript.json 文件中,该文件可以使用 View/Show 清单文件查看。这里有一个 reference,只是勉强向您展示了它们的样子。但基本思想是在右括号之前的最后一个条目之后放置一个逗号,并添加所需的行。

创建触发器后,最好进入 View/Current 项目触发器并将通知设置为立即。如果您遇到范围界定错误,它会告诉您要添加哪些。您添加它们,然后 运行 一个函数,您可以使用其他范围重新授权访问。您甚至可以 运行 像 function dummy(){}; 这样的空函数。

这是 onEdit 函数:

function searchOnEdit(e) {
  //e.source.toast('Entry');// I use these lines for debugging
  var sh=e.range.getSheet();
  if(sh.getName()!='API') return;
  var checkedValue='TRUE';//these are the defaults if you install the checkboxes from the Insert Menu
  var uncheckedValue='FALSE';
  if(e.range.columnStart==17 && e.range.rowStart>1 && e.value==checkedValue) {
    e.range.setValue(uncheckedValue);
    //e.source.toast('flag1');
    var r=sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).getValues()[0];
    var obj={datetime:r[14],url:r[13],event:e};//you dont really need e here
    var dObj=getDataDT1(obj);
    //Logger.log(dObj);
    sh.getRange(e.range.rowStart,4).setValue(dObj.OPEN);//loading OPEN on your spreadsheet
    sh.getRange(e.range.rowStart,5).setValue(dObj.CLOSE);//loadding CLOSE on your spreadsheet
  }
}

这是搜索功能。我尝试缓存数据,但它太大了。因此,如果您可以显着减小大小,这将有助于加快连续搜索的速度。

//{datetime:'',url:'',event:e}
function getDataDT1(obj) {
  var r=UrlFetchApp.fetch(obj.url);
  var data=JSON.parse(r.getContentText("UTF-8"));
  //Logger.log(data);
  var pair='USDJPY';
  var dat=new Date(obj.datetime);
  var dtv=new Date(dat.getFullYear(),dat.getMonth(),dat.getDate(),dat.getHours(),dat.getMinutes()).valueOf();
  for(var i=0;i<data.length;i++) {
    var dt=data[i].DATE_TIME.split(' ');
    var sd=new Date(data[i].DATE_TIME);
    var sdv=new Date(sd.getFullYear(),sd.getMonth(),sd.getDate(),sd.getHours(),sd.getMinutes()).valueOf();
    if(sdv==dtv) {
      var d=dt[0].split('-');
      var t=dt[1].split(':');
      var datestring=Utilities.formatString('%s/%s/%s',d[1],d[2],d[0]);
      var timestring=Utilities.formatString('%s:%s',t[0],t[1]);
      var rObj={DATE:datestring,TIME:timestring,PAIR:pair,OPEN:data[i].OPEN.toFixed(3),CLOSE:data[i].CLOSE.toFixed(3)};
      break;
    }
  }
  //Logger.log(rObj);
  return rObj;
}

这是创建触发器的功能。请注意不要多次 运行 并始终检查是否只有一个,并在您第一次打开它时将通知设置为立即,这样您会在错误发生后很快收到电子邮件。

function createSearchOnEditTrigger() {
  var ss=SpreadsheetApp.getActive();
  ScriptApp.newTrigger('searchOnEdit').forSpreadsheet(ss.getId()).onEdit().create();
}

动画:

这是带有复选框的电子表格的副本。

如果您有任何困难,请随时回来寻求帮助。我知道其中一些东西听起来有点令人生畏,但它有点像公制。一旦您测量并称重了一些东西,它就会开始听起来和感觉自然。

这是我的代码,与我现在 运行ning 中的脚本完全一样。可能是我在编辑评论时打错了字。这有一些调试行,其中 运行 Logger 和 display toasts 你应该注释掉。

function searchOnEdit(e) {
  e.source.toast('Entry');
  var sh=e.range.getSheet();
  if(sh.getName()!='API') return;
  var checkedValue='TRUE';
  var uncheckedValue='FALSE';
  if(e.range.columnStart==17 && e.range.rowStart>1 && e.value==checkedValue) {
    e.range.setValue(uncheckedValue);
    e.source.toast('flag1');
    var r=sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).getValues()[0];
    var obj={datetime:r[14],url:r[13],event:e};
    var dObj=getDataDT1(obj);
    Logger.log(dObj);
    sh.getRange(e.range.rowStart,4).setValue(dObj.OPEN);
    sh.getRange(e.range.rowStart,5).setValue(dObj.CLOSE);
  }
}

//{datetime:'',url:''}
function getDataDT1(obj) {
  var r=UrlFetchApp.fetch(obj.url);
  var data=JSON.parse(r.getContentText("UTF-8"));
  //Logger.log(data);
  var pair='USDJPY';
  var dat=new Date(obj.datetime);
  var dtv=new Date(dat.getFullYear(),dat.getMonth(),dat.getDate(),dat.getHours(),dat.getMinutes()).valueOf();
  for(var i=0;i<data.length;i++) {
    var dt=data[i].DATE_TIME.split(' ');
    var sd=new Date(data[i].DATE_TIME);
    var sdv=new Date(sd.getFullYear(),sd.getMonth(),sd.getDate(),sd.getHours(),sd.getMinutes()).valueOf();
    if(sdv==dtv) {
      var d=dt[0].split('-');
      var t=dt[1].split(':');
      var datestring=Utilities.formatString('%s/%s/%s',d[1],d[2],d[0]);
      var timestring=Utilities.formatString('%s:%s',t[0],t[1]);
      var rObj={DATE:datestring,TIME:timestring,PAIR:pair,OPEN:data[i].OPEN.toFixed(3),CLOSE:data[i].CLOSE.toFixed(3)};
      break;
    }
  }
  //Logger.log(rObj);
  return rObj;
}

function createSearchOnEditTrigger() {
  var ss=SpreadsheetApp.getActive();
  ScriptApp.newTrigger('searchOnEdit').forSpreadsheet(ss.getId()).onEdit().create();
}