409 lines
16 KiB
Python
Executable File
409 lines
16 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# this python script copy all
|
|
# default car previews
|
|
# tracks outlines
|
|
# from the tgiven svn repo to a specified folder in the webserver
|
|
|
|
# todo: check if directories exist: "cars" and "tracks" in "img",
|
|
# if not create them
|
|
|
|
import os
|
|
import sys
|
|
import shutil
|
|
import threading
|
|
import xml.etree.ElementTree as ET
|
|
import tkinter as tk
|
|
from tkinter import ttk, filedialog
|
|
|
|
|
|
class ExtractData(threading.Thread):
|
|
def __init__(self, sdRepoFolder, addStatus=None):
|
|
self.sdRepoFolder = sdRepoFolder
|
|
self.addStatus = addStatus
|
|
|
|
if not os.path.isdir(self.sdRepoFolder) or not os.access(self.sdRepoFolder, os.R_OK):
|
|
text = sys.argv[1] + " folder does not exist, is not a folder or does not have read permissions"
|
|
if self.addStatus:
|
|
self.addStatus(text)
|
|
else:
|
|
print(text, flush=True)
|
|
exit(1)
|
|
|
|
carMainFolder = self.sdRepoFolder + 'data/cars/models/'
|
|
carCatMainFolder = self.sdRepoFolder + 'data/cars/categories/'
|
|
|
|
trackMainFolder = self.sdRepoFolder + 'data/tracks/'
|
|
trackCatMainFolder = self.sdRepoFolder + 'data/tracks/'
|
|
|
|
self.root = None
|
|
|
|
##============================
|
|
## EXTRACT CARS CATEGORY DATA
|
|
##============================
|
|
carCategories = {}
|
|
|
|
catFiles = os.listdir(carCatMainFolder)
|
|
|
|
for catFile in catFiles:
|
|
xmlCatFile = carCatMainFolder+catFile
|
|
|
|
fileName, fileExtension = os.path.splitext(xmlCatFile)
|
|
|
|
if fileExtension == '.xml':
|
|
if os.path.isfile(xmlCatFile):
|
|
xmlFileUrl = xmlCatFile
|
|
parser = ET.XMLParser()
|
|
tree = ET.parse(xmlFileUrl, parser=parser)
|
|
|
|
# And get the self.root of the xml tree
|
|
self.root = tree.getroot()
|
|
|
|
catName = self.root.attrib['name']
|
|
catId = self.getTagAttr('Car', attstr='category')['val']
|
|
|
|
carCategories[catId] = {}
|
|
carCategories[catId]['cars'] = []
|
|
carCategories[catId]['name'] = catName
|
|
|
|
#print 'Processed: '+catId+' : '+catName
|
|
|
|
|
|
##============================
|
|
## EXTRACT CARS DATA
|
|
##============================
|
|
cars = {}
|
|
carFolders = os.listdir(carMainFolder)
|
|
|
|
for folder in carFolders:
|
|
dirName = carMainFolder + folder + '/'
|
|
|
|
xmlFileUrl = dirName+folder+'.xml'
|
|
imgFileUrl = dirName+folder+'-preview.jpg'
|
|
|
|
if os.path.isfile(xmlFileUrl):
|
|
|
|
if os.path.isfile(imgFileUrl):
|
|
newImgUrl = './public/img/cars/'+folder+'-preview.jpg'
|
|
carImg = './img/cars/'+folder+'-preview.jpg'
|
|
shutil.copyfile(imgFileUrl, newImgUrl)
|
|
|
|
tree = ET.parse(xmlFileUrl)
|
|
|
|
# And get the self.root of the xml tree
|
|
self.root = tree.getroot()
|
|
|
|
# Car name
|
|
carName = self.root.attrib['name']
|
|
carId = folder
|
|
# Car category
|
|
carCategory = self.getTagAttr('Car', attstr='category')['val']
|
|
carWidth = self.getTagAttr('Car', attnum='body length')['val']
|
|
|
|
# print('Processing car: {} : {} : {}'.format(carId, carName, carWidth))
|
|
text = 'Processing car: {} : {} : {}'.format(carId, carName, carWidth)
|
|
if self.addStatus:
|
|
self.addStatus(text)
|
|
else:
|
|
print(text, flush=True)
|
|
|
|
# Assign the car to a car categorie
|
|
carCategories[carCategory]['cars'].append(carId)
|
|
|
|
# Populate the car object with all the infos of the car
|
|
cars[carId] = {}
|
|
cars[carId]['id'] = carId
|
|
cars[carId]['name'] = carName
|
|
cars[carId]['img'] = carImg
|
|
cars[carId]['category'] = carCategory
|
|
|
|
try:
|
|
overall_width = self.getTagAttr('Car', attnum='overall width')
|
|
cars[carId]['width'] = '{} {}'.format(
|
|
overall_width['unit'],
|
|
overall_width['val']
|
|
)
|
|
except:
|
|
cars[carId]['width'] = "data unavailable"
|
|
|
|
try:
|
|
overall_length = self.getTagAttr('Car', attnum='overall length')
|
|
cars[carId]['length'] = '{} {}'.format(
|
|
overall_length['unit'],
|
|
overall_length['val']
|
|
)
|
|
except:
|
|
cars[carId]['lenght'] = "data unavailable"
|
|
|
|
try:
|
|
overall_mass = self.getTagAttr('Car', attnum='mass')
|
|
cars[carId]['mass'] = '{} {}'.format(
|
|
overall_mass['unit'],
|
|
overall_mass['val']
|
|
)
|
|
except:
|
|
cars[carId]['mass'] = "data unavailable"
|
|
|
|
# mpa11 musarasama has problems (missing some data)
|
|
try:
|
|
overall_fueltank = self.getTagAttr('Car', attnum='fuel tank')
|
|
cars[carId]['fueltank'] = '{} {}'.format(
|
|
overall_fueltank['unit'],
|
|
overall_fueltank['val']
|
|
)
|
|
except:
|
|
cars[carId]['fueltank'] = "data unavailable"
|
|
|
|
try:
|
|
cylinders = self.getTagAttr('Engine', attnum='cylinders')
|
|
shape = self.getTagAttr('Engine', attstr='shape')
|
|
capacity = self.getTagAttr('Engine', attnum='capacity')
|
|
|
|
cars[carId]['engine'] = '{} cylinders {} {} {}'.format(
|
|
cylinders['val'],
|
|
shape['val'],
|
|
capacity['val'],
|
|
capacity['unit']
|
|
)
|
|
|
|
except:
|
|
cars[carId]['engine'] = "data unavailable"
|
|
|
|
cars[carId]['drivetrain'] = self.getTagAttr('Drivetrain', attstr='type')['val']
|
|
text = 'Processed car: {} : {}'.format(carId, carName)
|
|
if self.addStatus:
|
|
self.addStatus(text)
|
|
else:
|
|
print(text, flush=True)
|
|
|
|
#self.addStatus(text)
|
|
|
|
##============================
|
|
## EXTRACT TRACKS CATEGORY DATA
|
|
##============================
|
|
trackCategories = {}
|
|
|
|
##============================
|
|
## EXTRACT TRACKS DATA
|
|
##============================
|
|
tracks = {}
|
|
trackCategoryFolders = os.listdir(trackMainFolder)
|
|
|
|
for category in trackCategoryFolders:
|
|
categoryFolder = trackMainFolder + category+'/'
|
|
|
|
if not os.path.isfile(trackMainFolder + category):
|
|
|
|
categoryFolders = os.listdir(categoryFolder)
|
|
|
|
# Log car categories info
|
|
trackCategories[category] = {}
|
|
trackCategories[category]['id'] = category
|
|
trackCategories[category]['name'] = category
|
|
trackCategories[category]['tracks'] = []
|
|
|
|
for track in categoryFolders:
|
|
#print categoryFolder+'\n'
|
|
#print track+'\n\n'
|
|
|
|
if not os.path.isfile(categoryFolder+track):
|
|
|
|
trackFolder = categoryFolder+track+'/'
|
|
xmlFileUrl = trackFolder+track+'.xml'
|
|
|
|
#print categoryFolder+track
|
|
|
|
if not os.path.isfile(categoryFolder+track):
|
|
if os.path.isfile(xmlFileUrl):
|
|
#print xmlFileUrl
|
|
|
|
parser = ET.XMLParser()
|
|
#parser._parser.UseForeignDTD(True)
|
|
parser.entity['default-surfaces'] = u'\u00A0'
|
|
parser.entity['default-objects'] = u'\u00A0'
|
|
tree = ET.parse(xmlFileUrl, parser=parser)
|
|
|
|
# And get the self.root of the xml tree
|
|
self.root = tree.getroot()
|
|
|
|
#trackId=self.root.attrib['name']
|
|
trackId = track
|
|
trackName = self.getTagAttr('Header', attstr='name')['val']
|
|
trackCategory = self.getTagAttr('Header', attstr='category')['val']
|
|
imgFileUrl = trackFolder+'outline.png'
|
|
|
|
# We want to ignore development tracks
|
|
if (trackCategory == "development"):
|
|
#print('INFO: Ignoring track as is a development one for: ' + trackId + ' : ' + trackName)
|
|
text = 'INFO: Ignoring track as is a development one for: {} : {}'.format(trackId, trackName)
|
|
if self.addStatus:
|
|
self.addStatus(text)
|
|
else:
|
|
print(text, flush=True)
|
|
continue
|
|
|
|
if os.path.isfile(imgFileUrl):
|
|
newImgUrl = './public/img/tracks/' + track + '-outline.png'
|
|
trackImg = './img/tracks/' + track + '-outline.png'
|
|
shutil.copyfile(imgFileUrl, newImgUrl)
|
|
else:
|
|
'''print('WARNING: No track image defined for: {} : {}'.format(
|
|
trackId,
|
|
trackName
|
|
))'''
|
|
text = 'WARNING: No track image defined for: {} : {}'.format(
|
|
trackId,
|
|
trackName
|
|
)
|
|
|
|
if self.addStatus:
|
|
self.addStatus(text)
|
|
else:
|
|
print(text, flush=True)
|
|
trackImg = ''
|
|
|
|
# Populate the car object with all the infos of the track
|
|
tracks[trackId] = {}
|
|
tracks[trackId]['id'] = trackId
|
|
tracks[trackId]['name'] = trackName
|
|
tracks[trackId]['img'] = trackImg
|
|
tracks[trackId]['category'] = trackCategory
|
|
|
|
tracks[trackId]['author'] = self.getTagAttr('Header', attstr='author')['val']
|
|
temp = self.getTagAttr('Header', attstr='description')['val'].replace("'", "*")
|
|
tracks[trackId]['description'] = temp
|
|
#tracks[trackId]['version']=self.root.findall("./section[@name='Header']/attstr[@name='version']")[0].attrib['val']
|
|
|
|
trackCategories[category]['tracks'].append(trackId)
|
|
|
|
text = 'Processed track: {} : {}'.format(trackId, trackName)
|
|
if self.addStatus:
|
|
self.addStatus(text)
|
|
else:
|
|
print(text, flush=True)
|
|
|
|
|
|
# Save he carCategory info into a file
|
|
out_file = open("./writable/data/carCategories.txt", "w")
|
|
out_file.write(str(carCategories))
|
|
out_file.close()
|
|
|
|
# Save the cars info into a file
|
|
out_file = open("./writable/data/cars.txt", "w")
|
|
out_file.write(str(cars))
|
|
out_file.close()
|
|
|
|
# Save the carTrack info into a file
|
|
out_file = open("./writable/data/trackCategories.txt", "w")
|
|
out_file.write(str(trackCategories))
|
|
out_file.close()
|
|
|
|
# Save the tracks info into a file
|
|
out_file = open("./writable/data/tracks.txt", "w")
|
|
out_file.write(str(tracks))
|
|
out_file.close()
|
|
|
|
def getTagAttr(self, section, attstr=None, attnum=None):
|
|
"""
|
|
Find a attstr or attnum tag and return he attributes.
|
|
Args:
|
|
section (str): The section to find
|
|
attstr (str, optional): If find a attstr tag. Defaults to None.
|
|
attnum (str, optional): If find a attnum tag. Defaults to None.
|
|
"""
|
|
attribs = None
|
|
element = None
|
|
|
|
if attstr:
|
|
element = self.root.findall(
|
|
"./section[@name='{}']/attstr[@name='{}']".format(
|
|
section,
|
|
attstr
|
|
)
|
|
)
|
|
|
|
elif attnum:
|
|
element = self.root.findall(
|
|
"./section[@name='{}']/attnum[@name='{}']".format(
|
|
section,
|
|
attnum
|
|
)
|
|
)
|
|
|
|
if element and len(element) > 0:
|
|
attribs = element[0].attrib
|
|
|
|
return attribs
|
|
|
|
|
|
class Gui(tk.Frame):
|
|
def __init__(self, parent, gui=True, sdRepoFolder=None):
|
|
super().__init__(parent)
|
|
self.root = None
|
|
self.gui = gui
|
|
self.sdRepoFolder = sdRepoFolder
|
|
self.status = None
|
|
|
|
if self.gui:
|
|
top = self.winfo_toplevel()
|
|
top.rowconfigure(1, weight=1)
|
|
top.columnconfigure(2, weight=1)
|
|
|
|
self.entryFolder = tk.Entry(width=50)
|
|
self.entryFolder.grid(column=0, row=0, sticky='e')
|
|
self.openBtn = tk.Button(text='Open folder', command=self.selDataDir)
|
|
self.openBtn.grid(column=1, row=0, sticky='w')
|
|
|
|
frame = tk.Frame()
|
|
|
|
scroll_bar = ttk.Scrollbar(frame, orient=tk.VERTICAL)
|
|
|
|
self.status = tk.Listbox(frame, yscrollcommand=scroll_bar.set)
|
|
self.status.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
|
|
scroll_bar.configure(command=self.status.yview)
|
|
scroll_bar.pack(side=tk.RIGHT, fill=tk.Y)
|
|
|
|
frame.rowconfigure('all', weight=1)
|
|
frame.columnconfigure('all', weight=1)
|
|
frame.grid(column=0, row=1, columnspan=3, sticky="nsew")
|
|
|
|
self.startBtn = tk.Button(
|
|
text='Extract',
|
|
command=self.startThread,
|
|
state=tk.DISABLED)
|
|
self.startBtn.grid(column=2, row=0, sticky='w')
|
|
else:
|
|
self.startThread()
|
|
|
|
def selDataDir(self):
|
|
self.sdRepoFolder = filedialog.askdirectory() + '/'
|
|
if self.sdRepoFolder:
|
|
self.entryFolder.insert(0, self.sdRepoFolder)
|
|
self.startBtn['state'] = tk.NORMAL
|
|
|
|
def startThread(self):
|
|
threading.Thread(
|
|
target=ExtractData,
|
|
args=(self.sdRepoFolder, self.addStatus)
|
|
).start()
|
|
|
|
def addStatus(self, text):
|
|
if (self.gui):
|
|
self.status.insert(tk.END, text)
|
|
else:
|
|
print(text)
|
|
|
|
|
|
if len(sys.argv) == 2:
|
|
sdRepoFolder = sys.argv[1]
|
|
threading.Thread(
|
|
target=ExtractData,
|
|
args=(sdRepoFolder,)
|
|
).start()
|
|
else:
|
|
window = tk.Tk()
|
|
window.title("Speed Dreams: Extract Data from SVN")
|
|
window.geometry('640x480')
|
|
app = Gui(window)
|
|
window.mainloop()
|