def testGetDateAndTimeFormatDictionary(self): formatDic = DateTimeUtil.getDateAndTimeFormatDictionary('DD/MM/YY HH:mm') self.assertEqual('DD/MM/YY', formatDic[DateTimeUtil.LONG_DATE_FORMAT_KEY]) self.assertEqual('DD/MM', formatDic[DateTimeUtil.SHORT_DATE_FORMAT_KEY]) self.assertEqual('HH:mm', formatDic[DateTimeUtil.TIME_FORMAT_KEY]) formatDic = DateTimeUtil.getDateAndTimeFormatDictionary('YYYY.MM.DD HH.mm') self.assertEqual('YYYY.MM.DD', formatDic[DateTimeUtil.LONG_DATE_FORMAT_KEY]) self.assertEqual('MM.DD', formatDic[DateTimeUtil.SHORT_DATE_FORMAT_KEY]) self.assertEqual('HH.mm', formatDic[DateTimeUtil.TIME_FORMAT_KEY]) formatDic = DateTimeUtil.getDateAndTimeFormatDictionary('YYYY.MM.DD HH-mm') self.assertEqual('YYYY.MM.DD', formatDic[DateTimeUtil.LONG_DATE_FORMAT_KEY]) self.assertEqual('MM.DD', formatDic[DateTimeUtil.SHORT_DATE_FORMAT_KEY]) self.assertEqual('HH-mm', formatDic[DateTimeUtil.TIME_FORMAT_KEY]) formatDic = DateTimeUtil.getDateAndTimeFormatDictionary('MM-DD-YYYY HH.mm') self.assertEqual('MM-DD-YYYY', formatDic[DateTimeUtil.LONG_DATE_FORMAT_KEY]) self.assertEqual('MM-DD', formatDic[DateTimeUtil.SHORT_DATE_FORMAT_KEY]) self.assertEqual('HH.mm', formatDic[DateTimeUtil.TIME_FORMAT_KEY])
def _validateDateTimeData(self, localNow): ''' Ensures that date/time info contained in the parsedParmData dic are valid and in a right format. If everything is ok, returns True. ['1', '10', '0', '2', '58'] #btc usd 1/10/0 2:58 [None, None, None, '2', '57'] # btc usd 1 2:57 ['11', '10', None, None, None] # neo btc 11/10 :param localNow: :return: True if date/time values stored in the parsedParmData dic are valid. If an error was detected, a new ResultData with a meaningfull error msg is returned. ''' dtFormatDic = DateTimeUtil.getDateAndTimeFormatDictionary( self.configManager.dateTimeFormat) dateShortFormat = dtFormatDic[DateTimeUtil.SHORT_DATE_FORMAT_KEY] dateLongFormat = dtFormatDic[DateTimeUtil.LONG_DATE_FORMAT_KEY] timeFormat = dtFormatDic[DateTimeUtil.TIME_FORMAT_KEY] resultData = True dayStr = self.parsedParmData[self.DAY] monthStr = self.parsedParmData[self.MONTH] yearStr = self.parsedParmData[self.YEAR] hourStr = self.parsedParmData[self.HOUR] minuteStr = self.parsedParmData[self.MINUTE] if (yearStr == '0' and monthStr == '0' and dayStr == '0'): # RT price asked return True else: # Here, the three date components are not all equal to 0 ! if (yearStr == None and monthStr == None and dayStr == None and hourStr != None and minuteStr != None): # Here, only time was specified in the full request, which is now possible. # Current day, month and year are fornatted into the parsed parm data # and True is returned self.parsedParmData[self.DAY] = localNow.format('DD') self.parsedParmData[self.MONTH] = localNow.format('MM') self.parsedParmData[self.YEAR] = localNow.format('YYYY') return True elif (yearStr == None and monthStr == None and dayStr != None and hourStr != None and minuteStr != None): # Here, only day and time were specified in the full request, which is now possible. # Current month and year are fornatted into the parsed parm data # and True is returned self.parsedParmData[self.MONTH] = localNow.format('MM') self.parsedParmData[self.YEAR] = localNow.format('YYYY') return True elif (yearStr == '0' or # yearStr is None when only day/month specified -> valid ! monthStr == '0' or monthStr == None or dayStr == '0' or dayStr == None): # only when user enters -d0 for RT price, # is yearStr equal to '0' since 0 is put # by Requesèer into day, month and year ! resultData = ResultData() resultData.setValue(ResultData.RESULT_KEY_ERROR_MSG, "ERROR - date not valid") return resultData elif len(monthStr) > 2: resultData = ResultData() resultData.setValue( ResultData.RESULT_KEY_ERROR_MSG, "ERROR - {} not conform to accepted month format (MM or M)" .format(monthStr)) return resultData elif yearStr != None: yearStrLen = len(yearStr) if yearStrLen != 2 and yearStrLen != 4: resultData = ResultData() resultData.setValue( ResultData.RESULT_KEY_ERROR_MSG, "ERROR - {} not conform to accepted year format (YYYY, YY or '')" .format(yearStr)) # avoiding that invalid year will pollute next price requests self.parsedParmData[self.YEAR] = None return resultData # validating full date. Catch inval day or inval month, # like day == 123 or day == 32 or month == 31 if yearStr == None: yearStr = str(localNow.year) if hourStr == None: hourStr = str(localNow.hour) if minuteStr == None: minuteStr = str(localNow.minute) dateTimeTupleList = [('day', dayStr, dateShortFormat), ('month', monthStr, dateShortFormat), ('year', yearStr, dateLongFormat), ('hour', hourStr, timeFormat), ('minute', minuteStr, timeFormat)] try: for name, value, format in dateTimeTupleList: int(value) except ValueError as e: resultData = ResultData() resultData.setValue( ResultData.RESULT_KEY_ERROR_MSG, "ERROR - invalid value: {} violates format for {} ({})". format(value, name, format)) return resultData try: date = DateTimeUtil.dateTimeComponentsToArrowLocalDate( int(dayStr), int(monthStr), int(yearStr), int(hourStr), int(minuteStr), 0, self.configManager.localTimeZone) except ValueError as e: resultData = ResultData() resultData.setValue(ResultData.RESULT_KEY_ERROR_MSG, "ERROR - " + str(e)) return resultData
def _parseAndFillCommandPrice(self, inputStr): ''' This method try parsing a full or a partial request. :param inputStr: :seqdiag_return CommandPrice or CommandError :return: self.commandPrice or self.commandError or None, which will cause an error to be raised ''' groupList = self._tryMatchFullPriceCommand(inputStr) requestType = None if groupList == ( ): #full command pattern not matched --> try match partial command pattern groupList = self._tryMatchPartialPriceCommand(inputStr) if groupList != (): # partial request entered. Here, parms are associated to parrm tag (i.e -c or -d). # Means they have been entered in any order and are all optional ensuring # command price temporary data like unsupported command data from previous # request are purged. Necessary here when handling partial command(s) since, unlike # when a full command is processed, the command price is not reinitialized ! requestType = REQUEST_TYPE_PARTIAL self.commandPrice.resetTemporaryData() keys = self.inputParmParmDataDicKeyDic.keys() it = iter(groupList) for command in it: value = next(it) if value != None: commandUpper = command.upper() if commandUpper in keys: self.commandPrice.parsedParmData[ self.inputParmParmDataDicKeyDic[ commandUpper]] = value else: # unknown partial command symbol self.commandPrice.parsedParmData[ self.commandPrice. UNSUPPORTED_COMMAND] = command self.commandPrice.parsedParmData[ self.commandPrice. UNSUPPORTED_COMMAND_DATA] = value if self.commandPrice.parsedParmData[ CommandPrice.DAY_MONTH_YEAR] == '0': #-d0 which means RT entered. In this case, the previous #date/time info are no longer relevant ! self.commandPrice.parsedParmData[ CommandPrice.PRICE_TYPE] = CommandPrice.PRICE_TYPE_RT hourMinute, dayMonthYear = self._wipeOutDateTimeInfoFromCommandPrice( ) elif self.commandPrice.parsedParmData[ CommandPrice. DAY_MONTH_YEAR] == None and self.commandPrice.parsedParmData[ CommandPrice.HOUR_MINUTE] == None: #here, partial command(s) which aren't date/time related were entered: the previous request price type must be considered ! if self.commandPrice.parsedParmData[ CommandPrice. PRICE_TYPE] == CommandPrice.PRICE_TYPE_RT: hourMinute, dayMonthYear = self._wipeOutDateTimeInfoFromCommandPrice( ) else: #here, since previous request was not RT, hourMinute and dayRonthYear must be rebuilt #from the date/time values of the previous request. Don't forget that OAY_MONTH_YEAR #and HOUR_MINUTE are set to None once date/time values have been acquired ! if self._isMinimalDateTimeInfoFromPreviousRequestAvailable( ): dayMonthYear, hourMinute = self._rebuildPreviousRequestDateTimeValues( ) else: return None # will cause an error. This occurs in a special situation when the previous request # was in error, which explains why the date/time info from previous request is # incoherent. Such a case is tested by TestController. # testControllerHistoDayPriceIncompleteCommandScenario else: hourMinute = self.commandPrice.parsedParmData[ CommandPrice.HOUR_MINUTE] dayMonthYear = self.commandPrice.parsedParmData[ CommandPrice.DAY_MONTH_YEAR] else: #neither full nor parrial pattern matched return None # will cause an error. else: #full request entered. Here, parms were entered in an order reflected in the # pattern: crypto fiat in this mandatory order, then date time exchange, of which order # can be different. requestType = REQUEST_TYPE_FULL self.commandPrice.initialiseParsedParmData() self.commandPrice.parsedParmData[CommandPrice.CRYPTO] = groupList[ 0] #mandatory crrypto parm, its order is fixed self.commandPrice.parsedParmData[CommandPrice.FIAT] = groupList[ 1] #mandatory fiat parm, its order is fixed optionalParsedParmDataDic = self._buildFullCommandPriceOptionalParmsDic( groupList[2:]) if optionalParsedParmDataDic != None: self.commandPrice.parsedParmData.update( optionalParsedParmDataDic) else: # invalid full command format self.commandError.parsedParmData[ self.commandError. COMMAND_ERROR_TYPE_KEY] = self.commandError.COMMAND_ERROR_TYPE_FULL_REQUEST return self.commandError hourMinute = self.commandPrice.parsedParmData[ CommandPrice.HOUR_MINUTE] dayMonthYear = self.commandPrice.parsedParmData[ CommandPrice.DAY_MONTH_YEAR] if hourMinute != None: hourMinuteList = hourMinute.split(':') if len(hourMinuteList) == 1: #supplied time is invalid: does not respect expected format of 0:10 or 12:01 etc # invalid time partial command format invalidPartialCommand, invalidValue = self._wholeParmAndInvalidValue( '-t', inputStr) dtFormatDic = DateTimeUtil.getDateAndTimeFormatDictionary( self.configMgr.dateTimeFormat) timeFormat = dtFormatDic[DateTimeUtil.TIME_FORMAT_KEY] self.commandError.parsedParmData[ self.commandError. COMMAND_ERROR_TYPE_KEY] = self.commandError.COMMAND_ERROR_TYPE_PARTIAL_REQUEST self.commandError.parsedParmData[ self.commandError. COMMAND_ERROR_MSG_KEY] = self.commandError.PARTIAL_PRICE_COMMAND_TIME_FORMAT_INVALID_MSG.format( invalidPartialCommand, invalidValue, timeFormat) # remove invalid time specification form parsedParData to avoid polluting next partial # request ! self.commandPrice.parsedParmData[ CommandPrice.HOUR_MINUTE] = None return self.commandError else: minute = hourMinuteList[1] hour = hourMinuteList[ 0] #in both cases, first item in hourMinuteList is hour else: hour = self.commandPrice.parsedParmData[CommandPrice.HOUR] minute = self.commandPrice.parsedParmData[CommandPrice.MINUTE] self._fillHourMinuteInfo(hour, minute) if dayMonthYear != None: if dayMonthYear == '0': day = '0' month = '0' year = '0' self.commandPrice.parsedParmData[ CommandPrice.PRICE_TYPE] = CommandPrice.PRICE_TYPE_RT else: dayMonthYearList = dayMonthYear.split('/') if len( dayMonthYearList ) == 1: #only day specified, the case for -d12 for example (12th of current month) day = dayMonthYearList[0] if CommandPrice.DAY in self.commandPrice.parsedParmData: month = self.commandPrice.parsedParmData[ CommandPrice.MONTH] year = self.commandPrice.parsedParmData[ CommandPrice.YEAR] else: month = None year = None self.commandPrice.parsedParmData[ CommandPrice. PRICE_TYPE] = CommandPrice.PRICE_TYPE_HISTO elif len(dayMonthYearList) == 2: day = dayMonthYearList[0] month = dayMonthYearList[1] if CommandPrice.YEAR in self.commandPrice.parsedParmData: year = self.commandPrice.parsedParmData[ CommandPrice.YEAR] else: # year not provided and not obtained from previous full price command input. # Will be set by PriceRequester which knows in which timezone we are year = None self.commandPrice.parsedParmData[ CommandPrice. PRICE_TYPE] = CommandPrice.PRICE_TYPE_HISTO elif len(dayMonthYearList) == 3: day = dayMonthYearList[0] month = dayMonthYearList[1] year = dayMonthYearList[2] self.commandPrice.parsedParmData[ CommandPrice. PRICE_TYPE] = CommandPrice.PRICE_TYPE_HISTO else: #invalid date format here ! if CommandPrice.DAY in self.commandPrice.parsedParmData: day = self.commandPrice.parsedParmData[ CommandPrice.DAY] month = self.commandPrice.parsedParmData[ CommandPrice.MONTH] year = self.commandPrice.parsedParmData[ CommandPrice.YEAR] else: day = None month = None year = None else: if CommandPrice.DAY in self.commandPrice.parsedParmData: day = self.commandPrice.parsedParmData[CommandPrice.DAY] month = self.commandPrice.parsedParmData[CommandPrice.MONTH] year = self.commandPrice.parsedParmData[CommandPrice.YEAR] if day == '0' and month == '0' and year == '0': self.commandPrice.parsedParmData[ CommandPrice.PRICE_TYPE] = CommandPrice.PRICE_TYPE_RT else: self.commandPrice.parsedParmData[ CommandPrice. PRICE_TYPE] = CommandPrice.PRICE_TYPE_HISTO else: day = None month = None year = None self._fillDayMonthYearInfo(day, month, year) priceValueData = self.commandPrice.parsedParmData[ CommandPrice.PRICE_VALUE_DATA] if priceValueData != None: return self._fillPriceValueInfo(priceValueData, requestType) else: #no partial command -v specified here ! return self.commandPrice