def _buildFullDateAndTimeStrings(self, commandDic, timezoneStr): ''' This method ensures that the full command string is unified whatever the completness of the dated/time components specified in the request by the user. Ex: btc usd 1/1 bitfinex or btc usd 1/01/18 bitfinex or btc usd 1/1 12:23 bitfinex all return a full commaand of btc usd 01/01/18 00:00 bitfinex, btc usd 01/01/18 12:23 bitfinex respectively. This is important since the ful command string is what is stored in the command history list, with no duplicates. Otherwxise, btc usd 1/1 00:00 bitfinex and btc usd 01/01/18 00:00 bitfinex would be stored as 2 entries ! :param commandDic: :param timezoneStr: :seqdiag_return requestDateDMY, requestDateHM :return: ''' day = commandDic[CommandPrice.DAY] month = commandDic[CommandPrice.MONTH] year = commandDic[CommandPrice.YEAR] hour = commandDic[CommandPrice.HOUR] minute = commandDic[CommandPrice.MINUTE] requestDateDMY, requestDateHM = DateTimeUtil.formatPrintDateTimeFromStringComponents( day, month, year, hour, minute, timezoneStr, self.configurationMgr.dateTimeFormat) from seqdiagbuilder import SeqDiagBuilder SeqDiagBuilder.recordFlow() return requestDateDMY, requestDateHM
def analyse(self): ''' :seqdiag_return Analysis :return: ''' SeqDiagBuilder.recordFlow()
def d1(self, d1_p1): ''' :param d1_p1: :seqdiag_return Dd1Return :return: ''' SeqDiagBuilder.recordFlow()
def d2(self, d2_p1): ''' :param d2_p1: :seqdiag_return Dd2Return :return: ''' SeqDiagBuilder.recordFlow()
def doC1(self, p1): ''' This the first leaf method of our sequence diagram. ''' # The next instruction causes SeqDiagBuilder to record the # whole control flow which conducted to call the present method. SeqDiagBuilder.recordFlow()
def getCurrentPrice(self, crypto, fiat, exchange): url = "https://min-api.cryptocompare.com/data/price?fsym={}&tsyms={}&e={}".format( crypto, fiat, exchange) resultData = ResultData() resultData.setValue(ResultData.RESULT_KEY_CRYPTO, crypto) resultData.setValue(ResultData.RESULT_KEY_FIAT, fiat) resultData.setValue(ResultData.RESULT_KEY_EXCHANGE, exchange) resultData.setValue(ResultData.RESULT_KEY_PRICE_TYPE, resultData.PRICE_TYPE_RT) try: if self.ctx == None: #here, run in QPython under Python 3.2 webURL = urllib.request.urlopen(url) else: webURL = urllib.request.urlopen(url, context=self.ctx) except HTTPError as e: resultData.setValue( ResultData.RESULT_KEY_ERROR_MSG, 'ERROR - could not complete request ' + url + '. Reason: ' + str(e.reason)) except URLError as e: resultData.setValue( ResultData.RESULT_KEY_ERROR_MSG, 'ERROR - could not complete request ' + url + '. Reason: ' + str(e.reason)) except: the_type, the_value, the_traceback = sys.exc_info() resultData.setValue( ResultData.RESULT_KEY_ERROR_MSG, 'ERROR - could not complete request ' + url + '. Reason: ' + str(the_type)) else: page = webURL.read() soup = BeautifulSoup(page, 'html.parser') dic = json.loads(soup.prettify()) if fiat in dic: resultData.setValue(ResultData.RESULT_KEY_PRICE_TIME_STAMP, DateTimeUtil.utcNowTimeStamp()) resultData.setValue( ResultData.RESULT_KEY_PRICE, dic[fiat] ) #current price is indexed by fiat symbol in returned dic else: resultData = self._handleProviderError(dic, resultData, url, crypto, fiat, exchange, isRealTime=True) from seqdiagbuilder import SeqDiagBuilder SeqDiagBuilder.recordFlow() return resultData
def _buildFullDateAndTimeStrings(self, commandDic, timezoneStr): ''' This method ensures that the full command string is unified whatever the completness of the dated/time components specified in the request by the user. Ex: btc usd 1/1 bitfinex or btc usd 1/01/18 bitfinex or btc usd 1/1 12:23 bitfinex all return a full commaand of btc usd 01/01/18 00:00 bitfinex, btc usd 01/01/18 12:23 bitfinex respectively. This is important since the ful command string is what is stored in the command history list, with no duplicates. Otherwxise, btc usd 1/1 00:00 bitfinex and btc usd 01/01/18 00:00 bitfinex would be stored as 2 entries ! :param commandDic: :param timezoneStr: :seqdiag_return requestDateDMY, requestDateHM :return: ''' dayInt = int(commandDic[CommandPrice.DAY]) monthInt = int(commandDic[CommandPrice.MONTH]) year = commandDic[CommandPrice.YEAR] if year == None: now = DateTimeUtil.localNow(timezoneStr) yearInt = now.year else: yearInt = int(year) hour = commandDic[CommandPrice.HOUR] minute = commandDic[CommandPrice.MINUTE] if hour != None and minute != None: # hour can not exist without minute and vice versa ! hourInt = int(hour) minuteInt = int(minute) else: hourInt = 0 minuteInt = 0 requestArrowDate = DateTimeUtil.dateTimeComponentsToArrowLocalDate( dayInt, monthInt, yearInt, hourInt, minuteInt, 0, timezoneStr) dateTimeComponentSymbolList, separatorsList, dateTimeComponentValueList = DateTimeUtil.getFormattedDateTimeComponents( requestArrowDate, self.configurationMgr.dateTimeFormat) dateSeparator = separatorsList[0] timeSeparator = separatorsList[1] requestDateDMY = dateTimeComponentValueList[0] + dateSeparator + dateTimeComponentValueList[1] + dateSeparator + \ dateTimeComponentValueList[2] requestDateHM = dateTimeComponentValueList[ 3] + timeSeparator + dateTimeComponentValueList[4] from seqdiagbuilder import SeqDiagBuilder SeqDiagBuilder.recordFlow() return requestDateDMY, requestDateHM
def getContentAsListFromSuper(self): SeqDiagBuilder.recordFlow() return super().getContentAsList()
def getContentAsList(self): SeqDiagBuilder.recordFlow() return self.content
def _buildFullCommandPriceOptionalParmsDic(self, optionalParmList): ''' Since DAY_MONTH_YEAR, HOUR_MINUTE and EXCHANGE can be provided in any order after CRYPTO and FIAT, this method differentiate them build an optional command price parm data dictionary with the right key. This dictionary will be added to the CommandPrice parmData dictionary. :seqdiag_return optionalParsedParmDataDic :return optionalParsedParmDataDic or None in case of syntax error. ''' ''' ° 0 is legal for both date and time parms. Zero for either date or/and time means now, real time ! ° Date must be 0 or contain a '/'. ° Time must be composed of two numerical groups separated by ':', the second group being a 2 digits group. Note 00:00 does not mean now, but midnight ! ° Exchange name must start with a letter. May contain digits. Ex: Date can be 0, accepted. 1, rejected. 10, rejected. 01, rejected. 01/1, accepted. 01/10, accepted. 1/1, accepted. 1/10, accepted. 01/12/16, accepted. 01/12/2015, accepted. Hour minute can be 0, rejected. 1, rejected. 10, rejected. 01, rejected. 01:1, rejected. 01:01, accepted. 01:10, accepted. 1:10, accepted. 00:00, accepted. 0:00, accepted. 0:0, rejected. ''' COMMAND_OR_OPTION = 'commandOrOption' # changed r"\d+/\d+(?:/\d+)*|^0$" into r"\d+/\d+(?:/\d+)*|^\d+$" was required so # that a full request like btc usd 1 12:45 bitfinex does generate an ERROR - date not valid # in CommandPrice. With the old versionk of th pattern, CommandPrice.DAY_MONTH_YEAR was none, # which was considered like a valid full request with only the time providded, a feature which # was not supported before ! # # So, allowing the user to provide only the time in the full request implied that we are # more permissive at the level of the Requester in order for CommandPrice to be able # to correctly identify the invalid date/time full request component in the form of # D HH:MM or DD HH:MM patternCommandDic = { r"\d+/\d+(?:/\d+)*|^\d+$": CommandPrice.DAY_MONTH_YEAR, r"\d+:\d\d": CommandPrice.HOUR_MINUTE, r"[A-Za-z]+": CommandPrice.EXCHANGE, r"(?:-[vV])([sS]?)([\w\d/:\.]+)": CommandPrice.PRICE_VALUE_DATA, r"(?:-[vV])([sS]?)([\w\d/:\.]+)" + COMMAND_OR_OPTION: CommandPrice.PRICE_VALUE_SAVE, r"(-[^vV]{1}[sS]?)([\w\d/:\.]+)": CommandPrice. UNSUPPORTED_COMMAND_DATA, # see scn capture https://pythex.org/ in Evernote for test of this regexp ! r"(-[^vV]{1}[sS]?)([\w\d/:\.]+)" + COMMAND_OR_OPTION: CommandPrice.UNSUPPORTED_COMMAND } optionalParsedParmDataDic = {} for pattern in patternCommandDic.keys(): for group in optionalParmList: if group and re.search(pattern, group): if patternCommandDic[ pattern] not in optionalParsedParmDataDic: #if for example DMY already found in optional full command parms, #it will not be overwritten ! Ex: 12/09/17 0: both token match DMY #pattern ! data, optionalCommandModifier = self._extractData( pattern, group) if data != None: optionalParsedParmDataDic[ patternCommandDic[pattern]] = data patternCommandModifierKey = pattern + COMMAND_OR_OPTION if optionalCommandModifier != None and optionalCommandModifier != '': optionalParsedParmDataDic[patternCommandDic[ patternCommandModifierKey]] = optionalCommandModifier elif patternCommandModifierKey in optionalParsedParmDataDic.keys( ): optionalParsedParmDataDic[patternCommandDic[ patternCommandModifierKey]] = None else: #full command syntax error ! return None from seqdiagbuilder import SeqDiagBuilder SeqDiagBuilder.recordFlow() return optionalParsedParmDataDic
def getFullCommandString(self, resultData): ''' Recreate the full command string corresponding to a full or partial price request entered by the user. The full command string no options contains a full date and time which is formatted according to the date time format as specified in the configuration file. Even if the request only contained partial date time info, the full command string no options contains a full date time specification. The full command string no options will be stored in the command history list so it can be replayed or saved to file. An empty string is returned if the command generated an error (an empty string will not be added to the history list !) In case an option with save mode is added to the command - for example -vs -, then the full command with the save mode option is returned as well (fullCommandStrWithSaveOptionsForHistoryList). In the GUI, the full command with save mode option will replace the corresponding full command string no options (fullCommandStrNoOptions) in the command history list. If the added command option is not in save mode (no option or -v... or -f... for example), then None is returned for fullCommandStrWithSaveOptionsForHistoryList and the corresponding full command string no options (fullCommandStrNoOptions) will NOT have to be replaced in the command history list. Finally, what is the usefulness of the fullCommandStrWithNoSaveOptions returned string ? It serves to differentiate a partial request with option(s) without save mode from a full request with with option(s) without save mode. In case of partial request, the status bar content is different from in the case of a full request. :param copyResultToClipboard: :param resultData: result of the last full or partial request :seqdiag_return printResult, fullCommandStrNoOptions, fullCommandStrWithNoSaveOptions, fullCommandStrWithSaveOptionsForHistoryList, fullCommandStrForStatusBar :return: 1/ full command string with no command option corresponding to a full or partial price request entered by the user or empty string if the command generated an error msg. 2/ full request command with any non save command option 3/ full command string with command option in save mode or none if no command option in save mode is in effect or if the command option generated a warning. 4/ full command string for status bar Ex: 1/ eth usd 0 bitfinex 2/ None 3/ eth usd 0 bitfinex -vs0.1eth 1/ eth usd 0 bitfinex 2/ eth usd 0 bitfinex -v0.1btc (even if warning generated !) 3/ None (-v0.1btc command was entered, which generated a warning) 1/ eth usd 0 bitfinex 2/ None (no value command in effect) 3/ None (no value command with save option in effect) ''' if not resultData.noError(): return '', None, None, None commandDic = resultData.getValue( resultData.RESULT_KEY_INITIAL_COMMAND_PARMS) priceType = resultData.getValue(resultData.RESULT_KEY_PRICE_TYPE) if priceType == resultData.PRICE_TYPE_RT: fullCommandStrNoOptions = commandDic[CommandPrice.CRYPTO] + ' ' + \ commandDic[CommandPrice.UNIT] + ' 0 ' + \ commandDic[CommandPrice.EXCHANGE] else: requestDateDMY, requestDateHM = self._buildFullDateAndTimeStrings( commandDic, self.configurationMgr.localTimeZone) fullCommandStrNoOptions = commandDic[CommandPrice.CRYPTO] + ' ' + \ commandDic[CommandPrice.UNIT] + ' ' + \ requestDateDMY + ' ' + \ requestDateHM + ' ' + \ commandDic[CommandPrice.EXCHANGE] # the three next full command string, unless they are set to None, all start with # the content of fullCommandStrNoOptions fullCommandStrWithSaveOptionsForHistoryList = fullCommandStrNoOptions fullCommandStrWithNoSaveOptions = fullCommandStrNoOptions fullCommandStrForStatusBar = fullCommandStrNoOptions # handling value option if resultData.getValue(resultData.RESULT_KEY_OPTION_VALUE_CRYPTO): # if the ResultData contains the computed value option crypto amount, # this means that the specified value option is valid and so has to be added # to the returned GuiOutputFormatter fields ! fullCommandStrWithNoSaveOptions, \ fullCommandStrWithSaveOptionsForHistoryList, \ fullCommandStrForStatusBar = self._addOptionValueInfo(resultData, commandDic, fullCommandStrWithNoSaveOptions, fullCommandStrWithSaveOptionsForHistoryList, fullCommandStrForStatusBar) # handling fiat option fiatOptionSymbol = commandDic[CommandPrice.OPTION_FIAT_SYMBOL] fullCommandStrForStatusBarFiatComplement = '' if fiatOptionSymbol: fullCommandStrWithNoSaveOptions, \ fullCommandStrWithSaveOptionsForHistoryList, \ fullCommandStrForStatusBar, \ fullCommandStrForStatusBarFiatComplement = self._addOptionFiatInfo(resultData, commandDic, fiatOptionSymbol, fullCommandStrWithNoSaveOptions, fullCommandStrWithSaveOptionsForHistoryList, fullCommandStrForStatusBar) # handling price option priceOptionAmountStr = commandDic[CommandPrice.OPTION_PRICE_AMOUNT] if priceOptionAmountStr: fullCommandStrWithNoSaveOptions, \ fullCommandStrWithSaveOptionsForHistoryList, \ fullCommandStrForStatusBar = self._addOptionPriceInfo(resultData, priceOptionAmountStr, fullCommandStrWithNoSaveOptions, fullCommandStrWithSaveOptionsForHistoryList, fullCommandStrForStatusBar) from seqdiagbuilder import SeqDiagBuilder SeqDiagBuilder.recordFlow() if fullCommandStrWithSaveOptionsForHistoryList == fullCommandStrNoOptions: fullCommandStrWithSaveOptionsForHistoryList = None if fullCommandStrWithNoSaveOptions == fullCommandStrNoOptions: fullCommandStrWithNoSaveOptions = None fullCommandStrForStatusBar += fullCommandStrForStatusBarFiatComplement if fullCommandStrForStatusBar == fullCommandStrNoOptions: fullCommandStrForStatusBar = None return fullCommandStrNoOptions, fullCommandStrWithNoSaveOptions, fullCommandStrWithSaveOptionsForHistoryList, fullCommandStrForStatusBar
def _getHistoDayPriceAtUTCTimeStamp(self, crypto, fiat, timeStampUTC, exchange, resultData): ''' :param crypto: :param fiat: :param timeStampUTC: :param exchange: :param resultData: :seqdiag_return ResultData :return: ''' timeStampUTCStr = str(timeStampUTC) url = "https://min-api.cryptocompare.com/data/histoday?fsym={}&tsym={}&limit=1&aggregate=1&toTs={}&e={}".format( crypto, fiat, timeStampUTCStr, exchange) resultData.setValue(ResultData.RESULT_KEY_PRICE_TYPE, resultData.PRICE_TYPE_HISTO_DAY) try: if self.ctx == None: #here, run in QPython under Python 3.2 webURL = urllib.request.urlopen(url) else: webURL = urllib.request.urlopen(url, context=self.ctx) except HTTPError as e: resultData.setValue( ResultData.RESULT_KEY_ERROR_MSG, 'ERROR - could not complete request ' + url + '. Reason: ' + str(e.reason)) except URLError as e: resultData.setValue( ResultData.RESULT_KEY_ERROR_MSG, 'ERROR - could not complete request ' + url + '. Reason: ' + str(e.reason)) except: the_type, the_value, the_traceback = sys.exc_info() resultData.setValue( ResultData.RESULT_KEY_ERROR_MSG, 'ERROR - could not complete request ' + url + '. Reason: ' + str(the_type)) else: page = webURL.read() soup = BeautifulSoup(page, 'html.parser') dic = json.loads(soup.prettify()) if dic['Data'] != []: dataDic = dic['Data'][IDX_DATA_ENTRY_TO] resultData.setValue(ResultData.RESULT_KEY_PRICE_TIME_STAMP, dataDic['time']) resultData.setValue(ResultData.RESULT_KEY_PRICE, dataDic['close']) else: resultData = self._handleProviderError(dic, resultData, url, crypto, fiat, exchange, isRealTime=False) from seqdiagbuilder import SeqDiagBuilder SeqDiagBuilder.recordFlow() return resultData
def getFullCommandString(self, resultData): ''' Recreate the full command string corresponding to a full or partial price request entered by the user. The full command string contains a full date and time which is formatted according to the date time format as specified in the configuration file. Even if the request only contained partial date time info, the full command string ccontains a full date time specification. The full command string will be stored in the command history list so it can be replayed or saved to file. An empty string is returned if the command generated an error (empty string will not be added to history ! In case an option to the command with save mode is in effect - for example -vs -, then the full command with the save mode option is returned aswell. Othervise, if no command option in save mode is in effect (no option or -v for example), then None is returned as second return value. :param resultData: result of the last full or partial request :seqdiag_return printResult, fullCommandStr, fullCommandStrWithOptions, fullCommandStrWithSaveModeOptions :return: 1/ full command string with no command option corresponding to a full or partial price request entered by the user or empty string if the command generated an error msg. 2/ full request command with any non save command option 3/ full command string with command option in save mode or none if no command option in save mode is in effect or if the command option generated a warning. Ex: 1/ eth usd 0 bitfinex 2/ None 3/ eth usd 0 bitfinex -vs0.1eth 1/ eth usd 0 bitfinex 2/ eth usd 0 bitfinex -v0.1btc (even if warning generated !) 3/ None (-v0.1btc command was entered, which generated a warning) 1/ eth usd 0 bitfinex 2/ None (no value command in effect) 3/ None (no value command with save option in effect) ''' if resultData.isError(): return '', None, None commandDic = resultData.getValue( resultData.RESULT_KEY_INITIAL_COMMAND_PARMS) priceType = resultData.getValue(resultData.RESULT_KEY_PRICE_TYPE) if priceType == resultData.PRICE_TYPE_RT: fullCommandStr = commandDic[CommandPrice.CRYPTO] + ' ' + \ commandDic[CommandPrice.FIAT] + ' 0 ' + \ commandDic[CommandPrice.EXCHANGE] else: requestDateDMY, requestDateHM = self._buildFullDateAndTimeStrings( commandDic, self.configurationMgr.localTimeZone) fullCommandStr = commandDic[CommandPrice.CRYPTO] + ' ' + \ commandDic[CommandPrice.FIAT] + ' ' + \ requestDateDMY + ' ' + \ requestDateHM + ' ' + \ commandDic[CommandPrice.EXCHANGE] fullCommandStrWithSaveModeOptions = None fullCommandStrWithOptions = None if resultData.getValue(resultData.RESULT_KEY_PRICE_VALUE_SAVE): if not resultData.containsWarning( resultData.WARNING_TYPE_COMMAND_VALUE): #in case the value command generated a warning, if the value command data contains a crypto or fiat #different from the crypto or fiat of tthe request, the fullCommandStrWithSaveModeOptions remains #None and wont't be stored in the request history list of the GUI ! fullCommandStrWithSaveModeOptions = fullCommandStr + ' -vs' + commandDic[ CommandPrice.PRICE_VALUE_AMOUNT] + commandDic[ CommandPrice.PRICE_VALUE_SYMBOL] else: valueCommandAmountStr = commandDic[CommandPrice.PRICE_VALUE_AMOUNT] valueCommandSymbolStr = commandDic[CommandPrice.PRICE_VALUE_SYMBOL] if valueCommandAmountStr and valueCommandSymbolStr: #even in case the value command generated a warning, it will be displayed in the status bar ! fullCommandStrWithOptions = fullCommandStr + ' -v' + valueCommandAmountStr + valueCommandSymbolStr from seqdiagbuilder import SeqDiagBuilder SeqDiagBuilder.recordFlow() return fullCommandStr, fullCommandStrWithOptions, fullCommandStrWithSaveModeOptions
def _getHistoDayPriceAtUTCTimeStamp(self, crypto, unit, timeStampUTC, exchange, resultData): ''' :param crypto: :param unit: :param timeStampUTC: :param exchange: :param resultData: :seqdiag_return ResultData :return: resultData ''' timeStampUTCStr = str(timeStampUTC) url = "https://min-api.cryptocompare.com/data/histoday?fsym={}&tsym={}&limit=1&aggregate=1&toTs={}&e={}".format( crypto, unit, timeStampUTCStr, exchange) resultData.setValue(ResultData.RESULT_KEY_PRICE_TYPE, resultData.PRICE_TYPE_HISTO_DAY) try: if self.ctx == None: #here, run in QPython under Python 3.2 webURL = urllib.request.urlopen(url) else: webURL = urllib.request.urlopen(url, context=self.ctx) except HTTPError as e: resultData.setError('ERROR - could not complete request ' + url + '. Reason: ' + str(e.reason) + '.') except URLError as e: resultData.setError( 'ERROR - No internet. Fix the problem and retry !') except: the_type, the_value, the_traceback = sys.exc_info() resultData.setError('ERROR - could not complete request ' + url + '. Reason: ' + str(the_type) + '.') else: page = webURL.read() soup = BeautifulSoup(page, 'html.parser') dic = json.loads(soup.prettify()) # if dic['Data'] != [] and unit in dic: dataListOrDic = dic['Data'] if dataListOrDic != []: try: dataEntryDic = dataListOrDic[IDX_DATA_ENTRY_TO] resultData.setValue(ResultData.RESULT_KEY_PRICE_TIME_STAMP, dataEntryDic['time']) resultData.setValue(ResultData.RESULT_KEY_PRICE, dataEntryDic['close']) except IndexError: # does not happen in any test case resultData = self._handleProviderError(dic, resultData, url, crypto, unit, exchange, isRealTime=False) except KeyError: # happens when pair coupled to exchange do not return ay data. # Either the exchange does not exist or the pair is not # supported by the exchange. resultData = self._handleProviderError(dic, resultData, url, crypto, unit, exchange, isRealTime=False) else: resultData = self._handleProviderError(dic, resultData, url, crypto, unit, exchange, isRealTime=False) from seqdiagbuilder import SeqDiagBuilder SeqDiagBuilder.recordFlow() return resultData
def j(self): SeqDiagBuilder.recordFlow()