From 3da849a954efebc9d7a736dc48f57257eb3b4f93 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 14 Mar 2018 11:16:09 +0100 Subject: [PATCH] Status Report Module Implemented --- .vs/AACloudPlatform/v15/.suo | Bin 76800 -> 75776 bytes Project.njsproj | 3 + Status Report.js | 247 +++++++++++++++++++++++++++++++++++ platform.config.json | 6 +- 4 files changed, 253 insertions(+), 3 deletions(-) create mode 100644 Status Report.js diff --git a/.vs/AACloudPlatform/v15/.suo b/.vs/AACloudPlatform/v15/.suo index 65e2cac23a5b7d37e7c96237b4f014c6dc9bfa61..8080165c5dab490d827bf2fb6ba414d5f2be79c4 100644 GIT binary patch delta 2956 zcmb_e3s96*6u#%*U0|10p02AR{Dj(~C_4& z-0@QZY{Pr17)KZl96+FXo+kRp0VMkoB7AHeNe=YUc|hJ?^s&X0Jd_aQwe|J!z3(H_ zG{L>NG_&)|30~_H4p)7(YW^Z9nKK9~A=ghi#9VFBDBk%D)@rfquAr*RQc)vKKoiO}Y0`_+-|OO=82XabMN563C%M(9D{GkR0cDdT=lSty|=fm7Cju@DwT zf{E}pA(nu(>Nt1-o7LX|(m{f4WNSYuUl5x#W=R)^`*nIu(q*Xm!nj6fG;x8q9W-Gl zpr2Bjf12_%CJtcu;RT2&u$2=x3k9GnWJp?i`bb@lkWzM^{A zw?okIkqj4z4k*BT;(aJo?nDO)l4$$Ovrv;kEBScUl0{TR(?uwqkf0=(8zE#l4dCQ` zK>Z3rYiVwnc038I36o1RqabomW*wAT)pCL$_ZRAuL_raITm*TrAYHSdD>13!K~ET| zxUN}1nkAx`9HRkV%h>#39NLmI0>+TSzjCZ@5Ya3IXeN?!78k_EqC7lHvxl_v*qjoN z7gHke*vy>G;&2Kv`=Y8m6fC%`Si}q44IvFJt}lk3OYkqscPX$Ew^OqZi~1L7uOZ zbJVg)C_4?)lxfptRW#>iB@N}=aGAGvNnS;GkFW-#vtm4CdpQaqg5+7mQJV0K94MlN zC@vD@boG-S!>;ctNxwoZ-jr5glN5zNmSyA5Wk#I7Hd2XP-z+@p0)|25T`;md(w~mQ zY^zpF|;b0uzt%=8Vbt%~IY&e=X znedIe4Y;M+@SmI7TB3QDP2tUx>#$0$$NH*x>|33VyN?fmt+c%^|G(X~?bXtBBNm=m zN82yLA!ib>b<@WcWJZ=qqaa+82dPmDxsbt1N&J&LB|dgW+KM^#_`xbcf!Pf!tf z<1ea)u!N=2tDM8KnYU~!It0KV#dXdC*nFc8_Bof0E@yAtJHyas{H{$pF&KIyT)zVIvGm5onDO-S=MwD1r^vGC60ov4^mfzT z!De|lj>W>mHMrty(Rm;eb_1%fPr_=qL9twQfq!m(9u=#Fb3pm+R3REbL~E6vD~h{u zK47rRjKNn1IcTaceOEMpX}(SNN2URd*e3Sh5*=Ie3Fu3K4t`$SjTI+q2( z5~2R~G7bKI%;+?R!!?bD{zfqy&w%qn6x0Svmn@IGd)VYOnub$0+H*0OWGiK;fc|AZ L{75S6FInMFqq0gB delta 3286 zcmd^B3rv$&6z=`o3ayp*01>e~hG-}hsk{ViY3a~`LMD?LvJI6fjty*!M&wbQkNKE^ z|EL&U7X6vVElwW43!7u?_pzl@R33{nF*s(%vYEv>qnX4B0VvMjsJ$NA2= z=bn4d`R~14dzZ|9PFAN4Wh7$rl1O}zN)dOQPA9ce9@Ua}lR)+;wW8U;TQ3~$CC@?y zQ%hR->!YNE+=4-Ac&#To1|o2r(;cN+)aM1{%SJeaAXg!1eA;LNG|^$r=}bc& zF6uM^mwoC8PHj~{WC3Y9(K=;BZz6IUJppA9A{p@vf{uzuISrAD7!SV%g@d15tr&~i zY(y@k$!o_~AfvvnDo{?_GPEy;7Nu`}xy-8?l~tl6?PQhFs?Dg=i8+*zq|B9lK*?rh zVAO|V&jFMliJW?l1dRfn%0M|KPX1?)O+>y9eUo5?Y9(og>#ADP1Qp)3ii2pSC6&N! zMeI|aRf)6O{yOWJTKUkd{GhKJ!}>XRhffk={cU6^U*W$=%7zTFkmsbTavbbRi+6+` zVC3(h#Q`fKYRLc$M%*M8{`#cRveuw>a-TCe#uf1Dn0}&p3kT2|Y(%*Ru@*r?y4nWR zUqEa^ywsA{MC!{USXD3|99D2|ww3Z(*gZ6(f_}+g=&`NOOXPLhPQoo_z%t*5_t9M@ zTqM(4YGlBuiUXTI*F78k=;?&r4mD^QcllgY&O&!tEcBNJZ;vD@=rU-LtB0UvWG)+* z%CttSnMSn|GgC zxgK@8!!|(ZYvBqO86dXO4avVcWjEpGsJH|MuFi{U=qKc5e%<^^>C;Ipmsc<8C!9&f zEn_&#W8@RW)iLajU&#%)vULry0GawC6y699%*TZjM3z=^-w&K{<35MKYTUCQnkz`k1#!v0c--s1;h)`W&ihA6?l$F3Z}JIWV_ zC?(L}xgWN)#)9*k<)Hu6!t&?gQlmepxv@}w(g^8ilpwT3!THac(9HsUCsfeU6a`xj zwx+fdQ;HUUD-Bc3nTfj0I88>PE?yHCXVPj6nQ;b33Uvp| z;Y!aK$nS~zuk4#>@_;YQb;$mIpOHJxBtF^kvq90J3GiLtM1T5P8*UK-eK^p2+0+b% zrlo9?9!`DL&N&!T1on?7(j!95kkeav_ZBHVA$Zdl&v1(nOh^{=v=^gW_!&LkGCw8i^xdaI(8nfi(A_u|s(X&9x`uW98!A9{Z#=0_I6=ey$C4o4C*fz@A! z@kzfo6OdgjfRH=>y#7alu<3fQ@=s20TPv(TRa41+`_|>aH1M7pN$>iZypXOZWD<~z z17Q226SS9RJ$^OwS{lIZ%$l|u665T<(Kk?mY;#P6K^+1oR(DZzymlk@xax)2hfUhRwQLF38%M z1VWZTcLFTkl?K8LJv%)R^n>=c9e!l79CDBAK>1}ff8lDF+rD7D++FXMVHxrVUjp0H diff --git a/Project.njsproj b/Project.njsproj index f88c2ef97..45489f9dc 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -38,6 +38,9 @@ + + Code + Code diff --git a/Status Report.js b/Status Report.js new file mode 100644 index 000000000..962032f57 --- /dev/null +++ b/Status Report.js @@ -0,0 +1,247 @@ +exports.newStatusReport = function newStatusReport(BOT, DEBUG_MODULE, FILE_STORAGE, UTILITIES) { + + const FULL_LOG = true; + const LOG_FILE_CONTENT = false; + + /* + + A Status Report is a file that every bot reads at the begining of its execution and saves after it finishes its job. + The purpose of the file is to record checkpoint information of what was the last thing done by the bot and helpfull enough to start the next execution. + It usually does not include business related context data. + + */ + + const MODULE_NAME = "Status Report"; + + let thisObject = { + file: undefined, // Here we have the JSON object representing the file content. + initialize: initialize, + save: save + }; + + /* + + During the process we will create a new History Record. This will go to the Context History file which essentially mantains an + index of all the bots executions. This file will later be plotted by the bot s plotter on the timeline, allowing end users to + know where there is information related to the actions taken by the bot. + + */ + + let bot = BOT; + let ownerBot; // This is the bot owner of the Status Report. Only owners can save the report and override the existing content. + + const logger = DEBUG_MODULE.newDebugLog(); + logger.fileName = MODULE_NAME; + logger.bot = bot; + + /* Utilities needed. */ + + let utilities = UTILITIES.newUtilities(bot); + + /* Storage account to be used here. */ + + let cloudStorage = FILE_STORAGE.newAzureFileStorage(bot); + + return thisObject; + + function initialize(pOwnerBot, callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } + + /* + + Here we get the positions the bot did and that are recorded at the bot storage account. We will use them through out the rest + of the process. + + */ + + ownerBot = pOwnerBot; + + initializeStorage(); + + function initializeStorage() { + + cloudStorage.initialize(bot.codeName, onInizialized); + + function onInizialized(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + getFile(onDone); + + } else { + logger.write("[ERROR] initialize -> initializeStorage -> onInizialized -> err = " + err.message); + callBackFunction(err); + } + } + } + + function onDone(err) { + try { + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + logger.write("[INFO] initialize -> onDone -> Execution finished well. :-)"); + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + break; + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + logger.write("[ERROR] initialize -> onDone -> Retry Later. Requesting Execution Retry."); + callBackFunction(err); + return; + } + break; + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + logger.write("[ERROR] initialize -> onDone -> Operation Failed. Aborting the process."); + callBackFunction(err); + return; + } + break; + } + + } catch (err) { + logger.write("[ERROR] initialize -> onDone -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getFile(callBack) { + + try { + + if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> Entering function."); } + + let fileName = "Status.Report.json" + let filePath = ownerBot.devTeam + "/" + ownerBot.codeName + "." + ownerBot.version.major + "." + ownerBot.version.minor + "/" + ownerBot.dataSetVersion + "/Processes/" + ownerBot.process + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor; + + if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> fileName = " + fileName); } + if (FULL_LOG === true) { logger.write("[INFO] initialize -> getFile -> filePath = " + filePath); } + + cloudStorage.getTextFile(filePath, fileName, onFileReceived); + + function onFileReceived(err, text) { + + if (err.result === global.DEFAULT_FAIL_RESPONSE.result && err.message === 'Folder does not exist.') { + logger.write("[INFO] initialize -> getFile -> onFileReceived -> err = " + err.message); + + /* In this case we can assume that this is the first execution ever of this bot.*/ + + createConext(callBack); + return; + } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write("[ERROR] initialize -> getFile -> onFileReceived -> err = " + err.message); + callBack(err); + return; + } + + if (FULL_LOG === true) { + logger.write("[INFO] initialize -> getFile -> onFileReceived -> Content received = " + text); + } + + try { + + thisObject.statusReport = JSON.parse(text); + getExecutionHistory(callBack); + + } catch (err) { + + /* + It might happen that the file content is corrupt. We will consider this as a temporary situation, since sometimes the file + is being updated at the moment of the read. The bot can not run without a valid Status Report but we can request the platform to retry later. + */ + + logger.write("[ERROR] initialize -> getFile -> onFileReceived -> Bot cannot execute without a valid Status report. -> Err = " + err.message); + callBack(global.DEFAULT_RETRY_RESPONSE); + } + } + + } catch (err) { + logger.write("[ERROR] initialize -> getFile -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + logger.write("[ERROR] initialize -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function save(callBackFunction) { + + try { + + if (FULL_LOG === true) { logger.write("[INFO] save -> Entering function."); } + + let ownerId = ownerBot.devTeam + "-" + ownerBot.codeName; + let botId = bot.devTeam + "-" + bot.codeName; + + if (ownerId !== botId) { + + let err = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: "Only bots owners of a Status Report can save them." + } + logger.write("[ERROR] save -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + let fileName = "Status.Report.json" + let filePath = bot.devTeam + "/" + bot.codeName + "." + bot.version.major + "." + bot.version.minor + "/" + bot.dataSetVersion + "/Processes/" + bot.process + "/" + global.PLATFORM_CONFIG.codeName + "." + global.PLATFORM_CONFIG.version.major + "." + global.PLATFORM_CONFIG.version.minor; + + if (FULL_LOG === true) { logger.write("[INFO] save -> fileName = " + fileName); } + if (FULL_LOG === true) { logger.write("[INFO] save -> filePath = " + filePath); } + + utilities.createFolderIfNeeded(filePath, cloudStorage, onFolderCreated); + + function onFolderCreated(err) { + + try { + + if (FULL_LOG === true) { logger.write("[INFO] save -> onFolderCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write("[ERROR] save -> onFolderCreated -> err = " + err.message); + callBackFunction(err); + return; + } + + let fileContent = JSON.stringify(thisObject.statusReport); + + cloudStorage.createTextFile(filePath, fileName, fileContent + '\n', onFileCreated); + + function onFileCreated(err) { + + if (FULL_LOG === true) { logger.write("[INFO] save -> onFolderCreated -> onFileCreated -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + logger.write("[ERROR] save -> onFolderCreated -> onFileCreated -> err = " + err.message); + callBackFunction(err); + return; + } + + if (FULL_LOG === true) { + logger.write("[INFO] save -> onFolderCreated -> onFileCreated -> Content written = " + fileContent); + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + } + catch (err) { + logger.write("[ERROR] save -> onFolderCreated -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + logger.write("[ERROR] save -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +}; \ No newline at end of file diff --git a/platform.config.json b/platform.config.json index 95ce357d7..9b6050224 100644 --- a/platform.config.json +++ b/platform.config.json @@ -1,12 +1,12 @@ { - "codeName": "AACloudPlatform", + "codeName": "AACloud", "version": { "major": 1, "minor": 0, - "patch": 0 + "patch": 1 }, "bot": { - "path": "../Bots/AAMasters/AAMariam-Trading-Bot" + "path": "../Bots/AAMasters/AAOlivia-Indicator-Bot" }, "stopGracefully": "false" } \ No newline at end of file