I have a very simple piece of logic as follows:
var divert:Number = 0;
for (var connection in _connections) {
trace("target: " + _connections[connection].target + " || i: " + (i + 1));
if(int(_connections[connection].target) != (i + 1)) {
trace("bad connection");
divert++;
}
}
The problem is that when i + 1 and int(_connections[connection].target) are equal the if statement is returning true as can be seen in the output of my trace() statements below:
target: 0 || i: 1
bad connection
target: 1 || i: 1
bad connection
Can anyone see what could be causing this to happen?
EDIT: The function this is contained in as per request:
public function loadListener(i:Number, onProgress:Function, onComplete:Function):Void
{
trace("load listening to: "+i);
trace("next in queue: " + _queues["lower"][0] + " | " + _queues["upper"][0]);
_functions[i] = {onProgress:onProgress, onComplete:onComplete};
if (_queues["lower"][0] != i + 1 || _queues["upper"][0] != i + 1) {
var divert:Number = 0;
for (var connection in _connections) {
trace("target: "+_connections[connection].target+" || i: "+(i+1));
if(int(_connections[connection].target) != (i + 1)) {
trace("bad connection");
divert++;
}
}
if (divert == _connections.length) {
_diversion = i + 1;
trace("divert: "+divert+" || connections: "+_connections.length);
}
}
}
First of all, why use a for(var) loop when you can use
var divert:Number = 0;
for each(var connection in _connections) {
trace("target: " + connection.target + " || i: " + (i + 1));
if(int(connection.target) != (i + 1)) {
trace("bad connection");
divert++;
}
}
To debug further, replace
trace("target: " + connection.target + " || i: " + (i + 1));
with
trace("target: " + int(connection.target) + " || i: " + (i + 1));
If this traces zero, you know where the issue is.
You could try doing
if(connection.target.toString() != (i + 1).toString()) {
Related
I want to create a line notify which can send the event detail from my google calendar everyday.
I can get the title, description, location...etc, but I don't see the conference data in calendar API.
I use Google Apps Script to run the code.
Here is my code.
const Now = new Date();
const Start = new Date(new Date().setHours(0, 0, 0, 0));
const End = new Date(new Date().setHours(23, 59, 59, 999));
const calendarData = calendar.getEvents(Start, End);
function Notify() {
var NotifyContents = '';
var i = 1;
calendarData.forEach(item =>{
if (Now <= item.getStartTime()) {
NotifyContents += (item.getTitle() != "") ? ("\n" + i+". "+ item.getTitle() + "\n") : ("\n\nNo Title\n");
NotifyContents += (item.getDescription() != "") ? item.getDescription() + "\n" : "";
NotifyContents += (item.getStartTime() != "" && item.getEndTime() != "") ? "Time:" + item.getStartTime().toLocaleTimeString() + "-" + item.getEndTime().toLocaleTimeString() + "\n": "";
NotifyContents += (item.getconferencedata() != "") ? ("\n" + i+". "+ item.getconferencedata()) : ("No Conference\n");
i++;
}
}
)
if (typeof NotifyContents === 'string' && NotifyContents.length === 0) {
return;
}
NotifyTokens.forEach(function(value){
UrlFetchApp.fetch("https://notify-api.line.me/api/notify", {
"method" : "post",
"payload" : {"message" : NotifyContents},
"headers" : {"Authorization" : "Bearer " + value}
});
});
}
Reference - Calendar API Link
In order to retrieve the meet link from the event, it seems that in the current stage, Calendar API is required to be used. When this is reflected in your script, how about the following modification?
Modified script:
Before you use this script, please enable Calendar API at Advanced Google services.
From:
var NotifyContents = '';
var i = 1;
calendarData.forEach(item => {
if (Now <= item.getStartTime()) {
NotifyContents += (item.getTitle() != "") ? ("\n" + i + ". " + item.getTitle() + "\n") : ("\n\nNo Title\n");
NotifyContents += (item.getDescription() != "") ? item.getDescription() + "\n" : "";
NotifyContents += (item.getStartTime() != "" && item.getEndTime() != "") ? "Time:" + item.getStartTime().toLocaleTimeString() + "-" + item.getEndTime().toLocaleTimeString() + "\n" : "";
NotifyContents += (item.getconferencedata() != "") ? ("\n" + i + ". " + item.getconferencedata()) : ("No Conference\n");
i++;
}
}
)
To:
const eventList = Calendar.Events.list(calendar.getId(), { timeMin: Start.toISOString(), timeMax: End.toISOString(), maxResults: 2500 }).items.reduce((o, e) => (o[e.id] = e.conferenceData.entryPoints.map(({ uri }) => uri).join(","), o), {});
var NotifyContents = '';
var i = 1;
calendarData.forEach(item => {
if (Now <= item.getStartTime()) {
NotifyContents += (item.getTitle() != "") ? ("\n" + i + ". " + item.getTitle() + "\n") : ("\n\nNo Title\n");
NotifyContents += (item.getDescription() != "") ? item.getDescription() + "\n" : "";
NotifyContents += (item.getStartTime() != "" && item.getEndTime() != "") ? "Time:" + item.getStartTime().toLocaleTimeString() + "-" + item.getEndTime().toLocaleTimeString() + "\n" : "";
var eventId = item.getId().split("#")[0];
NotifyContents += eventList[eventId] != "" ? ("\n" + i + ". " + eventList[eventId]) : ("No Conference\n");
i++;
}
});
In this modification, it supposes that calendar has already been declaread. Please be careful about this.
Reference:
Events: list
The script doesn't throw any errors, but rarely completely works - i.e. complete successfully with all of the expected data in the destination tab. The results breakdown is generally:
no results in the destination sheet - this happens ~50-75% of the time
all of the results in the destination sheet, except in cell A1 - ~25% of the time
100% completely works - ~15-25% of the time
code snippet of the batchupdate() call
var data = [
{
range: (ss.getSheetName() + "!A1:AQ" + valueArray.length)
,values: valueArray
}
];
const resource = {
valueInputOption: "RAW"
,data: data
};
Logger.log("request = " + JSON.stringify(resource)
+ "\n" + "valueArray = " + valueArray.length
);
Logger.log(" Sheets.Spreadsheets.Values.batchUpdate(params, batchUpdateValuesRequestBody) ");
var response = Sheets.Spreadsheets.Values.batchUpdate(resource, spreadsheetId);
Logger.log("response = " + response.toString());
and the response
response = {
"totalUpdatedRows": 37776,
"responses": [{
"updatedCells": 1482389,
"updatedRange": "BatchUpdateDestination!A1:AP37776",
"updatedColumns": 42,
"spreadsheetId": "adahsdassadasdsadaasdasdasdasdasdasdasdasdas",
"updatedRows": 37776
}
],
"spreadsheetId": "adahsdassadasdsadaasdasdasdasdasdasdasdasdas",
"totalUpdatedCells": 1482389,
"totalUpdatedSheets": 1,
"totalUpdatedColumns": 42
}
Its obviously a very large dataset, but I've pruned the destination spreadsheet to ensure there is ample room for the data, and from earlier testing, I believe that a specific size error would be returned if that was the blocker.
How can I troubleshoot, or better yet, prevent these incomplete executions? is there any way to inspect the batch jobs that these requests initiate?
Answering my own question...
After toiling with this a little more, I couldn't figure out any way to troublshooting or inspect the odd, seemingly successfully batchUpdate() jobs. Thus, I resorted to batching the batchUpdate() calls into batches of 15000. This seems to work consistently, though maybe a bit slower:
// This is the very large 2D array that is populated elsewhere
var valueArray = [];
var maxRows = valueArray.length;
var maxCols = valueArray[0].length;
var batchSize = 15000;
var lastBatchSize = 1;
for (var currentRowCount = 1; currentRowCount <= maxRows; ++currentRowCount) {
if( currentRowCount % batchSize == 0
|| currentRowCount == maxRows
)
{
Logger.log("get new valuesToSet");
valuesToSet = valueArray.slice(lastBatchSize - 1, currentRowCount -1);
var data = [
{
range: (ss.getSheetName() + "!A" + lastBatchSize + ":AQ" + (lastBatchSize + valuesToSet.length))
,values: valuesToSet
}
];
const resource = {
valueInputOption: "RAW"
,data: data
};
Logger.log("request = " + JSON.stringify(resource).slice(1, 100)
+ "\n" + "valuesToSet.length = " + valuesToSet.length
);
try {
var checkValues = null;
var continueToNextBatch = false;
for (var i = 1; i <= 3; ++i) {
Logger.log("try # = " + i
+ "\n" + " continueToNextBatch = " + continueToNextBatch
+ "\n" + " make the batchUpdate() request, then sleep for 5 seconds, then check if there are values in the target range."
+ "\n" + " if no values, then wait 5 seconds, check again."
+ "\n" + " if still not values after 3 tries, then resubmit the batchUpdate() requestion and recheck values"
+ "\n" + "range to check = " + "A" + lastBatchSize + ":AQ" + lastBatchSize
);
Logger.log(" Sheets.Spreadsheets.Values.batchUpdate(params, batchUpdateValuesRequestBody) ");
var response = Sheets.Spreadsheets.Values.batchUpdate(resource, spreadsheetId);
Logger.log("response = " + response.toString());
/// loop and check for data in newly written range
for (var checks = 1; checks <= 3; ++checks) {
Utilities.sleep(5000);
var checkValues = ss.getRange(("A" + lastBatchSize + ":AQ" + lastBatchSize)).getValues();
Logger.log("new cell populated - checks # = " + checks
+ "\n" + "range to check = " + "A" + lastBatchSize + ":AQ" + lastBatchSize
+ "\n" + "checkValues.length = " + checkValues.length
+ "\n" + "checkValues = " + checkValues
);
if(checkValues.length > 1)
{
Logger.log("checkValues.length > 1, so continue to next batch"
+ "\n" + "range to check = " + "A" + lastBatchSize + ":AQ" + lastBatchSize
+ "\n" + "checkValues.length = " + checkValues.length
+ "\n" + "checkValues = " + checkValues
);
continueToNextBatch = true;
continue;
}
else
{
Logger.log("checkValues.length is still not > 1, so try the request again"
+ "\n" + "range to check = " + "A" + lastBatchSize + ":AQ" + lastBatchSize
);
}
}
if(continueToNextBatch)
{
continue;
}
}
}
catch (e) {
console.error("range.setValues(valuesToSet) - yielded an error: " + e
+ "\n" + "valuesToSet = " + valuesToSet.length
+ "\n" + "maxRows = " + maxRows
+ "\n" + "maxCols = " + maxCols
+ "\n" + "currentRowCount = " + currentRowCount
+ "\n" + "current range row start (lastBatchSize) = " + lastBatchSize
+ "\n" + "current range row end (j - lastBatchSize) = " + (currentRowCount - lastBatchSize)
);
}
lastBatchSize = currentRowCount;
}
}
I've got this fn().
Is there any simple way to return the number of filtered rows by this conditional statement in Google app scripts?
function getGlobers(globers, project){
var body = "";
var data = globers.getValues();
for( i = 0; i < data.length; i++ ){
if( data[i][2] == project ){
body += "<tr><td style=" + STYLE.TD + ">" + data[i].join("</td><td style=" + STYLE.TD + ">") + "</td></tr>";
}
}
return body;
}
Thanks.
for example (see comments in code)
function getGlobers(globers, project){
var body = "";
var n = 0; // use a variable to count
var data = globers.getValues();
for( i = 0; i < data.length; i++ ){
if( data[i][2] == project ){
n++;// increment each time condition is true
body += "<tr><td style=" + STYLE.TD + ">" + data[i].join("</td><td style=" + STYLE.TD + ">") + "</td></tr>";
}
}
return [body,n];// return an array of 2 values
}
Usage :
var result = getGlobers(range,project);
Logger.log('array result = '+result);
Logger.log('body (string) = '+result[0]);
Logger.log('length (integer) = '+result[1]);
note : instead of an array you could also return an object with 2 properties... a matter of choice.
EDIT : an example using object properties :
function getGlobers(globers, project){
var body = "";
var n = 0;
var result = {};
var data = globers.getValues();
for( i = 0; i < data.length; i++ ){
if( data[i][2] == project ){
n++;
body += "<tr><td style=" + STYLE.TD + ">" + data[i].join("</td><td style=" + STYLE.TD + ">") + "</td></tr>";
}
}
result['body'] = body;
result['length'] = n;
return result;
}
Usage :
var result = getGlobers(range,project);
Logger.log('body = '+result.body);
Logger.log('length = '+result.length);
I am using JSON to display a JsTree. The JSON is being built up as a string via a recursive function. Now I went through a few tests with smaller/less complicated trees and got it to work. I used JSONLint to check for valid JSON and eventually got the correct syntax. Now when I try and display the intended big tree its just stuck with the loading .gif (which used to be because the JSON was incorrect) but after checking it on JSONLint it was correct.
Any possible causes for this? I doubt the tree could be too big or anything.
Recursive Function:
public void getViewTree(ref string tree, Int32? id = null)
{
var topNodes = (from items in db.AssessmentViewItems
select items).Take(1);
#region getChildren via LINQ
if (id == null)
{
topNodes = from items in db.AssessmentViewItems
where items.ParentAssessmentViewItemID == null
&& items.AssessmentViewID == 17
select items;
}
else
{
topNodes = from items in db.AssessmentViewItems
where items.ParentAssessmentViewItemID == id
&& items.AssessmentViewID == 17
select items;
}
#endregion
int counter = 1;
int max = (int)topNodes.Count();
foreach (var node in topNodes)
{
if (node.ParentAssessmentViewItemID == null)
{
{\"id\":\"532topNode\",\"selected\":true},\"children\":[null,
tree += "{\"data\":\"" + node.Title.Trim().Replace("\"","").Replace("("," ").Replace(":"," ").Replace("-"," ").Replace("&","and").Replace("/"," ").Replace("\\"," ").Replace(","," ").Replace("•", " ") + "\",\"attr\":{\"id\":\"" + node.AssessmentViewItemID + "\", \"selected\":true}, \"children\":[";
getViewTree(ref tree, node.AssessmentViewItemID);
tree += "}]";
if (counter < max)
{
tree += "},";
}
}
else if (node.Type.Equals("Legal Topic"))
{
tree += "{\"data\":\"" + node.Title.Trim().Replace("\"", "").Replace("(", " ").Replace(":", " ").Replace("-", " ").Replace("&", "and").Replace("/", " ").Replace("\\", " ").Replace(",", " ").Replace("•", " ") + "\",\"attr\":{\"id\":\"" + node.AssessmentViewItemID + "\", \"selected\":true}";
if (counter < max)
{
tree += "},";
}
}
else
{
var topNodes1 = from items in db.AssessmentViewItems
where items.ParentAssessmentViewItemID == node.AssessmentViewItemID
&& items.AssessmentViewID == 17
select items;
if (topNodes1.Count() > 0)
{
tree += "{\"data\":\"" + node.Title.Trim().Replace("\"", "").Replace("(", " ").Replace(":", " ").Replace("-", " ").Replace("&", "and").Replace("/", " ").Replace("\\", " ").Replace(",", " ").Replace("•", " ") + "\",\"attr\":{\"id\":\"" + node.AssessmentViewItemID + "\", \"selected\":true}, \"children\":[";
}
else
{
tree += "{\"data\":\"" + node.Title.Trim().Replace("\"", "").Replace("(", " ").Replace(":", " ").Replace("-", " ").Replace("&", "and").Replace("/", " ").Replace("\\", " ").Replace(",", " ").Replace("•", " ") + "\",\"attr\":{\"id\":\"" + node.AssessmentViewItemID + "\", \"selected\":true}";
}
getViewTree(ref tree, node.AssessmentViewItemID);
if (topNodes1.Count() > 0)
{
tree += "}]";
}
if (counter < max)
{
tree += "}";
tree += ",";
}
}
counter++;
}
}
JS:
$(function () {
$("#demoTree").jstree({
"json_data": {
"data": treeModel
},
"plugins": ["themes", "json_data", "ui"],
});
});
Calling Recursive Function:
string tree = "[";
getViewTree(ref tree);
tree += "}]";
return View("About", "_Layout", tree);
After using Chrome Dev Tools, the error that I get from it:
Uncaught SyntaxError: Unexpected token ILLEGAL (program):54
Uncaught Neither data nor ajax settings supplied.
I did check it for syntax on JSONLint. Small tree is generated fine without those 2 errors
Problem got solved by using DevExpress tree. Exact same JSON string.
I've been working with highcharts and MVC 3 for two years by now (I never done anything complicated, just load data and make it work stuff), and I worked with two different scenarios:
Chart code written in the directly in the view, loading data through Json
Html helper responsible to plot the chart
The Html helper approach seems to me a more elegant choice ... but then, just to illustrate to you guys, here is how it looks like (just part of it):
public static string DisplayChart(
this HtmlHelper helper,
ChartOptions options,
TimedChartSeries[] data)
{
string[] axisList = data.GroupBy(t => t.Unit).Select(t => t.Key).ToArray();
string result = "";
result += "<script type=\"text/javascript\">\n";
result += "var " + options.ChartName + ";\n";
result += "$(document).ready(function() {\n";
result += options.ChartName + "= new Highcharts.Chart({\n";
result += "chart: {renderTo: '" + options.DivName + "',zoomType: ''},\n";
result += "title: { text: '" + options.Title + "'},\n";
result += "subtitle: {text: '" + options.SubTitle + "'},\n";
result += "xAxis: { type: 'datetime'," +
"\n dateTimeLabelFormats: {month: '%e. %b', year: '%b' },"
+ "labels:{rotation: -45}\n},\n";
string axes = "";
for (int i = 0; i < axisList.Length; i++)
{
var temporaryData = data.First(t => t.Unit == axisList[i]);
if (i != 0)
axes += ", ";
axes += "{labels:{\n " +
"formatter : function(){return this.value + '" + temporaryData.Unit + "';},\n" +
"style:{color:'#" + temporaryData.Color.Name.Remove(0, 2) + "'}},\n" +
"title:{text:'',style:{color:'#" + temporaryData.Color.Name.Remove(0, 2) + "'}},\n" +
"}\n";
}
result += "yAxis: [\n" + axes + "],\n";
string units = "";
for (int i = 0; i < data.Length; i++)
{
if (i != 0)
units += ", ";
units += "'" + data[i].Title + "': '" + data[i].Unit + "'\n";
}
result += "tooltip:{\nshared: true,\n backgroundColor: 'none' ,\nborderColor: 'none'," +
"\nshadow:false\n ,crosshairs: true,\n" +
"formatter: function() {var s = '<table class=\"table-list\"><tr><th>Hora</th><th>'+ test(this.x) +'</th></tr>';" +
"\n$.each(this.points, function(i, point) {" +
"\ns += '<tr><td>'+point.series.name + '</td><td>'+point.y+'</td></tr>'});s+='</table>';" +
"\n$('#tooltip').html(s);}},";
result += "lang: {" +
"months: ['Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho'," +
"'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro']," +
"weekdays: ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado']},";
string series = "";
int x = 0;
for (int j = 0; j < axisList.Length; j++)
{
var temporaryData = data.Where(t => t.Unit == axisList[j]).ToArray();
for (int i = 0; i < temporaryData.Length; i++)
{
if (x > 0)
series += ", ";
series += "{name: '" + temporaryData[i].Title + "',\n color: '#" + temporaryData[i].Color.Name.Remove(0, 2) +
"', \ntype: '" + temporaryData[i].Type + "',\nunit:'" + temporaryData.First().Unit + "', \nyAxis:" + j + " , \ndata:[" + FromArrayToString(temporaryData[i].Data) +
"], marker: { enabled: false}}\n";
x++;
}
}
result += "series: [\n" + series + "]\n";
result += "});});";
result += "\nfunction test(i)\n{\nvar j = new Date(i + 2*60*60*1000);\n" +
"return new Date(i + 3*60*60*1000).format('d/m/Y H:i:s.')+j.getMilliseconds();\n}\n</script>";
result += "\n<div id=\"" + options.DivName + "\" style=\"width:" + options.Width + ";height: " + options.Height + "\"></div>" +
"<div id=\"tooltip\"></div>";
return result;
}
It's really simple to call this helper:
#Html.Raw(Html.DisplayChart((ChartOptions)Model.Options,(TimedChartSeries[])Model.Series))
As you guys can see, I have to use the Html.Raw helper in order to make it work ... that is problem nº 1 (and it probably has an easy solution). But the second problem is really great: the chart becomes entirely tied to my domain. If I wanted to plot a, say, bar chart displaying data of the last 3 years in months (each month being represented by a bar), it would be impossible to use this helper.
And it also looks kind of ugly.
So, guys, which option do you think is more elegant, the Json or the Helper approach?
About the use of Html.Raw and the easy solution:
Change your function to
public static HtmlString DisplayChart(this HtmlHelper helper, ...)
{
...
return new HtmlString(result);
}
You then may use #Html.DisplayChart(...) in your razor views.
Also, please make sure that options.DivName, options.Title, options.SubTitle etc. are properly escaped - a title like Everybody's favorite chart will break the output.