from CommonLibrary import ConfluenceController from CommonLibrary import PerforceController from CommonLibrary.ConfluenceController import Representation as Representation import os import json import sys from HTMLParser import HTMLParser from xml.etree import cElementTree from xml.dom import minidom import copy import ntpath import re ######################################################################## # Classes for storing the info from the json ######################################################################## class HTMLtoEtreeParser(HTMLParser): def __init__(self): HTMLParser.__init__(self) self.tb = cElementTree.TreeBuilder() def handle_starttag(self, tag, attributes): self.set_cdata_mode(tag) self.tb.start(tag, dict(attributes)) def handle_endtag(self, tag): self.tb.end(tag) def handle_data(self, data): self.tb.data(str(data).encode("utf8")) def set_cdata_mode(self, tag): if tag=="ac:plain-text-body": self.interesting = re.compile("(?i)<\\/\\s*ac\\:plain-text-body\\s*>") self.cdata_tag = tag def close(self): HTMLParser.close(self) return self.tb.close() GALLERYTEMPLATE = os.path.join(os.path.dirname(os.path.realpath("StyleAutomationTool.py")) + "\\MiscFiles\\CharacterArtTool\\gallery_template.txt") ASSETTEMPLATE = os.path.join(os.path.dirname(os.path.realpath("StyleAutomationTool.py")) + "\\MiscFiles\\CharacterArtTool\\asset_template.txt") SETTINGFILE = os.path.join(os.path.dirname(os.path.realpath("StyleAutomationTool.py")) + "\\MiscFiles\\CharacterArtTool\\settings.xml") #global variables NAMESPACE="RSGGTAV" COMMONPARENTPAGE="372714157" FEMALEPARENTPAGE="372714162" MALEPARENTPAGE="372714164" COMMONATTACHMENTPREFIX="GTAV_Character_Art_Tracking_" SCREENSHOTFOLDER ="progress_tracker" ADREFERENCENAME ="ad_reference.jpg" #Confluence doesnt like certain symbols def FixHubLinkText(hubLinkText): return hubLinkText.replace("%3A", ":").replace("+", " ") ######################################################################## # Function to parse a html and using a structure which relates find and replace # and then returns it replaces ######################################################################## def FindAndReplaceInHtml(html, inputStruct): for elem in html.iter(): for attrib in elem.attrib: currentAttribValue = elem.attrib[attrib] if currentAttribValue in inputStruct: elem.attrib[attrib] = inputStruct[currentAttribValue] if elem.text is not None: for inputKey in inputStruct.keys(): if inputKey in elem.text: elem.text = str(elem.text).replace(inputKey, inputStruct[inputKey]) return html ######################################################################## # Function to retrieve contents of a html file and call to get the structure ######################################################################## def GetStructureOfHtml(FileLocation): htmlText = "" treeStruct = None with open(FileLocation, 'r') as f: htmlText = f.readlines() return GetStructureOfHtmlString(str.join("", htmlText)) ######################################################################## # Function to generate a element tree structure from a html string ######################################################################## def GetStructureOfHtmlString(inputString): myparser = HTMLtoEtreeParser() myparser.feed(inputString) treeStruct = myparser.close() return treeStruct ######################################################################## # Function to convert an element tree to a string while replacing any characters that got borked by the element tree ######################################################################## def ConvertEtreeToString(inputEtree): outputString = cElementTree.tostring(inputEtree).replace("<", "<").replace(">", ">") return outputString ######################################################################## # Function to normalize out the file extension as tools suddenly started giving it in uppercase ######################################################################## def NormalizeFileName(inputName): FileType = inputName.split('.')[-1] lowercaseFileType = FileType.lower() return str(inputName).replace(("." + FileType), ("." + lowercaseFileType)) def GetSettingFlagCL(): if(os.path.isfile(SETTINGFILE)): settingsConfig = minidom.parse(SETTINGFILE) toolsettings = settingsConfig.getElementsByTagName("settings")[0] clValue = toolsettings.getElementsByTagName("targetcl")[0].childNodes[0].nodeValue.lower() return int(clValue) else: return -1 def WriteSettingFlag(clVal): settingRoot = cElementTree.Element("settings") targetCl = cElementTree.SubElement(settingRoot, "targetcl") targetCl.text = str(clVal) settings = cElementTree.ElementTree(settingRoot) settings.write(SETTINGFILE) ################################################################## # Setup instance of shared utility classes ################################################################## myConfluenceController = ConfluenceController.ConfluenceUploadController() myPerforceController = PerforceController.PerforceController() #Store the contents of the templates into memory GalleryClassTreeStruct = GetStructureOfHtml(GALLERYTEMPLATE) AssetClassTreeStruct = GetStructureOfHtml(ASSETTEMPLATE) #array for file extensions we dont want the tool uploading DONOTPROCESS = ( ".psd" ) LOCATIONTOPROCESS = "//gta5_dlc/art/dev/peds/art_peds_world/.../Progress_Tracker/..." DEFAULTIMAGE = "03.in-game_progress.jpg" settings = {} if(os.path.isfile(SETTINGFILE)): minidom fileTriggerList = [] headCL = -1 flagCL = -1 regenerate = False #debug function to mimic submissions to a large perforce location if len(sys.argv) > 1 and sys.argv[1] == "generate": regenerate = True allFiles = myPerforceController.GetFilesInLocation(LOCATIONTOPROCESS, True) for file in allFiles: if SCREENSHOTFOLDER in file.lower(): fileTriggerList.append(file) else: flagCL = GetSettingFlagCL() if(flagCL > 0): headCL,fileTriggerList = myPerforceController.GetFilesBetweenClAndHeadAndReportHeadCL(LOCATIONTOPROCESS, flagCL) else: headCL,fileTriggerList = myPerforceController.GetFilesBetweenClAndHeadAndReportHeadCL(LOCATIONTOPROCESS, None) createdPages = {} updateCL = False if(regenerate): femalePages = myConfluenceController.getRecursiveChildrenPagesOfPage(FEMALEPARENTPAGE) malePages = myConfluenceController.getRecursiveChildrenPagesOfPage(MALEPARENTPAGE) for fP in femalePages: myConfluenceController.DeletePage(fP.pageId) for mP in malePages: myConfluenceController.DeletePage(mP.pageId) processLocationList = [] for fileTrigger in fileTriggerList: if (fileTrigger.lower().endswith(DONOTPROCESS)): continue if SCREENSHOTFOLDER not in fileTrigger.lower(): continue if 'art_peds_world' not in fileTrigger.lower(): continue fileTrigger = fileTrigger.strip('\r').strip() fileTrigger = fileTrigger.replace("\\", "/") if (fileTrigger.lower().startswith("x:")): fileTrigger = myPerforceController.GetP4LocationOfFile(os.path.dirname(fileTrigger)) filename = fileTrigger.split("/")[-1] trimmedLocation = fileTrigger.replace(filename, "") if (trimmedLocation not in processLocationList): processLocationList.append(fileTrigger.replace(filename, "")) for processLocation in processLocationList: filesInProcessLocation = myPerforceController.GetFilesInLocation(processLocation, False) attachmentsForUpload = [] attachmentsForDelete = [] for fileInLocation in filesInProcessLocation: if (fileInLocation.lower().endswith(DONOTPROCESS)): continue HeadAction = myPerforceController.GetFStat(fileInLocation)['headAction'] if ('delete' in HeadAction.lower()): attachmentsForDelete.append(fileInLocation) else: attachmentsForUpload.append(fileInLocation) print("Setting up structure for file {0}".format(processLocation)) #split the filetrigger ready to start the logic chain relevantPart = processLocation.lower().split('art_peds_world')[-1] #regex the string to facilitate it (regular expressions) m = re.search("(.*)\\/source\\/(.*)", relevantPart, re.IGNORECASE) if m != None: updateCL = True #store the parts for later use relevantPartsPreSource = m.group(1).split("/") if (len(attachmentsForUpload) == 0 and len(attachmentsForDelete) > 0): print("Deleted attachments only detected. Deleting page") FinalPagePart = relevantPartsPreSource[-1] existingPageTitle = FinalPagePart.capitalize() pageToDelete = myConfluenceController.getPageViaSearch(existingPageTitle, NAMESPACE, True) if (pageToDelete.pageId != None and pageToDelete.status.lower() != "scrapped"): myConfluenceController.DeletePage(pageToDelete.pageId) continue #if it is a file in the location we care about go ahead ParentPageToUse = "" BasePageName = "" Gender = "" ParentIdx = 0 #determine parent gender page for part in relevantPartsPreSource: ParentIdx += 1 if len(part) > 0: if part == "female": ParentPageToUse = FEMALEPARENTPAGE Gender = "Female" if part == "male": ParentPageToUse = MALEPARENTPAGE Gender = "Male" break #if no determinable gender skip if ParentPageToUse == "": print("Error: Could not determine parent page for file {0}".format(processLocation)) continue PreviousPageId = ParentPageToUse PreviousPageStructure = myConfluenceController.getPageViaId(ParentPageToUse).title errorsInTree = False #Generate Parent Structure down to items page if needed for part in relevantPartsPreSource[ParentIdx:-1]: if len(part) > 0: CurTitle = "{0} - {1}".format(part.capitalize(), Gender) print("Performing logic on {0}".format(part)) #search for child pages that already exist curPage = myConfluenceController.getChildPageViaSearch(CurTitle, PreviousPageId, NAMESPACE, True) #child doesnt exist with the name we want if curPage.pageId == None or curPage.status.lower() == "scrapped": #If we have already generated the page this run just use the page id we got earlier if (ParentPageToUse + part) in createdPages: curPage.pageId = createdPages[ParentPageToUse + part] else: #lets make a page print("Generating Gallery Page Content for {0}".format(CurTitle)) copiedGalleryTreeStruct = copy.deepcopy(GalleryClassTreeStruct) curPage.pageId = myConfluenceController.CreatePage(CurTitle, NAMESPACE, ConvertEtreeToString(copiedGalleryTreeStruct), PreviousPageId) createdPages[ParentPageToUse + part] = curPage.pageId #No page was found and one wasnt created so bail out if curPage.pageId == None: print("Error: Issues during creation of page {0} aborting...".format(CurTitle)) errorsInTree = True break PreviousPageStructure = CurTitle PreviousPageId = curPage.pageId if (errorsInTree): continue FinalPagePart = relevantPartsPreSource[-1] CurTitle = FinalPagePart.capitalize() existingPage = myConfluenceController.getPageViaSearch(CurTitle, NAMESPACE, True) if (existingPage.status != None and existingPage.status.lower() == "scrapped"): existingPage.pageId = None newPageId = None childrenPages = myConfluenceController.getChildrenListOfPage(PreviousPageId, CurTitle) if (len(childrenPages) == 0): if (existingPage.pageId != None): myConfluenceController.DeletePage(existingPage.pageId) else: newPageId = existingPage.pageId #get the final child pages print("Performing logic on {0}".format(FinalPagePart)) copiedAssetTreeStruct = copy.deepcopy(AssetClassTreeStruct) adReferenceFileName = COMMONATTACHMENTPREFIX + FinalPagePart + ADREFERENCENAME #dictionary for replacing in the template replaceableMapping = { '[NAME_OF_ASSET_FOLDER]' : CurTitle, '[P4_LOCATION_OF_ASSET_FOLDER]' : processLocation, '[AD_REFERENCE_FILENAME]' : adReferenceFileName } copiedAssetTreeStruct = FindAndReplaceInHtml(copiedAssetTreeStruct, replaceableMapping) if (newPageId == None): newPageId = myConfluenceController.CreatePage(CurTitle, NAMESPACE, ConvertEtreeToString(copiedAssetTreeStruct), PreviousPageId) if (newPageId == None): print("Error: Issues during creation of page {0} aborting...".format(CurTitle)) continue PreviousPageStructure = CurTitle PreviousPageId = newPageId haveTaggedMain = False for attachmentForUpload in attachmentsForUpload: attachmentName = attachmentForUpload.split("/")[-1] thumbNailName = COMMONATTACHMENTPREFIX + FinalPagePart + attachmentName.lower() thumbNailContents = "".join(myPerforceController.GetFileContents(attachmentForUpload)) if (DEFAULTIMAGE in thumbNailName): try: myConfluenceController.postAttachmentFile(thumbNailName, thumbNailContents, PreviousPageId, 'main', "image/jpg", True) except Exception as e: print("Error: Error during attachment of thumbnail {0}: {1}".format(thumbNailName, e)) haveTaggedMain = True else: if (attachmentForUpload == attachmentsForUpload[-1]): if (haveTaggedMain == False): try: myConfluenceController.postAttachmentFile(thumbNailName, thumbNailContents, PreviousPageId, 'main', "image/jpg", True) except Exception as e: print("Error: Error during attachment of thumbnail {0}: {1}".format(thumbNailName, e)) else: try: myConfluenceController.postAttachmentFile(thumbNailName, thumbNailContents, PreviousPageId, "", "image/jpg", True) except Exception as e: print("Error: Error during attachment of thumbnail {0}: {1}".format(thumbNailName, e)) else: try: myConfluenceController.postAttachmentFile(thumbNailName, thumbNailContents, PreviousPageId, "", "image/jpg", True) except Exception as e: print("Error: Error during attachment of thumbnail {0}: {1}".format(thumbNailName, e)) if(updateCL): WriteSettingFlag(headCL) sys.exit(0)