def metadataForInappPurchase(self, htmlTree): InappMetadata = namedtuple('InappMetadata', ['refname', 'cleared', 'languages', 'textid', 'numericid', 'price_tier', 'reviewnotes', 'hosted']) inappReferenceName = htmlTree.xpath('//span[@id="iapReferenceNameUpdateContainer"]//span/text()')[0].strip() textId = htmlTree.xpath('//div[@id="productIdText"]//span/text()')[0].strip() numericId = htmlTree.xpath('//label[.="Apple ID: "]/following-sibling::span/text()')[0].strip() hostedContent = len(htmlTree.xpath('//div[contains(@class,"hosted-content")]/following-sibling::p')) > 0 reviewNotes = htmlTree.xpath('//div[@class="hosted-review-notes"]//span/text()')[0].strip() clearedForSaleText = htmlTree.xpath('//div[contains(@class,"cleared-for-sale")]//span/text()')[0] clearedForSale = False if clearedForSaleText == 'Yes': clearedForSale = True inapptype = htmlTree.xpath('//div[@class="status-label"]//span/text()')[0].strip() priceTier = None if inapptype != "Free Subscription": priceTier = htmlTree.xpath('//tr[@id="interval-row-0"]//a/text()')[0].strip().split(' ') priceTier = int(priceTier[-1]) idAddon = "autoRenewableL" if (inapptype == "Free Subscription") else "l" languagesSpan = htmlTree.xpath('//span[@id="0' + idAddon + 'ocalizationListListRefreshContainerId"]')[0] activatedLanguages = languagesSpan.xpath('.//li[starts-with(@id, "0' + idAddon + 'ocalizationListRow")]/div[starts-with(@class, "ajaxListRowDiv")]/@itemid') activatedLangsIds = [languages.langCodeForLanguage(lang) for lang in activatedLanguages] languageAction = htmlTree.xpath('//div[@id="0' + idAddon + 'ocalizationListLightbox"]/@action')[0] # logging.info('Activated languages for inapp ' + self.numericId + ': ' + ', '.join(activatedLanguages)) logging.debug('Activated languages ids: ' + ', '.join(activatedLangsIds)) metadataLanguages = {} for langId in activatedLangsIds: metadataLanguages[langId] = {} languageParamStr = "&itemID=" + languages.appleLangIdForLanguage(langId) localizationTree = self.parseTreeForURL(languageAction + "?open=true" + languageParamStr) metadataLanguages[langId]['name'] = localizationTree.xpath('//div[@id="proposedDisplayName"]//input/@value')[0] metadataLanguages[langId]['description'] = localizationTree.xpath('//div[@id="proposedDescription"]//textarea/text()')[0].strip() localizedPublicationName = localizationTree.xpath('//div[@id="proposedPublicationName"]//input/@value') if len(localizedPublicationName) > 0: metadataLanguages[langId]['publication name'] = localizedPublicationName[0] return InappMetadata(refname=inappReferenceName , cleared=clearedForSale , languages=metadataLanguages , price_tier=priceTier , textid=textId , numericid=int(numericId) , hosted=hostedContent , reviewnotes=reviewNotes)
def editVersion(self, dataDict, lang=None, versionString=None, filename_format=None): if dataDict == None or len(dataDict) == 0: # nothing to change return if len(self.versions) == 0: self.getAppInfo() if len(self.versions) == 0: raise 'Can\'t get application versions' if versionString == None: # Suppose there's one or less editable versions versionString = next((versionString for versionString, version in self.versions.items() if version['editable']), None) if versionString == None: # Suppose there's one or less editable versions raise 'No editable version found' version = self.versions[versionString] if not version['editable']: raise 'Version ' + versionString + ' is not editable' languageId = languages.appleLangIdForLanguage(lang) languageCode = languages.langCodeForLanguage(lang) metadata = self.__parseAppVersionMetadata(version, lang) # activatedLanguages = metadata.activatedLanguages # nonactivatedLanguages = metadata.nonactivatedLanguages formData = {} #metadata.formData[languageId] formNames = metadata.formNames[languageId] submitAction = metadata.submitActions[languageId] formData["save"] = "true" formData[formNames['appNameName']] = dataDict.get('name', metadata.formData[languageId]['appNameValue']) formData[formNames['descriptionName']] = dataFromStringOrFile(dataDict.get('description', metadata.formData[languageId]['descriptionValue']), languageCode) if 'whatsNewName' in formNames: formData[formNames['whatsNewName']] = dataFromStringOrFile(dataDict.get('whats new', metadata.formData[languageId]['whatsNewValue']), languageCode) formData[formNames['keywordsName']] = dataFromStringOrFile(dataDict.get('keywords', metadata.formData[languageId]['keywordsValue']), languageCode) formData[formNames['supportURLName']] = dataDict.get('support url', metadata.formData[languageId]['supportURLValue']) formData[formNames['marketingURLName']] = dataDict.get('marketing url', metadata.formData[languageId]['marketingURLValue']) formData[formNames['pPolicyURLName']] = dataDict.get('privacy policy url', metadata.formData[languageId]['pPolicyURLValue']) iphoneUploadScreenshotForm = formNames['iphoneUploadScreenshotForm'] iphone5UploadScreenshotForm = formNames['iphone5UploadScreenshotForm'] ipadUploadScreenshotForm = formNames['ipadUploadScreenshotForm'] iphoneUploadScreenshotJS = iphoneUploadScreenshotForm.xpath('../following-sibling::script/text()')[0] iphone5UploadScreenshotJS = iphone5UploadScreenshotForm.xpath('../following-sibling::script/text()')[0] ipadUploadScreenshotJS = ipadUploadScreenshotForm.xpath('../following-sibling::script/text()')[0] self._uploadSessionData[DEVICE_TYPE.iPhone] = dict({'action': iphoneUploadScreenshotForm.attrib['action'] , 'key': iphoneUploadScreenshotForm.xpath(".//input[@name='uploadKey']/@value")[0] }, **self.parseURLSFromScript(iphoneUploadScreenshotJS)) self._uploadSessionData[DEVICE_TYPE.iPhone5] = dict({'action': iphone5UploadScreenshotForm.attrib['action'] , 'key': iphone5UploadScreenshotForm.xpath(".//input[@name='uploadKey']/@value")[0] }, **self.parseURLSFromScript(iphone5UploadScreenshotJS)) self._uploadSessionData[DEVICE_TYPE.iPad] = dict({'action': ipadUploadScreenshotForm.attrib['action'] , 'key': ipadUploadScreenshotForm.xpath(".//input[@name='uploadKey']/@value")[0] }, **self.parseURLSFromScript(ipadUploadScreenshotJS)) self._uploadSessionId = iphoneUploadScreenshotForm.xpath('.//input[@name="uploadSessionID"]/@value')[0] # get all images for device_type in [DEVICE_TYPE.iPhone, DEVICE_TYPE.iPhone5, DEVICE_TYPE.iPad]: self._images[device_type] = self.imagesForDevice(device_type) logging.debug(self._images) # logging.debug(formData) if 'images' in dataDict: imagesActions = dataDict['images'] languageCode = languages.langCodeForLanguage(lang) for dType in imagesActions: device_type = None if dType.lower() == 'iphone': device_type = DEVICE_TYPE.iPhone elif dType.lower() == 'iphone 5': device_type = DEVICE_TYPE.iPhone5 elif dType.lower() == 'ipad': device_type = DEVICE_TYPE.iPad else: continue deviceImagesActions = imagesActions[dType] if deviceImagesActions == "": continue for imageAction in deviceImagesActions: imageAction.setdefault('cmd') imageAction.setdefault('indexes') cmd = imageAction['cmd'] indexes = imageAction['indexes'] replace_language = ALIASES.language_aliases.get(languageCode, languageCode) replace_device = ALIASES.device_type_aliases.get(dType.lower(), DEVICE_TYPE.deviceStrings[device_type]) imagePath = filename_format.replace('{language}', replace_language) \ .replace('{device_type}', replace_device) logging.debug('Looking for images at ' + imagePath) if (indexes == None) and ((cmd == 'u') or (cmd == 'r')): indexes = [] for i in range(0, 5): realImagePath = imagePath.replace("{index}", str(i + 1)) logging.debug('img path: ' + realImagePath) if os.path.exists(realImagePath): indexes.append(i + 1) logging.debug('indexes ' + indexes.__str__()) logging.debug('Processing command ' + imageAction.__str__()) if (cmd == 'd') or (cmd == 'r'): # delete or replace. To perform replace we need to delete images first deleteIndexes = [img['id'] for img in self._images[device_type]] if indexes != None: deleteIndexes = [deleteIndexes[idx - 1] for idx in indexes] logging.debug('deleting images ' + deleteIndexes.__str__()) for imageIndexToDelete in deleteIndexes: img = next(im for im in self._images[device_type] if im['id'] == imageIndexToDelete) self.deleteScreenshot(device_type, img['id']) self._images[device_type] = self.imagesForDevice(device_type) if (cmd == 'u') or (cmd == 'r'): # upload or replace currentIndexes = [img['id'] for img in self._images[device_type]] if indexes == None: continue indexes = sorted(indexes) for i in indexes: realImagePath = imagePath.replace("{index}", str(i)) if os.path.exists(realImagePath): self.uploadScreenshot(device_type, realImagePath) self._images[device_type] = self.imagesForDevice(device_type) if cmd == 'r': newIndexes = [img['id'] for img in self._images[device_type]][len(currentIndexes):] if len(newIndexes) == 0: continue for i in indexes: currentIndexes.insert(i - 1, newIndexes.pop(0)) self.sortScreenshots(device_type, currentIndexes) self._images[device_type] = self.imagesForDevice(device_type) if (cmd == 's'): # sort if indexes == None or len(indexes) != len(self._images[device_type]): continue newIndexes = [self._images[device_type][i - 1]['id'] for i in indexes] self.sortScreenshots(device_type, newIndexes) self._images[device_type] = self.imagesForDevice(device_type) formData['uploadSessionID'] = self._uploadSessionId logging.debug(formData) # formData['uploadKey'] = self._uploadSessionData[DEVICE_TYPE.iPhone5]['key'] postFormResponse = self._parser.requests_session.post(ITUNESCONNECT_URL + submitAction, data = formData, cookies=cookie_jar) if postFormResponse.status_code != 200: raise 'Wrong response from iTunesConnect. Status code: ' + str(postFormResponse.status_code) if len(postFormResponse.text) > 0: logging.error("Save information failed. " + postFormResponse.text)
def editVersion(self, dataDict, lang=None, versionString=None, filename_format=None): if dataDict == None or len(dataDict) == 0: # nothing to change return if len(self.versions) == 0: self.getAppInfo() if len(self.versions) == 0: raise 'Can\'t get application versions' if versionString == None: # Suppose there's one or less editable versions versionString = next( (versionString for versionString, version in self.versions.items() if version['editable']), None) if versionString == None: # Suppose there's one or less editable versions raise 'No editable version found' version = self.versions[versionString] if not version['editable']: raise 'Version ' + versionString + ' is not editable' languageId = languages.appleLangIdForLanguage(lang) languageCode = languages.langCodeForLanguage(lang) metadata = self.__parseAppVersionMetadata(version, lang) # activatedLanguages = metadata.activatedLanguages # nonactivatedLanguages = metadata.nonactivatedLanguages formData = {} #metadata.formData[languageId] formNames = metadata.formNames[languageId] submitAction = metadata.submitActions[languageId] formData["save"] = "true" formData[formNames['appNameName']] = dataDict.get( 'name', metadata.formData[languageId]['appNameValue']) formData[formNames['descriptionName']] = dataFromStringOrFile( dataDict.get('description', metadata.formData[languageId]['descriptionValue']), languageCode) if 'whatsNewName' in formNames: formData[formNames['whatsNewName']] = dataFromStringOrFile( dataDict.get('whats new', metadata.formData[languageId]['whatsNewValue']), languageCode) formData[formNames['keywordsName']] = dataFromStringOrFile( dataDict.get('keywords', metadata.formData[languageId]['keywordsValue']), languageCode) formData[formNames['supportURLName']] = dataDict.get( 'support url', metadata.formData[languageId]['supportURLValue']) formData[formNames['marketingURLName']] = dataDict.get( 'marketing url', metadata.formData[languageId]['marketingURLValue']) formData[formNames['pPolicyURLName']] = dataDict.get( 'privacy policy url', metadata.formData[languageId]['pPolicyURLValue']) iphoneUploadScreenshotForm = formNames['iphoneUploadScreenshotForm'] iphone5UploadScreenshotForm = formNames['iphone5UploadScreenshotForm'] ipadUploadScreenshotForm = formNames['ipadUploadScreenshotForm'] iphoneUploadScreenshotJS = iphoneUploadScreenshotForm.xpath( '../following-sibling::script/text()')[0] iphone5UploadScreenshotJS = iphone5UploadScreenshotForm.xpath( '../following-sibling::script/text()')[0] ipadUploadScreenshotJS = ipadUploadScreenshotForm.xpath( '../following-sibling::script/text()')[0] self._uploadSessionData[DEVICE_TYPE.iPhone] = dict( { 'action': iphoneUploadScreenshotForm.attrib['action'], 'key': iphoneUploadScreenshotForm.xpath( ".//input[@name='uploadKey']/@value")[0] }, **self.parseURLSFromScript(iphoneUploadScreenshotJS)) self._uploadSessionData[DEVICE_TYPE.iPhone5] = dict( { 'action': iphone5UploadScreenshotForm.attrib['action'], 'key': iphone5UploadScreenshotForm.xpath( ".//input[@name='uploadKey']/@value")[0] }, **self.parseURLSFromScript(iphone5UploadScreenshotJS)) self._uploadSessionData[DEVICE_TYPE.iPad] = dict( { 'action': ipadUploadScreenshotForm.attrib['action'], 'key': ipadUploadScreenshotForm.xpath( ".//input[@name='uploadKey']/@value")[0] }, **self.parseURLSFromScript(ipadUploadScreenshotJS)) self._uploadSessionId = iphoneUploadScreenshotForm.xpath( './/input[@name="uploadSessionID"]/@value')[0] # get all images for device_type in [ DEVICE_TYPE.iPhone, DEVICE_TYPE.iPhone5, DEVICE_TYPE.iPad ]: self._images[device_type] = self.imagesForDevice(device_type) logging.debug(self._images) # logging.debug(formData) if 'images' in dataDict: imagesActions = dataDict['images'] languageCode = languages.langCodeForLanguage(lang) for dType in imagesActions: device_type = None if dType.lower() == 'iphone': device_type = DEVICE_TYPE.iPhone elif dType.lower() == 'iphone 5': device_type = DEVICE_TYPE.iPhone5 elif dType.lower() == 'ipad': device_type = DEVICE_TYPE.iPad else: continue deviceImagesActions = imagesActions[dType] if deviceImagesActions == "": continue for imageAction in deviceImagesActions: imageAction.setdefault('cmd') imageAction.setdefault('indexes') cmd = imageAction['cmd'] indexes = imageAction['indexes'] replace_language = ALIASES.language_aliases.get( languageCode, languageCode) replace_device = ALIASES.device_type_aliases.get( dType.lower(), DEVICE_TYPE.deviceStrings[device_type]) imagePath = filename_format.replace('{language}', replace_language) \ .replace('{device_type}', replace_device) logging.debug('Looking for images at ' + imagePath) if (indexes == None) and ((cmd == 'u') or (cmd == 'r')): indexes = [] for i in range(0, 5): realImagePath = imagePath.replace( "{index}", str(i + 1)) logging.debug('img path: ' + realImagePath) if os.path.exists(realImagePath): indexes.append(i + 1) logging.debug('indexes ' + indexes.__str__()) logging.debug('Processing command ' + imageAction.__str__()) if (cmd == 'd') or ( cmd == 'r' ): # delete or replace. To perform replace we need to delete images first deleteIndexes = [ img['id'] for img in self._images[device_type] ] if indexes != None: deleteIndexes = [ deleteIndexes[idx - 1] for idx in indexes ] logging.debug('deleting images ' + deleteIndexes.__str__()) for imageIndexToDelete in deleteIndexes: img = next(im for im in self._images[device_type] if im['id'] == imageIndexToDelete) self.deleteScreenshot(device_type, img['id']) self._images[device_type] = self.imagesForDevice( device_type) if (cmd == 'u') or (cmd == 'r'): # upload or replace currentIndexes = [ img['id'] for img in self._images[device_type] ] if indexes == None: continue indexes = sorted(indexes) for i in indexes: realImagePath = imagePath.replace( "{index}", str(i)) if os.path.exists(realImagePath): self.uploadScreenshot(device_type, realImagePath) self._images[device_type] = self.imagesForDevice( device_type) if cmd == 'r': newIndexes = [ img['id'] for img in self._images[device_type] ][len(currentIndexes):] if len(newIndexes) == 0: continue for i in indexes: currentIndexes.insert(i - 1, newIndexes.pop(0)) self.sortScreenshots(device_type, currentIndexes) self._images[device_type] = self.imagesForDevice( device_type) if (cmd == 's'): # sort if indexes == None or len(indexes) != len( self._images[device_type]): continue newIndexes = [ self._images[device_type][i - 1]['id'] for i in indexes ] self.sortScreenshots(device_type, newIndexes) self._images[device_type] = self.imagesForDevice( device_type) formData['uploadSessionID'] = self._uploadSessionId logging.debug(formData) # formData['uploadKey'] = self._uploadSessionData[DEVICE_TYPE.iPhone5]['key'] postFormResponse = self._parser.requests_session.post( ITUNESCONNECT_URL + submitAction, data=formData, cookies=cookie_jar) if postFormResponse.status_code != 200: raise 'Wrong response from iTunesConnect. Status code: ' + str( postFormResponse.status_code) if len(postFormResponse.text) > 0: logging.error("Save information failed. " + postFormResponse.text)
def parseCreateOrEditPage(self, htmlTree, version, language=None): tree = htmlTree AppMetadata = namedtuple('AppMetadata', ['activatedLanguages', 'nonactivatedLanguages' , 'formData', 'formNames', 'submitActions']) localizationLightboxAction = tree.xpath("//div[@id='localizationLightbox']/@action")[0] # if no lang provided, edit default #localizationLightboxUpdateAction = tree.xpath("//span[@id='localizationLightboxUpdate']/@action")[0] activatedLanguages = tree.xpath('//div[@id="modules-dropdown"] \ /ul/li[count(preceding-sibling::li[@class="heading"])=1]/a/text()') nonactivatedLanguages = tree.xpath('//div[@id="modules-dropdown"] \ /ul/li[count(preceding-sibling::li[@class="heading"])=2]/a/text()') activatedLanguages = [lng.replace("(Default)", "").strip() for lng in activatedLanguages] logging.info('Activated languages: ' + ', '.join(activatedLanguages)) logging.debug('Nonactivated languages: ' + ', '.join(nonactivatedLanguages)) langs = activatedLanguages if language != None: langs = [language] formData = {} formNames = {} submitActions = {} versionString = version['versionString'] for lang in langs: logging.info('Processing language: ' + lang) languageId = languages.appleLangIdForLanguage(lang) logging.debug('Apple language id: ' + languageId) if lang in activatedLanguages: logging.info('Getting metadata for ' + lang + '. Version: ' + versionString) elif lang in nonactivatedLanguages: logging.info('Add ' + lang + ' for version ' + versionString) editTree = self.parseTreeForURL(localizationLightboxAction + "?open=true" + ("&language=" + languageId if (languageId != None) else "")) hasWhatsNew = False formDataForLang = {} formNamesForLang = {} submitActionForLang = editTree.xpath("//div[@class='lcAjaxLightboxContentsWrapper']/div[@class='lcAjaxLightboxContents']/@action")[0] formNamesForLang['appNameName'] = editTree.xpath("//div[@id='appNameUpdateContainerId']//input/@name")[0] formNamesForLang['descriptionName'] = editTree.xpath("//div[@id='descriptionUpdateContainerId']//textarea/@name")[0] whatsNewName = editTree.xpath("//div[@id='whatsNewinthisVersionUpdateContainerId']//textarea/@name") if len(whatsNewName) > 0: # there's no what's new section for first version hasWhatsNew = True formNamesForLang['whatsNewName'] = whatsNewName[0] formNamesForLang['keywordsName'] = editTree.xpath("//div/label[.='Keywords']/..//input/@name")[0] formNamesForLang['supportURLName'] = editTree.xpath("//div/label[.='Support URL']/..//input/@name")[0] formNamesForLang['marketingURLName'] = editTree.xpath("//div/label[contains(., 'Marketing URL')]/..//input/@name")[0] formNamesForLang['pPolicyURLName'] = editTree.xpath("//div/label[contains(., 'Privacy Policy URL')]/..//input/@name")[0] formDataForLang['appNameValue'] = editTree.xpath("//div[@id='appNameUpdateContainerId']//input/@value")[0] formDataForLang['descriptionValue'] = getElement(editTree.xpath("//div[@id='descriptionUpdateContainerId']//textarea/text()"), 0) whatsNewValue = editTree.xpath("//div[@id='whatsNewinthisVersionUpdateContainerId']//textarea/text()") if len(whatsNewValue) > 0 and hasWhatsNew: formDataForLang['whatsNewValue'] = getElement(whatsNewValue, 0) formDataForLang['keywordsValue'] = getElement(editTree.xpath("//div/label[.='Keywords']/..//input/@value"), 0) formDataForLang['supportURLValue'] = getElement(editTree.xpath("//div/label[.='Support URL']/..//input/@value"), 0) formDataForLang['marketingURLValue'] = getElement(editTree.xpath("//div/label[contains(., 'Marketing URL')]/..//input/@value"), 0) formDataForLang['pPolicyURLValue'] = getElement(editTree.xpath("//div/label[contains(., 'Privacy Policy URL')]/..//input/@value"), 0) logging.debug("Old values:") logging.debug(formDataForLang) iphoneUploadScreenshotForm = editTree.xpath("//form[@name='FileUploadForm_35InchRetinaDisplayScreenshots']")[0] iphone5UploadScreenshotForm = editTree.xpath("//form[@name='FileUploadForm_iPhone5']")[0] ipadUploadScreenshotForm = editTree.xpath("//form[@name='FileUploadForm_iPadScreenshots']")[0] formNamesForLang['iphoneUploadScreenshotForm'] = iphoneUploadScreenshotForm formNamesForLang['iphone5UploadScreenshotForm'] = iphone5UploadScreenshotForm formNamesForLang['ipadUploadScreenshotForm'] = ipadUploadScreenshotForm formData[languageId] = formDataForLang formNames[languageId] = formNamesForLang submitActions[languageId] = submitActionForLang metadata = AppMetadata(activatedLanguages=activatedLanguages , nonactivatedLanguages=nonactivatedLanguages , formData=formData , formNames=formNames , submitActions=submitActions) return metadata
def parseCreateOrEditPage(self, htmlTree, version, language=None): tree = htmlTree AppMetadata = namedtuple('AppMetadata', [ 'activatedLanguages', 'nonactivatedLanguages', 'formData', 'formNames', 'submitActions' ]) localizationLightboxAction = tree.xpath( "//div[@id='localizationLightbox']/@action")[ 0] # if no lang provided, edit default #localizationLightboxUpdateAction = tree.xpath("//span[@id='localizationLightboxUpdate']/@action")[0] activatedLanguages = tree.xpath('//div[@id="modules-dropdown"] \ /ul/li[count(preceding-sibling::li[@class="heading"])=1]/a/text()' ) nonactivatedLanguages = tree.xpath('//div[@id="modules-dropdown"] \ /ul/li[count(preceding-sibling::li[@class="heading"])=2]/a/text()' ) activatedLanguages = [ lng.replace("(Default)", "").strip() for lng in activatedLanguages ] logging.info('Activated languages: ' + ', '.join(activatedLanguages)) logging.debug('Nonactivated languages: ' + ', '.join(nonactivatedLanguages)) langs = activatedLanguages if language != None: langs = [language] formData = {} formNames = {} submitActions = {} versionString = version['versionString'] for lang in langs: logging.info('Processing language: ' + lang) languageId = languages.appleLangIdForLanguage(lang) logging.debug('Apple language id: ' + languageId) if lang in activatedLanguages: logging.info('Getting metadata for ' + lang + '. Version: ' + versionString) elif lang in nonactivatedLanguages: logging.info('Add ' + lang + ' for version ' + versionString) editTree = self.parseTreeForURL( localizationLightboxAction + "?open=true" + ("&language=" + languageId if (languageId != None) else "")) hasWhatsNew = False formDataForLang = {} formNamesForLang = {} submitActionForLang = editTree.xpath( "//div[@class='lcAjaxLightboxContentsWrapper']/div[@class='lcAjaxLightboxContents']/@action" )[0] formNamesForLang['appNameName'] = editTree.xpath( "//div[@id='appNameUpdateContainerId']//input/@name")[0] formNamesForLang['descriptionName'] = editTree.xpath( "//div[@id='descriptionUpdateContainerId']//textarea/@name")[0] whatsNewName = editTree.xpath( "//div[@id='whatsNewinthisVersionUpdateContainerId']//textarea/@name" ) if len(whatsNewName ) > 0: # there's no what's new section for first version hasWhatsNew = True formNamesForLang['whatsNewName'] = whatsNewName[0] formNamesForLang['keywordsName'] = editTree.xpath( "//div/label[.='Keywords']/..//input/@name")[0] formNamesForLang['supportURLName'] = editTree.xpath( "//div/label[.='Support URL']/..//input/@name")[0] formNamesForLang['marketingURLName'] = editTree.xpath( "//div/label[contains(., 'Marketing URL')]/..//input/@name")[0] formNamesForLang['pPolicyURLName'] = editTree.xpath( "//div/label[contains(., 'Privacy Policy URL')]/..//input/@name" )[0] formDataForLang['appNameValue'] = editTree.xpath( "//div[@id='appNameUpdateContainerId']//input/@value")[0] formDataForLang['descriptionValue'] = getElement( editTree.xpath( "//div[@id='descriptionUpdateContainerId']//textarea/text()" ), 0) whatsNewValue = editTree.xpath( "//div[@id='whatsNewinthisVersionUpdateContainerId']//textarea/text()" ) if len(whatsNewValue) > 0 and hasWhatsNew: formDataForLang['whatsNewValue'] = getElement(whatsNewValue, 0) formDataForLang['keywordsValue'] = getElement( editTree.xpath("//div/label[.='Keywords']/..//input/@value"), 0) formDataForLang['supportURLValue'] = getElement( editTree.xpath( "//div/label[.='Support URL']/..//input/@value"), 0) formDataForLang['marketingURLValue'] = getElement( editTree.xpath( "//div/label[contains(., 'Marketing URL')]/..//input/@value" ), 0) formDataForLang['pPolicyURLValue'] = getElement( editTree.xpath( "//div/label[contains(., 'Privacy Policy URL')]/..//input/@value" ), 0) logging.debug("Old values:") logging.debug(formDataForLang) iphoneUploadScreenshotForm = editTree.xpath( "//form[@name='FileUploadForm_35InchRetinaDisplayScreenshots']" )[0] iphone5UploadScreenshotForm = editTree.xpath( "//form[@name='FileUploadForm_iPhone5']")[0] ipadUploadScreenshotForm = editTree.xpath( "//form[@name='FileUploadForm_iPadScreenshots']")[0] formNamesForLang[ 'iphoneUploadScreenshotForm'] = iphoneUploadScreenshotForm formNamesForLang[ 'iphone5UploadScreenshotForm'] = iphone5UploadScreenshotForm formNamesForLang[ 'ipadUploadScreenshotForm'] = ipadUploadScreenshotForm formData[languageId] = formDataForLang formNames[languageId] = formNamesForLang submitActions[languageId] = submitActionForLang metadata = AppMetadata(activatedLanguages=activatedLanguages, nonactivatedLanguages=nonactivatedLanguages, formData=formData, formNames=formNames, submitActions=submitActions) return metadata
def update(self, inappDict): tree = self._parser.parseTreeForURL(ITCInappPurchase.actionURLs['itemActionUrl'] + "?itemID=" + self.numericId) # for non-consumable iap we can change name, cleared-for-sale and pricing. Check if we need to: inappReferenceName = tree.xpath('//span[@id="iapReferenceNameUpdateContainer"]//span/text()')[0] clearedForSaleText = tree.xpath('//div[contains(@class,"cleared-for-sale")]//span/text()')[0] clearedForSale = False if clearedForSaleText == 'Yes': clearedForSale = True logging.debug('Updating inapp: ' + inappDict.__str__()) self.name = inappDict.get('name', self.name) self.clearedForSale = inappDict.get('cleared', self.clearedForSale) self.hostingContentWithApple = inappDict.get('hosting content with apple', self.hostingContentWithApple) self.reviewNotes = inappDict.get('review notes', self.reviewNotes) # TODO: change price tier if (inappReferenceName != self.name) \ or (clearedForSale != self.clearedForSale): editAction = tree.xpath('//div[@id="singleAddonPricingLightbox"]/@action')[0] inappTree = self._parser.parseTreeForURL(editAction) inappReferenceNameName = inappTree.xpath('//div[@id="referenceNameTooltipId"]/..//input/@name')[0] clearedForSaleName = inappTree.xpath('//div[contains(@class,"cleared-for-sale")]//input[@classname="radioTrue"]/@name')[0] clearedForSaleNames = {} clearedForSaleNames["true"] = inappTree.xpath('//div[contains(@class,"cleared-for-sale")]//input[@classname="radioTrue"]/@value')[0] clearedForSaleNames["false"] = inappTree.xpath('//div[contains(@class,"cleared-for-sale")]//input[@classname="radioFalse"]/@value')[0] inappPriceTierName = inappTree.xpath('//select[@id="price_tier_popup"]/@name')[0] dateComponentsNames = inappTree.xpath('//select[contains(@id, "_day")]/@name') dateComponentsNames.extend(inappTree.xpath('//select[contains(@id, "_month")]/@name')) dateComponentsNames.extend(inappTree.xpath('//select[contains(@id, "_year")]/@name')) postAction = inappTree.xpath('//div[@class="lcAjaxLightboxContents"]/@action')[0] formData = {} formData[inappReferenceNameName] = self.name formData[clearedForSaleName] = clearedForSaleNames["true" if self.clearedForSale else "false"] formData[inappPriceTierName] = 'WONoSelectionString' for dcn in dateComponentsNames: formData[dcn] = 'WONoSelectionString' formData['save'] = "true" postFormResponse = requests.post(ITUNESCONNECT_URL + postAction, data = formData, cookies=cookie_jar) if postFormResponse.status_code != 200: raise 'Wrong response from iTunesConnect. Status code: ' + str(postFormResponse.status_code) idAddon = "autoRenewableL" if (inapptype == "Free Subscription") else "l" languagesSpan = inappTree.xpath('//span[@id="0' + idAddon + 'ocalizationListListRefreshContainerId"]')[0] activatedLanguages = languagesSpan.xpath('.//li[starts-with(@id, "0' + idAddon + 'ocalizationListRow")]/div[starts-with(@class, "ajaxListRowDiv")]/@itemid') activatedLangsIds = [languages.langCodeForLanguage(lang) for lang in activatedLanguages] languageAction = tree.xpath('//div[@id="0' + idAddon + 'ocalizationListLightbox"]/@action')[0] logging.info('Activated languages for inapp ' + self.numericId + ': ' + ', '.join(activatedLanguages)) logging.debug('Activated languages ids: ' + ', '.join(activatedLangsIds)) langDict = inappDict.get('languages', {}) for langId, langVal in langDict.items(): if type(langVal) is str: if langId in activatedLangsIds and langVal == 'd': # TODO: delete lang pass return languageParamStr = "" isEdit = False if langId in activatedLangsIds: # edit languageParamStr = "&itemID=" + languages.appleLangIdForLanguage(langId) isEdit = True localizationTree = self._parser.parseTreeForURL(languageAction + "?open=true" + languageParamStr) self.__createUpdateLanguage(localizationTree, langId, langVal, isEdit=isEdit) # upload screenshot, edit review notes, hosting content with apple, etc formData = {"save":"true"} editHostedContentAction = tree.xpath('//div[@id="versionLightboxId0"]/@action')[0] hostedContentTree = self._parser.parseTreeForURL(editHostedContentAction + "?open=true") saveEditHostedContentAction = hostedContentTree.xpath('//div[@class="lcAjaxLightboxContents"]/@action')[0] if (self.type == "Non-Consumable"): hostingContentName = hostedContentTree.xpath('//div[contains(@class,"hosting-on-apple")]//input[@classname="radioTrue"]/@name')[0] hostingContentNames = {} hostingContentNames["true"] = hostedContentTree.xpath('//div[contains(@class,"hosting-on-apple")]//input[@classname="radioTrue"]/@value')[0] hostingContentNames["false"] = hostedContentTree.xpath('//div[contains(@class,"hosting-on-apple")]//input[@classname="radioFalse"]/@value')[0] formData[hostingContentName] = hostingContentNames["true" if self.hostingContentWithApple else "false"] if inappDict['review screenshot'] != None: uploadForm = hostedContentTree.xpath('//form[@name="FileUploadForm__screenshotId"]')[0] self._uploadScreenshotAction = uploadForm.xpath('./@action')[0] self._uploadSessionId = uploadForm.xpath('.//input[@id="uploadSessionID"]/@value')[0] self._uploadScreenshotKey = uploadForm.xpath('.//input[@id="uploadKey"]/@value')[0] statusURLScript = hostedContentTree.xpath('//script[contains(., "var uploader_screenshotId")]/text()')[0] matches = re.findall('statusURL:\s\'([^\']+)\'', statusURLScript) self._statusURL = matches[0] self.__uploadScreenshot(inappDict['review screenshot']) requests.get(ITUNESCONNECT_URL + self._statusURL, cookies=cookie_jar) formData["uploadSessionID"] = self._uploadSessionId formData["uploadKey"] = self._uploadScreenshotKey formData["filename"] = inappDict['review screenshot'] reviewNotesName = hostedContentTree.xpath('//div[@class="hosted-review-notes"]//textarea/@name')[0] formData[reviewNotesName] = self.reviewNotes self._parser.parseTreeForURL(saveEditHostedContentAction, method="POST", payload=formData)
def update(self, inappDict): tree = self._parser.parseTreeForURL( ITCInappPurchase.actionURLs['itemActionUrl'] + "?itemID=" + self.numericId) # for non-consumable iap we can change name, cleared-for-sale and pricing. Check if we need to: inappReferenceName = tree.xpath( '//span[@id="iapReferenceNameUpdateContainer"]//span/text()')[0] clearedForSaleText = tree.xpath( '//div[contains(@class,"cleared-for-sale")]//span/text()')[0] clearedForSale = False if clearedForSaleText == 'Yes': clearedForSale = True logging.debug('Updating inapp: ' + inappDict.__str__()) self.name = inappDict.get('name', self.name) self.clearedForSale = inappDict.get('cleared', self.clearedForSale) self.hostingContentWithApple = inappDict.get( 'hosting content with apple', self.hostingContentWithApple) self.reviewNotes = inappDict.get('review notes', self.reviewNotes) # TODO: change price tier if (inappReferenceName != self.name) \ or (clearedForSale != self.clearedForSale): editAction = tree.xpath( '//div[@id="singleAddonPricingLightbox"]/@action')[0] inappTree = self._parser.parseTreeForURL(editAction) inappReferenceNameName = inappTree.xpath( '//div[@id="referenceNameTooltipId"]/..//input/@name')[0] clearedForSaleName = inappTree.xpath( '//div[contains(@class,"cleared-for-sale")]//input[@classname="radioTrue"]/@name' )[0] clearedForSaleNames = {} clearedForSaleNames["true"] = inappTree.xpath( '//div[contains(@class,"cleared-for-sale")]//input[@classname="radioTrue"]/@value' )[0] clearedForSaleNames["false"] = inappTree.xpath( '//div[contains(@class,"cleared-for-sale")]//input[@classname="radioFalse"]/@value' )[0] inappPriceTierName = inappTree.xpath( '//select[@id="price_tier_popup"]/@name')[0] dateComponentsNames = inappTree.xpath( '//select[contains(@id, "_day")]/@name') dateComponentsNames.extend( inappTree.xpath('//select[contains(@id, "_month")]/@name')) dateComponentsNames.extend( inappTree.xpath('//select[contains(@id, "_year")]/@name')) postAction = inappTree.xpath( '//div[@class="lcAjaxLightboxContents"]/@action')[0] formData = {} formData[inappReferenceNameName] = self.name formData[clearedForSaleName] = clearedForSaleNames[ "true" if self.clearedForSale else "false"] formData[inappPriceTierName] = 'WONoSelectionString' for dcn in dateComponentsNames: formData[dcn] = 'WONoSelectionString' formData['save'] = "true" postFormResponse = self._parser.requests_session.post( ITUNESCONNECT_URL + postAction, data=formData, cookies=cookie_jar) if postFormResponse.status_code != 200: raise 'Wrong response from iTunesConnect. Status code: ' + str( postFormResponse.status_code) idAddon = "autoRenewableL" if (inapptype == "Free Subscription") else "l" languagesSpan = inappTree.xpath( '//span[@id="0' + idAddon + 'ocalizationListListRefreshContainerId"]')[0] activatedLanguages = languagesSpan.xpath( './/li[starts-with(@id, "0' + idAddon + 'ocalizationListRow")]/div[starts-with(@class, "ajaxListRowDiv")]/@itemid' ) activatedLangsIds = [ languages.langCodeForLanguage(lang) for lang in activatedLanguages ] languageAction = tree.xpath('//div[@id="0' + idAddon + 'ocalizationListLightbox"]/@action')[0] logging.info('Activated languages for inapp ' + self.numericId + ': ' + ', '.join(activatedLanguages)) logging.debug('Activated languages ids: ' + ', '.join(activatedLangsIds)) langDict = inappDict.get('languages', {}) for langId, langVal in langDict.items(): if type(langVal) is str: if langId in activatedLangsIds and langVal == 'd': # TODO: delete lang pass return languageParamStr = "" isEdit = False if langId in activatedLangsIds: # edit languageParamStr = "&itemID=" + languages.appleLangIdForLanguage( langId) isEdit = True localizationTree = self._parser.parseTreeForURL(languageAction + "?open=true" + languageParamStr) self.__createUpdateLanguage(localizationTree, langId, langVal, isEdit=isEdit) # upload screenshot, edit review notes, hosting content with apple, etc formData = {"save": "true"} editHostedContentAction = tree.xpath( '//div[@id="versionLightboxId0"]/@action')[0] hostedContentTree = self._parser.parseTreeForURL( editHostedContentAction + "?open=true") saveEditHostedContentAction = hostedContentTree.xpath( '//div[@class="lcAjaxLightboxContents"]/@action')[0] if (self.type == "Non-Consumable"): hostingContentName = hostedContentTree.xpath( '//div[contains(@class,"hosting-on-apple")]//input[@classname="radioTrue"]/@name' )[0] hostingContentNames = {} hostingContentNames["true"] = hostedContentTree.xpath( '//div[contains(@class,"hosting-on-apple")]//input[@classname="radioTrue"]/@value' )[0] hostingContentNames["false"] = hostedContentTree.xpath( '//div[contains(@class,"hosting-on-apple")]//input[@classname="radioFalse"]/@value' )[0] formData[hostingContentName] = hostingContentNames[ "true" if self.hostingContentWithApple else "false"] if inappDict['review screenshot'] != None: uploadForm = hostedContentTree.xpath( '//form[@name="FileUploadForm__screenshotId"]')[0] self._uploadScreenshotAction = uploadForm.xpath('./@action')[0] self._uploadSessionId = uploadForm.xpath( './/input[@id="uploadSessionID"]/@value')[0] self._uploadScreenshotKey = uploadForm.xpath( './/input[@id="uploadKey"]/@value')[0] statusURLScript = hostedContentTree.xpath( '//script[contains(., "var uploader_screenshotId")]/text()')[0] matches = re.findall('statusURL:\s\'([^\']+)\'', statusURLScript) self._statusURL = matches[0] self.__uploadScreenshot(inappDict['review screenshot']) self._parser.requests_session.get(ITUNESCONNECT_URL + self._statusURL, cookies=cookie_jar) formData["uploadSessionID"] = self._uploadSessionId formData["uploadKey"] = self._uploadScreenshotKey formData["filename"] = inappDict['review screenshot'] reviewNotesName = hostedContentTree.xpath( '//div[@class="hosted-review-notes"]//textarea/@name')[0] formData[reviewNotesName] = self.reviewNotes self._parser.parseTreeForURL(saveEditHostedContentAction, method="POST", payload=formData)
def metadataForInappPurchase(self, htmlTree): InappMetadata = namedtuple('InappMetadata', [ 'refname', 'cleared', 'languages', 'textid', 'numericid', 'price_tier', 'reviewnotes', 'hosted' ]) inappReferenceName = htmlTree.xpath( '//span[@id="iapReferenceNameUpdateContainer"]//span/text()' )[0].strip() textId = htmlTree.xpath( '//div[@id="productIdText"]//span/text()')[0].strip() numericId = htmlTree.xpath( '//label[.="Apple ID: "]/following-sibling::span/text()')[0].strip( ) hostedContent = len( htmlTree.xpath( '//div[contains(@class,"hosted-content")]/following-sibling::p' )) > 0 reviewNotes = htmlTree.xpath( '//div[@class="hosted-review-notes"]//span/text()')[0].strip() clearedForSaleText = htmlTree.xpath( '//div[contains(@class,"cleared-for-sale")]//span/text()')[0] clearedForSale = False if clearedForSaleText == 'Yes': clearedForSale = True inapptype = htmlTree.xpath( '//div[@class="status-label"]//span/text()')[0].strip() priceTier = None if inapptype != "Free Subscription": priceTier = htmlTree.xpath( '//tr[@id="interval-row-0"]//a/text()')[0].strip().split(' ') priceTier = int(priceTier[-1]) idAddon = "autoRenewableL" if (inapptype == "Free Subscription") else "l" languagesSpan = htmlTree.xpath( '//span[@id="0' + idAddon + 'ocalizationListListRefreshContainerId"]')[0] activatedLanguages = languagesSpan.xpath( './/li[starts-with(@id, "0' + idAddon + 'ocalizationListRow")]/div[starts-with(@class, "ajaxListRowDiv")]/@itemid' ) activatedLangsIds = [ languages.langCodeForLanguage(lang) for lang in activatedLanguages ] languageAction = htmlTree.xpath('//div[@id="0' + idAddon + 'ocalizationListLightbox"]/@action')[0] # logging.info('Activated languages for inapp ' + self.numericId + ': ' + ', '.join(activatedLanguages)) logging.debug('Activated languages ids: ' + ', '.join(activatedLangsIds)) metadataLanguages = {} for langId in activatedLangsIds: metadataLanguages[langId] = {} languageParamStr = "&itemID=" + languages.appleLangIdForLanguage( langId) localizationTree = self.parseTreeForURL(languageAction + "?open=true" + languageParamStr) metadataLanguages[langId]['name'] = localizationTree.xpath( '//div[@id="proposedDisplayName"]//input/@value')[0] metadataLanguages[langId]['description'] = localizationTree.xpath( '//div[@id="proposedDescription"]//textarea/text()')[0].strip( ) localizedPublicationName = localizationTree.xpath( '//div[@id="proposedPublicationName"]//input/@value') if len(localizedPublicationName) > 0: metadataLanguages[langId][ 'publication name'] = localizedPublicationName[0] return InappMetadata(refname=inappReferenceName, cleared=clearedForSale, languages=metadataLanguages, price_tier=priceTier, textid=textId, numericid=int(numericId), hosted=hostedContent, reviewnotes=reviewNotes)
def editVersion(self, dataDict, lang=None, versionString=None, filename_format=None): if dataDict == None or len(dataDict) == 0: # nothing to change return if len(self.versions) == 0: self.getAppInfo() if len(self.versions) == 0: raise "Can't get application versions" if versionString == None: # Suppose there's one or less editable versions versionString = next( (versionString for versionString, version in self.versions.items() if version["editable"]), None ) if versionString == None: # Suppose there's one or less editable versions raise "No editable version found" version = self.versions[versionString] if not version["editable"]: raise "Version " + versionString + " is not editable" languageId = languages.appleLangIdForLanguage(lang) languageCode = languages.langCodeForLanguage(lang) metadata = self.__parseAppVersionMetadata(version, lang) # activatedLanguages = metadata.activatedLanguages # nonactivatedLanguages = metadata.nonactivatedLanguages formData = {} # metadata.formData[languageId] formNames = metadata.formNames[languageId] submitAction = metadata.submitActions[languageId] formData["save"] = "true" if "name" in dataDict: formData[formNames["appNameName"]] = dataDict["name"] if "description" in dataDict: if isinstance(dataDict["description"], basestring): formData[formNames["descriptionName"]] = dataDict["description"] elif isinstance(dataDict["description"], dict): if "file name format" in dataDict["description"]: desc_filename_format = dataDict["description"]["file name format"] replace_language = ALIASES.language_aliases.get(languageCode, languageCode) descriptionFilePath = desc_filename_format.replace("{language}", replace_language) formData[formNames["descriptionName"]] = open(descriptionFilePath, "r").read() if ("whatsNewName" in formNames) and ("whats new" in dataDict): if isinstance(dataDict["whats new"], basestring): formData[formNames["whatsNewName"]] = dataDict["whats new"] elif isinstance(dataDict["whats new"], dict): if "file name format" in dataDict["whats new"]: desc_filename_format = dataDict["whats new"]["file name format"] replace_language = ALIASES.language_aliases.get(languageCode, languageCode) descriptionFilePath = desc_filename_format.replace("{language}", replace_language) formData[formNames["whatsNewName"]] = open(descriptionFilePath, "r").read() if "keywords" in dataDict: if isinstance(dataDict["keywords"], basestring): formData[formNames["keywordsName"]] = dataDict["keywords"] elif isinstance(dataDict["keywords"], dict): if "file name format" in dataDict["keywords"]: desc_filename_format = dataDict["keywords"]["file name format"] replace_language = ALIASES.language_aliases.get(languageCode, languageCode) descriptionFilePath = desc_filename_format.replace("{language}", replace_language) formData[formNames["keywordsName"]] = open(descriptionFilePath, "r").read() if "support url" in dataDict: formData[formNames["supportURLName"]] = dataDict["support url"] if "marketing url" in dataDict: formData[formNames["marketingURLName"]] = dataDict["marketing url"] if "privacy policy url" in dataDict: formData[formNames["pPolicyURLName"]] = dataDict["privacy policy url"] iphoneUploadScreenshotForm = formNames["iphoneUploadScreenshotForm"] iphone5UploadScreenshotForm = formNames["iphone5UploadScreenshotForm"] ipadUploadScreenshotForm = formNames["ipadUploadScreenshotForm"] iphoneUploadScreenshotJS = iphoneUploadScreenshotForm.xpath("../following-sibling::script/text()")[0] iphone5UploadScreenshotJS = iphone5UploadScreenshotForm.xpath("../following-sibling::script/text()")[0] ipadUploadScreenshotJS = ipadUploadScreenshotForm.xpath("../following-sibling::script/text()")[0] self._uploadSessionData[DEVICE_TYPE.iPhone] = dict( { "action": iphoneUploadScreenshotForm.attrib["action"], "key": iphoneUploadScreenshotForm.xpath(".//input[@name='uploadKey']/@value")[0], }, **self.__parseURLSFromScript(iphoneUploadScreenshotJS) ) self._uploadSessionData[DEVICE_TYPE.iPhone5] = dict( { "action": iphone5UploadScreenshotForm.attrib["action"], "key": iphone5UploadScreenshotForm.xpath(".//input[@name='uploadKey']/@value")[0], }, **self.__parseURLSFromScript(iphone5UploadScreenshotJS) ) self._uploadSessionData[DEVICE_TYPE.iPad] = dict( { "action": ipadUploadScreenshotForm.attrib["action"], "key": ipadUploadScreenshotForm.xpath(".//input[@name='uploadKey']/@value")[0], }, **self.__parseURLSFromScript(ipadUploadScreenshotJS) ) self._uploadSessionId = iphoneUploadScreenshotForm.xpath('.//input[@name="uploadSessionID"]/@value')[0] # get all images for device_type in [DEVICE_TYPE.iPhone, DEVICE_TYPE.iPhone5, DEVICE_TYPE.iPad]: self._images[device_type] = self.__imagesForDevice(device_type) logging.debug(self._images) # logging.debug(formData) if "images" in dataDict: imagesActions = dataDict["images"] languageCode = languages.langCodeForLanguage(lang) for dType in imagesActions: device_type = None if dType.lower() == "iphone": device_type = DEVICE_TYPE.iPhone elif dType.lower() == "iphone 5": device_type = DEVICE_TYPE.iPhone5 elif dType.lower() == "ipad": device_type = DEVICE_TYPE.iPad else: continue logging.debug("\n\n" + DEVICE_TYPE.deviceStrings[device_type] + "\n") deviceImagesActions = imagesActions[dType] if deviceImagesActions == "": continue for imageAction in deviceImagesActions: imageAction.setdefault("cmd") imageAction.setdefault("indexes") cmd = imageAction["cmd"] indexes = imageAction["indexes"] replace_language = ALIASES.language_aliases.get(languageCode, languageCode) replace_device = ALIASES.device_type_aliases.get( dType.lower(), DEVICE_TYPE.deviceStrings[device_type] ) imagePath = filename_format.replace("{language}", replace_language).replace( "{device_type}", replace_device ) logging.debug("Looking for images at " + imagePath) if (indexes == None) and ((cmd == "u") or (cmd == "r")): indexes = [] for i in range(0, 5): realImagePath = imagePath.replace("{index}", str(i + 1)) logging.debug("img path: " + realImagePath) if os.path.exists(realImagePath): indexes.append(i + 1) logging.debug("indexes " + indexes.__str__()) logging.debug("Processing command " + imageAction.__str__()) if (cmd == "d") or ( cmd == "r" ): # delete or replace. To perform replace we need to delete images first deleteIndexes = [img["id"] for img in self._images[device_type]] if indexes != None: deleteIndexes = [deleteIndexes[idx - 1] for idx in indexes] for imageIndexToDelete in deleteIndexes: img = next(im for im in self._images[device_type] if im["id"] == imageIndexToDelete) self.__deleteScreenshot(device_type, img["id"]) self._images[device_type] = self.__imagesForDevice(device_type) if (cmd == "u") or (cmd == "r"): # upload or replace currentIndexes = [img["id"] for img in self._images[device_type]] if indexes == None: continue indexes = sorted(indexes) for i in indexes: realImagePath = imagePath.replace("{index}", str(i)) if os.path.exists(realImagePath): self.__uploadScreenshot(device_type, realImagePath) self._images[device_type] = self.__imagesForDevice(device_type) if cmd == "r": newIndexes = [img["id"] for img in self._images[device_type]][len(currentIndexes) :] if len(newIndexes) == 0: continue for i in indexes: currentIndexes.insert(i - 1, newIndexes.pop(0)) self.__sortScreenshots(device_type, currentIndexes) self._images[device_type] = self.__imagesForDevice(device_type) if cmd == "s": # sort if indexes == None or len(indexes) != len(self._images[device_type]): continue newIndexes = [self._images[device_type][i - 1]["id"] for i in indexes] self.__sortScreenshots(device_type, newIndexes) self._images[device_type] = self.__imagesForDevice(device_type) formData["uploadSessionID"] = self._uploadSessionId # formData['uploadKey'] = self._uploadSessionData[DEVICE_TYPE.iPhone5]['key'] postFormResponse = requests.post(ITUNESCONNECT_URL + submitAction, data=formData, cookies=cookie_jar) if postFormResponse.status_code != 200: raise "Wrong response from iTunesConnect. Status code: " + str(postFormResponse.status_code) if len(postFormResponse.text) > 0: logging.error("Save information failed. " + postFormResponse.text)