343 lines
14 KiB
Python
Executable File
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("<", "<").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)
|