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

3597 lines
136 KiB
Python
Executable File

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 <default>
# 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('<L', socket.inet_aton(ip))[0]
net, bits = net_n_bits.split('/')
netaddr = struct.unpack('<L', socket.inet_aton(net))[0]
netmask = ((1L << int(bits)) - 1)
return ipaddr & netmask == netaddr & netmask
def GetDestinationPath(self, player=None):
"""Returns bug folder location.
Destination path is game_id[folder] + bug number"""
localMachineIPAddrStr = ([(s.connect(('8.8.8.8', 80)), s.getsockname()[0], s.close()) for s in [socket.socket(socket.AF_INET, socket.SOCK_DGRAM)]][0][1])
backUpNetworkList = (("10.10.0.0/16","rsgedi"),
("10.11.0.0/16","rsgedi"),
("10.8.0.0/16","rsglds"),
("10.12.0.0/16","rsglin"),
("10.13.0.0/16","rsgldn"),
("10.0.0.0/16","rsgsan"),
("10.24.0.0/16","rsgnwe"),
("10.2.0.0/16","rsgtor"),
("10.1.0.0/16","rsgnyc"),
("10.99.172.0/24","technicolor"),
("10.36.0.0/16","rsglic"))
networkList = []
toolsPath = os.environ.get('RS_TOOLSROOT')
if not toolsPath == None:
toolsPath = toolsPath + '\\'
configPath = os.path.join(toolsPath,'etc\\globals\\studios.meta')
tree = xml.etree.ElementTree.parse(configPath)
root = tree.getroot()
for studios in root.getiterator('Studios'):
for studio in studios:
studioNetDrive = studio.find('NetworkDrive').text
studionSubnet = studio.find('SubnetMask').text
if not studioNetDrive == None:
drive,stdName = studioNetDrive.split('\\')
stdName = stdName.lower()
networkList.append((studionSubnet,stdName))
else:
self.LogEvent("Your default tools directory (in RS_TOOLSROOT environmental variable) is not set up correctly. Try reinstalling project tools!",'__err__')
selectedFolder = ""
#self.LogEvent("Local machine's IP address - %s" % localMachineIPAddrStr)
for (subnet,folder) in networkList:
if self.addressInNetwork(localMachineIPAddrStr,subnet):
selectedFolder = folder
if selectedFolder == "":
self.LogEvent("Could not find appropriate studio destination folder from your IP address! Check if <studios> 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 = """
<html>
<title>mpLogGrabber Transfer Report (%(bugnumber)s)</title>
<body bgcolor=#bafeff>
<font size=+3><b>mpLogGrabber Transfer Report</b></font><br>
<hr>
<font size=-1>Transfer by %(username)s on %(current_date_time)s</font><br>
<font size=-1>Destination: %(destination_path)s</font><br><br>
<font size=+1><b>Players</b></font><br>
<table border=1>%(player_list)s</table><br><br>
<font size=+1><b>Files</b></font><br>
<table border=1>%(file_list)s</table>
<br><br><br><br>
<font size=-3 align=center>File generated by mpLogGrabber</font>
</body>
</html>
"""
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 = """
<tr>
<td>%(playername)s</td>
<td>%(machinename)s</td>
<td bgcolor=%(stat_col)s>%(status)s</td>
</tr>
"""
trans_temp = """
<tr>
<td>%(source)s</td>
<td>%(destination)s</td>
<td bgcolor=%(stat_col)s>%(status)s</td>
</tr>
"""
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)