import data.qa as qa import wx import wx.lib.delayedresult as delayedresult import wx.lib.dialogs import os import subprocess import traceback import time import zipfile import copy import shutil import codecs import struct import re import sys import platform import mysql.connector as connector import xml.etree.ElementTree import json from multiprocessing.connection import Listener, Client import socket import SocketServer import threading ZIP_MODE = False MACHINE_NAME_MODE = False #IN_SOCKET = None DEBUG_LOGGING = 0 def DebugLog(msg, override=0): if override == 1 or DEBUG_LOGGING == 1: filePath = os.path.join(sys.path[0],'mpLogGrabber_debug_log.txt') if os.path.exists(filePath): stats = os.stat(filePath) if stats.st_size > 41000: #>5mb f = open('mpLogGrabber_debug_log.txt', 'w') else: f = open('mpLogGrabber_debug_log.txt', 'a') f.write(msg+"[%s]\n" % stats.st_size) f.close() class MainPanel(wx.Panel): def __init__(self, *args, **kwargs): wx.Panel.__init__(self, *args, **kwargs) self.datafile = 'data\\mpLogGrabber.ini' self.abort_transfer = False self.transfer_aborted = False self.default_game_id = None self.checked_settings = [] self.unchecked_settings = [] self.CustomMessageBugPlaceholder = "" self.CustomClipboardMessage = "" ## self.update_timer_id = wx.NewId() ## self.update_timer = wx.Timer(self, self.update_timer_id) ## self.Bind(wx.EVT_TIMER, self.OnTimedUpdate, ## id=self.update_timer_id) self.copier = None self.pct_copied = 0 self.total_bytes = 0 self.bytes_copied = 0 self.data = qa.SettingsLoader() self.data.LoadFile(self.datafile) db_info = self.data['db'] self.db = DB(db_info['db_host'], db_info['db_database'], db_info['db_user'], db_info['db_pass']) self.menubar = wx.MenuBar() self.GetParent().SetMenuBar(self.menubar) p_frame = self.GetParent() p_frame.CreateStatusBar() m_data = wx.Menu() self.menubar.Append(m_data, 'Players') m_data_refresh = wx.MenuItem(m_data, wx.NewId(), 'Refresh Data\tF5', 'Refresh player lists from the database') m_data.AppendItem(m_data_refresh) p_frame.Bind(wx.EVT_MENU, self.RefreshData, id=m_data_refresh.GetId()) m_data.AppendSeparator() m_data_addplayer = wx.MenuItem(m_data, wx.NewId(), '&New Player', 'Add a new player to the database') m_data.AppendItem(m_data_addplayer) p_frame.Bind(wx.EVT_MENU, self.OnAddPlayer, id=m_data_addplayer.GetId()) m_data_removeplayer = wx.MenuItem(m_data, wx.NewId(), '&Remove a Player', 'Remove a player from the database') m_data.AppendItem(m_data_removeplayer) p_frame.Bind(wx.EVT_MENU, self.OnRemovePlayer, id=m_data_removeplayer.GetId()) m_data_modplayer = wx.MenuItem(m_data, wx.NewId(), '&Modify a player', 'Modify a players details') m_data.AppendItem(m_data_modplayer) p_frame.Bind(wx.EVT_MENU, self.OnModifyPlayer, id=m_data_modplayer.GetId()) m_bug = wx.Menu() self.menubar.Append(m_bug, 'Bug Folder') m_bug_open = wx.MenuItem(m_bug, wx.NewId(), '&Open bug folder\tF3', 'Open the bug folder in explorer') m_bug.AppendItem(m_bug_open) p_frame.Bind(wx.EVT_MENU, self.OnOpenBugFolder, id=m_bug_open.GetId()) m_bug_copy = wx.MenuItem(m_bug, wx.NewId(), '&Copy bug location\tF4', 'Copy bug network location to clipboard') m_bug.AppendItem(m_bug_copy) p_frame.Bind(wx.EVT_MENU, self.OnCopy, id=m_bug_copy.GetId()) m_bug_copy_custom = wx.MenuItem(m_bug, wx.NewId(), '&Copy bug location w/ message\tF6', 'Copy bug network location and custom comment message to clipboard') m_bug.AppendItem(m_bug_copy_custom) p_frame.Bind(wx.EVT_MENU, self.OnCopyCustom, id=m_bug_copy_custom.GetId()) m_bug.AppendSeparator() m_bug_report = wx.MenuItem(m_bug, wx.NewId(), '&View transfer report', 'Open the transfer report for the current bug folder') m_bug.AppendItem(m_bug_report) p_frame.Bind(wx.EVT_MENU, self.OnViewReport, id=m_bug_report.GetId()) m_groups = wx.Menu() self.menubar.Append(m_groups, 'Groups') m_groups_load = wx.MenuItem(m_groups, wx.NewId(), '&Load groups\tF8', 'Set the active players list from a previously stored groups') m_groups.AppendItem(m_groups_load) p_frame.Bind(wx.EVT_MENU, self.OnLoadGroup, id=m_groups_load.GetId()) m_groups_add = wx.MenuItem(m_groups, wx.NewId(), '&Add groups to current list\tF9', 'Append stored groups to current active players list') m_groups.AppendItem(m_groups_add) p_frame.Bind(wx.EVT_MENU, self.OnAddGroup, id=m_groups_add.GetId()) m_groups.AppendSeparator() m_groups_new = wx.MenuItem(m_groups, wx.NewId(), 'Create &new group', 'Create a new group from the current list of active players') m_groups.AppendItem(m_groups_new) p_frame.Bind(wx.EVT_MENU, self.OnNewGroup, id=m_groups_new.GetId()) m_groups_replace = wx.MenuItem(m_groups, wx.NewId(), '&Update group', 'Replace the contents of a group with the current list of active players') m_groups.AppendItem(m_groups_replace) p_frame.Bind(wx.EVT_MENU, self.OnReplaceGroup, id=m_groups_replace.GetId()) m_groups_del = wx.MenuItem(m_groups, wx.NewId(), '&Delete group', 'Select a group to delete') m_groups.AppendItem(m_groups_del) p_frame.Bind(wx.EVT_MENU, self.OnDeleteGroup, id=m_groups_del.GetId()) self.m_settings = wx.Menu() self.menubar.Append(self.m_settings, 'Settings') m_set_zipmode = wx.MenuItem(self.m_settings, wx.NewId(), 'Auto-Zip Logs', 'Automatically compresses logfiles into zips after finishing the copy operation', wx.ITEM_CHECK) self.m_settings.AppendItem(m_set_zipmode) #if m_set_zipmode.GetLabel().lower() in self.checked_settings: # m_set_zipmode.Check(True) #elif m_set_zipmode.GetLabel().lower() in self.unchecked_settings: # m_set_zipmode.Check(False) #else: # This is for the default value for this option (if the user settings file is missing) m_set_zipmode.Check(True) #p_frame.Bind(wx.EVT_MENU, self.OnToggleZipMode, #id=m_set_zipmode.GetId()) self.m_set_zipmode = m_set_zipmode m_set_usefilter = wx.MenuItem(self.m_settings, wx.NewId(), 'Use Filters', 'Skip files that match the selected patterns.', wx.ITEM_CHECK) self.m_settings.AppendItem(m_set_usefilter) #if m_set_usefilter.GetLabel().lower() in self.checked_settings: # m_set_usefilter.Check(True) #elif m_set_usefilter.GetLabel().lower() in self.unchecked_settings: # m_set_usefilter.Check(False) #else: # This is for the default value for this option (if the user settings file is missing) m_set_usefilter.Check(False) self.m_set_usefilter = m_set_usefilter m_set_selectfilter = wx.MenuItem(self.m_settings, wx.NewId(), 'Select Filters', 'Select the filters that will be applied to filenames.') self.m_settings.AppendItem(m_set_selectfilter) p_frame.Bind(wx.EVT_MENU, self.OnSetFilters, id = m_set_selectfilter.GetId()) m_set_usemachinename = wx.MenuItem(self.m_settings, wx.NewId(), 'Use Machine Names', 'Add machine name next to gamer tag.', wx.ITEM_CHECK) self.m_settings.AppendItem(m_set_usemachinename) p_frame.Bind(wx.EVT_MENU, self.OnUseMachineName, id = m_set_usemachinename.GetId()) self.m_set_usemachinename = m_set_usemachinename m_set_custommessage = wx.MenuItem(self.m_settings, wx.NewId(), 'Set Comment Message', 'Set custom text to be copied into clipboard together with bug location.') self.m_settings.AppendItem(m_set_custommessage) p_frame.Bind(wx.EVT_MENU, self.OnSetCustomMessage, id = m_set_custommessage.GetId()) self.m_set_custommessage = m_set_custommessage m_set_remote = wx.MenuItem(self.m_settings, wx.NewId(), 'Experimental Remote Control', 'send commands over the network IT IS AN EXPERIMENT', wx.ITEM_CHECK) self.m_settings.AppendItem(m_set_remote) self.m_set_remote = m_set_remote HOST, PORT = "0.0.0.0", 9999 rc = RemoteControl rc.parent = self #SocketServer.TCPServer.allow_reuse_address = True self.remote_server = SocketServer.TCPServer((HOST, PORT), rc) self.server_thread = threading.Thread(target=self.remote_server.serve_forever) self.server_thread.daemon = True self.server_thread.start() self.remote_busy = False if self.m_set_remote.GetLabel().lower() in self.checked_settings: self.m_set_remote.Check(True) elif self.m_set_remote.GetLabel().lower() in self.unchecked_settings: self.m_set_remote.Check(False) else: self.m_set_remote.Check(True) main_sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(main_sizer) ## player_sizer = wx.StaticBoxSizer(wx.StaticBox(self, ## label='Players'), ## wx.HORIZONTAL) player_sizer = wx.BoxSizer(wx.HORIZONTAL) main_sizer.Add(player_sizer, flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=3, proportion=5) player_box_sizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Inactive Players'), wx.VERTICAL) player_sizer.Add(player_box_sizer, flag=wx.EXPAND, proportion=1) self.players = wx.ListBox(self, style=wx.LB_EXTENDED) player_box_sizer.Add(self.players, flag=wx.EXPAND, proportion=1) self.players_filter = wx.Choice(self) player_box_sizer.Add(self.players_filter, flag=wx.EXPAND | wx.TOP, proportion=0, border=1) self.players_filter.Bind(wx.EVT_CHOICE, self.OnFilterChange) player_btn_sizer = wx.BoxSizer(wx.VERTICAL) player_sizer.Add(player_btn_sizer, flag=wx.ALIGN_CENTER_VERTICAL) btn_add = wx.Button(self, label='>', size=(32, 32)) player_btn_sizer.Add(btn_add) btn_add.Bind(wx.EVT_BUTTON, self.ActivateSelectedPerson) self.players.Bind(wx.EVT_LISTBOX_DCLICK, self.ActivateSelectedPerson) self.btn_add = btn_add btn_remove = wx.Button(self, label='<', size=(32, 32)) player_btn_sizer.Add(btn_remove) btn_remove.Bind(wx.EVT_BUTTON, self.DeActivateSelectedPerson) self.btn_remove = btn_remove players_actives_sizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Active Players')) player_sizer.Add(players_actives_sizer, flag=wx.EXPAND, proportion=1) self.actives = wx.ListBox(self, style=wx.LB_EXTENDED) players_actives_sizer.Add(self.actives, flag=wx.EXPAND, proportion=1) self.actives.Bind(wx.EVT_LISTBOX_DCLICK, self.DeActivateSelectedPerson) #data_sizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Data'), #wx.HORIZONTAL) #main_sizer.Add(data_sizer, flag=wx.EXPAND | #wx.TOP | wx.LEFT | wx.RIGHT, #border=3, proportion=0) #self.btn_refresh = wx.Button(self, label = 'Refresh Data') #data_sizer.Add(self.btn_refresh) #self.btn_refresh.Bind(wx.EVT_BUTTON, self.RefreshData) #data_sizer.AddSpacer(5) #self.btn_add_player = wx.Button(self, label = 'Add Player') #data_sizer.Add(self.btn_add_player) #self.btn_add_player.Bind(wx.EVT_BUTTON, self.OnAddPlayer) #self.btn_remove_player = wx.Button(self, label = 'Remove Player') #data_sizer.Add(self.btn_remove_player) #self.btn_remove_player.Bind(wx.EVT_BUTTON, self.OnRemovePlayer) controls_sizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Controls'), wx.VERTICAL) controls_row1 = wx.BoxSizer(wx.HORIZONTAL) controls_row2 = wx.BoxSizer(wx.HORIZONTAL) controls_row3 = wx.BoxSizer(wx.HORIZONTAL) self.game_choice = wx.Choice(self) controls_row1.Add(self.game_choice, flag=wx.EXPAND, proportion=0) controls_row1.AddSpacer(5) bug_number_text = wx.StaticText(self, label='#') controls_row1.Add(bug_number_text, flag=wx.ALIGN_CENTER_VERTICAL) self.bug_number = wx.TextCtrl(self) controls_row1.Add(self.bug_number, flag=wx.EXPAND, proportion=1) v_label = wx.StaticText(self, label=' v') controls_row1.Add(v_label, flag=wx.ALIGN_CENTER_VERTICAL) self.version_text = wx.TextCtrl(self) controls_row1.Add(self.version_text, flag=wx.EXPAND, proportion=1) #controls_row1.AddSpacer(5) #extra_controls_sizer = wx.StaticBoxSizer(wx.StaticBox(self, #label='Extra Controls'), #wx.HORIZONTAL) #main_sizer.Add(extra_controls_sizer, flag=wx.EXPAND | #wx.TOP | wx.LEFT | wx.RIGHT, #border = 3, proportion = 0) #self.btn_copy = wx.Button(self, label = 'Copy Bug Location') #controls_row2.Add(self.btn_copy) #self.btn_copy.Bind(wx.EVT_BUTTON, self.OnCopy) #controls_row2.AddSpacer(2) #self.btn_open = wx.Button(self, label='Open Bug Folder') #controls_row2.Add(self.btn_open) #self.btn_open.Bind(wx.EVT_BUTTON, self.OnOpenBugFolder) #controls_row2.AddSpacer(2) self.clear_logs = wx.Button(self, label='Clear Logs') controls_row2.Add(self.clear_logs) self.clear_logs.Bind(wx.EVT_BUTTON, self.OnClearLogs) controls_row2.AddSpacer(2) self.kill_ragandsys = wx.Button(self, label='Kill Rag and SysTray') controls_row2.Add(self.kill_ragandsys) self.kill_ragandsys.Bind(wx.EVT_BUTTON, self.KillRagAndSystray) controls_row2.AddSpacer(2) self.start_ragandsys = wx.Button(self, label='Start Rag and SysTray') controls_row2.Add(self.start_ragandsys) self.start_ragandsys.Bind(wx.EVT_BUTTON, self.StartRagAndSystray) #controls_row2.AddSpacer(30) #This element pushes everything to the bottom/right of the sizer controls_row2.Add((0,0),1) self.btn_start = wx.Button(self, label='Start') controls_row2.Add(self.btn_start) self.btn_start.Bind(wx.EVT_BUTTON, self.OnStart) controls_row2.AddSpacer(2) self.btn_abort = wx.Button(self, label='Abort') controls_row2.Add(self.btn_abort) self.btn_abort.Bind(wx.EVT_BUTTON, self.OnAbort) self.btn_abort.Enable(False) self.dump_platform = wx.Choice(self, choices=['PS4','XboxOne','PC/x64','PS3','360']) self.dump_platform.SetSelection(0) controls_row3.Add(self.dump_platform, flag=wx.TOP, border=1) self.dump_platform.Bind(wx.EVT_CHOICE, self.OnDumpFilterChange) self.dump_platform.Show(False) controls_row3.AddSpacer(5) self.dump_exe = wx.Choice(self, choices=['Beta','BankRelease','Release','Final']) self.dump_exe.SetSelection(0) controls_row3.Add(self.dump_exe, flag=wx.TOP, border=1) self.dump_exe.Show(False) controls_row3.AddSpacer(3) self.btn_dump = wx.Button(self, label='Grab Dump') controls_row3.Add(self.btn_dump) self.btn_dump.Bind(wx.EVT_BUTTON, self.OnGrabDump) self.btn_dump.Show(False) controls_row3.AddSpacer(2) #self.deployed_check = wx.CheckBox(self, label='Deployed') #controls_row3.Add(self.deployed_check, flag=wx.TOP, border=5) #This element pushes everything to the bottom/right of the sizer #controls_row3.Add((0,0),1) #self.addLogs_check = wx.CheckBox(self, label='Add to folder') #controls_row3.Add(self.addLogs_check, flag=wx.RIGHT, border=40) controls_sizer.Add(controls_row1, flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT) #controls_sizer.AddSpacer(10) controls_sizer.Add(controls_row2, flag=wx.EXPAND | wx.TOP, border=5) #TODO: Row that contains controls for grabbing dumps. Functionality is not yet finished for it #controls_sizer.Add(controls_row3, flag=wx.EXPAND | # wx.TOP, border=5) main_sizer.Add(controls_sizer, flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border=3, proportion=0) #self.btn_open = wx.Button(self, label='Open Bug Folder') #extra_controls_sizer.Add(self.btn_open) #self.btn_open.Bind(wx.EVT_BUTTON, self.OnOpenBugFolder) #self.btn_copy = wx.Button(self, label = 'Copy Bug Location') #extra_controls_sizer.Add(self.btn_copy) #self.btn_copy.Bind(wx.EVT_BUTTON, self.OnCopy) self.stage_indicator = wx.StaticText(self, label='Welcome to mpLogGrabber!', style=wx.ALIGN_CENTER | wx.ST_NO_AUTORESIZE) main_sizer.Add(self.stage_indicator, flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 8) file_pct_sizer = wx.StaticBoxSizer(wx.StaticBox(self, label='File Progress'), wx.VERTICAL) main_sizer.Add(file_pct_sizer, flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 3, proportion=0) self.file_pct = wx.Gauge(self) file_pct_sizer.Add(self.file_pct, flag=wx.EXPAND, proportion=1) file_pct_sizer.AddSpacer(3) self.file_pct_sizer = file_pct_sizer self.file_pct_text = wx.StaticText(self, label='', style=wx.ST_NO_AUTORESIZE | wx.ALIGN_CENTER) file_pct_sizer.Add(self.file_pct_text, flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) total_pct_sizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Overall Progress'), wx.VERTICAL) self.total_pct_sizer = total_pct_sizer main_sizer.Add(total_pct_sizer, flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 3, proportion=0) self.total_pct = wx.Gauge(self) total_pct_sizer.Add(self.total_pct, flag=wx.EXPAND, proportion = 1) total_pct_sizer.AddSpacer(3) self.total_pct_text = wx.StaticText(self, label='', style= wx.ST_NO_AUTORESIZE | wx.ALIGN_CENTRE) total_pct_sizer.Add(self.total_pct_text, flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND) log_sizer = wx.StaticBoxSizer(wx.StaticBox(self, label='Log')) main_sizer.Add(log_sizer, flag=wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT | wx.BOTTOM, border = 3, proportion = 2) self.log = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH) log_sizer.Add(self.log, flag=wx.EXPAND, proportion=1) self.actives_dict = {} self.RefreshData() # Try loading settings from last time if the local settings file is present self.sFilename = os.path.join(sys.path[0],'personal_settings.ini') if os.path.exists(self.sFilename): try: self.settings_ini = codecs.open(self.sFilename, 'r','utf-8') self.LoadUserSettings() self.settings_ini.close() except IOError: print ('Error: Unable to access the local settings file, user may not have write' 'permission for "%s"' % self.sFilename) print 'Press any key to exit' #qa.wait_for_key() sys.exit() self.PopulateGamesList() #self.PopulatePeopleList() self.selected_filters = [] self.filters = self.LoadFilters() #all filters on by default #self.selected_filters = self.filters.keys() #do this inside self.LoadFilters now, so we can turn things on and off with # This setting has to be loaded at the end as it needs other UI elements (populated people list) to be initialized first if self.m_set_usemachinename.GetLabel().lower() in self.checked_settings: self.m_set_usemachinename.Check(True) self.OnUseMachineName(self.m_set_usemachinename.GetId()) elif self.m_set_usemachinename.GetLabel().lower() in self.unchecked_settings: self.m_set_usemachinename.Check(False) else: # This is for the default value for this option (if the user settings file is missing) self.m_set_usemachinename.Check(False) self.report_log = [] p_frame.Bind(wx.EVT_CLOSE, self.OnWindowClose) #self.parent.LogEvent('###########'+platform.node()) def StartRagAndSystray(self, event): """Start RAG and System Tray applications from /tools_ng/ folder""" self.LogEvent('Attempting to start RAG and Systray...') rag_path = "X:\\gta5\\tools_ng\\bin\\rag\\rag.exe" systray_path = "X:\\gta5\\tools_ng\\bin\\SysTrayRfs.exe" if os.path.isfile(rag_path) and os.path.isfile(systray_path): self.LogEvent('Starting RAG from %s' % rag_path) self.LogEvent('Starting SysTray from %s' % systray_path) os.startfile(os.path.dirname(os.path.realpath(__file__))+"\\scripts\\Start RAG and SYSTRAYRFS.bat") else: self.LogEvent('Could not find at least one of the following files:') self.LogEvent(rag_path) self.LogEvent(systray_path) def KillRagAndSystray(self, event): """Kill RAG and System Tray processes if they're running""" self.LogEvent('Attempting to kill RAG and Systray...') try: os.system("taskkill /F /IM xbwatson.exe /T") os.system("taskkill /F /IM rag.exe /T") os.system("taskkill /F /IM ragApp.exe /T") os.system("taskkill /F /IM sysTrayRFS.exe /T") except: #Gotta catch them all! e = sys.exc_info()[1] self.LogEvent('Error: %s' %e,'__err__') def OnDumpFilterChange(self, event): platform = self.dump_platform.GetStringSelection() self.LogEvent(platform) #if platform == '360' or platform == 'PS3' or platform == 'PC/x64': # self.deployed_check.Enable(False) # self.deployed_check.Hide() #else: # self.LogEvent('trying to show') # self.deployed_check.Enable(True) # self.deployed_check.Show() def OnGrabDump(self, event): """Grab latest crash dump for the selected platform from appropriate location""" platform = self.dump_platform.GetStringSelection() exe = self.dump_exe.GetStringSelection() exe_name = "" valid_extensions = ['.exe','.cmp','.elf','.self','.map','.pdb','.sym','.xdb','.xex'] dump_path = [] dump_ext = "" dump_keywoard = "" files_to_grab = [] dump_dir = os.environ.get('RAGE_CRASHDUMP_DIR') if dump_dir != None: dump_path.append(dump_dir) dump_path.append('X:\\gta5\\build\\dev_ng\\') dump_path.append('X:\\gta5\\build\\dev\\') if 'X:\\dumps' not in dump_path: dump_path.append('X:\\dumps') self.LogEvent('Trying to grab '+platform+' '+exe+' dump...') if platform == 'PS4': exe_name = 'game_orbis_'+exe.lower() #sample: ps4_dump_1965903.orbisdmp #sample: game_orbis_bankrelease.orbisdmp dump_ext = '.orbisdmp' elif platform == 'XboxOne': exe_name = 'game_durango_'+exe.lower() #sample: game_durango_beta-20140725-075054-276.dmp #TODO: Grab exes for deployed from N:\RSGEDI\Distribution\QA_Build\gta5\dev_xbone\ with alert screen to choose between current/prebuild or choose manually dump_ext = '.dmp' dump_keyword = 'durango' elif platform == 'PC/x64': exe_name = 'game_win64_'+exe.lower() dump_ext = '.dmp' dump_keyword = 'x64' elif platform == 'PS3': exe_name = 'game_psn_'+exe.lower()+'_snc' #sample: ps3core-1396400493-0x01000500-game_psn_bankrelease_snc.self.elf #sample: ps3core-1396400493-0x01000500-game_psn_bankrelease_snc.self.log dump_ext = '.elf' dump_keyword = 'ps3core' elif platform == '360': exe_name = 'game_xenon_'+exe.lower() #sample: game_xenon_bankrelease.xex.dmp #sample: game_xenon_bankrelease.xex1.dmp #^might be several around the same time... :( dump_ext = '.dmp' dump_keyword = 'xenon' else: self.LogEvent('Platform not recognised: %s' % platform) return # Find the latest dump file (by modified date) in any of the dump locations latest_mdate = None latest_dump = None latest_360dumps = [] for path in dump_path: if os.path.exists(path): for file in os.listdir(path): if platform == '360': do_nothing = [] #self.LogEvent('Searching for 360 dumps...') else: if file.endswith(dump_ext): if (platform != 'PS4') and (dump_keyword not in file): continue #self.LogEvent('Found dump: %s' % path+file) mdate = time.ctime(os.path.getmtime(path+file)) #self.LogEvent('Last modified: %s' % mdate) if latest_mdate == None: latest_mdate = mdate latest_dump = path+file elif mdate > latest_mdate: latest_mdate = mdate latest_dump = path+file #grab exe files here filename, ext = os.path.splitext(file) if (ext in valid_extensions) and (filename == exe_name): self.LogEvent('Grabbing: %s' % path+file) files_to_grab.append(path+file) #grab the PS3 coredump .log file as well if ext == '.log' and platform == 'PS3' and dump_keyword in filename: self.LogEvent('Grabbing: %s' % path+file) files_to_grab.append(path+file) if latest_dump == None and latest_360dumps == []: self.LogEvent('No dumps found for selected platform!') return elif latest_dump != None: self.LogEvent('Grabbing dump: %s' % latest_dump) files_to_grab.append(latest_dump) elif latest_360dumps != []: for dump in latest_360dumps: self.LogEvent('Grabbing dump: %s' % dump) files_to_grab.append(dump) def OnClearLogs(self, event): if self.remote_busy: self.LogEvent('Can\'t clear logs now, because busy with remote command.') return confirm = wx.MessageDialog(self.GetParent(), 'Are you sure you want to clear logs?', 'Confirm', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) #confirm.CenterOnParent() if confirm.ShowModal() == wx.ID_YES: mpath = 'x:\\' folders_to_clear = [] for path in self.data[self.GetSelectedGame()]['source']: #self.LogEvent('Clearing logs from \"%s\"'% os.path.join(mpath, path)) if os.path.exists(os.path.join(mpath, path)): folders_to_clear.append(os.path.join(mpath, path)) # Stop the main controls from being initiated and set machines remote status to busy self.btn_start.Enable(False) #self.btn_abort.Enable(False) self.EnablePlayerControls(False) self.remote_busy = True # Start of clear logs script iFilesDeleted = 0 for folderName in folders_to_clear: self.LogEvent('Attempting to delete log files from: \"%s\"' % folderName) #os.system("PING 1.1.1.1 -n 1 -w 2000 >NUL") dirDev=os.listdir(folderName) for devFiles in dirDev: if str.count(devFiles,'.log') > 0: self.deleteLogFile(folderName+'\\'+devFiles) iFilesDeleted = iFilesDeleted + 1 if str.count(devFiles,'.csv') > 0: self.deleteLogFile(folderName+'\\'+devFiles) iFilesDeleted = iFilesDeleted + 1 if str.count(devFiles,'.dmp') > 0: self.deleteLogFile(folderName+'\\'+devFiles) iFilesDeleted = iFilesDeleted + 1 if str.count(devFiles,'.tmp') > 0: self.deleteLogFile(folderName+'\\'+devFiles) iFilesDeleted = iFilesDeleted + 1 if iFilesDeleted == 0: self.LogEvent('No log files found') self.btn_start.Enable(True) #self.btn_abort.Enable(False) self.EnablePlayerControls(True) self.remote_busy = False def deleteLogFile(self, ftd): try: self.LogEvent('Deleting: \"%s\"' % ftd) os.remove(ftd) except: #Gotta catch them all! e = sys.exc_info()[1] self.LogEvent('Error: %s' %e,'__err__') def OnWindowClose(self, event): #socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect(("0.0.0.0", 9999)) #self.remote_server.shutdown() # Save various settings to a local file before closing the application self.LogEvent("Saving user settings") checked_settings = [] unchecked_settings = [] self.WindowHeight = str(self.GetParent().GetSize().GetHeight()) self.WindowWidth = str(self.GetParent().GetSize().GetWidth()) # Note the checked/unchecked "settings" #if self.m_set_usefilter.IsChecked(): # checked_settings.append(self.m_set_usefilter.GetLabel().lower()) #else: # unchecked_settings.append(self.m_set_usefilter.GetLabel().lower()) #if self.m_set_zipmode.IsChecked(): # checked_settings.append(self.m_set_zipmode.GetLabel().lower()) #else: # unchecked_settings.append(self.m_set_zipmode.GetLabel().lower()) if self.m_set_usemachinename.IsChecked(): checked_settings.append(self.m_set_usemachinename.GetLabel().lower()) else: unchecked_settings.append(self.m_set_usemachinename.GetLabel().lower()) #if self.m_set_remote.IsChecked(): # checked_settings.append(self.m_set_remote.GetLabel().lower()) #else: # unchecked_settings.append(self.m_set_remote.GetLabel().lower()) # Note the currently selected default group/path/folder to grab from game_name = self.data[self.GetSelectedGame()]['name'] try: self.settings_ini = codecs.open(self.sFilename, 'w+','utf-8') self.settings_ini.write('[window_size]\n'+self.WindowWidth+','+self.WindowHeight+'\n') self.settings_ini.write('\n') self.settings_ini.write('[checked_settings]\n') for sett in checked_settings: self.settings_ini.write(sett+'\n') self.settings_ini.write('\n') self.settings_ini.write('[unchecked_settings]\n') for sett in unchecked_settings: self.settings_ini.write(sett+'\n') self.settings_ini.write('\n') self.settings_ini.write('[default_folder]\n'+game_name) #self.settings_ini.write('\n\n') #self.settings_ini.write('[default_d_platform]\n'+self.dump_platform.GetStringSelection()) #self.settings_ini.write('\n\n') #self.settings_ini.write('[default_d_exe]\n'+self.dump_exe.GetStringSelection()) #if self.deployed_check.IsEnabled(): # self.settings_ini.write('\n\n') # self.settings_ini.write('[deployed]\n'+str(self.deployed_check.IsChecked())) if self.CustomClipboardMessage != "": self.settings_ini.write('\n\n') self.settings_ini.write('[bug_message_template]\n'+self.CustomClipboardMessage) self.settings_ini.close() except IOError: print ('Logging(): IOError while writing to %s' % self.sFilename) #qa.wait_for_key() sys.exit() # dlg = wx.MessageDialog(self,"Do you really want to close this application?","Confirm Exit", wx.OK|wx.CANCEL|wx.ICON_QUESTION) # result = dlg.ShowModal() # dlg.Destroy() # if result == wx.ID_OK: # self.Destroy() #Propagate the close event up the hierarchy so that frame and app can close event.Skip() def OnToggleZipMode(self, *args, **kwargs): global ZIP_MODE ZIP_MODE = self.m_set_zipmode.IsChecked() def OnStartServer(self, *args, **kwargs): pass def LoadUserSettings(self): """Load user settings from an .ini file if there is one""" id_start = u'[' id_end = u']' current_id = None default_game = None bug_message = "" for line in self.settings_ini: # Only clean up the text if it's not the custom bug message as we want everything as it was for it if current_id != 'bug_message_template': # Skip empty lines if line in ['\n', '\r\n']: continue # Skip comments if line.startswith('#'): continue # Strip newlines and comments line = line.strip('\n') if '#' in line: line = line[:line.find('#')] line = line.strip().lower() if line.startswith(id_start) and line.endswith(id_end): current_id = line.strip('[]') continue # Skip the line if we dont have current_id yet if current_id is None: continue elif current_id == 'checked_settings': self.checked_settings.append(line) elif current_id == 'unchecked_settings': self.unchecked_settings.append(line) elif current_id == 'default_folder': default_game = line elif current_id == 'bug_message_template': bug_message = bug_message+line #elif current_id == 'default_d_platform': # self.dump_platform.SetSelection(self.dump_platform.FindString(line)) #if line == 'ps4' or line == 'xboxone': # self.deployed_check.Enable(True) # self.deployed_check.Show() #else: # self.deployed_check.Enable(False) # self.deployed_check.Hide() #elif current_id == 'default_d_exe': # self.dump_exe.SetSelection(self.dump_exe.FindString(line)) #elif current_id == 'deployed': # if line == 'false': self.deployed_check.SetValue(False) # elif line == 'true': self.deployed_check.SetValue(True) self.CustomClipboardMessage = bug_message DebugLog("[LoadUserSettings] default game - %s" % default_game) if not default_game is None: count = -1 for game_id in self.data['info']['games']: count += 1 DebugLog("[LoadUserSettings] iterating through games list: current - %s" % self.data[game_id]['name'].lower()) if self.data[game_id]['name'].lower() == default_game: #self.game_choice.SetSelection(count) self.default_game_id = count DebugLog("[LoadUserSettings] found a match: name - %s; id - %s" % (self.data[game_id]['name'].lower(), count)) def RefreshData(self, *args, **kwargs): """ refresh player lists and game list """ #first, store the IDs of the currently active players #so we can restore them later active_player_ids = [] for idx in range(0, self.actives.GetCount()): active_player_ids.append(self.actives.GetClientData(idx).db_id) self.db.Connect() cursor = self.db.cursor cursor.execute('SELECT id, machine_name, person_name, filter_group FROM users ORDER BY person_name') users = cursor.fetchall() self.db.Disconnect() self.player_objs = [] self.player_objs_dict = {} for player in users: self.player_objs_dict[player[0]] = Player(player, self) self.player_objs.append(self.player_objs_dict[player[0]]) if player[0] in active_player_ids: self.player_objs_dict[player[0]].Activate() self.PopulateFilters() self.PopulatePlayerLists() #self.PopulateGamesList() def EnablePlayerControls(self, enable): ctrls = [self.players, self.actives, self.btn_add, self.btn_remove, self.game_choice, self.bug_number, self.version_text, self.m_set_zipmode, self.m_set_remote, self.m_set_usefilter, self.clear_logs] for ctrl in ctrls: ctrl.Enable(enable) def SaveData(self, *args, **kwargs): qa.WriteAllGIDs(self.data, self.datafile) def OnAddPlayer(self, *args, **kwargs): dlg = AddPlayerDlg(self) if dlg.ShowModal() == wx.ID_CANCEL: print 'cancel' dlg.Destroy() return False if dlg.GetMachine() == '': self.LogEvent('Machine name blank, player not added', '__err__') dlg.Destroy() return False if dlg.GetPlayer() == '': dlg.Destroy() self.LogEvent('Player name blank, player not added','__err__') return False if dlg.GetFilter() == '': dlg.Destroy() self.LogEvent('Filter left blank, player not added','__err__') return False print 'adding player to data' self.data['people'][dlg.GetMachine()] = dlg.GetPlayer() new_user = {'machine_name': dlg.GetMachine(), 'person_name': dlg.GetPlayer(), 'filter_group': dlg.GetFilter()} dlg.Destroy() print 'inserting data' self.db.Connect() self.db.Insert('users', new_user) self.db.Disconnect() #self.SaveData() print 'refreshing data' self.RefreshData() def OnModifyPlayer(self, event=None): # Start with dialog to choose what are you searching for - player name or machine name cdlg = TwoChoicesScreen(self) searchFor = cdlg.ShowModal() cdlg.Destroy() choice_str = [] choice_objs = [] sortedMachineList = [] if searchFor == wx.ID_NO: # Sorts the list by machine name if searching by it sortedMachineList = sorted(self.player_objs, key=lambda x: x.machine_name.lower(), reverse=False) self.LogEvent('Searching by machine name...') for player in sortedMachineList: choice_str.append('%s (%s)' % (player.machine_name, player.person_name)) choice_objs.append(player) elif (searchFor == wx.ID_YES) or (searchFor == wx.ID_CANCEL): self.LogEvent('Searching by player name...') for player in self.player_objs: choice_str.append('%s (%s)' % (player.person_name, player.machine_name)) choice_objs.append(player) dlg = wx.SingleChoiceDialog(self, 'Select player to modify', 'Modify player', choice_str) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return False player = choice_objs[dlg.GetSelection()] dlg.Destroy() dlg = AddPlayerDlg(self, '__modify__') dlg.SetPlayer(player.person_name) dlg.SetMachine(player.machine_name) dlg.SetFilter(player.filter_group) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return False if dlg.GetMachine() == '': self.LogEvent('Machine name blank, player not modified', '__err__') dlg.Destroy() return False if dlg.GetPlayer() == '': dlg.Destroy() self.LogEvent('Player name blank, player not modified','__err__') return False if dlg.GetFilter() == '': dlg.Destroy() self.LogEvent('Filter left blank, player not modified','__err__') return False stmt_data = {'machine': dlg.GetMachine(), 'person': dlg.GetPlayer(), 'filter': dlg.GetFilter(), 'id': player.db_id} dlg.Destroy() db = self.db try: db.Connect() db.cursor.execute('UPDATE `users` ' 'SET `machine_name`=%(machine)s, ' ' `person_name`=%(person)s, ' ' `filter_group`=%(filter)s ' 'WHERE id=%(id)s', stmt_data) finally: db.Disconnect() self.LogEvent('Modified user %s with id: %s' % (player.person_name, player.db_id)) self.RefreshData() def OnRemovePlayer(self, *args, **kwargs): cdlg = TwoChoicesScreen(self) searchFor = cdlg.ShowModal() cdlg.Destroy() choice_str = [] choice_objs = [] sortedMachineList = [] if searchFor == wx.ID_NO: # Sorts the list by machine name if searching by it sortedMachineList = sorted(self.player_objs, key=lambda x: x.machine_name.lower(), reverse=False) self.LogEvent('Searching by machine name...') for player in sortedMachineList: choice_str.append('%s (%s)' % (player.machine_name, player.person_name)) choice_objs.append(player) elif (searchFor == wx.ID_YES) or (searchFor == wx.ID_CANCEL): self.LogEvent('Searching by player name...') for player in self.player_objs: choice_str.append('%s (%s)' % (player.person_name, player.machine_name)) choice_objs.append(player) dlg = wx.MultiChoiceDialog(self, 'Select players to be removed. This will affect all users of mpLogGrabber', 'Remove Players', choice_str) if not dlg.ShowModal() == wx.ID_OK: dlg.Destroy() return False print dlg.GetSelections() for idx in dlg.GetSelections(): self.LogEvent('Removing player "%(name)s" with id: %(db_id)s' % {'name': choice_objs[idx].person_name, 'db_id': choice_objs[idx].db_id}) self.db.Connect() self.db.cursor.execute('DELETE ' 'FROM group_members ' 'WHERE user_id = %s' % (choice_objs[idx].db_id,)) self.db.cursor.execute('DELETE FROM users WHERE id = %s', [choice_objs[idx].db_id]) self.db.Disconnect() self.RefreshData() dlg.Destroy() def OnStart(self, event): if self.remote_busy: self.LogEvent('ignoring start button click, because busy with remote command') self.report_log = [] self.players_used = [] # Clean the busy player list and transfer size self.busyPlayersForTransfer = [] self.totalPlayersForTransfer = self.actives.GetCount() self.transfer_started = time.clock() if not self.IsBugFolderValid(): return False #if self.addLogsToFolder.IsChecked(): if self.CheckForBugFolder(): self.LogEvent('"%s" already exists, please enter a ' 'different version number' % self.GetDestinationPath(), '__err__') self.to_transfer = [] return False self.btn_start.Enable(False) self.btn_abort.Enable(True) self.EnablePlayerControls(False) self.remote_busy = True self.UpdateStageText('Gathering File Lists...') player_list = [] for idx in range(0, self.actives.GetCount()): player_list.append(self.actives.GetClientData(idx)) self.LogEvent('Checking machine activity') for player in player_list: if player.machine_name == socket.gethostname(): self.totalPlayersForTransfer -= 1 if not player.IsMachineOnline(): self.ReportEvent('%(name)s (%(machine)s) could not be found (no PING response)' % {'name': player.person_name, 'machine': player.machine_name}, False) # dont do this in remote mode # need to do this on the client only instead if not self.m_set_remote.IsChecked(): self.LogEvent('Checking path availability') for player in player_list: if player.online: player.SetValidPaths(self.GetSelectedGame()) if len(player.valid_paths) == 0: self.ReportEvent('%(name)s (%(machine)s) could not be found (path not shared)' % {'name': player.person_name, 'machine': player.machine_name}, False) self.LogEvent('Searching for files') for player in player_list: if len(player.valid_paths) > 0: player.SetFileList(self.GetSelectedGame()) # ---- self.LogEvent('Creating destination folders') self.CreatePath(self.GetDestinationPath()) if ZIP_MODE or self.m_set_remote.IsChecked(): pass else: for player in player_list: if len(player.file_list) > 0: self.CreatePath(self.GetDestinationPath(player)) self.to_transfer = [] # need to build the to_transfer list like this on client side in RC mode if not self.m_set_remote.IsChecked(): for player in player_list: if len(player.priority_log_file_list) > 0: self.to_transfer.extend(player.priority_log_file_list) if len(player.file_list) > 0: self.to_transfer.extend(player.file_list) if len(player.console_log_file_list) > 0: self.to_transfer.extend(player.console_log_file_list) self.player_list = player_list self.total_bytes = 0 for transporter in self.to_transfer: self.total_bytes += transporter.filesize self.bytes_transferred = 0 self.remote_busy = False self.UpdateStageText('Transferring Files, please do not reboot consoles') self.DoNextTransfer() def DoNextTransfer(self): # self.update_timer.Start(5) if self.abort_transfer: self.AbortTransfer() if len(self.to_transfer) == 0: if self.remote_busy: self.RCTransfersComplete() return False if self.m_set_remote.IsChecked(): self.remote_busy = False self.SendInitDataToClient() self.AllTransfersComplete() return False if ZIP_MODE: self.AutoZipComplete() return False else: self.AllTransfersComplete() return False transfer = self.to_transfer.pop(0) if transfer.error: wx.CallAfter(self.DoNextTransfer) return False if ZIP_MODE or self.remote_busy: # Gets called on the client only self.LogEvent('Copying "%s" to "%s"' % (transfer.source, transfer.destination)) elif not self.m_set_remote.IsChecked(): transfer.FindSafeDestination() self.LogEvent('Host is checking file paths: source - "%s", destination - "%s"' % (transfer.source, transfer.destination)) if (not self.m_set_remote.IsChecked()) or self.remote_busy: if not os.path.exists(transfer.source): self.LogEvent('FAILED: Source file could not be found', '__err__') self.ReportEvent('"%s" Copy Failed (source not found)' % transfer.source, False) transfer.Finished('Source file not found') self.current_transfer = 'None' wx.CallAfter(self.DoNextTransfer) return False if not self.m_set_remote.IsChecked(): if not self.remote_busy: if self.m_set_usefilter.IsChecked(): fname = os.path.basename(transfer.source) filters = self.GetActiveFilters() for data in filters: reg = data['pattern'] filter_type = data['type'] match = reg.match(fname) #self.LogEvent('%s : %s' % (fname, match)) #self.LogEvent('%s or %s = %s' % (match, filter_type, match or filter_type)) if bool(match) == filter_type: self.LogEvent('SKIPPED: %s matches %s' % (fname, data['description']), '__err__') self.ReportEvent('"%s" Skipped (filter match)' % transfer.source, False) transfer.Finished('Skipped by %s' % data['description']) self.current_transfer = 'None' #trick the total progress bar self.total_bytes -= transfer.filesize wx.CallAfter(self.DoNextTransfer) return False self.current_transfer = transfer #don't actually do the transfer if we are in RC mode if self.m_set_remote.IsChecked() and not self.remote_busy: wx.CallAfter(self.DoNextTransfer) transfer.error = 'remote' transfer.finished = True return True if ZIP_MODE or self.remote_busy: #print 'starting copy to zip' delayedresult.startWorker(self.Consumer, self.CopyFileToZip, wargs=(transfer,)) else: delayedresult.startWorker(self.Consumer, self.CopyFile, wargs=(transfer.source, transfer.destination)) def SendInitDataToClient(self): player_list = [] for idx in range(0, self.actives.GetCount()): player_list.append(self.actives.GetClientData(idx)) for player in player_list: if player.SendInitData(): player.stage = 'Requested' self.PopulatePlayerLists() def AllTransfersComplete(self): if self.transfer_aborted: self.UpdateStageText('Log Grab ABORTED!') self.transfer_aborted = False return False if not ZIP_MODE: self.WriteTransferReport(self.GetReportFilename()) t_time = '%sh %sm %ss' % qa.ConvertTime(int(time.clock() - self.transfer_started)) if ZIP_MODE: self.LogEvent('Compression Stage Completed in %s' % t_time) else: self.LogEvent('Transfer Stage Completed in %s' % t_time) self.UpdateStageText('Log Grab Complete!') if not ZIP_MODE and self.m_set_zipmode.IsChecked() and not self.m_set_remote.IsChecked(): self.DoAutoZips() self.DoNextTransfer() return False self.btn_start.Enable(True) self.btn_abort.Enable(False) self.EnablePlayerControls(True) self.remote_busy = False #print 'transfers complete' self.player_list = [] for idx in range(0, self.actives.GetCount()): self.actives.GetClientData(idx).Reset() def RCStartTransfers(self): self.btn_start.Enable(False) self.btn_abort.Enable(False) self.EnablePlayerControls(False) self.bytes_transferred = 0 self.total_bytes = 0 for transporter in self.to_transfer: if transporter.error == None: self.total_bytes += transporter.filesize self.UpdateStageText('Remote Copying for %s' % self.remote_master) self.LogEvent('files to copy: %s' % len(self.to_transfer)) self.DoNextTransfer() def RCTransfersComplete(self): self.btn_start.Enable(True) self.btn_abort.Enable(False) self.EnablePlayerControls(True) self.remote_busy = False try: self.SendMessage('__complete__') except: self.LogEvent('Error received while sending completion signal to "%s" (%s): %s' % (self.remote_master, self.remote_reply, sys.exc_info()[0]), '__err__') self.UpdateStageText('Remote Copy for %s Complete!' % self.remote_master) del self.remote_master del self.remote_reply def SendData(self, msg): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.connect((self.remote_reply, 9999)) sock.sendall(msg) finally: sock.close() def SendMessage(self, msg, extra={}): data = {'time': time.time(), 'source': os.environ.get('username'), 'machine': socket.gethostname(), 'msg': msg} data.update(extra) json_data = json.dumps(data) self.SendData(json_data) def AutoZipComplete(self): if self.transfer_aborted: self.transfer_aborted = False self.UpdateStageText('Log Compression ABORTED!') return False self.LogEvent('Compression complete, doing cleanup') self.EraseUncompressedLogs() self.AllTransfersComplete() self.UpdateStageText('Log Grab and Compression Complete!') global ZIP_MODE ZIP_MODE = False def EraseUncompressedLogs(self): for player in self.player_list: path = self.GetDestinationPath(player) self.LogEvent('Erasing Directory %s' % path) shutil.rmtree(path, ignore_errors=True) def RemoteTransferComplete(self, data): machine = data['machine'] player = self.GetPlayer(machine) player.stage = 'Complete' self.PopulatePlayerLists() def RemotePlayerBusy(self, data): machine = data['machine'] player = self.GetPlayer(machine) player.stage = 'Busy' self.PopulatePlayerLists() if machine not in self.busyPlayersForTransfer: self.busyPlayersForTransfer.append(machine) if len(self.busyPlayersForTransfer) == self.totalPlayersForTransfer: showBusyList = [] for idx in range(0, self.actives.GetCount()): player_machine = self.actives.GetClientData(idx).machine_name player_name = self.actives.GetClientData(idx).GetPlayerName() if player_machine in self.busyPlayersForTransfer: showBusyList.append(player_name) dial = wx.MessageDialog(self, 'Some players were busy:\n\n'+'\n'.join(map(str, showBusyList))+'\n\nWould you like to update \'Active Players\' list to try grabbing their logs again?', 'Busy Players', wx.YES_NO) result = dial.ShowModal() if result == wx.ID_YES: for idx in range(0, self.actives.GetCount()): player_machine = self.actives.GetClientData(idx).machine_name player_name = self.actives.GetClientData(idx).GetPlayerName() if player_machine not in self.busyPlayersForTransfer: self.actives.GetClientData(idx).Deactivate() self.PopulatePlayerLists() self.busyPlayersForTransfer = [] self.totalPlayersForTransfer = 0 def GetPlayer(self, machine): for player in self.player_objs: if player.machine_name.lower() == machine.lower(): return player return None def OnViewReport(self, event=None): tr_path = os.path.join(self.GetDestinationPath(), 'TransferReport.html') if not os.path.exists(tr_path): self.LogEvent('Transfer report not found (%s)' % tr_path, '__err__') else: os.startfile(tr_path) def CheckForBugFolder(self): return os.path.exists(self.GetDestinationPath()) def OnAbort(self, event): self.abort_transfer = True self.LogEvent('Attempting to ABORT') def AbortTransfer(self): if not ZIP_MODE: self.copier.OnAbort() self.abort_transfer = False self.transfer_aborted = True self.btn_start.Enable(True) self.btn_abort.Enable(False) self.EnablePlayerControls(True) self.UpdateFileProgressBar(0, 0, 0) self.LogEvent('Transfers ABORTED') self.WriteTransferReport(self.GetReportFilename()) for player in self.player_objs: player.Reset() self.to_transfer = [] def DoAutoZips(self): self.LogEvent('Starting Auto-Zip Procedure') self.UpdateStageText('Compressing Logs, players may now reboot their consoles') #self.player_list_zip_backup = copy.deepcopy(self.player_list) zip_dest = '%s\\logs_%s_%s.zip' % (self.GetDestinationPath(), self.GetBugNumber(), self.GetVersionText()) self.LogEvent('Creating archive %s' % zip_dest) for player in self.player_list: zip_file_list = [] for transfer in player.priority_log_file_list: if os.path.exists(transfer.destination): zip_file_list.append(FileTransporter(transfer.destination, zip_dest, player)) player.priority_log_file_list = zip_file_list zip_file_list = [] for transfer in player.file_list: if os.path.exists(transfer.destination): zip_file_list.append(FileTransporter(transfer.destination, zip_dest, player)) player.file_list = zip_file_list zip_file_list = [] for transfer in player.console_log_file_list: if os.path.exists(transfer.destination): zip_file_list.append(FileTransporter(transfer.destination, zip_dest, player)) player.console_log_file_list = zip_file_list global ZIP_MODE ZIP_MODE = True self.to_transfer = [] for player in self.player_list: if len(player.priority_log_file_list) > 0: self.to_transfer.extend(player.file_list) for player in self.player_list: if len(player.file_list) > 0: self.to_transfer.extend(player.file_list) for player in self.console_log_file_list: if len(player.file_list) > 0: self.to_transfer.extend(player.file_list) self.bytes_transferred = 0 self.UpdateTotalProgressBar() def PopulateGamesList(self): self.game_choice.Clear() for game_id in self.data['info']['games']: self.game_choice.Append(self.data[game_id]['name'], game_id) DebugLog("[PopulateGamesList] populating with default id - %s" % self.default_game_id) if not self.default_game_id is None: self.game_choice.SetSelection(self.default_game_id) else: self.game_choice.SetSelection(0) def PopulatePlayerLists(self): self.players.Clear() self.actives.Clear() for player in self.player_objs: if player.active: #self.actives.Append(player.person_name, player) self.actives.Append(player.GetActivesText(), player) else: if self.GetActivePlayerFilter() == None: self.players.Append(player.GetPlayerName(), player) elif self.GetActivePlayerFilter() == player.filter_group: self.players.Append(player.GetPlayerName(), player) else: pass def PopulateFilters(self): db = self.db db.Connect() db.cursor.execute('SELECT DISTINCT `filter_group` ' 'FROM `users`') filters = db.cursor.fetchall() db.Disconnect() self.players_filter.Clear() self.players_filter.Append('No Filter', None) for item in filters: self.players_filter.Append(item[0], item[0]) self.players_filter.SetSelection(0) def GetActivePlayerFilter(self): sel = self.players_filter.GetSelection() if self.players_filter.GetSelection() == -1: return None else: return self.players_filter.GetClientData(self.players_filter.GetSelection()) def OnFilterChange(self, event=None): self.PopulatePlayerLists() def ActivateSelectedPerson(self, event): sel = self.players.GetSelections() if len(sel) == 0: return False for selection in sel: self.players.GetClientData(selection).Activate() self.PopulatePlayerLists() def DeActivateSelectedPerson(self, event): sel = self.actives.GetSelections() if len(sel) == 0: return False for selection in sel: self.actives.GetClientData(selection).Deactivate() self.PopulatePlayerLists() def TestCopy(self, event): self.OnStart(None) ## for machine, player in self.actives_dict.iteritems(): ## print '%s: %s' % (player, self.IsMachineActive(machine)) ## self.LogEvent('mainpath: %s' % self.GetDestinationPath()) ## self.CreatePath(self.GetDestinationPath()) ## for machine, player in self.actives_dict.iteritems(): ## self.LogEvent('%s: %s' % (machine, self.GetDestinationPath(machine))) ## self.CreatePath(self.GetDestinationPath(machine)) ## src = 'N:\\RSGEDI\\GameLogs\\GTA E1 Japan.zip' ## dst = 'd:\\test_copy.zip' ## ## self.abort_transfer = False ## self.btn_start.Enable(False) ## self.btn_abort.Enable(True) ## #### self.copier = qa.CopyFile(src, dst) #### self.CopyFile() ## ## self.update_timer.Start(500) #### ## delayedresult.startWorker(self.Consumer, ## self.CopyFile, ## wargs=(src, dst)) def Consumer(self, result): #print '%s bytes copied' % result.get() try: self.bytes_transferred += result.get() except IOError: self.LogEvent('"%s": Copy Failed (IOError)' % self.current_transfer, '__err__') self.ReportEvent('"%s": Copy Failed (IOError)' % self.current_transfer, False) self.current_transfer.Finished('IOError') else: self.ReportEvent('"%s": Copy Succesful' % self.current_transfer, True) self.current_transfer.Finished() self.current_transfer = 'None' self.UpdateTotalProgressBar() self.DoNextTransfer() def CopyFileToZip(self, transfer): if self.abort_transfer: self.AbortTransfer() return False if self.remote_busy: player_path = '' else: player_path = '%s_%s' % (transfer.player.machine_name, self.GetCleanUsername(transfer.player)) arc_fname = os.path.join(player_path, os.path.basename(transfer.source)) zip_out = zipfile.ZipFile(transfer.destination, 'a', compression=zipfile.ZIP_DEFLATED, allowZip64=True) zip_out.write(transfer.source, arcname=arc_fname) zip_out.close() transfer.Finished() return transfer.filesize def CopyFile(self, source, destination): self.copier = qa.CopyFile(source, destination) copier = self.copier #print 'starting file copy' while not copier.file_copied: copier.CopyNextBlock() #self.pct_copied = copier.PercentageCopied() self.UpdateFileProgressBar(copier.PercentageCopied(), copier.bytes_copied, copier.filesize) self.UpdateTotalProgressBar(self.bytes_transferred + copier.bytes_copied) if self.abort_transfer: #print 'aborting transfer' self.AbortTransfer() return False #print 'copy complete' return copier.bytes_copied def UpdateFileProgressBar(self, pct, copied, total): self.file_pct.SetValue(pct) pct_str = '%(pct).2f%% %(copied)s / %(total)s bytes copied' % {'pct': pct, 'copied': copied, 'total': total} self.file_pct_text.SetLabel(pct_str) self.file_pct_sizer.Layout() def UpdateTotalProgressBar(self, bytes_transferred = None): if bytes_transferred == None: bytes_transferred = self.bytes_transferred try: overall_percentage = (1.0 * bytes_transferred / self.total_bytes) * 100 except ZeroDivisionError: overall_percentage = 100 self.total_pct.SetValue(overall_percentage) pct_str = '%(pct).2f%% %(copied)s / %(total)s bytes copied' % {'pct': overall_percentage, 'copied': bytes_transferred, 'total': self.total_bytes} self.total_pct_text.SetLabel(pct_str) self.total_pct_sizer.Layout() def UpdateStageText(self, text): self.stage_indicator.SetLabel(text) def LogEvent(self, msg, val='__msg__'): in_point = self.log.GetInsertionPoint() if val == '__msg__': self.log.SetDefaultStyle(wx.TextAttr('BLACK', 'WHITE')) elif val == '__err__': self.log.SetDefaultStyle(wx.TextAttr('RED', 'WHITE')) try: self.log.AppendText('\n%s' % msg) self.log.ScrollLines(1) #self.log.ShowPosition(0) #self.log.ShowPosition(in_point) #self.log.Layout() except: pass qa.logging(msg) def ReportEvent(self, msg, val): self.report_log.append([msg, val]) def IsMachineActive(self, machine): self.LogEvent('Sending PING request to %s' % machine) if int(os.system('PING %s -n 1' % machine)) == 0: self.LogEvent('%s responded to PING' % machine) return True else: self.LogEvent('%s did not respond to PING' % machine, '__err__') return False ## ## def IsPathAvailable(self, machine, path): ## if os.path.exists(os.path.join(machine, path)): ## return True def GetSelectedGame(self): sel = self.game_choice.GetSelection() if sel == -1: return None game_id = self.game_choice.GetClientData(sel) return game_id def GetFullPath(self, machine): mpath = '\\\\%s' % machine game = self.GetSelectedGame() if game == None: return mpath fpath = os.path.join(mpath, self.data[game]['source']) return fpath def GetFullPaths(self, machine): #print 'GetFullPaths\n=========' mpath = '\\\\%s' % machine game = self.GetSelectedGame() if game == None: return mpath paths = [] for path in self.data[game]['source']: print path paths.append(os.path.join(mpath, path)) return paths def GetFileList(self, path): dirlist = os.listdir(path) extensions = self.data[self.GetSelectedGame()]['filetypes'] valid_files = [] priority_logs = [] console_logs = [] #prioritize prediction and network logs because they get overwritten quickly! for fname in dirlist: for ext in extensions: if fname.endswith(ext): if fname.lower().find("prediction") != -1: priority_logs.append(os.path.join(path, fname)) elif fname.lower().find("console") != -1: console_logs.append(os.path.join(path, fname)) else: valid_files.append(os.path.join(path, fname)) continue valid_files[0:0] = priority_logs valid_files.extend(console_logs) return valid_files #def GetFilters(self): #filters = [] #with open('filters.txt', 'r') as f: #for line in f: #pattern = line.strip('\n') #if len(pattern) == 0: #continue #filters.append(re.compile(pattern)) #return filters def OnUseMachineName(self, id): """Sets the global variable MACHINE_NAME_MODE to true or false. Global variable necessary for the Player class to know if the settings item is checked. """ global MACHINE_NAME_MODE if MACHINE_NAME_MODE: MACHINE_NAME_MODE = False self.m_set_usemachinename.Check(False) self.RefreshData() else: MACHINE_NAME_MODE = True self.m_set_usemachinename.Check(True) self.RefreshData() def OnSetCustomMessage(self, *args, **kwargs): """Settings option to set a custom message to be copied together with bug location (self.OnCopyCustom())""" self.CustomMessageBugPlaceholder = 'X:\\I_AM_A\\PLACEHOLDER\\PATH\\' default_msg = '"%s"' % self.CustomMessageBugPlaceholder if self.CustomClipboardMessage != "": default_msg = self.CustomClipboardMessage dialog = wx.TextEntryDialog(self,"Enter text to be copied together with bug location. Placeholder path will\nbe replaced with correct one when you copy location.\n\nPress Cancel to reset the template.","Comment Template", default_msg, style=wx.TE_MULTILINE|wx.OK|wx.CANCEL|wx.TE_NOHIDESEL) dialog.SetSize((380,300)) dialog.CenterOnParent() result = dialog.ShowModal() if result == wx.ID_OK: self.CustomClipboardMessage = dialog.GetValue() elif result == wx.ID_CANCEL: self.CustomClipboardMessage = "" def OnSetFilters(self, *keys, **kwargs): filter_list = [] for key, filter_data in self.filters.iteritems(): filter_list.append(filter_data['description']) selections = [] for key in self.selected_filters: selections.append(self.filters.keys().index(key)) dlg = wx.MultiChoiceDialog(self, 'Select filters to stop certain files from being transferred', 'Filter Selection', filter_list) dlg.SetSelections(selections) dlg_ret = dlg.ShowModal() if dlg_ret == wx.ID_CANCEL: dlg.Destroy() return False self.selected_filters = [] for sel_id in dlg.GetSelections(): self.selected_filters.append(self.filters.keys()[sel_id]) return True def LoadFilters(self): filters = {} with open('data\\filters.xml', 'r') as f: tree = xml.etree.ElementTree.fromstring(f.read()) for ff in tree.findall('filter'): filter_id = ff.attrib['id'] f_dict = {'pattern': re.compile(ff.find('pattern').text), 'description': ff.find('description').text} filter_type = ff.find('type').text filter_types = {'exclude': True, 'include': False} f_dict['type'] = filter_types[filter_type] filters[filter_id] = f_dict if ff.find('default').text.lower() == 'on': self.selected_filters.append(filter_id) return filters def GetActiveFilters(self): filters = [] if self.m_set_usefilter.IsChecked(): for key in self.selected_filters: filters.append(self.filters[key]) return filters def GetBugNumber(self): return self.bug_number.GetValue().strip() def GetVersionText(self): return self.version_text.GetValue().strip() def GetMainPath(self): return os.path.join(self.GetBugNumber(), self.GetVersionText()) def addressInNetwork(self, ip, net_n_bits): ipaddr = struct.unpack(' information in $RS_TOOLSROOT$\\config.xml is set up correctly and/or try grabbing latest on that file! Will try using hardcoded subnet values...",'__err__') for (subnet,folder) in backUpNetworkList: if self.addressInNetwork(localMachineIPAddrStr,subnet): selectedFolder = folder if selectedFolder == "": self.LogEvent("Could not find appropriate studio destination folder even from a hardcoded list! Double check that $RS_TOOLSROOT$\\config.xml is set up correctly and/or try grabbing latest on that file! ",'__err__') settingsFolder = self.data[self.GetSelectedGame()]['folder']; studio_path = os.path.join(settingsFolder, selectedFolder) if not os.access(studio_path,os.F_OK): self.LogEvent("Chosen studio log path (\"%s\") does not exist!!!" % studio_path,'__err__') elif not os.access(studio_path, os.W_OK | os.X_OK): self.LogEvent("Chosen studio log path (\"%s\") exists, but you don't have write access to it!" % studio_path,'__err__') main_path = os.path.join(studio_path, self.GetMainPath()) #self.LogEvent("Full destination path for logs - \"%s\"" % main_path) if player == None: return main_path else: player_path = '%s_%s' % (player.machine_name, self.GetCleanUsername(player)) return os.path.join(main_path, player_path) def GetCleanUsername(self, player): """ turn a username into something we can add to a folder path """ name = player.person_name.lower() name = name.replace(' ', '.') dodgy_chars = ['\\', '/', ':', '*', '?', '"', '<', '>', '|'] for char in dodgy_chars: name = name.replace(char, '-') return name def IsBugFolderValid(self): if self.GetBugNumber() == '': self.LogEvent('Bug number is blank', '__err__') is_valid = False if self.GetVersionText() == '': self.LogEvent('Version text is blank', '__err__') is_valid = False try: return is_valid except UnboundLocalError: return True def GetReportFilename(self): fname = 'TransferReport.html' return os.path.join(self.GetDestinationPath(), fname) def OnOpenBugFolder(self, *args, **kwargs): self.LogEvent('Opening "%s"' % self.GetDestinationPath()) if os.path.exists(self.GetDestinationPath()): subprocess.Popen('explorer %s' % self.GetDestinationPath()) else: self.LogEvent('Path does not exist, cannot open', '__err__') def OnCopy(self, *args, **kwargs): """Copies bug location to the clipboard""" clip_data = wx.TextDataObject() clip_data.SetText('"%s"' % self.GetDestinationPath()) if wx.TheClipboard.Open(): self.LogEvent('Copying "%s" to clipboard' % self.GetDestinationPath()) wx.TheClipboard.SetData(clip_data) wx.TheClipboard.Close() wx.TheClipboard.Flush() def OnCopyCustom(self, *args, **kwargs): """Copies bug location and the custom message (from self.OnSetCustomMessage()) if there is one to clipboard""" clip_data = wx.TextDataObject() #Replace the placeholder with correct bug folder path bug_path = self.GetDestinationPath() clipboard_message = self.CustomClipboardMessage.replace(self.CustomMessageBugPlaceholder, bug_path) if self.CustomClipboardMessage == "": clip_data.SetText('"%s"' % self.GetDestinationPath()) else: clip_data.SetText('%s' % clipboard_message) if wx.TheClipboard.Open(): if self.CustomClipboardMessage == "": self.LogEvent('Copying %s to clipboard' % clip_data.GetText()) else: self.LogEvent('Copying: " %s " to clipboard' % clip_data.GetText()) wx.TheClipboard.SetData(clip_data) wx.TheClipboard.Close() wx.TheClipboard.Flush() def CreatePath(self, path): if not os.path.exists(path): self.LogEvent('Creating path "%s"' % path) os.makedirs(path) def GetSafeDestinations(self, transfers): """ take the list of transfers and ensure that no files will be overwritten, return an updated list of transfers with decorated filenames """ new_dests = [] new_src = [x[0] for x in transfers] for src, dest in transfers: if dest not in new_dests: new_dests.append(dest) else: i = 1 new_dest = self.DecorateFilename(dest, i) while new_dest in new_dests: i += 1 new_dest = self.DecorateFilename(dest, i) new_dests.append(new_dest) return zip(new_src, new_dests) def DecorateFilename(self, fname, num): template = '%(filename)s(%(num)s)%(ext)s' data = {} data['filename'], data['ext'] = os.path.splitext(fname) data['num'] = num return template % data def WriteTransferReport(self, path): out_file = open(path, 'w') big_template = """ mpLogGrabber Transfer Report (%(bugnumber)s) mpLogGrabber Transfer Report

Transfer by %(username)s on %(current_date_time)s
Destination: %(destination_path)s

Players
%(player_list)s


Files
%(file_list)s




File generated by mpLogGrabber """ report_dict = { 'bugnumber': self.GetBugNumber(), 'username': os.environ.get('username'), 'current_date_time': time.strftime('%Y-%m-%d %H:%M:%S'), 'destination_path': self.GetDestinationPath() } player_temp = """ %(playername)s %(machinename)s %(status)s """ trans_temp = """ %(source)s %(destination)s %(status)s """ player_table = '' file_table = '' for player in self.player_list: if player.online == False: player_table += player_temp % {'playername': player.person_name, 'machinename': player.machine_name, 'stat_col': 'red', 'status': 'No PING response'} elif len(player.file_list) == 0: player_table += player_temp % {'playername': player.person_name, 'machinename': player.machine_name, 'stat_col': 'red', 'status': 'No files found'} else: player_table += player_temp % {'playername': player.person_name, 'machinename': player.machine_name, 'stat_col': '#6fbf59', 'status': '%s files found' % len(player.file_list)} for transfer in player.file_list: trans_dict = {'source': transfer.source, 'destination': transfer.destination} if transfer.finished == False: trans_dict['stat_col'] = 'red' trans_dict['status'] = 'Aborted' elif transfer.error == None: trans_dict['stat_col'] = '#6fbf59' trans_dict['status'] = 'OK' elif transfer.error == 'remote': trans_dict['stat_col'] = '#6fbf59' trans_dict['status'] = 'Remote Request Sent' else: trans_dict['stat_col'] = 'red' trans_dict['status'] = transfer.error file_table += trans_temp % trans_dict report_dict['player_list'] = player_table report_dict['file_list'] = file_table #for item in report: #if item[1]: #template = true_template #else: #template = false_template #file_list_html += template % {'report': item[0]} #report_dict['file_list'] = file_list_html out_file.write(big_template % report_dict) out_file.close() def OnNewGroup(self, event=None): dlg = wx.TextEntryDialog(self, 'Please enter a name for this group') if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return False group_id = self.AddNewGroup(dlg.GetValue()) self.LogEvent('New group \'%s\' created' % dlg.GetValue()) group_name = dlg.GetValue() dlg.Destroy() for idx in range(0, self.actives.GetCount()): self.AddGroupMember(group_id, self.actives.GetClientData(idx).db_id) self.LogEvent('Player \'%s\' added to group \'%s\'' % (self.actives.GetClientData(idx).person_name, group_name)) def AddNewGroup(self, name): db = self.db data = {'name': name, 'owner': os.environ.get('username')} db.Connect() db.Insert('groups', data) group_id = db.cursor.execute('SELECT `id` ' 'FROM `groups` ' 'WHERE `name`=%s AND `owner`=%s', (data['name'], data['owner'])) new_id = db.cursor.fetchone() db.Disconnect() return new_id[0] def AddGroupMember(self, group_id, user_id): db = self.db data = {'group_id': group_id, 'user_id': user_id} db.Connect() db.Insert('group_members', data) db.Disconnect() def GetGroupMembers(self, group_id): db = self.db db.Connect() db.cursor.execute('SELECT user_id ' 'FROM group_members ' 'WHERE group_id=%s', group_id) members = db.cursor.fetchall() db.Disconnect() return members def GetGroupList(self): db = self.db db.Connect() db.cursor.execute('SELECT `id`, `name` FROM groups ORDER BY name') groups = db.cursor.fetchall() db.Disconnect() return groups def OnAddGroup(self, event=None): groups = self.GetGroupList() dlg = wx.MultiChoiceDialog(self, 'Please select a group to add to current player list', 'Add Group To Current List', [x[1] for x in groups]) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return False group_ids = [] for id in dlg.GetSelections(): group_id = groups[id][0] group_ids.append(group_id) group_name = groups[id][1] self.LogEvent('Group \'%s\' loaded' % group_name) dlg.Destroy() self.AddGroups(group_ids) def AddGroups(self, group_ids): db = self.db db.Connect() group_members = [] for group_id in group_ids: db.cursor.execute('SELECT user_id ' 'FROM group_members ' 'WHERE group_id=%s', (group_id,)) temp_members = db.cursor.fetchall() temp_members = [x[0] for x in temp_members] group_members += temp_members db.Disconnect() for player in self.player_objs: if player.active == True: continue if player.db_id in group_members: player.Activate() self.PopulatePlayerLists() def OnLoadGroup(self, event=None): groups = self.GetGroupList() dlg = wx.MultiChoiceDialog(self, 'Please select a group to load', 'Load Group', [x[1] for x in groups]) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return False group_ids = [] for id in dlg.GetSelections(): group_id = groups[id][0] group_ids.append(group_id) group_name = groups[id][1] self.LogEvent('Group \'%s\' loaded' % group_name) dlg.Destroy() self.LoadGroups(group_ids) def LoadGroups(self, group_ids): db = self.db db.Connect() group_members = [] for group_id in group_ids: self.LogEvent("Trying to query group %s" % group_id) db.cursor.execute('SELECT user_id ' 'FROM group_members ' 'WHERE group_id=%s', (group_id,)) temp_members = db.cursor.fetchall() temp_members = [x[0] for x in temp_members] group_members += temp_members db.Disconnect() for player in self.player_objs: if player.db_id in group_members: player.Activate() else: player.Deactivate() self.PopulatePlayerLists() def OnDeleteGroup(self, event=None): groups = self.GetGroupList() dlg = wx.SingleChoiceDialog(self, 'Please select a group to delete', 'Delete Group', [x[1] for x in groups]) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return False to_delete = groups[dlg.GetSelection()] dlg.Destroy() dlg = wx.MessageDialog(self, 'Are you sure you want to delete the group %s?' ' Note: Only the group will be deleted, not the ' 'members of the group. ' % to_delete[1], 'Confirm Group Deletion', wx.YES_NO | wx.ICON_QUESTION) if dlg.ShowModal() == wx.ID_NO: dlg.Destroy() return False self.DeleteGroup(to_delete[0]) self.LogEvent('Deleting group \'%s\' with id: \'%s\'' % (to_delete[1], to_delete[0])) def DeleteGroup(self, group_id): db = self.db db.Connect() #first delete any entries in group_members db.cursor.execute('DELETE ' 'FROM `group_members` ' 'WHERE `group_members`.`group_id`=%s', (group_id,)) #then delete the group from groups db.cursor.execute('DELETE ' 'FROM `groups` ' 'WHERE `groups`.`id`=%s', (group_id,)) db.Disconnect() def GetGroupName(self, group_id): db = self.db db.Connect() db.cursor.execute('SELECT `name` ' 'FROM groups ' 'WHERE `id`=%s', (group_id,)) name = db.cursor.fetchall() db.Disconnect() #this will cause indexerror if group_id is not valid, #catch indexerror further up the chain name_final = name[0] return name[0][0] def OnReplaceGroup(self, event): groups = self.GetGroupList() dlg = wx.SingleChoiceDialog(self, 'Please select a group to replace with the current player list', 'Replace Group', [x[1] for x in groups]) if dlg.ShowModal() != wx.ID_OK: dlg.Destroy() return False to_replace = groups[dlg.GetSelection()] dlg.Destroy() #are you SURE? dlg = wx.MessageDialog(self, 'Are you sure you want to replace the members of ' '"%s" with your current active players list?' % to_replace[1], 'Confirm Group Replace', wx.YES_NO | wx.ICON_QUESTION) if dlg.ShowModal() == wx.ID_NO: dlg.Destroy() return False self.ReplaceGroup(to_replace[0]) def ReplaceGroup(self, group_id): #get the group name group_name = self.GetGroupName(group_id) db = self.db db.Connect() #delete all old groupmembers using this ID db.cursor.execute('DELETE ' 'FROM `group_members` ' 'WHERE `group_members`.`group_id`=%s', (group_id,)) db.Disconnect() #insert new group members for idx in range(0, self.actives.GetCount()): self.AddGroupMember(group_id, self.actives.GetClientData(idx).db_id) self.LogEvent('Player \'%s\' added to group \'%s\'' % (self.actives.GetClientData(idx).person_name, group_name)) class TwoChoicesScreen(wx.Dialog): def __init__(self, parent, *args, **kw): wx.Dialog.__init__(self, parent, title='Sort By', *args, **kw) #pnl = wx.Panel(self) hsizer = wx.BoxSizer(wx.HORIZONTAL) btn1 = wx.Button(self,wx.ID_ANY,'Player Names') btn2 = wx.Button(self,wx.ID_ANY,'Machine Names') hsizer.Add(btn1,flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5) hsizer.Add(btn2,flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL, border=5) self.Bind(wx.EVT_BUTTON, self.OnSearchPlayerName, id=btn1.GetId()) self.Bind(wx.EVT_BUTTON, self.OnSearchMachineName, id=btn2.GetId()) self.SetSizerAndFit(hsizer) self.CenterOnParent() def OnSearchPlayerName(self, event): self.EndModal(wx.ID_YES) def OnSearchMachineName(self, event): self.EndModal(wx.ID_NO) class ReportScreen(wx.Dialog): def __init__(self, parent, report_log, *args, **kwargs): wx.Dialog.__init__(self, parent, title='Transfer Report', style= wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, *args, **kwargs) main_sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(main_sizer) self.text = wx.TextCtrl(self, style = wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_RICH) main_sizer.Add(self.text, flag=wx.EXPAND | wx.ALL, border = 3, proportion=1) self.btn_close = wx.Button(self, label='Close') main_sizer.Add(self.btn_close, flag=wx.ALIGN_RIGHT) self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose) self.FillReport(report_log) self.CenterOnParent() def OnClose(self, *args, **kwargs): self.Close() def FillReport(self, rep): for msg, val in rep: if val: self.text.SetDefaultStyle(wx.TextAttr('GREEN', 'WHITE')) else: self.text.SetDefaultStyle(wx.TextAttr('RED', 'WHITE')) self.text.AppendText('%s\n' % msg) self.text.SetScrollPos(wx.HORIZONTAL, 0) self.text.SetInsertionPoint(0) class AddPlayerDlg(wx.Dialog): def __init__(self, parent, mode='__add__', *args, **kwargs): wx.Dialog.__init__(self, parent, title='Add Player', *args, **kwargs) self.SetInitialSize((350,180)) main_sizer = wx.BoxSizer(wx.VERTICAL) #self.SetSizer(main_sizer) if mode == '__add__': description_str = 'Add a new player to the database' elif mode == '__modify__': description_str = 'Modify an existing player' self.SetTitle('Modify Player') description_text = wx.StaticText(self, label=description_str) main_sizer.Add(description_text, flag=wx.ALL, border=5) sizer_grid = wx.FlexGridSizer(3, 2, 5, 5) sizer_grid.AddGrowableCol(1, 1) main_sizer.Add(sizer_grid, flag=wx.EXPAND | wx.ALL, border = 3, proportion = 10) name_text = wx.StaticText(self, label = 'Player Name') sizer_grid.Add(name_text, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT) self.name_ctrl = wx.TextCtrl(self) sizer_grid.Add(self.name_ctrl, flag=wx.EXPAND, proportion=1) machine_text = wx.StaticText(self, label='Machine Name') sizer_grid.Add(machine_text, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT) self.machine_ctrl = wx.TextCtrl(self) sizer_grid.Add(self.machine_ctrl, flag=wx.EXPAND, proportion = 1) filter_text = wx.StaticText(self, label = 'Filter') sizer_grid.Add(filter_text, flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT) self.filter_ctrl = wx.TextCtrl(self) sizer_grid.Add(self.filter_ctrl, flag=wx.EXPAND, proportion = 1) hrule = wx.StaticLine(self) main_sizer.Add(hrule, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border = 10, proportion = 0) btn_sizer = wx.BoxSizer(wx.HORIZONTAL) main_sizer.Add(btn_sizer, flag=wx.ALIGN_RIGHT | wx.ALL, border = 5) ## btn_ok = wx.Button(self, label='Ok') ## btn_sizer.Add(btn_ok) ## btn_ok.Bind(wx.EVT_BUTTON, self.OnOk) ## ## btn_cancel = wx.Button(self, label = 'Cancel') ## btn_sizer.Add(btn_cancel) ## btn_cancel.Bind(wx.EVT_BUTTON, self.OnCancel) btns = self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL) btn_sizer.Add(btns) #self.SetSizerAndFit(main_sizer) self.SetSizer(main_sizer) self.CenterOnParent() def GetPlayer(self): return self.name_ctrl.GetValue() def SetPlayer(self, data): self.name_ctrl.SetValue(data) def GetMachine(self): return self.machine_ctrl.GetValue() def SetMachine(self, data): self.machine_ctrl.SetValue(data) def GetFilter(self): return self.filter_ctrl.GetValue() def SetFilter(self, data): self.filter_ctrl.SetValue(data) class DB: def __init__(self, host, database, user, password = None): self.host = host self.user = user self.password = password self.database = database self.connected = False def Connect(self): try: del self.connection_error except AttributeError: pass print 'opening connection' try: self.db = connector.Connect(host=self.host, user=self.user, password=self.password, database=self.database) self.cursor = self.db.cursor() print 'connected' self.connected = True return True except: self.connection_error = traceback.format_exc() return False def Disconnect(self): if self.connected: self.db.commit() self.cursor.close() self.db.close() print 'disconnected' self.connected = False #def Execute(self, cmd): #self.Connect() #self.cursor.execute(cmd) #self.Disconnect() def InsertTest(self): #statement = 'INSERT INTO junctions (name, x, y) VALUES (%s, %s, %s)' #self.Connect() #self.cursor.execute(statement, ('error', 123, 100)) #warnings = self.cursor.fetchwarnings() #if warnings: #print warnings #messages = self.cursor #self.Disconnect() self.Insert('junctions', {'name': 'test1', 'x': 3, 'y': 4}) #self.Insert('junctions', ['name', 'x', 'y'], ['insertest',80085, 1111]) def SelectTest(self): statement = 'SELECT * FROM junctions' self.Connect() self.cursor.execute(statement) for row in self.cursor.fetchall(): print row self.Disconnect() def Insert(self, table, data): keys = [] vals = [] for key, value in data.iteritems(): keys.append('`%s`' % key) vals.append(value) keys_str = self.GetRowsString(keys) vals_str = self.GetValsString(len(vals)) print keys_str print vals_str % tuple(vals) stmt = 'INSERT INTO %s %s VALUES %s' % (table, keys_str, vals_str) print stmt % tuple(vals) self.cursor.execute(stmt, tuple(vals)) #self.Disconnect() def GetRowsString(self, rows): row_str = '(' for row in rows: row_str += '%s, ' % row row_str = row_str.strip(', ') row_str += ')' return row_str def GetValsString(self, num_vals): val_str = '(' for x in range(0, num_vals): val_str += '%s, ' val_str = val_str.strip(', ') val_str += ')' return val_str class Player: def __init__(self, player_data, parent):#app_data, app_logger, parent):#db_id, machine_name, person_name, filter_group): self.db_id = player_data[0] self.machine_name = player_data[1] self.person_name = player_data[2] self.filter_group = player_data[3] self.parent = parent self.app_data = parent.data self.LogEvent = parent.LogEvent self.colour = (255, 255, 0)#None self.stage = None #bool to determine if this player is in the 'active' list or not self.active = False #set up the 'transfer setup' variables self.Reset() def GetActivesText(self): #Returns player name to display in the 'actives' list box if MACHINE_NAME_MODE: if self.stage: return '%s (%s) [%s]' % (self.person_name, self.machine_name, self.stage) else: return '%s (%s)' % (self.person_name, self.machine_name) else: if self.stage: return '%s [%s]' % (self.person_name, self.stage) else: return self.person_name def GetPlayerName(self): #Returns the player name to display in the 'players' list box if MACHINE_NAME_MODE: return '%s (%s)' % (self.person_name, self.machine_name) else: return self.person_name def Reset(self): self.online = None self.valid_paths = [] self.invalid_paths = [] self.file_list = [] self.priority_log_file_list = [] self.console_log_file_list = [] def GetFullPaths(self, game): paths = [] for path in self.app_data[game]['source']: paths.append(os.path.join('\\\\%s' % self.machine_name, path)) return paths def SetValidPaths(self, game): for path in self.GetFullPaths(game): if not os.path.exists(path): self.LogEvent('%s not available' % path, '__err__') self.invalid_paths.append(path) else: self.valid_paths.append(path) def SetFileList(self, game): exts = self.app_data[game]['filetypes'] dest_path = self.parent.GetDestinationPath(self) for path in self.valid_paths: for fname in os.listdir(path): for ext in exts: if fname.endswith(ext): if ZIP_MODE: #print 'zip mode enabled' dest = '%s.zip' % dest_path #print dest else: dest = os.path.join(dest_path, fname) if fname.endswith(".log") and (fname.lower().find("prediction") != -1): self.priority_log_file_list.append(FileTransporter(os.path.join(path, fname),dest,self)) self.LogEvent('[prediction] '+os.path.join(path, fname)) elif fname.endswith(".log") and (fname.lower().find("console") != -1): self.console_log_file_list.append(FileTransporter(os.path.join(path, fname),dest,self)) self.LogEvent('[console] '+os.path.join(path, fname)) else: self.file_list.append(FileTransporter(os.path.join(path, fname),dest,self)) self.LogEvent('[normal] '+os.path.join(path, fname)) def Activate(self): self.active = True #test remote connection self.TestRemote() def TestRemote(self): if self.parent.m_set_remote.IsChecked(): try: self.SendMessage('__hiya__') self.stage = 'Available' return True except: self.LogEvent('%s (%s) could not be reached' % (self.person_name, self.machine_name)) #e = sys.exc_info()[1] #self.LogEvent('%s' %e) self.stage = 'UNAVAILABLE' return False def Deactivate(self): self.active = False def IsActive(self): return self.active def IsMachineOnline(self): if self.online != None: return self.online self.LogEvent('sending PING request to %s' % self.machine_name) if int(os.system('PING %s -n 1' % self.machine_name)) == 0: self.LogEvent('%s responded to PING' % self.machine_name) self.online = True else: self.LogEvent('%s did not respond to PING' % self.machine_name, '__err__') self.online = False return self.online def SendData(self, msg): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.connect((self.machine_name, 9999)) sock.sendall(msg) finally: sock.close() def SendMessage(self, msg, extra={}): data = {'time': time.time(), 'source': os.environ.get('username'), 'msg': msg} data.update(extra) json_data = json.dumps(data) self.SendData(json_data) def SendInitData(self): if not self.TestRemote(): return False selected_game = self.parent.GetSelectedGame() filters = self.parent.GetActiveFilters() player_path = '%s_%s' % (self.parent.GetCleanUsername(self), self.machine_name) zip_dest = '%s\\%s_logs_%s_%s.zip' % (self.parent.GetDestinationPath(), player_path, self.parent.GetBugNumber(), self.parent.GetVersionText()) extra = {'machine_name': self.machine_name} extra['person_name'] = self.person_name extra['clean_name'] = self.parent.GetCleanUsername(self) extra['selected_game'] = selected_game extra['filters'] = filters extra['zip_dest'] = zip_dest extra['player_path'] = player_path self.SendMessage('__client_prepare__', extra) return True class FileTransporter: """object that holds all the details of a file transfer""" deco_template = '%(filename)s (%(num)s)%(ext)s' def __init__(self, source, destination, player=None): self.source = source self.destination = destination self.player = player os_stat = os.stat(source) self.filesize = os_stat.st_size self.mod_time = os_stat.st_mtime self.finished = False self.error = None def Start(self): self.started = True def Finished(self, err=None): self.finished = True self.error = err def isDestinationSafe(self): if os.path.exists(self.destination): return False else: return True def FindSafeDestination(self): fname, ext = os.path.splitext(self.destination) num = 0 while not self.isDestinationSafe(): num += 1 self.destination = self.deco_template % {'filename': fname, 'ext': ext, 'num': num} class RemoteControl(SocketServer.BaseRequestHandler): def handle(self): data_list = [] while True: data = self.request.recv(1024) if not data: break data_list.append(data) data_str = ''.join(data_list) data = json.loads(data_str) if 'msg' in data: if 'machine' in data: machine_name = data['machine']# self.parent.LogEvent('%s %s (%s), %s' % (data['time'], data['source'],data['machine'], data['msg'])) else: self.parent.LogEvent('%s %s, %s' % (data['time'], data['source'], data['msg'])) msg = data['msg'] else: msg = None machine_name = None if msg == '__client_prepare__': self.PrepareForCopy(data) if msg == '__complete__': self.parent.RemoteTransferComplete(data) if msg == '__busy__': self.parent.RemotePlayerBusy(data) if msg == '__hiya__': self.BusyReply(data) def PrepareForCopy(self, data): if self.parent.remote_busy: self.parent.LogEvent('copy command from %s ignored, still busy' % data['source']) self.SendMessage('__busy__') return False try: if len(self.parent.to_transfer) > 0: self.parent.LogEvent('copy command from %s ignored, local transfer list is not empty' % data['source']) return False except AttributeError: pass self.parent.remote_busy = True self.parent.remote_master = data['source'] self.parent.remote_reply = self.request.getpeername()[0] selected_game = data['selected_game'] machine_name = data['machine_name'] person_name = data['person_name'] clean_name = data['clean_name'] zip_dest = data['zip_dest'] self.parent.LogEvent('Checking path availability') paths = [] for path in self.parent.data[selected_game]['source']: paths.append(os.path.join('X:\\', path)) invalid_paths = [] valid_paths = [] for path in paths: if not os.path.exists(path): self.parent.LogEvent('%s not available' % path, '__err__') invalid_paths.append(path) else: valid_paths.append(path) if len(valid_paths) == 0: self.parent.ReportEvent('%(name)s (%(machine)s) could not be found (path not shared)' % {'name': person_name, 'machine': machine_name}, False) priority_log_file_list = [] console_log_file_list = [] file_list = [] self.parent.LogEvent('Searching for files') if valid_paths > 0: exts = self.parent.data[selected_game]['filetypes'] main_path = self.parent.GetDestinationPath() player_path = '%s_%s' % (machine_name, clean_name) dest_path = os.path.join(main_path, player_path) for path in valid_paths: for fname in os.listdir(path): for ext in exts: if fname.endswith(ext): if ZIP_MODE: dest = '%s.zip' % dest_path else: dest = os.path.join(dest_path, fname) #TODO: check if source already exists in the folder, and rename it if fname.endswith(".log") and (fname.lower().find("prediction") != -1): priority_log_file_list.append(FileTransporter(os.path.join(path, fname),zip_dest)) self.parent.LogEvent('[prediction] '+os.path.join(path, fname)) elif fname.endswith(".log") and (fname.lower().find("console") != -1): console_log_file_list.append(FileTransporter(os.path.join(path, fname),zip_dest)) self.parent.LogEvent('[console] '+os.path.join(path, fname)) else: file_list.append(FileTransporter(os.path.join(path, fname),zip_dest)) self.parent.LogEvent('[normal] '+os.path.join(path, fname)) self.parent.to_transfer = [] if len(priority_log_file_list) > 0: self.parent.to_transfer.extend(priority_log_file_list) if len(file_list) > 0: self.parent.to_transfer.extend(file_list) if len(console_log_file_list) > 0: self.parent.to_transfer.extend(console_log_file_list) self.parent.RCStartTransfers() def BusyReply(self, data): if self.parent.remote_busy: self.SendMessage('__busy__') def CopyFiles(self, data): self.parent.LogEvent('!!!!! BEGINNING LOG TRANSFER !!!!!') flist = data['files'] transporters = [] for src, dst, error in flist: t = FileTransporter(src, dst) t.error = error transporters.append(t) for idx, t in enumerate(transporters): if t.error != 'remote': print '%s %s' % (os.path.basename(t.source), t.error) continue self.parent.LogEvent('copying %s [%s of %s]' % (os.path.basename(t.source), idx+1, len(transporters))) arc_fname = os.path.join(data['player_path'], os.path.basename(t.source)) zip_out = zipfile.ZipFile(data['zip_dest'], 'a', compression=zipfile.ZIP_DEFLATED, allowZip64=True) zip_out.write(t.source, arcname=arc_fname) zip_out.close() self.parent.LogEvent("\n!!!!! LOG TRANSFER COMPLETE !!!!!") self.SendMessage('__complete__') def SendData(self, msg): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.connect((self.request.getpeername()[0], 9999)) sock.sendall(msg) finally: sock.close() def SendMessage(self, msg, extra={}): data = {'time': time.time(), 'source': os.environ.get('username'), 'machine': socket.gethostname(), 'msg': msg} data.update(extra) json_data = json.dumps(data) self.SendData(json_data) class CreateGroupDlg(wx.Dialog): def __init__(self, parent, *args, **kwargs): wx.Dialog.__init__(self, parent, title='Create Group', *args, **kwargs) class ExceptionDialog(wx.Dialog): def __init__(self, etype, value, tb, *args, **kwargs): wx.Dialog.__init__(self, None, title='Exception Caught', *args, **kwargs) tb_str = ''.join(traceback.format_exception(etype, value, tb)) exception_str = ''.join(traceback.format_exception_only(etype, value)) main_sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(main_sizer) err_sizer = wx.BoxSizer(wx.HORIZONTAL) main_sizer.Add(err_sizer, flag=wx.EXPAND) err_icon = wx.ArtProvider.GetIcon(wx.ART_ERROR, wx.ART_CMN_DIALOG) err_img = wx.StaticBitmap(self) err_img.SetIcon(err_icon) err_sizer.Add(err_img, flag=wx.TOP | wx.LEFT | wx.RIGHT , border=10) err_text = wx.StaticText(self, label='An exception has occured:\n%s' % exception_str.strip('\n')) err_sizer.Add(err_text, flag=wx.ALL, border=10) tb_txt = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_READONLY, value = '%s' % tb_str) main_sizer.Add(tb_txt, flag=wx.EXPAND | wx.LEFT | wx.RIGHT, border = 10, proportion=1) main_sizer.Add(self.CreateSeparatedButtonSizer(wx.OK), flag=wx.EXPAND | wx.ALL, border=10) #self.Fit() err_text.Wrap(350) self.CentreOnParent() def MyExceptHook(etype, value, tb): error_list = traceback.format_exception(etype, value, tb) error_str = '' for line in error_list: error_str += line #dlg = wx.MessageDialog(None, #'An exception has occured: %s' % error_str, #'EXCEPTION', #wx.OK | wx.ICON_ERROR) dlg = ExceptionDialog(etype, value, tb) dlg.ShowModal() dlg.Destroy() class print2(object): def write(*args, **kwargs): pass def LoadWindowSize(frame, panel, settings, *args, **kwargs): id_start = u'[' id_end = u']' current_id = None panel.LogEvent('Loading window size from file') for line in settings: # Skip empty lines if line in ['\n', '\r\n']: continue # Skip comments if line.startswith('#'): continue # Strip newlines and comments line = line.strip('\n') if '#' in line: line = line[:line.find('#')] line = line.strip().lower() if line.startswith(id_start) and line.endswith(id_end): current_id = line.strip('[]') continue # Skip the line if we dont have current_id yet if current_id is None: continue elif current_id == 'window_size': tokens = line.split(',') if len(tokens) == 2 and tokens[0].isdigit() and tokens[1].isdigit(): panel.LogEvent('Setting size to: %s,%s' % (int(tokens[0]),int(tokens[1]))) frame.SetSize((int(tokens[0]),int(tokens[1]))) return frame.SetSize((550, 700)) #class BusyPlayerEvent(wx.PyCommandEvent): # def __init__(self,etype,eid,player=None): # wxPyCommandEvent.__init__(self,etype,eid) # self._player = player # # def GetPlayer(self): # return self._value class BusyPlayersThread(threading.Thread): def __init__(self, parent): threading.Thread.__init__(self) self._parent = parent def run(self): busy_app = wx.App(False) busy_frame = BusyPlayersFrame(None, 'Busy Players') busy_frame.Show(True) busy_app.MainLoop() time.sleep(10) class BusyPlayersFrame(wx.Frame): def __init__(self, parent, title): wx.Frame.__init__(self,parent,title=title) self.player_list = [] # Do layout self.list_view = wx.ListView(parent=self, id=wx.ID_ANY) #self.Bind(EVT_BUSYPL, self.UpdateBusyPlayerList) #def UpdateBusyPlayerList(self, evt): # player = evt.GetPlayer() # if player not in self.player_list: # self.player_list.append(player) # self.list_view.InsertStringItem(1,player) # Custom frame which shows only a error dialog message when another instance of LogGrabber # is detected to be already running. Can't use the same frame because we want to detect # the old grabber instance before we create new one (basically the check needs to be in # the top level of code). #class ErrorFrame(wx.Frame): # def __init__(self, parent, title, msg): # wx.Frame.__init__(self,parent,title=title) # dial = wx.MessageDialog(None, 'Another instance of LogGrabber is running. Please close it or kill \'pythonw.exe\' process and try again!\n\n%s'%msg, 'Error', wx.OK | wx.ICON_ERROR) # dial.ShowModal() # sys.exit(1) #try: # Set up a socket listening to 9996 port to detect if another instance of the program is already running # in_HOST, in_PORT = "0.0.0.0", 9996 # try: # IN_SOCKET = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # IN_SOCKET.bind((in_HOST,in_PORT)) # except: #Gotta catch them all #Another log grabber instance is listening on this socket, so dont launch another one# # e = sys.exc_info()[1] # Create a custom frame to be able to show error dialog. Exit the script afterwards # temp_app = wx.App(False) # temp_frame = ErrorFrame(None, 'Error',e) # temp_app.MainLoop() DebugLog("#######################################################################") temp = os.environ['temp'] stdout_log = os.path.join(temp, 'mplg_stdout_%s.log' % time.strftime('%Y-%m-%d_%H%M%S')) sys.excepthook = MyExceptHook #app = wx.PySimpleApp() app = wx.App(redirect=True, filename = stdout_log) #app = MyApp(redirect=True, filename = stdout_log) mainframe = wx.Frame(None) mainpanel = MainPanel(mainframe) mainframe.SetTitle('mpLogGrabber') # Try setting the last used window size (need to do this before loading all other settings in the panel) settingsF= os.path.join(sys.path[0],'personal_settings.ini') if os.path.exists(settingsF): try: settingsF_ini = codecs.open(settingsF, 'r','utf-8') LoadWindowSize(mainframe, mainpanel, settingsF_ini) settingsF_ini.close() except IOError: print ('Error: Unable to access the local settings file, user may not have write' 'permission for "%s"' % settingsF) print 'Press any key to exit' #qa.wait_for_key() sys.exit() else: mainframe.SetSize((550, 700)) mainframe.Show() #import wx.lib.inspection #wx.lib.inspection.InspectionTool().Show() app.MainLoop() #finally: # If this is the only running LogGrabber instance then # if IN_SOCKET != None: # IN_SOCKET.close() # sys.exit(0)