AdWords Script With Divergent Data from Webview - google-ads-api

I have the following code on an AdWords script:
var campanha = 'xyz';
function main() {
campanhas = AdWordsApp.campaigns().withCondition("CampaignName = '" + campanha + "'").withCondition('Status = ENABLED').get();
while(campanhas.hasNext()) {
campanha = campanhas.next();
if(campanha.getBiddingStrategyType() != 'ENHANCED_CPC') {
Logger.log('Ajuste de lance da campanha inválido: ' +campanha.getBiddingStrategyType());
continue;
}
palavras = campanha.keywords().withCondition('Status = ENABLED').forDateRange("YESTERDAY").get();
while(palavras.hasNext()) {
palavra = palavras.next();
estatisticas = palavra.getStatsFor('YESTERDAY');
lances = palavra.bidding();
estimativa_primeira = Math.max(palavra.getTopOfPageCpc(), palavra.getFirstPageCpc());
posicao = estatisticas.getAveragePosition();
if (posicao > 1) {
Logger.log(palavra.getText() +" = " +" "+palavra.getTopOfPageCpc()+" "+palavra.getFirstPageCpc());
}
//Logger.log(palavra.getText() + ' = '+lances.getCpc()+" = "+estatisticas.getAveragePosition());
}
}
I expected to retrieve the estimated first page CPC and estimated first position CPC. But the values that I received are diferente from the AdWords webinterface. For exemple, for a given keyword the script returned +xxx +yyyy = 0.12 0.05, when I look those keywords on webinterface I have the following values for 0.12 for estimated first page CPC and 0.41 for estimated first position CPC.

Related

Can't get Indicator pivot points to match on EA code

I've been messing around with the Camarilla Indicator and I wanted to get the main pivot values for my EA, can I use iCustom() for that? or should I just pass the code to the EA? but where exactly would I place it? I tried to place it on the onTick() but with a condition of only calculating the pivots when there is a new daily candle, but the values most of the times don't match.
void OnTick()
{
static datetime today;
if (today != iTime (Symbol(), PERIOD_D1, 0))
{
today = iTime (Symbol(), PERIOD_D1, 0);
int counted_bars=IndicatorCounted();
//---- TODO: add your code here
int cnt=720;
//---- exit if period is greater than daily charts
if(Period() > 1440)
{
Print("Error - Chart period is greater than 1 day.");
}
//---- Get new daily prices & calculate pivots
day_high=0;
day_low=0;
yesterday_open=0;
today_open=0;
cur_day=0;
prev_day=0;
while (cnt!= 0)
{
if (TimeDayOfWeek(Time[cnt]) == 0)
{
cur_day = prev_day;
}
else
{
cur_day = TimeDay(Time[cnt]- (GMTshift*3600));
}
if (prev_day != cur_day)
{
yesterday_close = Close[cnt+1];
today_open = Open[cnt];
yesterday_high = day_high;
yesterday_low = day_low;
day_high = High[cnt];
day_low = Low[cnt];
prev_day = cur_day;
}
if (High[cnt]>day_high)
{
day_high = High[cnt];
}
if (Low[cnt]<day_low)
{
day_low = Low[cnt];
}
cnt--;
}
H3 = ((yesterday_high - yesterday_low)* D3) + yesterday_close;
H4 = ((yesterday_high - yesterday_low)* D4) + yesterday_close;
L3 = yesterday_close - ((yesterday_high - yesterday_low)*(D3));
L4 = yesterday_close - ((yesterday_high - yesterday_low)*(D4));
L5 = yesterday_close - (H5 - yesterday_close);
H5 = (yesterday_high/yesterday_low)*yesterday_close;
Print ("H3: " + DoubleToStr(H3));
Print ("H4: " + DoubleToStr(H4));
Print ("H5: " + DoubleToStr(H5));
Print ("L3: " + DoubleToStr(L3));
Print ("L4: " + DoubleToStr(L4));
Print ("L5: " + DoubleToStr(L5));
}

Is there a way to generate an SRT file (or similar) using Google Cloud Speech?

In order to generate subtitles for my videos, I converted them to audio files and used the Cloud Speech-to-Text. It works, but it only generates transcriptions, whereas what I need is a *.srt/*.vtt/similar file.
What I need is what YouTube does: to generate transcriptions and sync them with the video, like a subtitle format, ie.: transcriptions with the times when captions should appear.
Although I could upload them to YouTube and then download their auto-generated captions, it doesn't seem very correct.
Is there a way to generate an SRT file (or similar) using Google Cloud Speech?
There's no way really to do this directly from the Speech-to-Text API. What you could try to do is some post-processing on the speech recognition result.
For example, here's a request to the REST API using a model meant to transcribe video, with a public google-provided sample file:
curl -s -H "Content-Type: application/json" \
-H "Authorization: Bearer "$(gcloud auth application-default print-access-token) \
https://speech.googleapis.com/v1p1beta1/speech:longrunningrecognize \
--data "{
'config': {
'encoding': 'LINEAR16',
'sampleRateHertz': 16000,
'languageCode': 'en-US',
'enableWordTimeOffsets': true,
'enableAutomaticPunctuation': true,
'model': 'video'
},
'audio': {
'uri':'gs://cloud-samples-tests/speech/Google_Gnome.wav'
}
}"
The above uses asynchronous recognition (speech:longrunningrecognize), which is more fitting for larger files. Enabling punctuation ('enableAutomaticPunctuation': true) in combination with the start and end times of words ('enableWordTimeOffsets': true) near the start and end of each sentence (which you'd also have to convert from nanos to timestamps) could allow you to provide a text file in the srt format. You would probably also have to include some rules about the maximum length of a sentence appearing on the screen at any given time.
The above should not be too difficult to implement, however, there's a strong possibility that you would still encounter timing/synchronization issues.
There is no way to do it using Google Cloud itself buy as suggested you may post-process the result.
In this file I have made a quick code that kind of does the job. You may want to adapt it to your needs:
function convertGSTTToSRT(string) {
var obj = JSON.parse(string);
var i = 1;
var result = ''
for (const line of obj.response.results) {
result += i++;
result += '\n'
var word = line.alternatives[0].words[0]
var time = convertSecondStringToRealtime(word.startTime);
result += formatTime(time) + ' --> '
var word = line.alternatives[0].words[line.alternatives[0].words.length - 1]
time = convertSecondStringToRealtime(word.endTime);
result += formatTime(time) + '\n'
result += line.alternatives[0].transcript + '\n\n'
}
return result;
}
function formatTime(time) {
return String(time.hours).padStart(2, '0')+ ':' + String(time.minutes).padStart(2, '0') + ':' +
String(time.seconds).padStart(2, '0') + ',000';
}
function convertSecondStringToRealtime(string) {
var seconds = string.substring(0, string.length - 1);
var hours = Math.floor(seconds / 3600);
var minutes = Math.floor(seconds % 3600 / 60);
seconds = Math.floor(seconds % 3600 % 60);
return {
hours, minutes, seconds
}
}
here is the code I used
import math
import json
import datetime
def to_hms(s):
m, s = divmod(s, 60)
h, m = divmod(m, 60)
return '{}:{:0>2}:{:0>2}'.format(h, m, s)
def srt_generation(filepath, filename):
filename = 'DL_BIRTHDAY'
with open('{}{}.json'.format(filepath, filename), 'r') as file:
data = file.read()
results = json.loads(data)['response']['annotationResults'][0]['speechTranscriptions']
processed_results = []
counter = 1
lines = []
wordlist = []
for transcription in results:
alternative = transcription['alternatives'][0]
if alternative.has_key('transcript'):
# print(counter)
# lines.append(counter)
tsc = alternative['transcript']
stime = alternative['words'][0]['startTime'].replace('s','').split('.')
etime = alternative['words'][-1]['endTime'].replace('s','').split('.')
if(len(stime) == 1):
stime.append('000')
if(len(etime) == 1):
etime.append('000')
lines.append('{}\n{},{} --> {},{}\n{}\n\n\n'.format(counter, to_hms(int(stime[0])), stime[1], to_hms(int(etime[0])), etime[1],tsc.encode('ascii', 'ignore')))
counter = counter+1
wordlist.extend(alternative['words'])
srtfile = open('{}{}.srt'.format(filepath, filename), 'wr')
srtfile.writelines(lines)
srtfile.close()
## Now generate 3 seconds duration chunks of those words.
lines = []
counter = 1
strtime =0
entime = 0
words = []
standardDuration = 3
srtcounter = 1
for word in wordlist:
stime = word['startTime'].replace('s','').split('.')
etime = word['endTime'].replace('s','').split('.')
if(len(stime) == 1):
stime.append('000 ')
if(len(etime) == 1):
etime.append('000')
if(counter == 1):
strtime = '{},{}'.format(stime[0], stime[1])
entime = '{},{}'.format(etime[0], etime[1])
words.append(word['word'])
else:
tempstmime = int(stime[0])
tempentime = int(etime[0])
stimearr = strtime.split(',')
etimearr = entime.split(',')
if(tempentime - int(strtime.split(',')[0]) > standardDuration ):
transcript = ' '.join(words)
lines.append('{}\n{},{} --> {},{}\n{}\n\n\n'.format(srtcounter, to_hms(int(stimearr[0])), stimearr[1], to_hms(int(etimearr[0])), etimearr[1],transcript.encode('ascii', 'ignore')))
srtcounter = srtcounter+1
words = []
strtime = '{},{}'.format(stime[0], stime[1])
entime = '{},{}'.format(etime[0], etime[1])
words.append(' ')
words.append(word['word'])
else:
words.append(' ')
words.append(word['word'])
entime = '{},{}'.format(etime[0], etime[1])
counter = counter +1
if(len(words) > 0):
tscp = ' '.join(words)
stimearr = strtime.split(',')
etimearr = entime.split(',')
lines.append('{}\n{},{} --> {},{}\n{}\n\n\n'.format(srtcounter, to_hms(int(stimearr[0])), stimearr[1], to_hms(int(etimearr[0])), etimearr[1],tscp.encode('ascii', 'ignore')))
srtfile = open('{}{}_3_Sec_Custom.srt'.format(filepath, filename), 'wr')
srtfile.writelines(lines)
srtfile.close()
Use this request parameter "enable_word_time_offsets: True" to get the time stamps for the word groups. Then create an srt programmatically.
If you require a *.vtt file, here is a snippet to convert the API response received from GCP speech-to-text client into a valid *.vtt. Some answers above are for *.srt so sharing this here.
const client = new speech.SpeechClient();
const [response] = await client.recognize(request);
createVTT(response);
function createVTT(response) {
const wordsArray = response.results[0].alternatives[0].words;
let VTT = '';
let buffer = [];
const phraseLength = 10;
let startPointer = '00:00:00';
let endPointer = '00:00:00';
VTT += 'WEBVTT\n\n';
wordsArray.forEach((wordItem) => {
const { startTime, endTime, word } = wordItem;
const start = startTime.seconds;
const end = endTime.seconds;
if (buffer.length === 0) {
// first word of the phrase
startPointer = secondsToFormat(start);
}
if (buffer.length < phraseLength) {
buffer.push(word);
}
if (buffer.length === phraseLength) {
endPointer = secondsToFormat(end);
const phrase = buffer.join(' ');
VTT += `${startPointer + ' --> ' + endPointer}\n`;
VTT += `${phrase}\n\n`;
buffer = [];
}
});
if (buffer.length) {
// handle the left over buffer items
const lastItem = wordsArray[wordsArray.length - 1];
const end = lastItem.endTime.seconds;
endPointer = secondsToFormat(end);
const phrase = buffer.join(' ');
VTT += `${startPointer + ' --> ' + endPointer}\n`;
VTT += `${phrase}\n\n`;
}
return VTT;
}
function secondsToFormat(seconds) {
const timeHours = Math.floor(seconds / 3600)
.toString()
.padStart(2, '0');
const timeMinutes = Math.floor(seconds / 60)
.toString()
.padStart(2, '0');
const timeSeconds = (seconds % 60).toString().padStart(2, '0');
const formattedTime = timeHours + ':' + timeMinutes + ':' + timeSeconds + '.000';
return formattedTime;
}
Note: enableWordTimeOffsets: true must be set but that's already answered above. This answer is for people who want .vtt copy.
Hope this was helpful to someone :)

How to access the baseline comparison results in IBM Rational Doors from scripts?

The background to the question is, that I would like to create software change requests from Doors requirement changes.
For this I have to get the differences of requirements between two user selectable baselines of a module in a human readable format.
In the GUI I use the "Baseline Compare" function.
How can I access these results from a script (inside or outside of Doors) in a structured format?
A good starting point might the Doors DXL library Tools -> DXL Library -> baseline comparator example, but it needs heavy modifications for your overall use-case I guess. Take a look.
Found a modified version of the baseline compare script at this forum . I haven't tried it yet, but here's the code, which does use a GUI but you should be able to redirect the input & output pretty easily:
// Your Company Baseline Compare with Attributes
/*
Date Posted: 16-Jan-2008 00:03
Posted By: Juergen Albrecht
*/
/*
// (Your Company Baseline Compare) *
//---------------------------------------------------------------------------*
// *
// Project: Your Company Ottobrunn *
// *
//---------------------------------------------------------------------------*
// Module: all modules for baseline compare *
//---------------------------------------------------------------------------*
// Description: ... *
// *
// Functions: compare any two baselines of the current module *
// (or a baseline against the current version). *
// Sets a filter on objects which differ with request *
// to Object Heading, Object Text, Object Type and .... *
// *
// *
// Copyright (C) Your Company Your Town 2005-2007 Confidential. All rights reserved. *
//---------------------------------------------------------------------------*
// *
// $Header: /doors/lib/dxl/addins/user/BaselineCompare.dxl Ver. 1.0 15.03.06 18:00 UID-USER $
// *
//---------------------------------------------------------------------------*
// $History: BaselineCompare.dxl $
//
//********************************* Version 1.0 ****************************
// User: Date: Time: *
// Jürgen Albrecht (uid-al28423) 15.01.08 18:00
// Created in $/doors/lib/dxl/addins/user/BaselineCompare.dxl
//---------------------------------------------------------------------------*
// *
// *
// *
//---------------------------------------------------------------------------*
*/
pragma runLim, 0
////////////////////////////////////////////////////// constants for Attributes
const string DUMMY_LIST[] = {}
const string strAffectsArr[] = {"Ch.Bars", "Ch.Date", "History"}
const string strCharacterArr[] = {"System ", "Hidden ", "Customised"}
const string strValidityArr[] = {"Module ", "Object"}
const string strPropertyArr[] = {"Inherited", "Multi value"}
////////////////////////////////////////////////////// variables for Attributes
DB dbBLCompare = null
DBE dbeAttrDefNameList = null
DBE dbeAttrCharacter = null
DBE dbeAttrAffects = null
DBE dbeAttrValidity = null
DBE dbeAttrProperty = null
DBE dbeAttrType = null
DBE dbeDefaultValue = null
DBE dbeAttrTypeList = null
DBE dbeAttrTypeSelect = null
////////////////////////////////////////////////////// functions for Attributes
int chopInsert2(DBE inlist, string s)
{ int no_inlist = noElems inlist
int low = 0
int high = no_inlist-1
int hw = 0
string c
if (no_inlist == 0)
{ insert(inlist,0,s)
setCheck(inlist,0,false)
return 0
}
while (high-low > 1)
{ hw = low + ((high-low) / 2)
c = get(inlist,hw)
if (s < c)
high = hw
else
low = hw
}
c = get(inlist,high)
if (c < s)
{ insert(inlist,high+1,s)
setCheck(inlist,high+1,false)
return high + 1
}
c = get(inlist,low)
if (c > s)
{ insert(inlist,low,s)
setCheck(inlist,low,false)
return low
}
insert(inlist,low+1,s)
setCheck(inlist,low+1,false)
return low+1
} // chopInsert2
void populateLstAttrDef(bool bIncludeSystemAttr)
{ AttrDef ad
for ad in current Module do
{ if (ad.module)
continue
if (!bIncludeSystemAttr)
{ if (ad.system || ad.hidden)
continue
}
chopInsert2(dbeAttrDefNameList, ad.name "")
}
set(dbeAttrDefNameList, 0, true)
} // populateLstAttrDef
void doNothing(DBE dbeKlick, int iKlick)
{
} // doNothing
void cbSystemSelect(DBE dbeKlick)
{ bool bSelect = get(dbeAttrTypeSelect)
empty (dbeAttrDefNameList)
populateLstAttrDef (bSelect)
} // cbSystemSelect
void doSetAttrTypes(void)
{
AttrDef ad = find(current Module, get(dbeAttrDefNameList, get(dbeAttrDefNameList)))
AttrType at = find(current Module, ad.typeName "")
string strDefVal = "", strAttrType = "", s = ""
int iAttrChar = 0, iAttrValid = 0, iProperty = 0, iAffects = 0
if (ad.system)
iAttrChar = 1
else
iAttrChar = 4
if (ad.hidden)
iAttrChar = iAttrChar | 2
set(dbeAttrCharacter, iAttrChar)
if (ad.object)
iAttrValid = 2
if (ad.module)
iAttrValid = iAttrValid | 1
set(dbeAttrValidity, iAttrValid)
if (ad.inherit)
iProperty = 1
if (ad.multi)
iProperty = iProperty | 2
set(dbeAttrProperty, iProperty)
if (!ad.nobars)
iAffects = 1
if (!ad.nochanges)
iAffects = iAffects | 2
if (!ad.nohistory)
iAffects = iAffects | 4
set(dbeAttrAffects, iAffects)
if ( null at )
{ strAttrType = "Unable to determine type of attribute"
set(dbeAttrType, strAttrType)
return
}
strAttrType = at.type ""
if ( at.type == attrInteger )
{ int i
if ( at.minValue )
{ i = at.minValue
s = s "Min > " i "\n"
}
if ( at.maxValue )
{ i = at.maxValue
s = s "Max < " i "\n"
}
}
else if ( at.type == attrReal )
{ real r
if ( at.minValue )
{ r = at.minValue
s = s "Min > " r "\n"
}
if ( at.maxValue )
{ r = at.maxValue
s = s "Max < " r "\n"
}
}
else if ( at.type == attrDate )
{ Date d
if ( at.minValue )
{ d = at.minValue
s = s "Min > " d "\n"
}
if ( at.maxValue )
{ d = at.maxValue
s = s "Max < " d "\n"
}
}
else if ( at.type == attrEnumeration )
{ string strAttrTypeName = at.name
strAttrType = strAttrType ": " strAttrTypeName
int i
for ( i = 0; i < at.size; ++i )
s = s at.strings[i] "\n"
}
set(dbeAttrType, strAttrType)
set(dbeAttrTypeList, s)
if (ad.defval)
{ if (strDefVal == "")
strDefVal = ad.defval
set (dbeDefaultValue, strDefVal)
}
else
set (dbeDefaultValue, "")
// Ausgabe in File
// output << ad.name"\n"
// output << s"\n"
} // doSetAttrTypes
void cbDoSetAttrTypes(DBE dbeKlick, int iKlick)
{ doSetAttrTypes
} // cbDoSetAttrTypes
void showAttributes()
{ if ( null current Module )
{ ack "This tool must be run within a module..."
halt
}
// Ausgabe in File
// output << "\t\t"(current Module)."Name""\n"
populateLstAttrDef(true)
doSetAttrTypes()
// close output
} // showAttrInvestigatorDB
/////////////////////////////////////////// Definition for Modules
DBE lblOldList, lblNewList
DBE dbeListCurrent, dbeListBaseline // two global lists containing the baseline selected (or current version)
DBE dbeOutFileBrowse, dbeOutFileLabel
DBE dbeChangeBarToggle
DBE dbeShowDiffToggle
Stream outFile
Skip skBaselines = create // cache current baselines
bool bShowDiff
string strCellBorders = "\\clbrdrt\\brdrs\\brdrw15\\brdrcf11 \\clbrdrl\\brdrs\\brdrw15\\brdrcf11 \\clbrdrb\\brdrs\\brdrw15\\brdrcf11 \\clbrdrr\\brdrs\\brdrw15\\brdrcf11 "
string lastHeadingNumber(Object o)
{ Regexp reg = regexp "\\.0-[0-9]+$"
string onum = number o
if (onum[0:0] == "0")
{ return "0"
}
else if (reg onum)
{ int i = start 0
string hd = onum[0:i-1]
while (reg hd)
{ int j = start 0
hd = hd[0:j-1]
}
return hd
}
else
{ return onum
}
} // lastHeadingNumber
bool oleInsDel(Object o1,o2)
{ bool oleRec = false
string modType = ""
int oldOle = oleCount(o2."Object Text")
int newOle = oleCount(o1."Object Text")
if (newOle > oldOle)
{ oleRec = true
modType = "Figure/Table inserted"
}
if (newOle < oldOle)
{ oleRec = true
modType = "Figure/Table deleted"
}
if (oleRec)
{ outFile << "\\trowd \\trgaph108\\trleft-108\\trkeep"
outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx6129 " strCellBorders "\\cellx9531\n"
outFile << "\\intbl " identifier(o1) " section " lastHeadingNumber(o1) "\\cell OLE\\cell " modType "\\cell \\cell\\row\n"
}
return oleRec
} // oleInsDel
bool oleChange(Object o1,o2)
{ int i = oleCount o1."Object Text"
if (i > 0)
{ Buffer b1 = create
Buffer b2 = create
b1 = richTextWithOle o1."Object Text"
b2 = richTextWithOle o2."Object Text"
int l1 = length b1
int l2 = length b2
delete b1
delete b2
if (l1 != l2)
{ outFile << "\\trowd \\trgaph108\\trleft-108\\trkeep"
outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx6129 " strCellBorders "\\cellx9531\n"
outFile << "\\intbl " identifier(o1) " section " lastHeadingNumber(o1) "\\cell OLE\\cell Figure/Table modified\\cell \\cell\\row\n"
accept o1 // set filter
accept o2 // on both objects
return true
}
}
return false
} // oleChange
bool compareObjects(int absno, Object o1, o2, string attr)
{ // function to compare an attribute of two objects with same absolute number
Buffer s1 = create
Buffer s2 = create
Buffer s3 = create
s1 = o1.attr
s2 = o2.attr
if (s1!=s2)
{ outFile << "\\trowd \\trgaph108\\trleft-108\\trkeep"
// if ((attr=="Object Text") && bShowDiff)
if (bShowDiff)
// {
// diff(s3, s2, s1,"\\cf1\\strike ","\\cf3\\ul ")
// outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx9531\n"
// outFile << "\\intbl \\fs16 " identifier(o1) " section " lastHeadingNumber(o1) "\\cell " attr "\\cell " s3 "\\cell " s2 "\\cell\\row\n"
// }
{ diff(s3, s2, s1)
Regexp ct = regexp "colortbl[^}]*}"
string frag
if (ct s3)
{ frag = s3[end 0 + 1:(length s3) - 3]
}
else
{ frag = richTextFragment stringOf(s3)
}
outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx9531\n"
outFile << "\\intbl \\fs16 " identifier(o1) " section " lastHeadingNumber(o1) "\\cell " attr "\\cell " frag "\\cell\\row\n"
}
else
{ outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx6129 " strCellBorders "\\cellx9531\n"
outFile << "\\intbl \\fs16 " identifier(o1) " section " lastHeadingNumber(o1) "\\cell " attr "\\cell " s1 "\\cell " s2 "\\cell\\row\n"
}
accept o1 // set filter
accept o2 // on both objects
delete s1
delete s2
delete s3
return false
}
delete s1
delete s2
delete s3
return true
} // compareObjects
Skip getAbsnos(Module m)
{ // Build a skip list which maps absnos onto their corresponding objects. Also initialize the DXL filter to "reject"
Skip res = create
Object o
for o in m do
{ int a = o."Absolute Number"
reject o // filter those mentioned in report
put(res, a, o)
}
return res
} // getAbsnos
void applyCompareFunction (DB dbKlick)
{ // Main comparison routine: find out which modules to compare
// compare objects present in both, report on
// those present in only one.
string name1, name2, outf
int idx1, idx2
bool updateChangeBars
idx1 = get dbeListCurrent // position in list
idx2 = get dbeListBaseline
name1 = get dbeListCurrent // baseline name
name2 = get dbeListBaseline
outf = get dbeOutFileBrowse
updateChangeBars = get dbeChangeBarToggle
bShowDiff = get dbeShowDiffToggle
if (idx1 < 0 || idx2 < 0) // error checking
{ ack "two selections are needed"
return
}
else if (idx1 == idx2)
{ ack "same selection on both sides"
return
}
Regexp slash = regexp "[\\\\]"
if (!slash outf)
{ outf = currentDirectory "\\" outf
}
Baseline sel1, sel2
outFile = write outf
outFile << "{\\rtf1\\deff0{\\fonttbl{\\f0\\fswiss\\fcharset177 Times New Roman;}}{\\colortbl ;\\red255\\green0\\blue0;\\red0\\green255\\blue0;\\red0\\green0\\blue255;}\n"
outFile << "\\paperw11906\\paperh16838\\margl1134\\margr567\\margt1134\\margb851\\headery567\\footery567\n"
string where = (current Module)."Name"
outFile << "\\ul \\fs24 Modified Objects in Module: " where "\\ul0 \\par \\par\n"
outFile << "\\trowd \\trgaph108\\trleft-108\\trhdr"
outFile << strCellBorders "\\cellx1026 " strCellBorders "\\cellx2727 " strCellBorders "\\cellx6129 " strCellBorders "\\cellx9531\n"
outFile << "\\intbl \\fs20 Identifier\\cell Attribute\\cell Current Baseline\\cell Old Baseline\\cell\\row\n"
string str
for str in skBaselines do // find each baseline
{ Baseline b = key skBaselines // the baseline is the key
string str = (major b) "." (minor b) (suffix b)
if (name1==str) sel1 = b
if (name2==str) sel2 = b
}
progressStart(dbBLCompare, "Baseline Compare", "", 1)
Module old = current
Module b1, b2
if (idx1==0)
b1 = old // i.e. the current Module
else
{ progressMessage("Load First Baseline Module")
b1 = load(sel1, true) // load the baselines on the screen
}
progressStop()
progressStart(dbBLCompare, "Baseline Compare", "", 1)
if (idx2==0)
b2 = old // i.e.e the current Module
else
{ progressMessage("Load Second Baseline Module")
b2 = load(sel2, true)
}
progressStop()
if (updateChangeBars and (idx1 == 0))
{ AttrDef atcb
atcb = find(b1,"ChangeBar")
if (null atcb)
{ updateChangeBars = false
ack "No ChangeBar attribute in current module"
}
else
{ atcb = find(b1,"WordDocChangeLog")
if (!null atcb)
{ b1."WordDocChangeLog" = outf
}
}
}
else if (updateChangeBars)
{ updateChangeBars = false
ack "Can only update ChangeBar in current module"
}
Skip skCompAttributes = create
string aName
AttrDef atb1,atb2
int i, iSkip = 0, iElems = noElems (dbeAttrDefNameList)
bool bCheck
for (i=0;i<iElems;i++)
{ bCheck = getCheck (dbeAttrDefNameList, i)
if (bCheck)
{ aName = get (dbeAttrDefNameList, i)
atb1 = find(b1,aName)
atb2 = find(b2,aName)
if ((null atb1) or (null atb2))
{ if (null atb1)
infoBox aName " not in later baseline"
else
infoBox aName " not in earlier baseline"
}
else
put(skCompAttributes, iSkip++, aName)
}
}
current = b1 // make sure filtering is off
filtering off // on both sides.
current = b2
filtering off
current = old
Skip absno1 = getAbsnos b1 // build caches of absnos -> objects
Skip absno2 = getAbsnos b2
Object o1, o2
int iObj=0, iMaxObj=0, diffs=0
bool StartInsert = true
for o1 in absno1 do // loop through side 1
iMaxObj++
progressStart(dbBLCompare, "Baseline Compare", "", iMaxObj)
progressMessage("Compare selected attributes of all objects by Identifier")
for o1 in absno1 do // loop through side 1
{ progressStep(iObj++)
Object o2
int i = (int key absno1)
if (find(absno2, i, o2)) // absno exists in other baseline
{ bool ChangeFound = false
for aName in skCompAttributes do
{ ChangeFound = !compareObjects(i, o1, o2, aName) || ChangeFound
}
ChangeFound = oleInsDel(o1,o2) || ChangeFound
if (!ChangeFound)
{ ChangeFound = oleChange(o1,o2)
}
if (ChangeFound)
{ diffs++ // found a difference
if (updateChangeBars)
{ o1."ChangeBar" = true
}
}
else if (updateChangeBars)
{ o1."ChangeBar" = false
}
delete(absno2, i) // remove from dbeListBaseline
}
else
{ if (StartInsert)
{ outFile << "\\pard\\par\\ul \\fs24 Inserted Objects\\ul0\\par \\par "
StartInsert = false
}
outFile << "\\fs20" identifier(o1) " section " lastHeadingNumber(o1) "\\par "
accept o1
if (updateChangeBars)
{ o1."ChangeBar" = true
}
diffs++
}
}
progressStop()
raise (dbBLCompare)
infoBox "Baseline Compare has finished"
if (StartInsert)
{ outFile << "\\pard\\par\\ul \\fs24 Inserted Objects\\ul0\\par \\par "
}
outFile << "\n\\par\\ul \\fs24 Deleted Objects\\ul0\\par \\par "
for o2 in absno2 do // now we can check for objects not in dbeListCurrent
{ int i = (int key absno2)
outFile << "\\fs20" identifier(o2) " section " lastHeadingNumber(o2) "\\par "
accept o2
diffs++
}
delete absno1 // delete caches
delete absno2
delete skCompAttributes
bool doFilter // set to true if differences
if (diffs==0)
{ outFile << "\n\\par \\fs24 no differences found\\par "
doFilter=false
}
else // set filtering on in baselines
{ if (diffs==1)
outFile << "\n\\par \\fs24 one difference found\\par "
else
outFile << "\n\\par \\fs24 " diffs " differences found\\par "
doFilter=true
}
outFile << "\\par }"
close(outFile)
current = b1 // set filters
if (b1 != old)
close b1
// filtering doFilter
// refresh current
current = b2
if (b2 != old)
close b2
// filtering doFilter
// refresh current
current = old // return to former current module
} // applyCompareFunction
//// MAIN PROGRAM ////////////////////////
Module m = current // check calling context
if (null m)
{ ack "program requires current Module"
halt
}
Baseline b
int i=0
for b in m do // count number of baselines
{ i++
}
if (i==0)
{ ack "no baselines to compare"
halt
}
// Now make a dialog for selecting two baselines for comparison
string where = (current Module)."Name"
dbBLCompare = create "Your Company Baseline Compare V. 1.0 startet at Module: \"" where "\""
string strEmptyArr[] = {}
dbeListCurrent = list(dbBLCompare, "Current/Baseline:", 300, i+1 <? 5, strEmptyArr) // make maximum size of 5 elements
dbeListCurrent->"right"->"unattached" // make lists side by side
dbeListBaseline = list(dbBLCompare, "Baseline to compare with:", 300, i+1 <? 5, strEmptyArr)
dbeListBaseline->"left"->"flush"->dbeListCurrent
dbeListBaseline->"top"->"aligned"->dbeListCurrent
dbeListBaseline->"right"->"unattached"
separator (dbBLCompare)
dbeAttrDefNameList = listView(dbBLCompare, listViewOptionCheckboxes , 250, 14, DUMMY_LIST)
dbeAttrDefNameList->"left"->"form"
dbeAttrDefNameList->"right"->"unattached"
dbeAttrAffects = checkBox(dbBLCompare, "", strAffectsArr, 0)
dbeAttrAffects->"top"->"aligned"->dbeAttrDefNameList
dbeAttrAffects->"left"->"flush"->dbeAttrDefNameList
dbeAttrAffects->"right"->"unattached"
inactive (dbeAttrAffects)
dbeAttrCharacter = checkBox(dbBLCompare, "", strCharacterArr, 0)
dbeAttrCharacter->"top"->"flush"->dbeAttrAffects
dbeAttrCharacter->"left"->"flush"->dbeAttrDefNameList
dbeAttrCharacter->"right"->"unattached"
inactive (dbeAttrCharacter)
dbeAttrValidity = checkBox(dbBLCompare, "valid for: ", strValidityArr, 0)
dbeAttrValidity->"top"->"flush"->dbeAttrCharacter
dbeAttrValidity->"left"->"flush"->dbeAttrDefNameList
dbeAttrValidity->"right"->"unattached"
inactive (dbeAttrValidity)
dbeAttrProperty = checkBox(dbBLCompare, "Properties: ", strPropertyArr, 0)
dbeAttrProperty->"top"->"flush"->dbeAttrValidity
dbeAttrProperty->"left"->"flush"->dbeAttrDefNameList
dbeAttrProperty->"right"->"unattached"
inactive (dbeAttrProperty)
dbeAttrType = field (dbBLCompare, "Type: ", "", 27, true)
dbeAttrType->"top"->"spaced"->dbeAttrProperty
dbeAttrType->"left"->"flush"->dbeAttrDefNameList
dbeAttrType->"right"->"unattached"
dbeDefaultValue = field (dbBLCompare, "Default value:", "", 21, true)
dbeDefaultValue->"top"->"flush"->dbeAttrType
dbeDefaultValue->"left"->"flush"->dbeAttrDefNameList
dbeDefaultValue->"right"->"unattached"
dbeAttrTypeList = text(dbBLCompare, "Possible values or defined borders:", "", 215, 142, true)
dbeAttrTypeList->"top"->"spaced"->dbeDefaultValue
dbeAttrTypeList->"left"->"flush"->dbeAttrDefNameList
dbeAttrTypeList->"right"->"unattached"
//dbeAttrTypeList->"bottom"->"unattached"
dbeAttrTypeSelect = toggle(dbBLCompare,"show System and Hidden Attributes", true)
dbeAttrTypeSelect->"top"->"flush"->dbeAttrDefNameList
dbeAttrTypeSelect->"left"->"form"
dbeAttrTypeSelect->"right"->"unattached"
dbeAttrTypeSelect->"bottom"->"unattached"
separator (dbBLCompare)
dbeOutFileLabel = label(dbBLCompare, "Output to:")
dbeOutFileBrowse = fileName(dbBLCompare, "D:\\Project - DOORS Demonstration\\BL_Comp_Result.rtf", "*.rtf", "Rich Text files", false)
dbeChangeBarToggle = toggle(dbBLCompare,"Update ChangeBar attribute", false)
beside (dbBLCompare)
dbeShowDiffToggle = toggle(dbBLCompare,"Show Object Text changes as markup", true)
// dummy
DBE dbeDummy = label(dbBLCompare, "")
dbeDummy->"top"->"spaced"->dbeShowDiffToggle
dbeDummy->"left"->"form"
// Copyright
DBE dbeCopyRight = label(dbBLCompare, "Copyright (C) Your Company Your Town 2005-2007 Confidential. All rights reserved.")
dbeCopyRight->"bottom"->"spaced"->dbeDummy
dbeCopyRight->"left"->"form"
apply(dbBLCompare, "Compare Now", applyCompareFunction)
set(dbeAttrDefNameList, cbDoSetAttrTypes, doNothing, doNothing)
set(dbeAttrTypeSelect, cbSystemSelect)
realize (dbBLCompare, 0, 0) // we realize so that the lists can be populated using insert
insertColumn(dbeAttrDefNameList, 0, "Attribute Name", 200, iconNone)
for b in m do // fill up the baselines skip list with current baselines
{ string str = (major b) "." (minor b) (suffix b)
put(skBaselines, b, str)
insert(dbeListCurrent, 0, str)
insert(dbeListBaseline, 0, str)
}
insert(dbeListCurrent, 0, "current") // put current at head of lists
set(dbeListCurrent, 0)
insert(dbeListBaseline, 0, "current")
set(dbeListBaseline, 1)
showAttributes()
show dbBLCompare // off we go.......
// end of BaselineCompare.dxl

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;
}

Want several font colors in my cell

Im creating a new cell = new XRTableCell();
I'll show a sample of my code.
I always ending up with the same color in my cell, even if I change it in my "If".
foreach (TaskTime tt in di.TaskTimes[i])
{
.....
TimeToAdd = ClassLibrary.Utils.GetTimeStringFromSec(tt.FromSec % 86400) + " - " + ClassLibrary.Utils.GetTimeStringFromSec(tt.UntilSec % 86400) + " (" + tt.Abbreviation+ ")";
if (taskTimeTextBox.Text.Length > 0)
taskTimeTextBox.Text += "\n";
taskTimeTextBox.Text += TimeToAdd;
if (tt.OnOtherCostDivision)
{
taskTimeTextBox.SelectionStart = taskTimeTextBox.Find(TimeToAdd);
taskTimeTextBox.SelectionFont = new Font("Calibri", 9, FontStyle.Italic);
taskTimeTextBox.SelectionColor = Color.Gray;
}
else
{
taskTimeTextBox.SelectionStart = taskTimeTextBox.Find(TimeToAdd);
taskTimeTextBox.SelectionFont = new Font("Calibri", 9, FontStyle.Italic);
taskTimeTextBox.SelectionColor = Color.Blue;
}
richTxtObject.Rtf = taskTimeTextBox.Rtf;
cell.Controls.Add(richTxtObject);
}
row.Cells.Add(cell);
Soulved it! Didn't worked when I had it this way "taskTimeTextBox.Text += xxxxxxxx ". So I made a list of string, saved my text in if it were "onOtherCostDivision". Later on I hade to loop true my list and see if the final taskTimeTextBox.Text had the text in it and in that cause I added the color.
foreach (var task in taskCostDivList)
{
if (taskTimeTextBox.Text.Contains(task))
{
taskTimeTextBox.SelectionStart = taskTimeTextBox.Find(task);
taskTimeTextBox.Font = new Font("Calibri", 9, FontStyle.Italic);
taskTimeTextBox.SelectionColor = Color.Gray;
}
else
{
taskTimeTextBox.Font = new Font("Calibri", 9, FontStyle.Regular);
taskTimeTextBox.SelectionColor = Color.Black;
}
}

Resources