Files
gtav-src/tools_ng/bin/ConfluencePythonTools/CharacterArtTool.py
T
2025-09-29 00:52:08 +02:00

343 lines
14 KiB
Python
Executable File

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("&lt;", "<").replace("&gt;", ">")
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)