Skip to content
Browse files

table sorting, server side completions, stability

  • Loading branch information...
1 parent 2c55fea commit 3b9be01eba85b334d724cd7c8e6a7ac769e01f0f @voloko voloko committed
Showing with 2,968 additions and 3,359 deletions.
  1. +5 −11 ads/controller/app.js
  2. +4 −2 ads/controller/bulkImport.js
  3. +1 −1 ads/controller/delete.js
  4. +93 −120 ads/controller/download.js
  5. +0 −1 ads/controller/downloadBCT.js
  6. +0 −78 ads/controller/downloadCompletions.js
  7. +49 −0 ads/controller/migrateDB/removeCompletions.js
  8. +60 −0 ads/controller/migrateDB/textIDs.js
  9. +0 −129 ads/controller/migrateDb.js
  10. +14 −12 ads/controller/paste.js
  11. +1 −1 ads/controller/revert.js
  12. +9 −7 ads/controller/upload.js
  13. +13 −16 ads/db.js
  14. +56 −43 ads/job/adImporter.js
  15. +29 −15 ads/job/campImporter.js
  16. +7 −2 ads/job/downloadBCT.js
  17. +0 −126 ads/job/downloadCompletions.js
  18. +45 −28 ads/job/tabSeparatedParser.js
  19. +142 −0 ads/lib/completions.js
  20. +44 −0 ads/lib/countries.js
  21. +3 −9 ads/lib/demoLinkBuilder.js
  22. +25 −15 ads/lib/estimateTargetingStats.js
  23. +0 −53 ads/lib/imageReader.js
  24. +34 −0 ads/lib/locales.js
  25. +0 −213 ads/lib/md5.js
  26. +7 −1 ads/lib/model/tabSeparated.js
  27. +12 −8 ads/lib/prop/bidType.js
  28. +25 −17 ads/lib/prop/flatArray.js
  29. +5 −22 ads/lib/prop/idArray.js
  30. +1 −1 ads/lib/prop/timestamp.js
  31. +5 −19 ads/lib/prop/userAdClusters.js
  32. +1 −1 ads/lib/typeahead/DateDataSource.js
  33. +14 −11 ads/lib/typeahead/GraphAPIDataSource.js
  34. +1 −1 ads/lib/typeahead/TimeDataSource.js
  35. +4 −4 ads/model/account.js
  36. +5 −7 ads/model/ad.js
  37. +58 −44 ads/model/ad/props.js
  38. +5 −63 ads/model/ad/resultSet.js
  39. +5 −5 ads/model/adCreative.js
  40. +1 −1 ads/model/adStat.js
  41. +27 −14 ads/model/baseStat.js
  42. +1 −4 ads/model/bct.js
  43. +1 −1 ads/model/campStat.js
  44. +7 −6 ads/model/campaign.js
  45. +3 −60 ads/model/campaign/campResultSet.js
  46. +0 −592 ads/model/completion.js
  47. +0 −104 ads/model/completion/usRegions.js
  48. +3 −7 ads/model/connectedObject.js
  49. +3 −3 ads/model/image.js
  50. +120 −0 ads/model/resultSet.js
  51. +1 −1 ads/model/topline.js
  52. +0 −1 ads/models.js
  53. +27 −36 ads/view/adEditor/DSTargeting.js
  54. +64 −78 ads/view/adEditor/advDem.js
  55. +7 −8 ads/view/adEditor/connections.js
  56. +102 −132 ads/view/adEditor/eduWork.js
  57. +167 −203 ads/view/adEditor/locDem.js
  58. +1 −1 ads/view/adEditor/pricing.js
  59. +284 −344 ads/view/adPane.js
  60. +0 −4 ads/view/adPane/adPane.css
  61. +9 −9 ads/view/adPane/formatters.js
  62. +162 −0 ads/view/basePane.js
  63. +3 −3 ads/view/campEditor.js
  64. +166 −225 ads/view/campPane.js
  65. +4 −6 ads/view/campPane/formatters.js
  66. +17 −36 ads/{lib/userStorage.js → view/campPane/periodEditor.js}
  67. +15 −33 ads/{lib/deferredList.js → view/campPane/statusEditor.js}
  68. +1 −1 ads/view/contractEditor.js
  69. +5 −5 ads/view/contractPane.js
  70. +2 −2 ads/view/contractPane/formatters.js
  71. +5 −5 ads/view/control/dateRange.js
  72. +13 −7 ads/view/dataTable/list.js
  73. +54 −19 ads/view/downloadProgress.js
  74. +6 −32 ads/view/downloadProgress/downloadProgress.html
  75. +12 −12 ads/view/head.js
  76. +1 −1 ads/view/imageSelector.js
  77. +1 −1 ads/view/reachEstimate.js
  78. 0 {storage → }/lib/async.js
  79. +11 −6 {storage → }/lib/connect.js
  80. +47 −0 lib/cookieGetter.js
  81. +31 −24 {ads → }/lib/dateRange.js
  82. +72 −0 lib/deferredList.js
  83. +65 −0 lib/errorReport.js
  84. +24 −18 {ads → }/lib/formatters.js
  85. +178 −0 lib/graphlink.js
  86. +80 −0 lib/imageReader.js
  87. +3 −1 {storage → }/lib/pathUtils.js
  88. +25 −19 {ads → }/lib/searcher.js
  89. +1 −1 {storage → }/lib/urllib.js
  90. +69 −0 lib/userStorage.js
  91. +1 −1 {storage → }/lib/utils.js
  92. +0 −124 storage/lib/graphlink.js
  93. +30 −31 storage/storage.js
  94. +3 −25 uki-core/function.js
  95. +1 −1 uki-core/view/base.js
  96. +1 −1 uki-fb/view/dataList.js
  97. +2 −0 uki-fb/view/dataList/pack.js
  98. +156 −20 uki-fb/view/dataTable.js
  99. BIN uki-fb/view/dataTable/column-down.png
  100. BIN uki-fb/view/dataTable/column-up.png
  101. +53 −0 uki-fb/view/dataTable/compare.js
  102. +30 −0 uki-fb/view/dataTable/dataTable.css
  103. +1 −1 uki-fb/view/dataTable/header.html
  104. +3 −1 uki-fb/view/dataTable/pack.js
  105. +4 −0 uki-fb/view/searchInput.js
  106. +0 −2 uki-fb/view/typeahead.js
View
16 ads/controller/app.js
@@ -40,7 +40,7 @@ var dom = require("../../uki-core/dom"),
db = require("../db"),
models = require("../models"),
- UserStorage = require("../lib/userStorage").UserStorage,
+ UserStorage = require("../../lib/userStorage").UserStorage,
AccountResultWrapper =
require("../lib/accountResultWrapper").AccountResultWrapper,
@@ -115,8 +115,6 @@ function init(uid) {
}
function dbInitCallback() {
- require("./downloadCompletions")
- .DownloadCompletions.download(initList);
require("./downloadBCT")
.DownloadBCT.download(initList);
}
@@ -140,16 +138,12 @@ function initList(preserveVisState) {
models.Contract.prepare(function(contracts) {
models.Topline.prepare(function(toplines) {
models.Campaign.prepare(function(campaigns) {
- // same as ConnectedObject
- models.Completion.prepare(['locales'],
- function() {
- _buildOrRestoreCampaignList(
- preserveVisState,
- accounts, campaigns,
- contracts, toplines);
+ _buildOrRestoreCampaignList(
+ preserveVisState,
+ accounts, campaigns,
+ contracts, toplines);
- }, true);
}, true);
}, true);
}, true);
View
6 ads/controller/bulkImport.js
@@ -31,13 +31,13 @@ var utils = require("../../uki-core/utils"),
BulkImportDialog = require("../view/bulkImportDialog").BulkImportDialog,
LogDialog = require("../view/logDialog").LogDialog,
- imageReader = require("../lib/imageReader"),
+ imageReader = require("../../lib/imageReader"),
models = require("../models"),
ParserJob = require("../job/tabSeparatedParser").Parser,
CampImporterJob = require("../job/campImporter").Importer,
- DeferredList = require("../lib/deferredList").DeferredList,
+ DeferredList = require("../../lib/deferredList").DeferredList,
App = require("./app").App,
Mutator = require("./mutator").Mutator;
@@ -182,6 +182,8 @@ function initErrors() {
}
BulkImport.importInto = function(account, text, imageLookup) {
+ require("../lib/completions").dialog = BulkImport.progressDialog();
+
var parser = new ParserJob(account, text, imageLookup);
parser.oncomplete(function() {
View
2 ads/controller/delete.js
@@ -27,7 +27,7 @@ var view = require("../../uki-core/view"),
env = require("../../uki-core/env"),
evt = require("../../uki-core/event"),
- DeferredList = require("../lib/deferredList").DeferredList;
+ DeferredList = require("../../lib/deferredList").DeferredList;
View
213 ads/controller/download.js
@@ -24,29 +24,24 @@
var utils = require("../../uki-core/utils"),
fun = require("../../uki-core/function"),
- storage = require("../../storage/storage"),
- App = require("./app").App,
- storeUtils = require("../../storage/lib/utils"),
-
- asyncUtils = require("../../storage/lib/async"),
evt = require("../../uki-core/event"),
env = require("../../uki-core/env"),
+
+ storage = require("../../storage/storage"),
+ libUtils = require("../../lib/utils"),
+ asyncUtils = require("../../lib/async"),
+ graphlink = require("../../lib/graphlink"),
+
+ App = require("./app").App,
DownloadDialog = require("../view/downloadDialog").DownloadDialog,
- DownloadProgress = require("../view/downloadProgress").DownloadProgress;
+ DownloadProgress = require("../view/downloadProgress").DownloadProgress,
-var Account = require("../model/account").Account,
+ Account = require("../model/account").Account,
Ad = require("../model/ad").Ad,
AdCreative = require("../model/adCreative").AdCreative,
- AdGroup = require("../model/ad/group").Group,
Img = require("../model/image").Image,
- AdStat = require("../model/adStat").AdStat,
Campaign = require("../model/campaign").Campaign,
- CampGroup = require("../model/campaign/group").Group,
- CampStat = require("../model/campStat").CampStat,
ConnectedObject = require("../model/connectedObject").ConnectedObject,
- Group = require("../model/group").Group,
- Completion = require("../model/completion").Completion,
-
Contract = require("../model/contract").Contract,
Topline = require("../model/topline").Topline;
@@ -59,29 +54,10 @@ var META_REFRESH_TIMEOUT = 1000 * 60 * 60 * 24 * 7; // once per week
var Download = {};
-
// -------- DOWNLOAD DIALOG SETUP -----------
// Logic from old sync.js
// Moved flow logic to make syncing easier later
-/**
-* Helper to create DownloadProgress status
-*/
-function createProgressStatus() {
- return {
- accounts: 0,
- contracts: 0,
- toplines: 0,
- campaigns: 0,
- accounts_with_campaigns: 0,
- contracts_with_toplines: 0,
- ads: 0,
- adcreatives: 0,
- campaigns_with_ads: 0,
- objects: 0
- };
-}
-
Download.dialog = function() {
if (!this._dialog) {
this._dialog = new DownloadDialog();
@@ -113,18 +89,13 @@ Download.handle = function() {
};
Download._ondownload = function(e) {
- var progress = this._progress || (this._progress = new DownloadProgress());
+ var progress = new DownloadProgress();
progress.visible(true);
Download.loadModels(
- function(status) { progress.status(status); },
+ progress,
function() {
progress.visible(false);
- require("./downloadCompletions")
- .DownloadCompletions.download(function() {
- // force app update upon completion
- App.reload();
- });
require("./downloadBCT")
.DownloadBCT.download(function() {
// force app update upon completion
@@ -139,34 +110,47 @@ Download._ondownload = function(e) {
/**
* handler to start downloading including contract/topline
*
-* @param pc DownloadProgress callback
+* @param progress DownloadProgress instance
* @param callback called on finish
-* @param account_ids limit donwload to account_ids specified
+* @param account_ids array of account ids being dl'd
*/
-Download.loadModels = function(pc, callback, account_ids) {
- var status = createProgressStatus();
- pc = pc || fun.FT;
-
+Download.loadModels = function(progress, callback, account_ids) {
+ progress = progress || fun.FT;
callback = callback || fun.FT;
- pc(status);
+
+ progress.statusUpdate();
+ graphlink.progress = progress;
var state = null;
+
+ // If you manually enter accounts, they should be valid account numbers
+ // if you just ask to download your own accounts though, that should work
+ if (!!account_ids) {
+ // strip out invalid accounts
+ account_ids = account_ids.filter(function(acc_id) {
+ // nothing but numbers
+ return (/^\d+$/).test(acc_id + '');
+ });
+ if (!account_ids.length) {
+ callback();
+ return;
+ }
+ }
account_ids = account_ids || [];
- account_ids = account_ids.filter(function(acc_id) {
- // is true for numbers too apparently
- return (/\d+/).test(acc_id);
- });
// Special treatment since we need the account_ids for everything
// subsequently, and the download may be invoked without explicitly
// specifying ids
- loadAccounts(account_ids, null, status, pc, function(used_account_ids) {
- account_ids = storeUtils.wrapArray(used_account_ids).filter(
+ loadAccounts(account_ids, null, progress, function(used_account_ids) {
+
+ account_ids = libUtils.wrapArray(used_account_ids).filter(
function(acc_id) {
return !!acc_id;
}
);
+ // numerous checks for valid account_ids since things can go wrong
+ // server or client side
if (!account_ids || !account_ids.length) {
callback();
return;
@@ -178,7 +162,7 @@ Download.loadModels = function(pc, callback, account_ids) {
asyncUtils.forEach(loaders,
function(loader, i, iterCallback) {
- loader(account_ids, state, status, pc, function(returnedState) {
+ loader(account_ids, state, progress, function(returnedState) {
state = returnedState;
iterCallback();
});
@@ -187,7 +171,6 @@ Download.loadModels = function(pc, callback, account_ids) {
// all loading is done.
// dismiss the progress loader
// celebrate!
- // alert("We're all done loading here!");
callback();
},
this);
@@ -201,18 +184,18 @@ Download.loadModels = function(pc, callback, account_ids) {
* Download all accounts in one chunk. Clears related ads and
* campaigns in next loader
*
-* @param accounts array of campaigns to download from
-* @param status current download status for DownloadProgress
-* @param pc DownloadProgress callback
+* @param account_ids array of account ids being dl'd
+* @param state unused
+* @param progress DownloadProgress instance
* @param callback called on finish
*/
-function loadAccounts(account_ids, state, status, pc, callback) {
+function loadAccounts(account_ids, state, progress, callback) {
+ progress.setStep('accounts');
var allAccounts = [];
Account.loadFromIds(
account_ids,
function(accounts) {
- status.accounts = accounts.length;
- pc(status);
+ progress.completeStep('accounts');
allAccounts.push.apply(allAccounts, accounts);
callback(utils.pluck(allAccounts, 'id'));
}
@@ -223,7 +206,7 @@ function loadAccounts(account_ids, state, status, pc, callback) {
/**
* removing data associated with accounts being refreshed
*/
-function removeOldData(account_ids, state, status, pc, callback) {
+function removeOldData(account_ids, state, progress, callback) {
// clean up all previous contracts and toplines
Contract.findAllBy(
'id',
@@ -263,38 +246,36 @@ function removeOldData(account_ids, state, status, pc, callback) {
/**
* Download ConnectedObjects
*
-* @param accounts array of campaigns to download from
-* @param status current download status for DownloadProgress
-* @param pc DownloadProgress callback
+* @param account_ids array of account ids being dl'd
+* @param state unused
+* @param progress DownloadProgress instance
* @param callback called on finish
*/
-function loadObjects(account_ids, state, status, pc, callback) {
- status.objects = 1;
- pc(status);
- ConnectedObject.loadFromAccountIds(account_ids,
- function(cobjs) {
- // filter objects here?
- status.objects = 2;
- pc(status);
- ConnectedObject.prepare(function() {
- callback(null);
- }, true);
- });
+function loadObjects(account_ids, state, progress, callback) {
+ progress.setStep('objects');
+ ConnectedObject.loadFromAccountIds(account_ids,
+ function(cobjs) {
+ // filter objects here?
+ progress.completeStep('objects');
+ ConnectedObject.prepare(function() {
+ callback(null);
+ }, true);
+ });
}
/**
* load contracts
*/
-function loadContracts(account_ids, state, status, pc, callback) {
+function loadContracts(account_ids, state, progress, callback) {
+ progress.setStep('contracts');
var account_options = {};
if (account_ids) {
account_options.account_ids = account_ids;
}
Contract.loadFromRESTAPI(account_options, function(contracts) {
- status.contracts = contracts.length;
- pc(status);
+ progress.completeStep('contracts');
contracts.prefetch && contracts.prefetch();
callback(contracts);
});
@@ -307,16 +288,17 @@ function loadContracts(account_ids, state, status, pc, callback) {
* Download all toplines in contracts[0], then
* in contracts[1], etc
*
+* @param account_ids array of account ids being dl'd
* @param contracts array of contracts to download from
-* @param status current download status for DownloadProgress
-* @param pc DownloadProgress callback
+* @param progress DownloadProgress instance
* @param callback called on finish with all topliness downloaded
* as a parameter
*/
-function loadToplines(acc_ids, contracts, status, pc, callback,
- totalToplines) {
+function loadToplines(acc_ids, contracts, progress, callback,
+ _totalToplines) {
- totalToplines = totalToplines || [];
+ progress.setStep('toplines');
+ _totalToplines = _totalToplines || [];
if (!contracts.length) {
Topline.prepare(function(toplines) {
@@ -328,13 +310,11 @@ function loadToplines(acc_ids, contracts, status, pc, callback,
Topline.loadFromRESTAPI(
{ account_id: contracts[0].id() },
function(toplines) {
- status.contracts_with_toplines++;
- status.toplines += toplines.length;
- pc(status);
- totalToplines = totalToplines.concat(toplines);
+ progress.completeStep('toplines');
+ _totalToplines = _totalToplines.concat(toplines);
loadToplines(acc_ids, contracts.slice(1),
- status, pc, callback, totalToplines);
+ progress, callback, _totalToplines);
}
);
}
@@ -345,27 +325,24 @@ function loadToplines(acc_ids, contracts, status, pc, callback,
* Download all campaigns in accounts[0], then
* in accounts[1], etc
*
-* @param accounts array of accounts to download from
-* @param status current download status for DownloadProgress
-* @param pc DownloadProgress callback
+* @param account_ids array of account ids to download from
+* @param state unused
+* @param progress DownloadProgress instance
* @param callback called on finish with all campaigns downloaded
* as a parameter
*/
-function loadCampaigns(account_ids, state, status, pc, callback) {
+function loadCampaigns(account_ids, state, progress, callback) {
+ progress.setStep('campaigns');
var totalCampaigns = [];
Campaign.loadFromAccountIds(
account_ids,
function(campaigns) {
- status.campaigns += campaigns.length;
- pc(status);
+ progress.completeStep('campaigns');
totalCampaigns = totalCampaigns.concat(campaigns);
- status.campaigns_isdone = true;
- pc(status);
-
Campaign.prepare(function(campaigns) {
totalCampaigns.prefetch && totalcampaigns.prefetch();
callback(totalCampaigns);
@@ -379,25 +356,22 @@ function loadCampaigns(account_ids, state, status, pc, callback) {
* Downloads ads from up to 10 campaigns from a single account
* in on chunk
*
-* @param campaigns array of campaigns to download from
-* @param status current download status for DownloadProgress
-* @param pc DownloadProgress callback
+* @param account_ids array of account ids to dl from
+* @param state unused
+* @param progress DownloadProgress instance
* @param callback called on finish
*/
-function loadAds(account_ids, state, status, pc, callback) {
+function loadAds(account_ids, state, progress, callback) {
+ progress.setStep('ads');
var totalAds = [];
Ad.loadFromAccountIds(account_ids,
function(ads, isDone) {
- status.ads += ads.length;
- pc(status);
+ progress.completeStep('ads');
totalAds.push.apply(totalAds, ads);
- status.ads_isdone = true;
- pc(status);
-
totalAds.prefetch && totalAds.prefetch();
callback(totalAds);
}
@@ -408,8 +382,9 @@ function loadAds(account_ids, state, status, pc, callback) {
/**
* load ad creatives
*/
-function loadAdCreatives(account_ids, ads, status, pc, callback) {
+function loadAdCreatives(account_ids, ads, progress, callback) {
+ progress.setStep('adcreatives');
if (!ads.length) {
callback([]);
return;
@@ -417,7 +392,7 @@ function loadAdCreatives(account_ids, ads, status, pc, callback) {
var adMapByCreative = {};
var creative_ids = [];
- storeUtils.wrapArray(ads).map(function(ad) {
+ libUtils.wrapArray(ads).map(function(ad) {
var creativeId = (ad.creative_ids() || [])[0];
if (creativeId) {
adMapByCreative[creativeId] = adMapByCreative[creativeId] || [];
@@ -431,15 +406,11 @@ function loadAdCreatives(account_ids, ads, status, pc, callback) {
// load creatives all together
AdCreative.loadFromIds(creative_ids,
function(data) {
-
- status.adcreatives += data.length;
- pc(status);
-
- storeUtils.wrapArray(data).map(function(creative) {
+ libUtils.wrapArray(data).map(function(creative) {
var creativeId = String(creative.creative_id);
if (adMapByCreative[creativeId]) {
delete creative.name;
- storeUtils.wrapArray(adMapByCreative[creativeId]).map(function(ad) {
+ libUtils.wrapArray(adMapByCreative[creativeId]).map(function(ad) {
ad
.muteChanges(true)
.fromRemoteObject(creative)
@@ -448,8 +419,7 @@ function loadAdCreatives(account_ids, ads, status, pc, callback) {
}
});
- status.adcreatives_isdone = true;
- pc(status);
+ progress.completeStep('adcreatives');
// commit the ad changes back to db
storage.Storage.storeMulti.call(Ad, ads, function(data) {
@@ -464,7 +434,8 @@ function loadAdCreatives(account_ids, ads, status, pc, callback) {
* Ultimately load adimages distinctly
* for now, update images / hashes in ads from the creatives
*/
-function loadAdImages(account_ids, ads, status, pc, callback) {
+function loadAdImages(account_ids, ads, progress, callback) {
+ progress.setStep('adimages');
// when you're done loading ads
// populate image lookup table with newly downloaded ads
if (!ads.length) {
@@ -474,7 +445,9 @@ function loadAdImages(account_ids, ads, status, pc, callback) {
Img.updateImagesInAllAccounts(account_ids, ads, function() {
// commit the ad changes back to db
storage.Storage.storeMulti.call(Ad, ads, function(data) {
- callback(null);
+ progress.statusUpdate(ads.length);
+ progress.completeStep('adimages');
+ callback(null);
});
});
}
View
1 ads/controller/downloadBCT.js
@@ -31,7 +31,6 @@ var DownloadBCT = {
download: function(callback) {
var job = new Job();
var dialog = DownloadBCT.dialog();
-
job
.onprogress(function(e) {
var status = e.status;
View
78 ads/controller/downloadCompletions.js
@@ -1,78 +0,0 @@
-/**
-* Copyright 2011 Facebook, Inc.
-*
-* You are hereby granted a non-exclusive, worldwide, royalty-free license to
-* use, copy, modify, and distribute this software in source code or binary
-* form for use in connection with the web services and APIs provided by
-* Facebook.
-*
-* As with any software that integrates with the Facebook platform, your use
-* of this software is subject to the Facebook Developer Principles and
-* Policies [http://developers.facebook.com/policy/]. This copyright notice
-* shall be included in all copies or substantial portions of the software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-* DEALINGS IN THE SOFTWARE.
-*
-*
-*/
-
-var fun = require("../../uki-core/function");
-var build = require("../../uki-core/builder").build;
-var Job = require("../job/downloadCompletions").DownloadCompletions;
-var Mustache = require("../../uki-core/mustache").Mustache;
-
-var MSG = 'Downloading {{category}} ({{completed}} out of {{total}})';
-
-var DownloadCompletions = {
- download: function(callback) {
- var job = new Job();
- var dialog = DownloadCompletions.dialog();
-
- job
- .onprogress(function(e) {
- var status = e.status;
- if (!status.cached) {
- dialog.visible(true);
- dialog.progress.html(Mustache.to_html(MSG, status));
- }
- })
- .oncomplete(function() {
- dialog.visible(false);
- callback();
- })
- .start();
- },
-
- clearLastSync: function() {
- new Job().clearLastSync();
- },
-
- dialog: function() {
- if (!this._dialog) {
- var col = build({ view: 'Dialog', modal: true, childViews: [
- { view: 'DialogHeader', text: "Downloading Completions" },
- { view: 'DialogContent', childViews: [
- { view: 'DialogBody', childViews: [
- { view: 'Text',
- text: 'Your completions (countries, cities, workplaces, etc) ' +
- 'are out of date. Updating. ' +
- 'This may take several minutes on a slow connection.' },
- { view: 'Text', as: 'progress' }
- ] }
- ] }
- ]});
- this._dialog = col[0];
- this._dialog.progress = col.view('progress');
- }
- return this._dialog;
- }
-};
-
-
-exports.DownloadCompletions = DownloadCompletions;
View
49 ads/controller/migrateDB/removeCompletions.js
@@ -0,0 +1,49 @@
+/**
+* Copyright 2011 Facebook, Inc.
+*
+* You are hereby granted a non-exclusive, worldwide, royalty-free license to
+* use, copy, modify, and distribute this software in source code or binary
+* form for use in connection with the web services and APIs provided by
+* Facebook.
+*
+* As with any software that integrates with the Facebook platform, your use
+* of this software is subject to the Facebook Developer Principles and
+* Policies [http://developers.facebook.com/policy/]. This copyright notice
+* shall be included in all copies or substantial portions of the software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*
+*
+*/
+
+var fun = require("../../../uki-core/function");
+var utils = require("../../../uki-core/utils");
+var storage = require("../../../storage/storage");
+
+var Completion = storage.newStorage({}).tableName('completion');
+
+function migrate(uid, newDb, callback) {
+ delete localStorage[uid + ':model:completion:2'];
+ Completion.db(newDb);
+ newDb.transaction(function(tx) {
+ Completion.withTransaction(tx, function() { this.dbDrop(); });
+ }, storage.errorCallback, callback);
+}
+
+function migrateIndexDB(uid, newDb) {
+ delete localStorage[uid + ':model:completion:2'];
+ if (utils.toArray(newDb.objectStoreNames)
+ .indexOf(Completion.objectStoreName()) > -1) {
+ Completion.db(newDb);
+ Completion.dbDrop();
+ }
+}
+
+exports.migrate = migrate;
+exports.migrateIndexDB = migrateIndexDB;
View
60 ads/controller/migrateDB/textIDs.js
@@ -0,0 +1,60 @@
+/**
+* Copyright 2011 Facebook, Inc.
+*
+* You are hereby granted a non-exclusive, worldwide, royalty-free license to
+* use, copy, modify, and distribute this software in source code or binary
+* form for use in connection with the web services and APIs provided by
+* Facebook.
+*
+* As with any software that integrates with the Facebook platform, your use
+* of this software is subject to the Facebook Developer Principles and
+* Policies [http://developers.facebook.com/policy/]. This copyright notice
+* shall be included in all copies or substantial portions of the software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*
+*
+*/
+
+function migrate(uid, DB, callback) {
+ DB.transaction(function(tx) {
+ tx.executeSql('SELECT id FROM account LIMIT 1', [],
+ function(tx, r) {
+ if (r.rows && r.rows.length && (typeof r.rows.item(0).id == 'number')) {
+ var Account = require("../../model/account").Account;
+
+ // findAll will try to fetch id from an indexed column,
+ // and it is broken. So load data manually
+ tx.executeSql('SELECT data FROM account LIMIT 1', [],
+ function(tx, r) {
+ var accounts = [];
+ for (var i = 0; i < r.rows.length; i++) {
+ var obj = JSON.parse(r.rows.item(i).data);
+ accounts.push(new Account().fromDBObject(obj).id(obj.id));
+ }
+
+ // recreate table
+ Account.dbDrop(function() {
+ // there's no easy way to get a callback from dbInit
+ // so create a separate transation and use it's callback instead
+ DB.transaction(function(tx) {
+ Account.withTransaction(tx, function() { this.dbInit(); });
+ }, storage.errorCallback, function() {
+ Account.storeMulti(accounts, callback || fun.FT);
+ });
+ });
+ });
+ } else {
+ callback();
+ }
+ });
+ });
+}
+
+exports.migrate = migrate;
View
129 ads/controller/migrateDb.js
@@ -1,129 +0,0 @@
-/**
-* Copyright 2011 Facebook, Inc.
-*
-* You are hereby granted a non-exclusive, worldwide, royalty-free license to
-* use, copy, modify, and distribute this software in source code or binary
-* form for use in connection with the web services and APIs provided by
-* Facebook.
-*
-* As with any software that integrates with the Facebook platform, your use
-* of this software is subject to the Facebook Developer Principles and
-* Policies [http://developers.facebook.com/policy/]. This copyright notice
-* shall be included in all copies or substantial portions of the software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-* DEALINGS IN THE SOFTWARE.
-*
-*
-*/
-
-var fun = require("../../uki-core/function");
-var utils = require("../../uki-core/utils");
-var storage = require("../../storage/storage");
-
-
-function migrateToUIDStorage(uid, newDb, callback) {
- var models = require("../models");
- var tables = [];
- utils.forEach(models, function(m, key) {
- m.tableName && tables.push(m.tableName());
- });
- var oldDb = global.openDatabase(
- 'bamboo',
- "",
- 'bamboo main storage',
- 100 * 1000 * 1000);
-
- function copyTable(table, index, createCallback) {
- oldDb.readTransaction(function(tx) {
-
- tx.executeSql(
- 'SELECT * FROM ' + table,
- [],
- function(tx, rows) {
- rows = rows.rows;
- newDb.transaction(function(tx) {
-
- for (var i = 0; i < rows.length; i++) {
- var row = rows.item(i);
- var values = [];
- var placeholders = [];
- var names = [];
- utils.forEach(row, function(value, key) {
- placeholders.push('?');
- values.push(value);
- names.push(key);
- });
- tx.executeSql(
- 'INSERT INTO ' + table + ' (' + names.join(',') + ')' +
- ' VALUES (' + placeholders.join(',') + ')', values);
- }
-
- }, createCallback, createCallback); // newDb.tx
- }); // oldDb.tx.executeSql
- }); // oldDb.tx
- }
-
- oldDb.readTransaction(function(tx) {
- tx.executeSql('SELECT * FROM ad LIMIT 1',
- [], function(tx, result) {
- if (result && result.rows && result.rows.length) {
- // database exists, go on change everything
- require("../../storage/lib/async").forEach(tables, copyTable, function() {
- callback();
- oldDb.transaction(function(tx) {
- utils.forEach(tables, function(table) {
- tx.executeSql('DROP TABLE ' + table);
- });
- callback();
- });
- });
- } else {
- callback();
- }
- }, function() { callback(); });
- });
-}
-
-function migrateToTEXTIds(uid, DB, callback) {
- DB.transaction(function(tx) {
- tx.executeSql('SELECT id FROM account LIMIT 1', [],
- function(tx, r) {
- if (r.rows && r.rows.length && (typeof r.rows.item(0).id == 'number')) {
- var Account = require("../model/account").Account;
-
- // findAll will try to fetch id from an indexed column,
- // and it is broken. So load data manually
- tx.executeSql('SELECT data FROM account LIMIT 1', [],
- function(tx, r) {
- var accounts = [];
- for (var i = 0; i < r.rows.length; i++) {
- var obj = JSON.parse(r.rows.item(i).data);
- accounts.push(new Account().fromDBObject(obj).id(obj.id));
- }
-
- // recreate table
- Account.dbDrop(function() {
- // there's no easy way to get a callback from dbInit
- // so create a separate transation and use it's callback instead
- DB.transaction(function(tx) {
- Account.withTransaction(tx, function() { this.dbInit(); });
- }, storage.errorCallback, function() {
- Account.storeMulti(accounts, callback || fun.FT);
- });
- });
- });
- } else {
- callback();
- }
- });
- });
-}
-
-exports.migrateToUIDStorage = migrateToUIDStorage;
-exports.migrateToTEXTIds = migrateToTEXTIds;
View
26 ads/controller/paste.js
@@ -65,7 +65,7 @@ Paste.init = function() {
// FF only allows paste event on editable elements,
// something that we cannot do here. So manually check
// keydown event
- if (!env.ua.match(/Gecko/)) {
+ if (!env.ua.match(/Gecko\/\d+/)) {
return;
}
evt.on(env.doc.body, 'keydown', function(e) {
@@ -114,18 +114,19 @@ function copyDummy() {
* @param text the text the user pasted
*/
Paste.handler = function(v, text) {
- // Windows tends to replace \n -> \r\n during copy
- text = text.replace(/(\r\n|\r|\n)/g, '\n').replace(/\r/g, '\n');
+ // Windows tends to replace \n -> \r\n during copy
+ text = text.replace(/(\r\n|\r|\n)/g, '\n').replace(/\r/g, '\n');
- var row = view.byId('campaignList-list').selectedRow();
- var account = row.account ? row.account() : row;
+ var row = view.byId('campaignList-list').selectedRow();
+ var account = row.account ? row.account() : row;
- Paste.resetDialog();
- if (v.copySourceId && v.copySourceId() === 'campaigns') {
- Paste.pasteIntoCamps(account, text);
- } else {
- Paste.pasteIntoAds(account, text, view.byId('content').campaigns());
- }
+ Paste.resetDialog();
+ require("../lib/completions").dialog = Paste.dialog();
+ if (v.copySourceId && v.copySourceId() === 'campaigns') {
+ Paste.pasteIntoCamps(account, text);
+ } else {
+ Paste.pasteIntoAds(account, text, view.byId('content').campaigns());
+ }
};
// Error logging
@@ -134,7 +135,8 @@ Paste.handler = function(v, text) {
// for that error. Dialog will grow with more errors being added.
// User can close dialog with 'Close' button
Paste.dialog = function() {
- return this._dialog || (this._dialog = new LogDialog().title('Paste Errors'));
+ return this._dialog ||
+ (this._dialog = new LogDialog().title('Paste Progress'));
};
Paste.resetDialog = function() {
View
2 ads/controller/revert.js
@@ -26,7 +26,7 @@ var view = require("../../uki-core/view"),
utils = require("../../uki-core/utils"),
App = require("./app").App,
- DeferredList = require("../lib/deferredList").DeferredList;
+ DeferredList = require("../../lib/deferredList").DeferredList;
/**
View
16 ads/controller/upload.js
@@ -27,10 +27,10 @@ var view = require("../../uki-core/view"),
fun = require("../../uki-core/function"),
build = require("../../uki-core/builder").build,
- asyncUtils = require("../../storage/lib/async"),
- FBConnection = require("../../storage/lib/connect").FBConnection,
- graphlink = require("../../storage/lib/graphlink"),
- pathUtils = require("../../storage/lib/pathUtils"),
+ asyncUtils = require("../../lib/async"),
+ FBConnection = require("../../lib/connect").FBConnection,
+ graphlink = require("../../lib/graphlink"),
+ pathUtils = require("../../lib/pathUtils"),
App = require("./app").App,
UploadDialog = require("../view/uploadDialog").UploadDialog,
@@ -186,6 +186,7 @@ function uploadCampsResponse(camp, camps, callback, result) {
camp.remove(function() {
graphlink.fetchObject(
'/' + (result.id || camp.id()),
+ {},
function(reloadedCampData) {
if (!reloadedCampData) {
@@ -253,7 +254,8 @@ function uploadImages(ads, callback) {
return;
}
// update remote image on the server
- FB.api('/act_' + image.account_id() + '/adimages',
+ FB.api(
+ pathUtils.join('/act_' + image.account_id(), '/adimages'),
'POST', {
bytes: image.url().split(',')[1]
}, function(result) {
@@ -366,7 +368,7 @@ function uploadAdsResponse(ad, ads, originalAds, result) {
// reload the ad from server after update/create
var path = '/' + (result.id || ad.id());
- graphlink.fetchObject(path, function(reloadedAdData) {
+ graphlink.fetchObject(path, {}, function(reloadedAdData) {
if (!reloadedAdData) {
if (ad.adgroup_status() === 3) {
@@ -382,7 +384,7 @@ function uploadAdsResponse(ad, ads, originalAds, result) {
var reloadedAd = models.Ad.createFromRemote(reloadedAdData);
var path = '/' + reloadedAd.creative_ids()[0];
- graphlink.fetchObject(path, function(creative) {
+ graphlink.fetchObject(path, {}, function(creative) {
delete creative.name;
ad.remove(function() {
reloadedAd.muteChanges(true);
View
29 ads/db.js
@@ -29,7 +29,7 @@ var fun = require("../uki-core/function"),
storage = require("../storage/storage");
-var DB_VERSION = '1.0';
+var IDB_VERSION = '1.1';
var DB;
var init, drop;
@@ -44,14 +44,14 @@ if (storage.impl === 'IndexedDB') {
var models = require("./models");
[models.Account, models.Ad, models.Image, models.AdStat,
- models.Campaign, models.CampStat, models.Completion, models.Contract,
+ models.Campaign, models.CampStat, models.Contract,
models.Topline, models.ConnectedObject,
models.BCT].forEach(function(m) {
m.db(DB);
});
- if (DB.version != DB_VERSION) {
- DB.setVersion(DB_VERSION).onsuccess = function() {
+ if (DB.version != IDB_VERSION) {
+ DB.setVersion(IDB_VERSION).onsuccess = function() {
req.result.oncomplete = callback;
models.Account.dbInit();
models.Ad.dbInit();
@@ -59,11 +59,12 @@ if (storage.impl === 'IndexedDB') {
models.AdStat.dbInit();
models.Campaign.dbInit();
models.CampStat.dbInit();
- models.Completion.dbInit();
models.Contract.dbInit();
models.Topline.dbInit();
models.ConnectedObject.dbInit();
models.BCT.dbInit();
+ require("./controller/migrateDB/removeCompletions")
+ .migrateIndexDB(uid, DB);
};
} else {
callback();
@@ -87,7 +88,6 @@ if (storage.impl === 'IndexedDB') {
models.Contract.dbDrop();
models.Topline.dbDrop();
if (!soft) {
- models.Completion.dbDrop();
models.BCT.dbDrop();
}
};
@@ -101,13 +101,13 @@ if (storage.impl === 'IndexedDB') {
DB = global.openDatabase(
uid + '_powereditor',
// 'bamboo',
- DB_VERSION,
+ '1.0',
'PowerEditor main storage',
100 * 1000 * 1000);
var models = require("./models");
[models.Account, models.Ad, models.Image, models.AdStat,
- models.Campaign, models.CampStat, models.Completion, models.Contract,
+ models.Campaign, models.CampStat, models.Contract,
models.Topline, models.ConnectedObject,
models.BCT].forEach(function(m) {
m.db(DB);
@@ -121,18 +121,16 @@ if (storage.impl === 'IndexedDB') {
models.AdStat.withTransaction(tx, function() { this.dbInit(); });
models.Campaign.withTransaction(tx, function() { this.dbInit(); });
models.CampStat.withTransaction(tx, function() { this.dbInit(); });
- models.Completion.withTransaction(tx, function() { this.dbInit(); });
models.ConnectedObject.withTransaction(tx, function() { this.dbInit(); });
models.Contract.withTransaction(tx, function() { this.dbInit(); });
models.Topline.withTransaction(tx, function() { this.dbInit(); });
models.BCT.withTransaction(tx, function() { this.dbInit(); });
}, storage.errorCallback, function() {
- // callback();
- var migrate = require("./controller/migrateDb");
-
- migrate.migrateToUIDStorage(uid, DB, function() {
- migrate.migrateToTEXTIds(uid, DB, callback || fun.FT);
- });
+ require("./controller/migrateDB/textIDs")
+ .migrate(uid, DB, function() {
+ require("./controller/migrateDB/removeCompletions")
+ .migrate(uid, DB, callback || fun.FT);
+ });
});
};
@@ -150,7 +148,6 @@ if (storage.impl === 'IndexedDB') {
models.Contract.withTransaction(tx, function() { this.dbDrop(); });
models.Topline.withTransaction(tx, function() { this.dbDrop(); });
if (!soft) {
- models.Completion.withTransaction(tx, function() { this.dbDrop(); });
models.BCT.withTransaction(tx, function() { this.dbDrop(); });
}
}, storage.errorCallback, callback || fun.FT);
View
99 ads/job/adImporter.js
@@ -105,7 +105,7 @@ var Importer = fun.newClass(Job, {
.adMapById(mapById)
.adMapByName(mapByName);
- require("../../storage/lib/async").forEach(
+ require("../../lib/async").forEach(
this.ads(),
this._routeAd,
this._complete,
@@ -196,6 +196,8 @@ var Importer = fun.newClass(Job, {
* better readability and consistence with camps.
*/
_routeAd: function(newAd, index, callback) {
+ try {
+
var campMapById = this.campMapById();
var adMapById = this.adMapById();
var adMapByName = this.adMapByName();
@@ -260,6 +262,9 @@ var Importer = fun.newClass(Job, {
}));
callback();
}
+ } catch (e) {
+ require("../../lib/errorReport").handleException(e, 'ai:route');
+ }
},
_failAd: function(error) {
@@ -268,59 +273,67 @@ var Importer = fun.newClass(Job, {
},
_updateAd: function(existingAd, newAd, callback) {
- this.results().push({ action: 'update', id: existingAd.id() });
+ try {
+ this.results().push({ action: 'update', id: existingAd.id() });
- // remove camp from the name map in case we update the name
- delete this.adMapByName()[nameKey(existingAd)];
+ // remove camp from the name map in case we update the name
+ delete this.adMapByName()[nameKey(existingAd)];
- var updated = false;
- this.propsToCopy().forEach(function(name) {
- if (['id', 'account_id', 'campaign_id'].indexOf(name) !== -1) {
- return;
- }
- updated = true;
- existingAd[name](newAd[name]());
- });
+ var updated = false;
+ this.propsToCopy().forEach(function(name) {
+ if (['id', 'account_id', 'campaign_id'].indexOf(name) !== -1) {
+ return;
+ }
+ updated = true;
+ existingAd[name](newAd[name]());
+ });
- this.adMapByName()[nameKey(existingAd)] = existingAd;
+ this.adMapByName()[nameKey(existingAd)] = existingAd;
- if (updated) {
- existingAd
- .resetCampaign()
- .validateAll()
- .store(callback);
- } else {
- callback();
+ if (updated) {
+ existingAd
+ .resetCampaign()
+ .validateAll()
+ .store(callback);
+ } else {
+ callback();
+ }
+ } catch (e) {
+ require("../../lib/errorReport").handleException(e, 'ai:update');
}
},
_createAd: function(newAd, camp, callback) {
- // enforce parents
- newAd
- .muteChanges(true)
- .id(- new Date() - (env.guid++))
- .account_id(this.account().id())
- .campaign_id(camp.id());
-
- if (!this.useNameMatching()) {
- newAd.name(uniqName(
- newAd.name(),
- this.adMapByName(),
- function(name) {
- return newAd.campaign_id() + '_' + name.toLowerCase();
- }
- ));
- }
- this.adMapByName()[nameKey(newAd)] = newAd;
+ try {
+ // enforce parents
+ newAd
+ .muteChanges(true)
+ .id(- new Date() - (env.guid++))
+ .account_id(this.account().id())
+ .campaign_id(camp.id());
+
+ if (!this.useNameMatching()) {
+ newAd.name(uniqName(
+ newAd.name(),
+ this.adMapByName(),
+ function(name) {
+ return newAd.campaign_id() + '_' + name.toLowerCase();
+ }
+ ));
+ }
+ this.adMapByName()[nameKey(newAd)] = newAd;
- newAd.muteChanges(false);
- newAd.validateAll();
- newAd.updateCampaign();
+ newAd.muteChanges(false);
+ newAd.validateAll();
+ newAd.updateCampaign();
- this.adMapById()[newAd.id()] = newAd;
- this.results().push({ action: 'create' });
+ this.adMapById()[newAd.id()] = newAd;
+ this.results().push({ action: 'create' });
- newAd.store(callback);
+ newAd.store(callback);
+ } catch (e) {
+ require("../../lib/errorReport").handleException(e, 'ai:create');
+ }
}
});
View
44 ads/job/campImporter.js
@@ -87,7 +87,7 @@ var Importer = fun.newClass(Job, {
.mapById(mapById)
.mapByName(mapByName);
- require("../../storage/lib/async").forEach(
+ require("../../lib/async").forEach(
this.camps(),
this._routeCamp,
this._importAds,
@@ -150,6 +150,7 @@ var Importer = fun.newClass(Job, {
* better readability and consistence with ads.
*/
_routeCamp: function(newCamp, index, callback) {
+ try {
var mapById = this.mapById();
var mapByName = this.mapByName();
var name = (newCamp.name() || '').toLowerCase();
@@ -172,6 +173,9 @@ var Importer = fun.newClass(Job, {
this._createCamp(newCamp, callback);
}
+ } catch (e) {
+ require("../../lib/errorReport").handleException(e, 'ci:route');
+ }
},
_failCamp: function(error) {
@@ -181,28 +185,34 @@ var Importer = fun.newClass(Job, {
},
_updateCamp: function(existingCamp, newCamp, callback) {
- this.results().push({ action: 'update', id: existingCamp.id() });
+ try {
+ this.results().push({ action: 'update', id: existingCamp.id() });
- var updated = false;
- // remove camp from the name map in case we update the name
- delete this.mapByName()[existingCamp.name().toLowerCase()];
+ var updated = false;
+ // remove camp from the name map in case we update the name
+ delete this.mapByName()[existingCamp.name().toLowerCase()];
- this.propsToCopy().forEach(function(found) {
- if (['id', 'account_id'].indexOf(found) !== -1) { return; }
- updated = true;
- existingCamp[found](newCamp[found]());
- });
+ this.propsToCopy().forEach(function(found) {
+ if (['id', 'account_id'].indexOf(found) !== -1) { return; }
+ updated = true;
+ existingCamp[found](newCamp[found]());
+ });
- this.mapByName()[existingCamp.name().toLowerCase()] = existingCamp;
+ this.mapByName()[existingCamp.name().toLowerCase()] = existingCamp;
- if (updated) {
- existingCamp.store(callback);
- } else {
- callback();
+ if (updated) {
+ existingCamp.store(callback);
+ } else {
+ callback();
+ }
+ } catch (e) {
+ require("../../lib/errorReport").handleException(e, 'ci:update');
}
},
_createCamp: function(newCamp, callback) {
+ try {
+
// enforce parent
newCamp
.id(- new Date() - (env.guid++))
@@ -226,6 +236,10 @@ var Importer = fun.newClass(Job, {
newCamp.validateAll();
newCamp.store(callback);
+
+ } catch (e) {
+ require("../../lib/errorReport").handleException(e, 'ci:create');
+ }
}
});
View
9 ads/job/downloadBCT.js
@@ -29,7 +29,7 @@ var BCT = require("../model/bct").BCT;
var Job = require("./base").Job;
var REFRESH_TIMEOUT = 86400 * 7; // once per week
-var CACHE_KEY = 'model:bct:0';
+var CACHE_KEY = 'model:bct:1';
var DownloadBCT = fun.newClass(Job, {
userStorage: fun.newProp('userStorage'),
@@ -56,15 +56,20 @@ var DownloadBCT = fun.newClass(Job, {
if (accounts.length > 0) {
var first_account = accounts[0];
var account_id = first_account.id();
- var user_id = first_account.user_perm_map()[0].uid;
+ var user_id = require("../controller/app").App.userStorage().uid;
BCT.loadFromRESTAPI(
{ account_id: account_id, user_id: user_id},
fun.bind(function() {
stored = { time: t };
this.userStorage().setItem(CACHE_KEY, stored);
+ this._complete();
}, this));
+ } else {
+ this._complete();
}
}, this));
+ } else {
+ this._complete();
}
}
});
View
126 ads/job/downloadCompletions.js
@@ -1,126 +0,0 @@
-/**
-* Copyright 2011 Facebook, Inc.
-*
-* You are hereby granted a non-exclusive, worldwide, royalty-free license to
-* use, copy, modify, and distribute this software in source code or binary
-* form for use in connection with the web services and APIs provided by
-* Facebook.
-*
-* As with any software that integrates with the Facebook platform, your use
-* of this software is subject to the Facebook Developer Principles and
-* Policies [http://developers.facebook.com/policy/]. This copyright notice
-* shall be included in all copies or substantial portions of the software.
-*
-* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-* DEALINGS IN THE SOFTWARE.
-*
-*
-*/
-
-var fun = require("../../uki-core/function");
-var utils = require("../../uki-core/utils");
-var Account = require("../model/account").Account;
-var Completion = require("../model/completion").Completion;
-var Job = require("./base").Job;
-
-var REFRESH_TIMEOUT = 1000 * 60 * 60 * 24 * 7; // once per week
-var CACHE_KEY = 'model:completion:2';
-
-var DownloadCompletions = fun.newClass(Job, {
- userStorage: fun.newProp('userStorage'),
-
- categories: fun.newProp('categories'),
-
- init: function() {
- Job.prototype.init.call(this);
-
- var userStorage = require("../controller/app").App.userStorage();
- this.userStorage(userStorage);
- },
-
- clearLastSync: function() {
- require("../controller/app").App.userStorage().deleteItem(CACHE_KEY);
- },
-
- start: function() {
- this._findCorporateAccount(fun.bind(this._startDownloading, this));
- },
-
- _findCorporateAccount: function(callback) {
- require("../model/account").Account.findAll(fun.bind(function(accounts) {
- accounts.prefetch();
- var selectedAccount = accounts[0];
- accounts.forEach(function(acc) {
- if (acc.isCorporate()) { selectedAccount = acc; }
- });
- // only process when we have found at least an account.
- if (selectedAccount) {
- callback(selectedAccount);
- } else {
- this._complete();
- }
- }, this));
- },
-
- _startDownloading: function(selectedAccount) {
- var categories = Completion.categories(selectedAccount);
- this.categories(categories);
- this._progress({
- category: '',
- total: this.categories().length,
- completed: 0
- });
- this._downloadCategory(
- selectedAccount,
- categories,
- fun.bind(this._complete, this));
- },
-
- _downloadCategory: function(selectedAccount, categories, callback) {
- var category = categories[0];
- if (!category) {
- callback();
- } else {
- categories = categories.slice(1);
- var status = {
- category: category,
- total: this.categories().length,
- completed: this.categories().length - categories.length
- };
-
- var next = fun.bind(
- this._downloadCategory,
- this,
- selectedAccount,
- categories,
- callback);
- var stored = this.userStorage().getItem(CACHE_KEY) || {};
- var t = +new Date();
-
- if (stored[category] && (t - stored[category].time) < REFRESH_TIMEOUT) {
- status.cached = true;
- this._progress(status);
- next();
- } else {
- this._progress(status);
- Completion.loadFromRESTAPI(
- {
- account_id: selectedAccount.id(),
- category: category
- },
- fun.bind(function() {
- stored[category] = { time: t };
- this.userStorage().setItem(CACHE_KEY, stored);
- next();
- }, this));
- }
- }
- }
-});
-
-exports.DownloadCompletions = DownloadCompletions;
View
73 ads/job/tabSeparatedParser.js
@@ -26,7 +26,7 @@ var fun = require("../../uki-core/function");
var utils = require("../../uki-core/utils");
var Job = require("./base").Job;
var AdError = require("../lib/error").Error;
-var async = require("../../storage/lib/async");
+var async = require("../../lib/async");
var ESCAPED_CHAR_RE = /"(.)/g;
var ESCAPED_CHECK_RE = /[\t\n]/;
@@ -45,6 +45,8 @@ var CHUNKER_RE_PASTE =
/(\t|\n|^)(?:"((?:(?:(?:"")*[^"]*)*)|(?:[^\t\n]*))"|([^\t\n]*))/gi;
function parseTSV(string, excelPaste) {
+ try { // error report
+
// exit fast
if (!string) { return []; }
var re = excelPaste ? CHUNKER_RE_PASTE : CHUNKER_RE;
@@ -69,6 +71,11 @@ function parseTSV(string, excelPaste) {
result = utils.filter(result, function(s) {
return s.join('').match(/\S/);
});
+
+ } catch (e) {
+ require("../../lib/errorReport").handleException(e, 'tsp:pars');
+ }
+
return result;
}
@@ -128,14 +135,12 @@ var Parser = fun.newClass(Job, {
_prepare: function() {
var next = fun.bind(this._parse, this);
- require("../model/connectedObject").ConnectedObject.prepare(function() {
- require("../model/completion").Completion.prepare(
- ['cities', 'countries', 'regions', 'zips', 'college_majors',
- 'locales', 'workplaces', 'colleges'], next);
- });
+ require("../model/connectedObject").ConnectedObject.prepare(next);
},
_parse: function() {
+ try { // error report
+
var rows = parseTSV(this.string(), this.excelPaste());
if (rows.length < 2) {
this._fail(new NotEnoughRowsError());
@@ -163,6 +168,10 @@ var Parser = fun.newClass(Job, {
this._createAds(dataRows, function() {
this._createCamps(dataRows, this._complete);
});
+
+ } catch (e) {
+ require("../../lib/errorReport").handleException(e, 'tsp:parse');
+ }
},
_createAds: function(rows, callback) {
@@ -174,19 +183,23 @@ var Parser = fun.newClass(Job, {
async.forEach(
rows,
function(row, index, iteratorCallback) {
- var ad = new Ad()
- .muteChanges(true)
- .account_id(this.account().id());
- this.ads().push(ad);
-
- ad.fromTabSeparatedMap(
- row,
- this.foundAdProps(),
- function() {
- ad.muteChanges(false);
- iteratorCallback();
- },
- this.imageLookup() || { data: {}, hashes: {} });
+ try {
+ var ad = new Ad()
+ .muteChanges(true)
+ .account_id(this.account().id());
+ this.ads().push(ad);
+
+ ad.fromTabSeparatedMap(
+ row,
+ this.foundAdProps(),
+ function() {
+ ad.muteChanges(false);
+ iteratorCallback();
+ },
+ this.imageLookup() || { data: {}, hashes: {} });
+ } catch (e) {
+ require("../../lib/errorReport").handleException(e, 'tsp:ads');
+ }
},
callback,
this);
@@ -203,15 +216,19 @@ var Parser = fun.newClass(Job, {
async.forEach(
rows,
function(row, index, iteratorCallback) {
- var camp = new Campaign()
- .muteChanges(true)
- .account_id(this.account().id());
- this.camps().push(camp);
-
- camp.fromTabSeparatedMap(row, this.foundCampProps(), function() {
- camp.muteChanges(false);
- iteratorCallback();
- });
+ try {
+ var camp = new Campaign()
+ .muteChanges(true)
+ .account_id(this.account().id());
+ this.camps().push(camp);
+
+ camp.fromTabSeparatedMap(row, this.foundCampProps(), function() {
+ camp.muteChanges(false);
+ iteratorCallback();
+ });
+ } catch (e) {
+ require("../../lib/errorReport").handleException(e, 'tsp:camps');
+ }
},
callback,
this);
View
142 ads/lib/completions.js
@@ -0,0 +1,142 @@
+/**
+* Copyright 2011 Facebook, Inc.
+*
+* You are hereby granted a non-exclusive, worldwide, royalty-free license to
+* use, copy, modify, and distribute this software in source code or binary
+* form for use in connection with the web services and APIs provided by
+* Facebook.
+*
+* As with any software that integrates with the Facebook platform, your use
+* of this software is subject to the Facebook Developer Principles and
+* Policies [http://developers.facebook.com/policy/]. This copyright notice
+* shall be included in all copies or substantial portions of the software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*
+*
+*/
+
+
+/**
+* Search in countires and locales
+* @param Object items objects to search in
+* @param String query search query
+* @param Integer limit max number of results
+* @param Array exclusions ids to exclude
+* @return Array Found items
+*/
+function searchIn(items, query, limit, exclusions) {
+ if (!query) { return []; }
+
+ exclusions = exclusions || [];
+ var Util = require("../../uki-fb/view/typeahead/util").Util;
+ var query_regexps = Util.tokenize(query).map(function(token) {
+ return new RegExp('^' + Util.escape(token));
+ });
+
+ var result = [];
+ try {
+ require("../../uki-core/utils").forEach(items, function(text, key) {
+ if (exclusions.indexOf(key) != -1) { return; }
+ var object_tokens = Util.tokenize(text);
+
+ var count = 0;
+ for (var i = 0; i < query_regexps.length; i++) {
+ for (var j = 0; j < object_tokens.length; j++) {
+ if (query_regexps[i].test(object_tokens[j])) {
+ count++;
+ break;
+ }
+ }
+ }
+ if (count === query_regexps.length) {
+ result.push({ id: key, text: text });
+ if (result.length >= limit) { throw 'break'; }
+ }
+ });
+ } catch (e) {}
+ return result;
+}
+
+var cache = {};
+
+function bestMatch(query, items) {
+ var Util = require("../../uki-fb/view/typeahead/util").Util;
+ query = Util.tokenize(query).join(' ');
+ for (var i = 0; i < items.length; i++) {
+ if (Util.tokenize(items[i].text || items[i].name).join(' ') == query) {
+ return items[i];
+ }
+ }
+ return items[0];
+}
+
+var BEST_MATCH_LIMIT = 7;
+
+exports.findBest = function(type, query, callback) {
+ var key = type + ':' + query;
+ var options = { type: 'ad' + type, q: query, limit: BEST_MATCH_LIMIT };
+ if (type == 'country') {
+ callback(
+ bestMatch(query, this.searchCountries(query, BEST_MATCH_LIMIT)));
+ } else if (type == 'locale') {
+ callback(
+ bestMatch(query, this.searchLocales(query, BEST_MATCH_LIMIT)));
+ } else if (type == 'city' || type == 'region') {
+ key = type + ':' + query.country + ':' + query.query;
+ options.q = query.query;
+ options.country_list = query.country.toLowerCase();
+ }
+ key = key.toLowerCase();
+
+ if (cache[key] !== undefined) {
+ callback(cache[key]);
+ } else {
+ if (exports.dialog) {
+ var log = exports.dialog.visible(true).append(
+ { view: 'Text', text: 'Looking for ' + type + ' "' + options.q + '"' });
+ }
+ require("../../lib/connect").FB.api(
+ '/search',
+ options,
+ function(r) {
+ cache[key] = bestMatch(options.q, r.data || []);
+ if (log) {
+ log.text(log.text() + ' - done');
+ }
+ callback(cache[key]);
+ });
+ }
+};
+
+/**
+* Search in countires
+* @param String query search query
+* @param Integer limit max number of results
+* @param Array exclusions ids to exclude
+* @return Array Found countries
+*/
+exports.searchCountries = function(query, limit, exclusions) {
+ return searchIn(require("./countries").countries, query, limit, exclusions);
+};
+
+/**
+* Search in countires
+* @param String query search query
+* @param Integer limit max number of results
+* @param Array exclusions ids to exclude
+* @return Array Found locales
+*/
+exports.searchLocales = function(query, limit, exclusions) {
+ return searchIn(require("./locales").locales, query, limit, exclusions);
+};
+
+exports.dialog = null;
+exports.cache = cache;
+
View
44 ads/lib/countries.js
@@ -0,0 +1,44 @@
+/**
+* Copyright 2011 Facebook, Inc.
+*
+* You are hereby granted a non-exclusive, worldwide, royalty-free license to
+* use, copy, modify, and distribute this software in source code or binary
+* form for use in connection with the web services and APIs provided by
+* Facebook.
+*
+* As with any software that integrates with the Facebook platform, your use
+* of this software is subject to the Facebook Developer Principles and
+* Policies [http://developers.facebook.com/policy/]. This copyright notice
+* shall be included in all copies or substantial portions of the software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+* DEALINGS IN THE SOFTWARE.
+*
+*
+* This file is automatically generated by
+* scripts/admanager/powereditor/generate_js/geo.php
+* do not modify
+*
+* @nolint
+*/
+
+var countries =
+{"US":"United States","CA":"Canada","GB":"United Kingdom","AR":"Argentina","AU":"Australia","AT":"Austria","BE":"Belgium","BR":"Brazil","CL":"Chile","CN":"China","CO":"Colombia","HR":"Croatia","DK":"Denmark","DO":"Dominican Republic","EG":"Egypt","FI":"Finland","FR":"France","DE":"Germany","GR":"Greece","HK":"Hong Kong","IN":"India","ID":"Indonesia","IE":"Ireland","IL":"Israel","IT":"Italy","JP":"Japan","JO":"Jordan","KW":"Kuwait","LB":"Lebanon","MY":"Malaysia","MX":"Mexico","NL":"Netherlands","NZ":"New Zealand","NG":"Nigeria","NO":"Norway","PK":"Pakistan","PA":"Panama","PE":"Peru","PH":"Philippines","PL":"Poland","RU":"Russia","SA":"Saudi Arabia","RS":"Serbia","SG":"Singapore","ZA":"South Africa","KR":"South Korea","ES":"Spain","SE":"Sweden","CH":"Switzerland","TW":"Taiwan","TH":"Thailand","TR":"Turkey","AE":"United Arab Emirates","VE":"Venezuela","PT":"Portugal","LU":"Luxembourg","BG":"Bulgaria","CZ":"Czech Republic","SI":"Slovenia","IS":"Iceland","SK":"Slovakia","LT":"Lithuania","TT":"Trinidad and Tobago","BD":"Bangladesh","LK":"Sri Lanka","KE":"Kenya","HU":"Hungary","MA":"Morocco","CY":"Cyprus","JM":"Jamaica","EC":"Ecuador","RO":"Romania","BO":"Bolivia","GT":"Guatemala","CR":"Costa Rica","QA":"Qatar","SV":"El Salvador","HN":"Honduras","NI":"Nicaragua","PY":"Paraguay","UY":"Uruguay","PR":"Puerto Rico","BA":"Bosnia and Herzegovina","PS":"Palestine","TN":"Tunisia","BH":"Bahrain","VN":"Vietnam","GH":"Ghana","MU":"Mauritius","UA":"Ukraine","MT":"Malta","BS":"The Bahamas","MV":"Maldives","OM":"Oman","MK":"Macedonia","LV":"Latvia","EE":"Estonia","IQ":"Iraq","DZ":"Algeria","AL":"Albania","NP":"Nepal","MO":"Macau","ME":"Montenegro","SN":"Senegal","GE":"Georgia","BN":"Brunei","UG":"Uganda","GP":"Guadeloupe","BB":"Barbados","AZ":"Azerbaijan","TZ":"Tanzania","LY":"Libya","MQ":"Martinique","CM":"Cameroon","BW":"Botswana","ET":"Ethiopia","KZ":"Kazakhstan","NA":"Namibia","AN":null,"MG":"Madagascar","NC":"New Caledonia","MD":"Moldova","FJ":"Fiji","BY":"Belarus","JE":"Jersey","GU":"Guam","YE":"Yemen","ZM":"Zambia","IM":"Isle Of Man","HT":"Haiti","KH":"Cambodia","AW":"Aruba","PF":"French Polynesia","AF":"Afghanistan","BM":"Bermuda","GY":"Guyana","AM":"Armenia","MW":"Malawi","AG":"Antigua","RW":"Rwanda","GG":"Guernsey","GM":"The Gambia","FO":"Faroe Islands","LC":"St. Lucia","KY":"Cayman Islands","BJ":"Benin","AD":"Andorra","GD":"Grenada","VI":"US Virgin Islands","BZ":"Belize","VC":"Saint Vincent and the Grenadines","MN":"Mongolia","MZ":"Mozambique","ML":"Mali","AO":"Angola","GF":"French Guiana","UZ":"Uzbekistan","DJ":"Djibouti","BF":"Burkina Faso","MC":"Monaco","TG":"Togo","GL":"Greenland","GA":"Gabon","GI":"Gibraltar","CD":"Democratic Republic Congo","KG":"Kyrgyzstan","PG":"Papua New Guinea","BT":"Bhutan","KN":"Saint Kitts and Nevis","SZ":"Swaziland","LS":"Lesotho","LA":"Laos","LI":"Liechtenstein","MP":"Northern Mariana Islands","SR":"Suriname","SC":"Seychelles","VG":"British Virgin Islands","TC":"Turks and Caicos Islands","DM":"Dominica","MR":"Mauritania","AX":"Aland Islands","SM":"San Marino","SL":"Sierra Leone","NE":"Niger","CG":"Republic of the Congo","AI":"Anguilla","YT":"Mayotte","CV":"Cape Verde","GN":"Guinea","TM":"Turkmenistan","BI":"Burundi","TJ":"Tajikistan","VU":"Vanuatu","SB":"Solomon Islands","ER":"Eritrea","WS":"Samoa","AS":"American Samoa","FK":"Falkland Islands","GQ":"Equatorial Guinea","TO":"Tonga","KM":"Comoros","PW":"Palau","FM":"Federated States of Micronesia","CF":"Central African Republic","SO":"Somalia","MH":"Marshall Islands","VA":"Vatican City","TD":"Chad","KI":"Kiribati","ST":"Sao Tome and Principe","TV":"Tuvalu","NR":"Nauru","RE":"R\u00e9union"};
+var countriesWithRegions =
+["US","CA","AU","GB"];
+var countriesWithCities =
+["US","GB","CA","AU","FR","ES","IT","SW","NO","DK","DE","BE","VE","TR","CO","FI","JP","CL","CH","AR","SE","NL","LE","PT","GR","NZ","IE","ID","MY","TH","VN","PL","CZ","RO","RS","BO","BR","CR","DO","EC","SV","GT","HN","MX","NI","PA","PY","PE","PR","UY","AT","IL","IN","TW","RU","ES","EG","SA","AE","AT","CH","PH","ZA","KR","UA"];
+
+exports.countries = countries;
+exports.hasCities = function(country) {
+ return countriesWithCities.indexOf(country.toUpperCase()) != -1;
+};
+exports.hasRegions = function(country) {
+ return countriesWithRegions.indexOf(country.toUpperCase()) != -1;
+};
+
View
12 ads/lib/demoLinkBuilder.js
@@ -6,11 +6,10 @@ var fun = require("../../uki-core/function"),
utils = require("../../uki-core/utils"),
Campaign = require("../model/campaign").Campaign,
Ad = require("../model/ad").Ad,
- MD5 = require("./md5"),
StatusMap = require("./prop/adgroupStatus").STATUS_MAP;
var PROFILE_BASE = "http://www.facebook.com/profile.php/",
- HOME_BASE = "http://www.facebook.com/home.php/";
+ HOME_BASE = "http://www.facebook.com/";
var headerArr = [
'campaign_id',
@@ -78,15 +77,10 @@ function _fillCSV(accountId, callback) {
* return: string the 10 digit hash that must match the 'h' param
*
*/
-function _demoAdHash(adgroup_id) {
- var salt = "chocolate milk and m&ms, mmmm";
- var md5Str = MD5.md5(salt + adgroup_id);
- return md5Str.substr(0, 10);
-}
+// XXX TODO zahanm fix demo link hash generation
function _buildDemoAdLink(base, adgroup_id) {
- var str = base + '?demo_ad=' + adgroup_id + '&h=' +
- _demoAdHash(adgroup_id);
+ var str = base + '?demo_ad=' + adgroup_id;
return str;
}
View
40 ads/lib/estimateTargetingStats.js
@@ -24,8 +24,8 @@
var fun = require("../../uki-core/function");
-var FB = require("../../storage/lib/connect").FB;
-
+var graphlink = require("../../lib/graphlink"),