(Discord Bot Related Question) I make a mute command but when I run it, I get this "Cannot Read Property 'split' of undefined" - autocode

I'm using autocode for this question, Every time I rerun the command there is always an error that said "Cannot Read Property 'split' of undefined" Here is the code below:
const event = context.params.event
const { guild_id, token, channel_id, member, author} = event;
const error = require('../../../helpers/error.js')
try {
if (!event?.mentions?.length) throw new Error(`You need to mention a user!`);
let emojis = await lib.utils.kv['#0.1.16'].get({key: `emojis`, defaultValue: {moderator: null, user: null, reason: null, channel: null, error: null, timeout: null, clock: null}});
let base = await lib.airtable.query['#1.0.0'].select({table: `Config`, where: [{'Guild__is': guild_id}]});
if (!base?.rows?.length) base = await lib.airtable.query['#1.0.0'].insert({table: `Config`, fieldsets: [{'Guild': guild_id}]});
let cNum = (await lib.airtable.query['#1.0.0'].max({table: `Cases`, where: [{'Guild__is': guild_id}], field: `Case`})).max.max + 1
let isAdmin = false, targetAdmin = false, reason = event.content.split(' ').slice(3).join(' ') || `No reason provided`
let guildInfo = await lib.discord.guilds['#0.2.4'].retrieve({guild_id});
let target = await lib.discord.guilds['#0.2.4'].members.retrieve({user_id: event.mentions[0].id, guild_id});
if (guildInfo.owner_id == author.id) isAdmin = true
if (guildInfo.owner_id == target.user.id) targetAdmin = true
if (member.roles.includes(base.rows[0].fields.Moderator)) isAdmin = true
if (target.roles.includes(base.rows[0].fields.Moderator)) targetAdmin = true
if (guildInfo.roles.filter(role => (role.permissions & (1 << 3)) === 1 << 3 && member.roles.includes(role.id))?.length) isAdmin = true
if (guildInfo.roles.filter(role => (role.permissions & (1 << 3)) === 1 << 3 && target.roles.includes(role.id))?.length) targetAdmin = true
if (!isAdmin)
return lib.discord.channels['#release'].messages.create({channel_id, content: `<:error:${emojis.error}> You don't have permission to use \`sm.mute\``});
if (targetAdmin)
return lib.discord.channels['#release'].messages.create({channel_id, content: `<:error:${emojis.error}> You can't mute a member who is mod/admin`});
let match = event.context.split(' ')[2].match(/(\d+)(s|m|h|d)/)
if (!match)
throw new Error(`Please provide a duration in days(\`d\`), hours(\`h\`), minutes(\`m\`) or seconds(\`s\`).`)
let secs = new Date().getSeconds()
let time = parseInt(match[1]), unit = match[2]
if (unit === 's') time *= 1
else if (unit === 'm') time *= 60
else if (unit === 'h') time *= 60 * 60
else if (unit === 'd') time *= 24 * 60 * 60
let until = `<t:${Math.floor(new Date().setSeconds(secs + time) / 1000)}>`
await lib.discord.guilds['#release'].members.timeout.update({user_id: target.user.id, guild_id, communication_disabled_until_seconds: time, reason});
await lib.discord.channels['#release'].messages.create({channel_id, content: `<:succes:${emojis.success}>**${target.user.username}** was muted for ${match[1]} ${match[2].replace('s', 'seconds').replace('d', 'days').replace('h', 'hours').replace('m', 'minutes')} | ${reason}`});
if (base.rows[0].fields.Logging) {
await lib.discord.channels['#release'].messages.create({
channel_id: base.rows[0].fields.Logging,
content: ``,
embeds: [{
type: 'rich',
color: 0xE72020,
description: `
<:user:${emojis.user}> | <#${target.user.id}> | ${target.user.username}#${target.user.discriminator}
<:moderator:${emojis.moderator}> | <#${author.id}> | ${author.username}#${author.discriminator}
<:reason:${emojis.reason}> | ${reason}
<:timeout:${emojis.timeout} | ${until.replace('>', ':R>')} | ${time}s`,
footer: {text: `Case Number: ${cNum}`},
author: {name: `${cNum} | Mute`, icon_url: target.user.avatar_url || 'https://cdn.discordapp.com/attachments/911294620439293993/965586325208195142/avatar.png'},
timestamp: new Date().toISOString(),
}],
});
}
await lib.airtable.query['#1.0.0'].insert({table: `Cases`,
fieldsets: [{
User: target.user.id,
Reason: reason,
Type: `Mute`,
Moderator: author.id,
Timestamp: `<t:${Math.floor(new Date().getTime() / 1000)}>`,
Case: cNum,
Guild: guild_id,
}],
});
} catch (e) {
await error.prefix(channel_id, e.message)
Help me! I need some help to fix this. Please explain this answer of how to fix "Cannot Read Property 'split' of undefined"

Related

AsyncResult and handling rollback

(warning: this was posted also on https://forums.fsharp.org/t/asyncresult-and-handling-rollback/928)
Trying to implement 2PC Transaction Like workflow in F# (see http://learnmongodbthehardway.com/article/transactions/) and hitting an issue with computation expressions (asyncResult for example) and rollback.
If you have the following pseudocode:
let rollbackWorkflow parX parY =
… here calling rollbackService1 and rollbackService2
let executeWorkflow par1 par2 par3 =
asyncResult {
let! result1 = callService1 x y z
let! result2 = callService2 x2 y2 z2
}
how can I check in executeWorkflow if result1 and/or result2 is Error and then call rollbackWorkflow function?? Should I change callService1 and callService2 to raise exceptions instead of returning results also in expected error cases (not sufficient funds, limits exceeded), or should I use some function like teeError? Any suggestion highly appreciated!
P.S. This is what I want to eventually implement:
function executeTransaction(from, to, amount) {
var transactionId = ObjectId();
transactions.insert({
_id: transactionId,
source: from,
destination: to,
amount: amount,
state: “initial”
});
var result = transactions.updateOne(
{ _id: transactionId },
{ $set: { state: “pending” } }
);
if (result.modifiedCount == 0) {
cancel(transactionId);
throw Error(“Failed to move transaction " + transactionId + " to pending”);
}
// Set up pending debit
result = accounts.updateOne({
name: from,
pendingTransactions: { $ne: transactionId },
balance: { $gte: amount }
}, {
$inc: { balance: -amount },
$push: { pendingTransactions: transactionId }
});
if (result.modifiedCount == 0) {
rollback(from, to, amount, transactionId);
throw Error(“Failed to debit " + from + " account”);
}
// Setup pending credit
result = accounts.updateOne({
name: to,
pendingTransactions: { $ne: transactionId }
}, {
$inc: { balance: amount },
$push: { pendingTransactions: transactionId }
});
if (result.modifiedCount == 0) {
rollback(from, to, amount, transactionId);
throw Error(“Failed to credit " + to + " account”);
}
// Update transaction to committed
result = transactions.updateOne(
{ _id: transactionId },
{ $set: { state: “committed” } }
);
if (result.modifiedCount == 0) {
rollback(from, to, amount, transactionId);
throw Error(“Failed to move transaction " + transactionId + " to committed”);
}
// Attempt cleanup
cleanup(from, to, transactionId);
}
executeTransaction(“Joe Moneylender”, “Peter Bum”, 100);
Just do some error handling outside the workflow, like this:
type TransactionError =
| NoFunds
| Other
let rollbackWorkflow parX parY = async.Return ( printfn "here calling rollbackService1 and rollbackService2"; Ok -1 )
let callService1 parX parY = async.Return ( printfn "callService1"; if parX + parY > 0 then Ok 1 else Error NoFunds )
let callService2 parX parY = async.Return ( printfn "callService2"; if parX + parY > 0 then Ok 2 else Error Other )
let executeWorkflow par1 par2 par3 =
asyncResult {
let! result1 = callService1 par1 par2
let! result2 = callService2 result1 par3
return result2
} |> AsyncResult.bindError (fun x -> if x = NoFunds then rollbackWorkflow 0 1 else rollbackWorkflow 1 0)
I wrote that example with the AsyncResult from the code you linked. Plus bindError which should be something like:
/// Apply a monadic function to an AsyncResult error
let bindError (f: 'a -> AsyncResult<'b,'c>) (xAsyncResult : AsyncResult<_, _>) :AsyncResult<_,_> = async {
let! xResult = xAsyncResult
match xResult with
| Ok x -> return Ok x
| Error err -> return! f err
}
If you think about it, bindError is like a pure version of the catch function, see for example this code fragment , using another library.

3.5.1 error can't get just created node by id

EDIT: Just got the same behavior on 3.4
EDIT2: If I remove the disableLosslessIntegers from the connection the issue goes away, but the all integer numbers come back as {low: 20, high:0} type structures which breaks my entire application
The following code works fine on neo4j 3.3 using the 1.7.2 neo4j-driver for node:
import {v1 as neo4j} from 'neo4j-driver';
const url: string = process.env.COREDB_URL || '';
const user: string = process.env.COREDB_USERNAME || '';
const password: string = process.env.COREDB_PASSWORD || '';
const driver = neo4j.driver(url, neo4j.auth.basic(user, password), {disableLosslessIntegers: true});
let connection = driver.session()
async function go() {
let res = await connection.run(`create (b:Banana {tag: 'test'}) return b,id(b) as id`, {});
let b = res.records[0].get('b').properties
console.log('b',b)
let id = res.records[0].get('id')
console.log('id',id)
res = await connection.run(`MATCH (u) where id(u)=$id return u as id`, {id: id});
console.log(res.records)
let id2 = res.records[0].get('id').properties;
console.log('id2',id2)
}
go().then(() => console.log('done')).catch((e) => console.log(e.message))
it gives the following output:
> node tools\test-id.js
b { tag: 'test' }
id 1858404
[ Record {
keys: [ 'id' ],
length: 1,
_fields: [ [Node] ],
_fieldLookup: { id: 0 } } ]
id2 { tag: 'test' }
done
Under 3.5.1 it does not work. The second statement returns no records:
> node tools\test-id.js
b { tag: 'test' }
id 1856012
[]
Cannot read property 'get' of undefined
BTW, the reason I need to do the get by id right after the create is that I am using an apoc trigger to add things to the node after creation, and apoc triggers apparently run after the object is created and returned, so I need the second get to see the transformed node.
But, for this distilled example I removed the trigger from my DB to ensure that it was not causing the issue

use spark Scala dataframe cube/pivot to do Olap, always report attribute conflicts sql parsing error

Error reported always until I added a manual check point to save the data frame after cube and before pivot.
Only manual save and reload the table works, other ways such as persist or check point all no use.
My question is how to avoid this. The error message:
Conflicting attributes: groupby_keys#90179
;;
'Join UsingJoin(Inner,List(groupby_keys))
My code is a little mess for to test in spark-shell but anyways:
case class FlagsStr(groupby_keys: String, profit_flag_str: String, cmd_str: String, open_followed_str: String, close_followed_str: String, close_or_open_flag_str: String, point: Int)
import org.apache.spark.sql.functions._
import org.apache.spark.sql.SparkSession
import org.apache.spark.storage.StorageLevel
def merge_flags(flag1: String, flag2: String): String = {
var ret: String = null
if( flag1 != null & flag2 != null)
ret = flag1 + "_" + flag2
else if ( flag1 != null || flag2 != null) {
if( flag1 != null)
ret = flag1
else
ret = flag2
} else ret = null
ret
}
def concat_str_flags = udf((profit_flag_str: String, cmd_str: String, open_followed_str: String, close_followed_str: String, close_or_open_flag_str: String) => {
//val retArr: Array[String] = Array(str_flags:_*)
val retArr: Array[String] = Array(profit_flag_str, cmd_str, open_followed_str, close_followed_str, close_or_open_flag_str)
val ret = retArr.reduce(merge_flags)
ret
})
def add_target_2_flags(flag: String, prefix: String): String = {
if( flag != null)
prefix + "_" + flag
else
prefix
}
def add_target_agg_2_flags(flag: String, prefix: String, suffix: String): String = {
if( flag != null)
prefix + "_" + flag + "_" + suffix
else
prefix + "_" + suffix
}
def add_order_2_flags = udf((flag: String) => {
add_target_2_flags(flag, "order")
})
def add_point_2_flags = udf((flag: String) => {
add_target_2_flags(flag, "point")
})
def add_point_max_2_flags = udf((flag: String) => {
add_target_agg_2_flags(flag, "point", "max")
})
val ss = SparkSession.builder().appName("TestCubePivot").enableHiveSupport().getOrCreate()
import ss.implicits._
val sqlContext = ss.sqlContext
val flagsStrDataFrame = sqlContext.createDataFrame(List(
FlagsStr("3_223", "profit", "long", "os", "cs", "close", 5),
FlagsStr("4_423", null, "long", "os", "cs", "close", 10),
FlagsStr("5_523", "profit", null, "os", "cs", "close", 15)
))
val h_mt4trades_with_target_agg_4_pivot_in_memory=flagsStrDataFrame .cube($"groupby_keys", $"profit_flag_str", $"cmd_str", $"open_followed_str", $"close_followed_str", $"close_or_open_flag_str") .agg(
count($"*").as("count_1"),
sum($"point").as("sum_point"),
max($"point").as("max_point")
) .filter($"groupby_keys".isNotNull) .withColumn("flags",concat_str_flags($"profit_flag_str", $"cmd_str", $"open_followed_str", $"close_followed_str", $"close_or_open_flag_str"))
h_mt4trades_with_target_agg_4_pivot_in_memory.write.format("orc").saveAsTable("h_mt4trades_with_target_agg_4_pivot_in_memory")
val h_mt4trades_with_target_agg_4_pivot = sqlContext.read.table("h_mt4trades_with_target_agg_4_pivot_in_memory")
//h_mt4trades_with_target_agg_4_pivot.checkpoint
h_mt4trades_with_target_agg_4_pivot.persist(StorageLevel.MEMORY_AND_DISK)
ss.sparkContext.broadcast(h_mt4trades_with_target_agg_4_pivot)
val h_mt4trades_stat_cube_order = h_mt4trades_with_target_agg_4_pivot.withColumn("flags_order", add_order_2_flags($"flags")) .groupBy($"groupby_keys") .pivot("flags_order") .max("count_1")
//h_mt4trades_stat_cube_order.checkpoint
val h_mt4trades_stat_cube_point = h_mt4trades_with_target_agg_4_pivot.withColumn("flags_point", add_point_2_flags($"flags")) .groupBy($"groupby_keys") .pivot("flags_point") .max("sum_point")
//h_mt4trades_stat_cube_point.checkpoint
val h_mt4trades_stat_cube_point_max = h_mt4trades_with_target_agg_4_pivot.withColumn("flags_point_max", add_point_max_2_flags($"flags")) .groupBy($"groupby_keys") .pivot("flags_point_max") .max("max_point")
val test = h_mt4trades_stat_cube_order.join(h_mt4trades_stat_cube_point, "groupby_keys").join(h_mt4trades_stat_cube_point_max, "groupby_keys")

Google AdWords script issue

I am trying to set up an AdWords script for the first time but cannot get it to function properly. It is essentially supposed to crawl all of our clients' AdWords accounts, send all of the account information to a Google Sheet, and send me an email to let me know if there are any anomalies detected in any of the accounts. I have not had any luck with getting the info to send to the Google sheet, let a lone an email notification. This is the primary error I'm currently getting when I preview the script: TypeError: Cannot call method "getValue" of null. (line 510)
Here is the Google resource page (https://developers.google.com/adwords/scripts/docs/solutions/mccapp-account-anomaly-detector) for the script and the actual script itself that I'm using is below.
Any recommendations on how to get this to function properly would be greatly appreciated. Thank you!
// Copyright 2017, Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* #name MCC Account Anomaly Detector
*
* #fileoverview The MCC Account Anomaly Detector alerts the advertiser whenever
* one or more accounts in a group of advertiser accounts under an MCC account
* is suddenly behaving too differently from what's historically observed. See
* https://developers.google.com/adwords/scripts/docs/solutions/mccapp-account-anomaly-detector
* for more details.
*
* #author AdWords Scripts Team [adwords-scripts#googlegroups.com]
*
* #version 1.4
*
* #changelog
* - version 1.4
* - Added conversions to tracked statistics.
* - version 1.3.2
* - Added validation for external spreadsheet setup.
* - version 1.3.1
* - Improvements to time zone handling.
* - version 1.3
* - Cleanup the script a bit for easier debugging and maintenance.
* - version 1.2
* - Added AdWords API report version.
* - version 1.1
* - Fix the script to work in accounts where there is no stats.
* - version 1.0
* - Released initial version.
*/
var SPREADSHEET_URL = 'https://docs.google.com/a/altitudemarketing.com/spreadsheets/d/1ELWZPcGLqf7n9GDnTx5o7xWOFZHVbgaLakeXAu5NY-E/edit?usp=sharing';
var CONFIG = {
// Uncomment below to include an account label filter
// ACCOUNT_LABEL: 'High Spend Accounts'
};
var CONST = {
FIRST_DATA_ROW: 12,
FIRST_DATA_COLUMN: 2,
MCC_CHILD_ACCOUNT_LIMIT: 50,
TOTAL_DATA_COLUMNS: 9
};
var STATS = {
'NumOfColumns': 4,
'Impressions':
{'Column': 3, 'Color': 'red', 'AlertRange': 'impressions_alert'},
'Clicks': {'Column': 4, 'Color': 'orange', 'AlertRange': 'clicks_alert'},
'Conversions':
{'Column': 5, 'Color': 'dark yellow 2', 'AlertRange': 'conversions_alert'},
'Cost': {'Column': 6, 'Color': 'yellow', 'AlertRange': 'cost_alert'}
};
var DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
'Saturday', 'Sunday'];
/**
* Configuration to be used for running reports.
*/
var REPORTING_OPTIONS = {
// Comment out the following line to default to the latest reporting version.
apiVersion: 'v201605'
};
function main() {
var account;
var alertText = [];
Logger.log('Using spreadsheet - %s.', SPREADSHEET_URL);
var spreadsheet = validateAndGetSpreadsheet(SPREADSHEET_URL);
spreadsheet.setSpreadsheetTimeZone(AdWordsApp.currentAccount().getTimeZone());
var dataRow = CONST.FIRST_DATA_ROW;
SheetUtil.setupData(spreadsheet);
Logger.log('MCC account: ' + mccManager.mccAccount().getCustomerId());
while (account = mccManager.next()) {
Logger.log('Processing account ' + account.getCustomerId());
alertText.push(processAccount(account, spreadsheet, dataRow));
dataRow++;
}
sendEmail(mccManager.mccAccount(), alertText, spreadsheet);
}
/**
* For each of Impressions, Clicks, Conversions, and Cost, check to see if the
* values are out of range. If they are, and no alert has been set in the
* spreadsheet, then 1) Add text to the email, and 2) Add coloring to the cells
* corresponding to the statistic.
*
* #return {string} the next piece of the alert text to include in the email.
*/
function processAccount(account, spreadsheet, startingRow) {
var sheet = spreadsheet.getSheets()[0];
var thresholds = SheetUtil.thresholds();
var today = AdWordsApp.report(SheetUtil.getTodayQuery(), REPORTING_OPTIONS);
var past = AdWordsApp.report(SheetUtil.getPastQuery(), REPORTING_OPTIONS);
var hours = SheetUtil.hourOfDay();
var todayStats = accumulateRows(today.rows(), hours, 1); // just one week
var pastStats = accumulateRows(past.rows(), hours, SheetUtil.weeksToAvg());
var alertText = ['Account ' + account.getCustomerId()];
var validWhite = ['', 'white', '#ffffff']; // these all count as white
// Colors cells that need alerting, and adds text to the alert email body.
function generateAlert(field, emailAlertText) {
// There are 2 cells to check, for Today's value and Past value
var bgRange = [
sheet.getRange(startingRow, STATS[field].Column, 1, 1),
sheet.getRange(startingRow, STATS[field].Column + STATS.NumOfColumns,
1, 1)
];
var bg = [bgRange[0].getBackground(), bgRange[1].getBackground()];
// If both backgrounds are white, change background Colors
// and update most recent alert time.
if ((-1 != validWhite.indexOf(bg[0])) &&
(-1 != validWhite.indexOf(bg[1]))) {
bgRange[0].setBackground([[STATS[field]['Color']]]);
bgRange[1].setBackground([[STATS[field]['Color']]]);
spreadsheet.getRangeByName(STATS[field]['AlertRange']).
setValue('Alert at ' + hours + ':00');
alertText.push(emailAlertText);
}
}
if (thresholds.Impressions &&
todayStats.Impressions < pastStats.Impressions * thresholds.Impressions) {
generateAlert('Impressions',
' Impressions are too low: ' + todayStats.Impressions +
' Impressions by ' + hours + ':00, expecting at least ' +
parseInt(pastStats.Impressions * thresholds.Impressions));
}
if (thresholds.Clicks &&
todayStats.Clicks < (pastStats.Clicks * thresholds.Clicks).toFixed(1)) {
generateAlert('Clicks',
' Clicks are too low: ' + todayStats.Clicks +
' Clicks by ' + hours + ':00, expecting at least ' +
(pastStats.Clicks * thresholds.Clicks).toFixed(1));
}
if (thresholds.Conversions &&
todayStats.Conversions <
(pastStats.Conversions * thresholds.Conversions).toFixed(1)) {
generateAlert(
'Conversions',
' Conversions are too low: ' + todayStats.Conversions +
' Conversions by ' + hours + ':00, expecting at least ' +
(pastStats.Conversions * thresholds.Conversions).toFixed(1));
}
if (thresholds.Cost &&
todayStats.Cost > (pastStats.Cost * thresholds.Cost).toFixed(2)) {
generateAlert(
'Cost',
' Cost is too high: ' + todayStats.Cost + ' ' +
account.getCurrencyCode() + ' by ' + hours +
':00, expecting at most ' +
(pastStats.Cost * thresholds.Cost).toFixed(2));
}
// If no alerts were triggered, we will have only the heading text. Remove it.
if (alertText.length == 1) {
alertText = [];
}
var dataRows = [[
account.getCustomerId(), todayStats.Impressions, todayStats.Clicks,
todayStats.Conversions, todayStats.Cost, pastStats.Impressions.toFixed(0),
pastStats.Clicks.toFixed(1), pastStats.Conversions.toFixed(1),
pastStats.Cost.toFixed(2)
]];
sheet.getRange(startingRow, CONST.FIRST_DATA_COLUMN,
1, CONST.TOTAL_DATA_COLUMNS).setValues(dataRows);
return alertText;
}
var SheetUtil = (function() {
var thresholds = {};
var upToHour = 1; // default
var weeks = 26; // default
var todayQuery = '';
var pastQuery = '';
var setupData = function(spreadsheet) {
Logger.log('Running setupData');
spreadsheet.getRangeByName('date').setValue(new Date());
spreadsheet.getRangeByName('account_id').setValue(
mccManager.mccAccount().getCustomerId());
var getThresholdFor = function(field) {
thresholds[field] = parseField(spreadsheet.
getRangeByName(field).getValue());
};
getThresholdFor('Impressions');
getThresholdFor('Clicks');
getThresholdFor('Conversions');
getThresholdFor('Cost');
var now = new Date();
// Basic reporting statistics are usually available with no more than a 3-hour
// delay.
var upTo = new Date(now.getTime() - 3 * 3600 * 1000);
upToHour = parseInt(getDateStringInTimeZone('h', upTo));
spreadsheet.getRangeByName('timestamp').setValue(
DAYS[getDateStringInTimeZone('u', now)] + ', ' + upToHour + ':00');
if (upToHour == 1) {
// First run of the day, clear existing alerts.
spreadsheet.getRangeByName(STATS['Clicks']['AlertRange']).clearContent();
spreadsheet.getRangeByName(STATS['Impressions']['AlertRange']).
clearContent();
spreadsheet.getRangeByName(STATS['Conversions']['AlertRange'])
.clearContent();
spreadsheet.getRangeByName(STATS['Cost']['AlertRange']).clearContent();
// Reset background and font Colors for all data rows.
var bg = [];
var ft = [];
var bg_single = [
'white', 'white', 'white', 'white', 'white', 'white', 'white', 'white',
'white'
];
var ft_single = [
'black', 'black', 'black', 'black', 'black', 'black', 'black', 'black',
'black'
];
// Construct a 50-row array of colors to set.
for (var a = 0; a < CONST.MCC_CHILD_ACCOUNT_LIMIT; ++a) {
bg.push(bg_single);
ft.push(ft_single);
}
var dataRegion = spreadsheet.getSheets()[0].getRange(
CONST.FIRST_DATA_ROW, CONST.FIRST_DATA_COLUMN,
CONST.MCC_CHILD_ACCOUNT_LIMIT, CONST.TOTAL_DATA_COLUMNS);
dataRegion.setBackgrounds(bg);
dataRegion.setFontColors(ft);
}
var weeksStr = spreadsheet.getRangeByName('weeks').getValue();
weeks = parseInt(weeksStr.substring(0, weeksStr.indexOf(' ')));
var dateRangeToCheck = getDateStringInPast(0, upTo);
var dateRangeToEnd = getDateStringInPast(1, upTo);
var dateRangeToStart = getDateStringInPast(1 + weeks * 7, upTo);
var fields = 'HourOfDay, DayOfWeek, Clicks, Impressions, Conversions, Cost';
todayQuery = 'SELECT ' + fields +
' FROM ACCOUNT_PERFORMANCE_REPORT DURING ' + dateRangeToCheck + ',' +
dateRangeToCheck;
pastQuery = 'SELECT ' + fields +
' FROM ACCOUNT_PERFORMANCE_REPORT WHERE DayOfWeek=' +
DAYS[getDateStringInTimeZone('u', now)].toUpperCase() +
' DURING ' + dateRangeToStart + ',' + dateRangeToEnd;
};
var getThresholds = function() { return thresholds; };
var getHourOfDay = function() { return upToHour; };
var getWeeksToAvg = function() { return weeks; };
var getPastQuery = function() { return pastQuery; };
var getTodayQuery = function() { return todayQuery; };
// The SheetUtil public interface.
return {
setupData: setupData,
thresholds: getThresholds,
hourOfDay: getHourOfDay,
weeksToAvg: getWeeksToAvg,
getPastQuery: getPastQuery,
getTodayQuery: getTodayQuery
};
})();
function sendEmail(account, alertTextArray, spreadsheet) {
var bodyText = '';
alertTextArray.forEach(function(alertText) {
// When zero alerts, this is an empty array, which we don't want to add.
if (alertText.length == 0) { return }
bodyText += alertText.join('\n') + '\n\n';
});
bodyText = bodyText.trim();
var email = spreadsheet.getRangeByName('email').getValue();
if (bodyText.length > 0 && email && email.length > 0 &&
email != 'foo#example.com') {
Logger.log('Sending Email');
MailApp.sendEmail(email,
'AdWords Account ' + account.getCustomerId() + ' misbehaved.',
'Your account ' + account.getCustomerId() +
' is not performing as expected today: \n\n' +
bodyText + '\n\n' +
'Log into AdWords and take a look: ' +
'adwords.google.com\n\nAlerts dashboard: ' +
SPREADSHEET_URL);
}
else if (bodyText.length == 0) {
Logger.log('No alerts triggered. No email being sent.');
}
}
function toFloat(value) {
value = value.toString().replace(/,/g, '');
return parseFloat(value);
}
function parseField(value) {
if (value == 'No alert') {
return null;
} else {
return toFloat(value);
}
}
function accumulateRows(rows, hours, weeks) {
var result = {Clicks: 0, Impressions: 0, Conversions: 0, Cost: 0};
while (rows.hasNext()) {
var row = rows.next();
var hour = row['HourOfDay'];
if (hour < hours) {
result = addRow(row, result, 1 / weeks);
}
}
return result;
}
function addRow(row, previous, coefficient) {
if (!coefficient) {
coefficient = 1;
}
if (!row) {
row = {Clicks: 0, Impressions: 0, Conversions: 0, Cost: 0};
}
if (!previous) {
previous = {Clicks: 0, Impressions: 0, Conversions: 0, Cost: 0};
}
return {
Clicks: parseInt(row['Clicks']) * coefficient + previous.Clicks,
Impressions:
parseInt(row['Impressions']) * coefficient + previous.Impressions,
Conversions:
parseInt(row['Conversions']) * coefficient + previous.Conversions,
Cost: toFloat(row['Cost']) * coefficient + previous.Cost
};
}
function checkInRange(today, yesterday, coefficient, field) {
var yesterdayValue = yesterday[field] * coefficient;
if (today[field] > yesterdayValue * 2) {
Logger.log('' + field + ' too much');
} else if (today[field] < yesterdayValue / 2) {
Logger.log('' + field + ' too little');
}
}
/**
* Produces a formatted string representing a date in the past of a given date.
*
* #param {number} numDays The number of days in the past.
* #param {date} date A date object. Defaults to the current date.
* #return {string} A formatted string in the past of the given date.
*/
function getDateStringInPast(numDays, date) {
date = date || new Date();
var MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
var past = new Date(date.getTime() - numDays * MILLIS_PER_DAY);
return getDateStringInTimeZone('yyyyMMdd', past);
}
/**
* Produces a formatted string representing a given date in a given time zone.
*
* #param {string} format A format specifier for the string to be produced.
* #param {date} date A date object. Defaults to the current date.
* #param {string} timeZone A time zone. Defaults to the account's time zone.
* #return {string} A formatted string of the given date in the given time zone.
*/
function getDateStringInTimeZone(format, date, timeZone) {
date = date || new Date();
timeZone = timeZone || AdWordsApp.currentAccount().getTimeZone();
return Utilities.formatDate(date, timeZone, format);
}
/**
* Module that deals with fetching and iterating through multiple accounts.
*
* #return {object} callable functions corresponding to the available
* actions. Specifically, it currently supports next, current, mccAccount.
*/
var mccManager = (function() {
var accountIterator;
var mccAccount;
var currentAccount;
// Private one-time init function.
var init = function() {
var accountSelector = MccApp.accounts();
// Use this to limit the accounts that are being selected in the report.
if (CONFIG.ACCOUNT_LABEL) {
accountSelector.withCondition("LabelNames CONTAINS '" +
CONFIG.ACCOUNT_LABEL + "'");
}
accountSelector.withLimit(CONST.MCC_CHILD_ACCOUNT_LIMIT);
accountIterator = accountSelector.get();
mccAccount = AdWordsApp.currentAccount(); // save the mccAccount
currentAccount = AdWordsApp.currentAccount();
};
/**
* After calling this, AdWordsApp will have the next account selected.
* If there are no more accounts to process, re-selects the original
* MCC account.
*
* #return {AdWordsApp.Account} The account that has been selected.
*/
var getNextAccount = function() {
if (accountIterator.hasNext()) {
currentAccount = accountIterator.next();
MccApp.select(currentAccount);
return currentAccount;
}
else {
MccApp.select(mccAccount);
return null;
}
};
/**
* Returns the currently selected account. This is cached for performance.
*
* #return {AdWords.Account} The currently selected account.
*/
var getCurrentAccount = function() {
return currentAccount;
};
/**
* Returns the original MCC account.
*
* #return {AdWords.Account} The original account that was selected.
*/
var getMccAccount = function() {
return mccAccount;
};
// Set up internal variables; called only once, here.
init();
// Expose the external interface.
return {
next: getNextAccount,
current: getCurrentAccount,
mccAccount: getMccAccount
};
})();
/**
* Validates the provided spreadsheet URL and email address
* to make sure that they're set up properly. Throws a descriptive error message
* if validation fails.
*
* #param {string} spreadsheeturl The URL of the spreadsheet to open.
* #return {Spreadsheet} The spreadsheet object itself, fetched from the URL.
* #throws {Error} If the spreadsheet URL or email hasn't been set
*/
function validateAndGetSpreadsheet(spreadsheeturl) {
if (spreadsheeturl == 'YOUR_SPREADSHEET_URL') {
throw new Error('Please specify a valid Spreadsheet URL. You can find' +
' a link to a template in the associated guide for this script.');
}
var spreadsheet = SpreadsheetApp.openByUrl(spreadsheeturl);
var email = spreadsheet.getRangeByName('email').getValue();
if ('foo#example.com' == email) {
throw new Error('Please either set a custom email address in the' +
' spreadsheet, or set the email field in the spreadsheet to blank' +
' to send no email.');
}
return spreadsheet;
}

Problems rendering an ISO Serialized DateTime JSON into a Highcharts time-series?

I am trying to use latest Highcharts to render data from Web API which is ISO formatted. I am using TypeScript and datejs (http://www.datejs.com/).
I am intending to update this chart on a poll, and the series can be dramatically transformed between requests so I have decided to destroy and recreate the chart from scratch. I tried setting the options directly and redrawing but that did not seem to have any effect.
But to be sure I commented out the initial chart creation and destruction, and I still got the same error. I have checked to see that the parser is returning a date object and it is, and the data seems to be correct.
My guess is it has something to do with how I'm constructing my data element.
Is this an acceptable structure for time-series data for high-charts:
var data: [Date, number][] = [, ];
My Model:
export interface Chart {
Title: string;
Series: ChartSeries[];
XAxisTitle: string;
YAxisTtile: string;
}
export interface ChartSeries {
Title: string;
Points: ChartDataPoint[];
}
export interface ChartDataPoint {
X: string;
Y: number;
}
An example series in XML (using NewtonSoft.Json to do the Json serialization)
<ChartSeries>
<Points>
<ChartDataPoint>
<X>2015-12-16T00:00:00</X>
<Y>184</Y>
</ChartDataPoint>
<ChartDataPoint>
<X>2015-12-16T05:00:00</X>
<Y>168</Y>
</ChartDataPoint>
<ChartDataPoint>
<X>2015-12-16T07:00:00</X>
<Y>282</Y>
</ChartDataPoint>
</Points>
<Title>Version: UNK;Service Type: 1002;Server: UNK;Method Name: GetAllCustomerDetails
</Title>
</ChartSeries>
Relevant portion of the ViewModel:
protected Draw(): void {
var chart = this.ChartData();
if (!Util.Obj.isNullOrUndefined(chart)) {
this.IsDrawing(true);
var series: HighchartsSeriesOptions[] = [];
for (var i = 0; i < chart.Series.length; i++) {
var s = chart.Series[i];
var data: [Date, number][] = [, ];
for (var p = 0; p < s.Points.length; p++) {
var point = s.Points[p];
data.push([Date.parse(point.X), point.Y]);
}
series.push({
type: "line",
data: data,
name: s.Title
});
}
var options: HighchartsOptions = {
chart: {
renderTo: this.chartElementName,
ignoreHiddenSeries: false
},
series: series,
title: chart.Title,
yAxis: {
type: 'logarithmic',
minorTickInterval: 1,
title: chart.YAxisTtile
},
xAxis: {
type: 'datetime',
title: chart.XAxisTitle
}
};
this.Chart.destroy();
this.Chart = new Highcharts.Chart(options);
this.IsDrawing(false);
}
}
The error:
Line: 14084
Error: Unable to set property 'index' of undefined or null reference
Some context for the error from highcharts.src.js:
generatePoints: function () {
var series = this,
options = series.options,
dataOptions = options.data,
data = series.data,
dataLength,
processedXData = series.processedXData,
processedYData = series.processedYData,
pointClass = series.pointClass,
processedDataLength = processedXData.length,
cropStart = series.cropStart || 0,
cursor,
hasGroupedData = series.hasGroupedData,
point,
points = [],
i;
if (!data && !hasGroupedData) {
var arr = [];
arr.length = dataOptions.length;
data = series.data = arr;
}
for (i = 0; i < processedDataLength; i++) {
cursor = cropStart + i;
if (!hasGroupedData) {
if (data[cursor]) {
point = data[cursor];
} else if (dataOptions[cursor] !== UNDEFINED) { // #970
data[cursor] = point = (new pointClass()).init(series, dataOptions[cursor], processedXData[i]);
}
points[i] = point;
} else {
// splat the y data in case of ohlc data array
points[i] = (new pointClass()).init(series, [processedXData[i]].concat(splat(processedYData[i])));
}
points[i].index = cursor; // For faster access in Point.update <-- error happens here, there is a single item in the collection that is undefined
}
As Requested, stack trace:
Series.generatePoints # highcharts.src.js:14084
Series.translate # highcharts.src.js:14165
(anonymous function) # highcharts-more.js:53
obj.(anonymous function) # highcharts.src.js:660
(anonymous function) # highcharts.src.js:12830
each # highcharts.src.js:1106
Chart.renderSeries # highcharts.src.js:12829
(anonymous function) # highcharts-3d.js:26
obj.(anonymous function) # highcharts.src.js:660
Chart.render # highcharts.src.js:12937
Chart.firstRender # highcharts.src.js:13112
Chart.init # highcharts.src.js:11841
(anonymous function) # highcharts-3d.js:25
obj.(anonymous function) # highcharts.src.js:660
Chart.getArgs # highcharts.src.js:11746
Highcharts.Chart # highcharts.src.js:11720
ChartViewModel.Draw # ChartViewModel.ts:160
(anonymous function) # ChartViewModel.ts:180
(anonymous function) # jquery-2.1.4.js:3256
fire # jquery-2.1.4.js:3099
self.fireWith # jquery-2.1.4.js:3211
deferred.(anonymous function) # jquery-2.1.4.js:3301
(anonymous function) # ChartViewModel.ts:244
(anonymous function) # jquery-2.1.4.js:3256
fire # jquery-2.1.4.js:3099
self.fireWith # jquery-2.1.4.js:3211
done # jquery-2.1.4.js:8264
(anonymous function) # jquery-2.1.4.js:8605
As it turns out there were two problems:
var data: [Date, number][] = [, ];
In order to solve the exception:
var data: [Date, number][] = [];
In order for the days to render correctly:
var data: [number, number][] = [];
and
var d = Date.parse(point.X);
data.push([
Date.UTC(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()),
point.Y
]);

Resources