2to3 conversion tool

actual converted files.
This commit is contained in:
Gabor Körber 2017-05-12 18:19:36 +02:00
parent 288065d066
commit 149fc122d0
25 changed files with 3445 additions and 3445 deletions

View File

@ -1,90 +1,90 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Tool to analyze Logs in general. Tool to analyze Logs in general.
""" """
import os, sys, logging import os, sys, logging
from logs.logfiles import LogFileResolver as LogFile from .logs.logfiles import LogFileResolver as LogFile
from logs import combat, game, chat from .logs import combat, game, chat
from logs.session import LogSessionCollector from .logs.session import LogSessionCollector
from logs.game import ClientInfo from .logs.game import ClientInfo
# for windows its kinda this: # for windows its kinda this:
settings = {'root_path': os.path.join(os.path.expanduser('~'), settings = {'root_path': os.path.join(os.path.expanduser('~'),
'Documents', 'Documents',
'My Games', 'My Games',
'StarConflict',), 'StarConflict',),
'logfiles': os.path.join(os.path.expanduser('~'), 'logfiles': os.path.join(os.path.expanduser('~'),
'Documents', 'Documents',
'My Games', 'My Games',
'StarConflict', 'StarConflict',
'logs' 'logs'
)} )}
if __name__ == '__main__': if __name__ == '__main__':
import logging import logging
logging.basicConfig(level=logging.DEBUG, logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(message)s', format='%(asctime)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S') datefmt='%Y-%m-%d %H:%M:%S')
coll = LogSessionCollector(os.path.join(os.path.expanduser('~'), coll = LogSessionCollector(os.path.join(os.path.expanduser('~'),
'Documents', 'My Games', 'sc')) 'Documents', 'My Games', 'sc'))
coll.collect_unique() coll.collect_unique()
#f = open('output.txt', 'w') #f = open('output.txt', 'w')
rex_combat = {} rex_combat = {}
rex_game = {} rex_game = {}
rex_chat = {} rex_chat = {}
LOG_GOOD = True # Log good packets. LOG_GOOD = True # Log good packets.
for logf in coll.sessions: for logf in coll.sessions:
logf.parse_files(['game.log', 'combat.log', 'chat.log']) logf.parse_files(['game.log', 'combat.log', 'chat.log'])
print "----- Log %s -----" % logf.idstr print(("----- Log %s -----" % logf.idstr))
if logf.combat_log: if logf.combat_log:
for l in logf.combat_log.lines: for l in logf.combat_log.lines:
if isinstance(l, dict): if isinstance(l, dict):
#print l #print l
rex_combat['dict'] = rex_combat.get('dict', 0) + 1 rex_combat['dict'] = rex_combat.get('dict', 0) + 1
else: else:
if not l.unpack() or LOG_GOOD: if not l.unpack() or LOG_GOOD:
rex_combat[l.__class__.__name__] = rex_combat.get(l.__class__.__name__, 0) + 1 rex_combat[l.__class__.__name__] = rex_combat.get(l.__class__.__name__, 0) + 1
if not isinstance(l, combat.UserEvent): if not isinstance(l, combat.UserEvent):
if not LOG_GOOD: if not LOG_GOOD:
print l.values['log'] print((l.values['log']))
if logf.game_log: if logf.game_log:
for l in logf.game_log.lines: for l in logf.game_log.lines:
if isinstance(l, dict): if isinstance(l, dict):
rex_game['dict'] = rex_game.get('dict', 0) + 1 rex_game['dict'] = rex_game.get('dict', 0) + 1
elif isinstance(l, str): elif isinstance(l, str):
print l print(l)
else: else:
if l.unpack() and not LOG_GOOD: if l.unpack() and not LOG_GOOD:
pass pass
else: else:
rex_game[l.__class__.__name__] = rex_game.get(l.__class__.__name__, 0) + 1 rex_game[l.__class__.__name__] = rex_game.get(l.__class__.__name__, 0) + 1
if not LOG_GOOD: if not LOG_GOOD:
print l.values['log'] print((l.values['log']))
if logf.chat_log: if logf.chat_log:
for l in logf.chat_log.lines: for l in logf.chat_log.lines:
if isinstance(l, dict): if isinstance(l, dict):
rex_chat['dict'] = rex_chat.get('dict', 0) + 1 rex_chat['dict'] = rex_chat.get('dict', 0) + 1
elif isinstance(l, str): elif isinstance(l, str):
print l print(l)
else: else:
if l.unpack() and not LOG_GOOD: if l.unpack() and not LOG_GOOD:
pass pass
else: else:
rex_chat[l.__class__.__name__] = rex_chat.get(l.__class__.__name__, 0) + 1 rex_chat[l.__class__.__name__] = rex_chat.get(l.__class__.__name__, 0) + 1
if not LOG_GOOD: if not LOG_GOOD:
print l.values['log'] print((l.values['log']))
logf.clean(True) logf.clean(True)
# additional cleanup: # additional cleanup:
logf.chat_log.lines = [] logf.chat_log.lines = []
logf.game_log.lines = [] logf.game_log.lines = []
logf.combat_log.lines = [] logf.combat_log.lines = []
print 'Analysis complete:' print('Analysis complete:')
print '#'*20+' RexCombat ' + '#' *20 print(('#'*20+' RexCombat ' + '#' *20))
print rex_combat print(rex_combat)
print '#'*20+' RexGame ' + '#' *20 print(('#'*20+' RexGame ' + '#' *20))
print rex_game print(rex_game)
print '#'*20+' RexChat ' + '#' *20 print(('#'*20+' RexChat ' + '#' *20))
print rex_chat print(rex_chat)

View File

@ -1,142 +1,142 @@
import os, logging import os, logging
from PyQt4 import QtCore, QtGui, QtWebKit, QtNetwork from PyQt4 import QtCore, QtGui, QtWebKit, QtNetwork
from treeview import TreeViewModel, Node from treeview import TreeViewModel, Node
from django.test import Client from django.test import Client
class DebugPage(QtWebKit.QWebPage): class DebugPage(QtWebKit.QWebPage):
def sayMyName(self): def sayMyName(self):
return 'DebugPage' return 'DebugPage'
class LocalWebView(QtWebKit.QWebView): class LocalWebView(QtWebKit.QWebView):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
basedir = kwargs.pop('basedir', None) basedir = kwargs.pop('basedir', None)
QtWebKit.QWebView.__init__(self, *args, **kwargs) QtWebKit.QWebView.__init__(self, *args, **kwargs)
oldManager = self.page().networkAccessManager() oldManager = self.page().networkAccessManager()
self.setPage(DebugPage()) self.setPage(DebugPage())
self.page().setNetworkAccessManager(LocalNetworkAccessManager(self, basedir)) self.page().setNetworkAccessManager(LocalNetworkAccessManager(self, basedir))
def set_basedir(self, basedir): def set_basedir(self, basedir):
self.page().setNetworkAccessManager(LocalNetworkAccessManager(self, basedir)) self.page().setNetworkAccessManager(LocalNetworkAccessManager(self, basedir))
class LocalNetworkAccessManager(QtNetwork.QNetworkAccessManager): class LocalNetworkAccessManager(QtNetwork.QNetworkAccessManager):
USE_NETWORK = False USE_NETWORK = False
def __init__(self, parent=None, basedir=None): def __init__(self, parent=None, basedir=None):
QtNetwork.QNetworkAccessManager.__init__(self, parent=None) QtNetwork.QNetworkAccessManager.__init__(self, parent=None)
if not basedir: if not basedir:
# take current dir as basedir. # take current dir as basedir.
self.basedir = os.path.dirname(os.path.abspath(__file__)) self.basedir = os.path.dirname(os.path.abspath(__file__))
else: else:
self.basedir = basedir self.basedir = basedir
def createRequest(self, operation, request, data): def createRequest(self, operation, request, data):
scheme = request.url().scheme() scheme = request.url().scheme()
if scheme != 'page' and scheme != 'image': if scheme != 'page' and scheme != 'image':
if self.USE_NETWORK: if self.USE_NETWORK:
return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data) return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data)
elif scheme == 'page': elif scheme == 'page':
if operation == self.GetOperation: if operation == self.GetOperation:
# Handle page:// URLs separately by creating custom # Handle page:// URLs separately by creating custom
# QNetworkReply objects. # QNetworkReply objects.
reply = PageReply(self, request.url(), self.GetOperation) reply = PageReply(self, request.url(), self.GetOperation)
#print('here') #print('here')
#print reply #print reply
return reply return reply
elif operation == self.PostOperation: elif operation == self.PostOperation:
#print data.readAll() #print data.readAll()
#print request #print request
reply = PageReply(self, request.url(), self.PostOperation) reply = PageReply(self, request.url(), self.PostOperation)
return reply return reply
elif scheme == 'image': elif scheme == 'image':
if operation == self.GetOperation: if operation == self.GetOperation:
return ImageReply(self, request.url(), self.GetOperation, self.basedir) return ImageReply(self, request.url(), self.GetOperation, self.basedir)
else: else:
if self.USE_NETWORK: if self.USE_NETWORK:
return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data) return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data)
return NoNetworkReply(self, request.url(), self.GetOperation) return NoNetworkReply(self, request.url(), self.GetOperation)
class BasePageReply(QtNetwork.QNetworkReply): class BasePageReply(QtNetwork.QNetworkReply):
content_type = 'text/html; charset=utf-8' content_type = 'text/html; charset=utf-8'
def __init__(self, parent, url, operation): def __init__(self, parent, url, operation):
QtNetwork.QNetworkReply.__init__(self, parent) QtNetwork.QNetworkReply.__init__(self, parent)
self.content = self.initialize_content(url, operation) self.content = self.initialize_content(url, operation)
self.offset = 0 self.offset = 0
self.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, self.get_content_type()) self.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, self.get_content_type())
self.setHeader(QtNetwork.QNetworkRequest.ContentLengthHeader, len(self.content)) self.setHeader(QtNetwork.QNetworkRequest.ContentLengthHeader, len(self.content))
QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('readyRead()')) QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('readyRead()'))
QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('finished()')) QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('finished()'))
self.open(self.ReadOnly | self.Unbuffered) self.open(self.ReadOnly | self.Unbuffered)
self.setUrl(url) self.setUrl(url)
def get_content_type(self): def get_content_type(self):
return self.content_type return self.content_type
def initialize_content(self, url, operation): def initialize_content(self, url, operation):
return ''' return '''
<html> <html>
<head><title>Test</title></head> <head><title>Test</title></head>
<body><form method="POST" action="."> <body><form method="POST" action=".">
<img src="image:///scon/conflict-logo.png"> <img src="image:///scon/conflict-logo.png">
<input type="text" name="a"></input> <input type="text" name="a"></input>
<input type="text" name="b"></input> <input type="text" name="b"></input>
<button class="submit">Submit</button></form></body> <button class="submit">Submit</button></form></body>
</html> </html>
''' '''
def abort(self): def abort(self):
pass pass
def bytesAvailable(self): def bytesAvailable(self):
return len(self.content) - self.offset + QtNetwork.QNetworkReply.bytesAvailable(self) return len(self.content) - self.offset + QtNetwork.QNetworkReply.bytesAvailable(self)
def isSequential(self): def isSequential(self):
return True return True
def readData(self, maxSize): def readData(self, maxSize):
if self.offset < len(self.content): if self.offset < len(self.content):
end = min(self.offset + maxSize, len(self.content)) end = min(self.offset + maxSize, len(self.content))
data = self.content[self.offset:end] data = self.content[self.offset:end]
self.offset = end self.offset = end
return data return data
class PageReply(BasePageReply): class PageReply(BasePageReply):
def initialize_content(self, url, operation): def initialize_content(self, url, operation):
c = Client() c = Client()
print "Response for %s, method %s" % (url.path(), operation) print(("Response for %s, method %s" % (url.path(), operation)))
if operation == LocalNetworkAccessManager.GetOperation: if operation == LocalNetworkAccessManager.GetOperation:
response = c.get(unicode(url.path()), ) response = c.get(str(url.path()), )
elif operation == LocalNetworkAccessManager.PostOperation: elif operation == LocalNetworkAccessManager.PostOperation:
response = c.post(unicode(url.path())) response = c.post(str(url.path()))
# response code # response code
print "Response Status: %s" % response.status_code print(("Response Status: %s" % response.status_code))
# note: on a 404, we might need to trigger file response. # note: on a 404, we might need to trigger file response.
return response.content return response.content
class NoNetworkReply(BasePageReply): class NoNetworkReply(BasePageReply):
def initialize_content(self, url, operation): def initialize_content(self, url, operation):
return ''' return '''
<html> <html>
<head><title>No Network Access.</title></head> <head><title>No Network Access.</title></head>
<body> <body>
Internal access to the network has been disabled. Internal access to the network has been disabled.
</body> </body>
</html> </html>
''' '''
class ImageReply(BasePageReply): class ImageReply(BasePageReply):
content_type = 'image/png' content_type = 'image/png'
def __init__(self, parent, url, operation, basedir): def __init__(self, parent, url, operation, basedir):
self.basedir = basedir self.basedir = basedir
BasePageReply.__init__(self, parent, url, operation) BasePageReply.__init__(self, parent, url, operation)
def initialize_content(self, url, operation): def initialize_content(self, url, operation):
path = os.path.join(self.basedir, unicode(url.path()).lstrip('/')) path = os.path.join(self.basedir, str(url.path()).lstrip('/'))
if not os.path.exists(path): if not os.path.exists(path):
logging.error('Image does not exist: %s' % path) logging.error('Image does not exist: %s' % path)
return '' return ''
h = url.host() h = url.host()
try: try:
f = open(path, 'rb') f = open(path, 'rb')
return f.read() return f.read()
finally: finally:
f.close() f.close()

View File

@ -1,71 +1,71 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Backup Directories, Handle Files... Backup Directories, Handle Files...
""" """
import os, logging, zipfile import os, logging, zipfile
def make_zipfile(output_filename, source_dir): def make_zipfile(output_filename, source_dir):
relroot = os.path.abspath(os.path.join(source_dir, os.pardir)) relroot = os.path.abspath(os.path.join(source_dir, os.pardir))
with zipfile.ZipFile(output_filename, "w", zipfile.ZIP_DEFLATED) as zip: with zipfile.ZipFile(output_filename, "w", zipfile.ZIP_DEFLATED) as zip:
for root, dirs, files in os.walk(source_dir): for root, dirs, files in os.walk(source_dir):
# add directory (needed for empty dirs) # add directory (needed for empty dirs)
zip.write(root, os.path.relpath(root, relroot)) zip.write(root, os.path.relpath(root, relroot))
for file in files: for file in files:
filename = os.path.join(root, file) filename = os.path.join(root, file)
if os.path.isfile(filename): # regular files only if os.path.isfile(filename): # regular files only
arcname = os.path.join(os.path.relpath(root, relroot), file) arcname = os.path.join(os.path.relpath(root, relroot), file)
zip.write(filename, arcname) zip.write(filename, arcname)
def backup_log_directory(log_directory, backup_directory, compress=True, def backup_log_directory(log_directory, backup_directory, compress=True,
ommit_level=2, verbose=False): ommit_level=2, verbose=False):
# @todo: raw copy # @todo: raw copy
# ommit_level 0: overwrite. # ommit_level 0: overwrite.
# ommit_level 1: write if selected compression method not backuped yet # ommit_level 1: write if selected compression method not backuped yet
# ommit_level 2: write only if neither method contains directory. # ommit_level 2: write only if neither method contains directory.
nothing_found = True nothing_found = True
# get all directory names in log_directory. # get all directory names in log_directory.
# zip them into backup_directory # zip them into backup_directory
for directory in os.listdir(log_directory): for directory in os.listdir(log_directory):
full_dir = os.path.join(log_directory, directory) full_dir = os.path.join(log_directory, directory)
nothing_found = False nothing_found = False
if os.path.isdir(full_dir): if os.path.isdir(full_dir):
if os.path.exists(os.path.join(full_dir, 'combat.log'))\ if os.path.exists(os.path.join(full_dir, 'combat.log'))\
and os.path.exists(os.path.join(full_dir, 'game.log'))\ and os.path.exists(os.path.join(full_dir, 'game.log'))\
and os.path.exists(os.path.join(full_dir, 'chat.log'))\ and os.path.exists(os.path.join(full_dir, 'chat.log'))\
and os.path.exists(os.path.join(full_dir, 'game.net.log')): and os.path.exists(os.path.join(full_dir, 'game.net.log')):
output_filename = '%s.zip' % directory output_filename = '%s.zip' % directory
if os.path.exists(os.path.join(backup_directory, output_filename))\ if os.path.exists(os.path.join(backup_directory, output_filename))\
and ((ommit_level >= 1 and compress) or (ommit_level==2 and not compress)): and ((ommit_level >= 1 and compress) or (ommit_level==2 and not compress)):
logging.warning('Log %s exists as zip backup, ommited.' % output_filename) logging.warning('Log %s exists as zip backup, ommited.' % output_filename)
elif os.path.exists(os.path.join(backup_directory, directory))\ elif os.path.exists(os.path.join(backup_directory, directory))\
and ((ommit_level == 2 and compress) or (ommit_level>=1 and not compress)): and ((ommit_level == 2 and compress) or (ommit_level>=1 and not compress)):
logging.warning('Log %s exists as directory backup, ommited.' % directory) logging.warning('Log %s exists as directory backup, ommited.' % directory)
else: else:
# do the backup # do the backup
if compress: if compress:
make_zipfile(os.path.join(backup_directory, output_filename), make_zipfile(os.path.join(backup_directory, output_filename),
full_dir) full_dir)
logging.info('Backed up %s' % directory) logging.info('Backed up %s' % directory)
if verbose: if verbose:
print "Backed up %s" % directory print(("Backed up %s" % directory))
else: else:
if verbose: if verbose:
print "Directory Raw Backup not implemented yet." print("Directory Raw Backup not implemented yet.")
raise NotImplementedError raise NotImplementedError
else: else:
if verbose: if verbose:
print "%s is not a directory." % full_dir print(("%s is not a directory." % full_dir))
if verbose and nothing_found: if verbose and nothing_found:
print "Nothing to backup found in %s" % log_directory print(("Nothing to backup found in %s" % log_directory))
if __name__ == '__main__': if __name__ == '__main__':
print "Performing Log Backup (Dev)" print("Performing Log Backup (Dev)")
log_source = os.path.join(os.path.expanduser('~'), log_source = os.path.join(os.path.expanduser('~'),
'Documents', 'My Games', 'StarConflict', 'logs') 'Documents', 'My Games', 'StarConflict', 'logs')
log_dest = os.path.join(os.path.expanduser('~'), log_dest = os.path.join(os.path.expanduser('~'),
'Documents', 'My Games', 'sc') 'Documents', 'My Games', 'sc')
backup_log_directory(log_source, log_dest, verbose=True, compress=True) backup_log_directory(log_source, log_dest, verbose=True, compress=True)

View File

@ -1,33 +1,33 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Tool to analyze Logs in general. Tool to analyze Logs in general.
""" """
import os, sys, logging import os, sys, logging
from logs.logfiles import LogFileResolver as LogFile from .logs.logfiles import LogFileResolver as LogFile
from logs import combat, game, chat from .logs import combat, game, chat
from logs.session import LogSessionCollector from .logs.session import LogSessionCollector
from logs.game import ClientInfo from .logs.game import ClientInfo
# for windows its kinda this: # for windows its kinda this:
settings = {'root_path': os.path.join(os.path.expanduser('~'), settings = {'root_path': os.path.join(os.path.expanduser('~'),
'Documents', 'Documents',
'My Games', 'My Games',
'StarConflict',), 'StarConflict',),
'logfiles': os.path.join(os.path.expanduser('~'), 'logfiles': os.path.join(os.path.expanduser('~'),
'Documents', 'Documents',
'My Games', 'My Games',
'StarConflict', 'StarConflict',
'logs' 'logs'
)} )}
if __name__ == '__main__': if __name__ == '__main__':
coll = LogSessionCollector(os.path.join(os.path.expanduser('~'), coll = LogSessionCollector(os.path.join(os.path.expanduser('~'),
'Documents', 'My Games', 'sc')) 'Documents', 'My Games', 'sc'))
coll.collect_unique() coll.collect_unique()
for logf in coll.sessions: for logf in coll.sessions:
logf.parse_files(['game.log', 'combat.log']) logf.parse_files(['game.log', 'combat.log'])
logf.clean() logf.clean()
if logf.combat_log: if logf.combat_log:
print 'length combat log ', len(logf.combat_log.lines) print(('length combat log ', len(logf.combat_log.lines)))
if logf.game_log: if logf.game_log:
print 'length game log ', len(logf.game_log.lines) print(('length game log ', len(logf.game_log.lines)))

View File

@ -1,85 +1,85 @@
""" """
Brainstorm File for Star Conflict Log Parsing Brainstorm File for Star Conflict Log Parsing
Needed Needed
- find steam/scon folder on windows - find steam/scon folder on windows
- find steam/scon folder on mac - find steam/scon folder on mac
- find steam/scon folder on linux - find steam/scon folder on linux
- what about steamless installs? - what about steamless installs?
Elaborate Elaborate
- which GUI to use? wx? PyQt4? PySide? - which GUI to use? wx? PyQt4? PySide?
- take over the database stuff from weltenfall.starconflict? - take over the database stuff from weltenfall.starconflict?
Investigate Investigate
- language based log files? - language based log files?
""" """
#from win32com.shell import shell, shellcon #from win32com.shell import shell, shellcon
import os, sys, logging import os, sys, logging
from logs.logfiles import LogFileResolver as LogFile from .logs.logfiles import LogFileResolver as LogFile
from logs import combat from .logs import combat
# for windows its kinda this: # for windows its kinda this:
settings = {'root_path': os.path.join(os.path.expanduser('~'), settings = {'root_path': os.path.join(os.path.expanduser('~'),
'Documents', 'Documents',
'My Games', 'My Games',
'StarConflict',), 'StarConflict',),
'logfiles': os.path.join(os.path.expanduser('~'), 'logfiles': os.path.join(os.path.expanduser('~'),
'Documents', 'Documents',
'My Games', 'My Games',
'StarConflict', 'StarConflict',
'logs' 'logs'
)} )}
def find_log_files(logpath): def find_log_files(logpath):
''' returns a list of 4-tuples representing ''' returns a list of 4-tuples representing
(combat.log, game.log, chat.log, game.net.log) (combat.log, game.log, chat.log, game.net.log)
for each directory in the logpath for each directory in the logpath
''' '''
ret = [] ret = []
for directory in os.listdir(logpath): for directory in os.listdir(logpath):
full_dir = os.path.join(logpath, directory) full_dir = os.path.join(logpath, directory)
if os.path.isdir(full_dir): if os.path.isdir(full_dir):
if os.path.exists(os.path.join(full_dir, 'combat.log'))\ if os.path.exists(os.path.join(full_dir, 'combat.log'))\
and os.path.exists(os.path.join(full_dir, 'game.log'))\ and os.path.exists(os.path.join(full_dir, 'game.log'))\
and os.path.exists(os.path.join(full_dir, 'chat.log'))\ and os.path.exists(os.path.join(full_dir, 'chat.log'))\
and os.path.exists(os.path.join(full_dir, 'game.net.log')): and os.path.exists(os.path.join(full_dir, 'game.net.log')):
ret.append(( ret.append((
os.path.join(full_dir, 'combat.log'), os.path.join(full_dir, 'combat.log'),
os.path.join(full_dir, 'game.log'), os.path.join(full_dir, 'game.log'),
os.path.join(full_dir, 'chat.log'), os.path.join(full_dir, 'chat.log'),
os.path.join(full_dir, 'game.net.log') os.path.join(full_dir, 'game.net.log')
)) ))
return ret return ret
def parse_games(logfiles): def parse_games(logfiles):
_logfiles = [] _logfiles = []
for logpack in logfiles: for logpack in logfiles:
combatlog, gamelog, chatlog, gamenetlog = logpack combatlog, gamelog, chatlog, gamenetlog = logpack
_logfiles.append(LogFile(combatlog)) _logfiles.append(LogFile(combatlog))
#_logfiles.append(LogFile(gamelog)) #_logfiles.append(LogFile(gamelog))
#_logfiles.append(LogFile(chatlog)) #_logfiles.append(LogFile(chatlog))
#_logfiles.append(LogFile(gamenetlog)) #_logfiles.append(LogFile(gamenetlog))
return _logfiles return _logfiles
if __name__ == '__main__': if __name__ == '__main__':
logfiles = find_log_files(settings['logfiles']) logfiles = find_log_files(settings['logfiles'])
logfiles = parse_games(logfiles) logfiles = parse_games(logfiles)
#f = open('output.txt', 'w') #f = open('output.txt', 'w')
rex = {} rex = {}
for logf in logfiles: for logf in logfiles:
logf.read() logf.read()
logf.parse() logf.parse()
for l in logf.lines: for l in logf.lines:
if isinstance(l, dict): if isinstance(l, dict):
#print l #print l
pass pass
else: else:
if not l.unpack(): if not l.unpack():
rex[l.__class__.__name__] = rex.get(l.__class__.__name__, 0) + 1 rex[l.__class__.__name__] = rex.get(l.__class__.__name__, 0) + 1
if not isinstance(l, combat.UserEvent): if not isinstance(l, combat.UserEvent):
print l.values['log'] print((l.values['log']))
#f.write(l.values['log'] + '\n') #f.write(l.values['log'] + '\n')
#f.close() #f.close()
#print type(l) #print type(l)
print rex print(rex)

View File

@ -1,119 +1,119 @@
""" """
Simple brainstorm to display a config file. Simple brainstorm to display a config file.
""" """
import os, logging import os, logging
from settings import settings from .settings import settings
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
# import ET: # import ET:
try: try:
ET = None ET = None
import lxml.etree as ET import lxml.etree as ET
logging.info('Using LXML.') logging.info('Using LXML.')
except ImportError: except ImportError:
try: try:
import cElementTree as ET import cElementTree as ET
logging.info('Using cElementTree') logging.info('Using cElementTree')
except ImportError: except ImportError:
try: try:
import elementtree.ElementTree as ET import elementtree.ElementTree as ET
logging.info('Using ElementTree') logging.info('Using ElementTree')
except ImportError: except ImportError:
import xml.etree.ElementTree as ET # python 2.5 import xml.etree.ElementTree as ET # python 2.5
logging.info('Using xml.ElementTree') logging.info('Using xml.ElementTree')
finally: finally:
if not ET: if not ET:
raise NotImplementedError, "XML Parser not found in your Python." raise NotImplementedError("XML Parser not found in your Python.")
################################################################################################## ##################################################################################################
class ConfigFile(object): class ConfigFile(object):
def __init__(self, config_file=None): def __init__(self, config_file=None):
self.cvars = [] self.cvars = []
if config_file: if config_file:
self.config_file = config_file self.config_file = config_file
elif settings: elif settings:
# settings based loading. # settings based loading.
self.config_file = os.path.join(settings.get_path(), 'user_config.xml') self.config_file = os.path.join(settings.get_path(), 'user_config.xml')
def open(self, filename = None): def open(self, filename = None):
# reads a config file. # reads a config file.
filename = filename or self.config_file filename = filename or self.config_file
self.tree = ET.parse(filename) self.tree = ET.parse(filename)
doc = self.tree.getroot() doc = self.tree.getroot()
if doc.tag == 'UserConfig' \ if doc.tag == 'UserConfig' \
and len(doc) == 1\ and len(doc) == 1\
and doc[0].tag == 'CVars'\ and doc[0].tag == 'CVars'\
and doc[0].attrib['version'] == '4': and doc[0].attrib['version'] == '4':
logging.info( "Found valid config file." ) logging.info( "Found valid config file." )
# save my cvars # save my cvars
self.cvars = doc[0] self.cvars = doc[0]
else: else:
logging.info( "Config File not supported." ) logging.info( "Config File not supported." )
return self return self
def pprint(self): def pprint(self):
# print out my cvars # print out my cvars
for child in self.cvars: for child in self.cvars:
print '%s = %s' % (child.tag, child.attrib['val']) print(('%s = %s' % (child.tag, child.attrib['val'])))
def write(self, filename): def write(self, filename):
output = '<?xml version="1.0"?>\n' output = '<?xml version="1.0"?>\n'
doc = self.tree.getroot() doc = self.tree.getroot()
# we manually serialize it to keep it exactly the same # we manually serialize it to keep it exactly the same
# like original SC to avoid problems with their software. # like original SC to avoid problems with their software.
def append_node(node, depth=0): def append_node(node, depth=0):
# xml serializing helper function... # xml serializing helper function...
s = ['%s<%s' % (' '*depth*2, node.tag),] s = ['%s<%s' % (' '*depth*2, node.tag),]
for key, val in node.attrib.items(): for key, val in list(node.attrib.items()):
s.append(' %s="%s"' % (key, val)) s.append(' %s="%s"' % (key, val))
if len(node): if len(node):
s.append('>\n') s.append('>\n')
# append children # append children
for child in node: for child in node:
s.extend(append_node(child, depth+1)) s.extend(append_node(child, depth+1))
s.append('%s</%s>\n' % (' '*depth*2, node.tag)) s.append('%s</%s>\n' % (' '*depth*2, node.tag))
else: else:
s.append(' />\n') s.append(' />\n')
return s return s
l = append_node(doc) l = append_node(doc)
output = output + ''.join( l ) output = output + ''.join( l )
if filename is None: if filename is None:
# dev. # dev.
assert output[-1], '\n' assert output[-1], '\n'
else: else:
try: try:
f = open(filename, 'w') f = open(filename, 'w')
f.write(output) f.write(output)
finally: finally:
f.close() f.close()
return output return output
def debug_serializing(self): def debug_serializing(self):
# detects if output would result in the same data as input # detects if output would result in the same data as input
input, output = None, None input, output = None, None
try: try:
f = open(self.config_file, 'r') f = open(self.config_file, 'r')
input = f.read() input = f.read()
finally: finally:
f.close() f.close()
output = self.write(None) output = self.write(None)
return output == input return output == input
def read_config(config_file): def read_config(config_file):
tree = ET.parse(config_file) tree = ET.parse(config_file)
# doc = tree.getroot() # doc = tree.getroot()
return tree return tree
if __name__ == '__main__': if __name__ == '__main__':
# Read the config # Read the config
settings.autodetect() settings.autodetect()
c = ConfigFile().open() c = ConfigFile().open()
print '#' * 80 print(('#' * 80))
print "Output File would be:" print("Output File would be:")
print c.write(None) print((c.write(None)))
print '#' * 80 print(('#' * 80))
print "Detected Settings:" print("Detected Settings:")
c.pprint() c.pprint()
print '#' * 80 print(('#' * 80))
print 'Serializing Test successful: %s' % c.debug_serializing() print(('Serializing Test successful: %s' % c.debug_serializing()))

View File

@ -1,31 +1,31 @@
import os import os
import platform import platform
class Settings(dict): class Settings(dict):
def autodetect(self, path=None): def autodetect(self, path=None):
# autodetect settings. # autodetect settings.
d = path d = path
system = platform.system() system = platform.system()
if system == 'Windows' or system.startswith('CYGWIN_NT'): if system == 'Windows' or system.startswith('CYGWIN_NT'):
# try to find user folder: # try to find user folder:
d = d or os.path.join(os.path.expanduser('~'), d = d or os.path.join(os.path.expanduser('~'),
'Documents', 'Documents',
'My Games', 'My Games',
'StarConflict',) 'StarConflict',)
elif system == 'Linux': elif system == 'Linux':
raise NotImplementedError, "Implement Linux!" raise NotImplementedError("Implement Linux!")
elif system == 'Darwin': elif system == 'Darwin':
raise NotImplementedError, "Implement Mac!" raise NotImplementedError("Implement Mac!")
else: else:
raise NotImplementedError, "Unknown System! %s" % platform.system() raise NotImplementedError("Unknown System! %s" % platform.system())
if not os.path.exists(d) or not os.path.isdir(d): if not os.path.exists(d) or not os.path.isdir(d):
raise Exception, "Configuration Autodetection failed. " raise Exception("Configuration Autodetection failed. ")
self['root_path'] = d self['root_path'] = d
def get_path(self): def get_path(self):
return self.get('root_path', None) return self.get('root_path', None)
def get_logs_path(self): def get_logs_path(self):
return os.path.join(self.get_path, 'logs') return os.path.join(self.get_path, 'logs')
settings = Settings() settings = Settings()

View File

@ -1,84 +1,84 @@
import logging, os import logging, os
try: try:
from django.conf import settings from django.conf import settings
except: except:
logging.error('Django Settings could not be loaded. Maybe Django has not been initialized?') logging.error('Django Settings could not be loaded. Maybe Django has not been initialized?')
settings = None settings = None
class FolderLibrary(object): class FolderLibrary(object):
def __init__(self, folders=None): def __init__(self, folders=None):
self._folders = {} self._folders = {}
try: try:
if settings: if settings:
self.folders.update( getattr(settings, 'DEJAQT_DIRS', {}) ) self.folders.update( getattr(settings, 'DEJAQT_DIRS', {}) )
except: except:
logging.error('DEJAQT_DIRS in django settings threw error.') logging.error('DEJAQT_DIRS in django settings threw error.')
import traceback import traceback
traceback.print_exc() traceback.print_exc()
if folders: if folders:
# no try here: if this fails, you got yourself a programming error. # no try here: if this fails, you got yourself a programming error.
self.folders.update(folders) self.folders.update(folders)
self._keys = [] self._keys = []
self.build_keycache() self.build_keycache()
def get_folders(self): def get_folders(self):
return self._folders return self._folders
def set_folders(self, folders): def set_folders(self, folders):
self._folders = folders self._folders = folders
self.build_keycache() self.build_keycache()
folders = property(get_folders, set_folders) folders = property(get_folders, set_folders)
def build_keycache(self): def build_keycache(self):
self._keys = self._folders.keys() self._keys = list(self._folders.keys())
self._keys.sort(key=lambda item: (-len(item), item)) self._keys.sort(key=lambda item: (-len(item), item))
def add_folder(self, url, folder): def add_folder(self, url, folder):
if not url: if not url:
url = '' url = ''
self._folders[url] = folder self._folders[url] = folder
self.build_keycache() self.build_keycache()
def match(self, url): def match(self, url):
# run down our keycache, first match wins. # run down our keycache, first match wins.
for key in self._keys: for key in self._keys:
if url.startswith(key): if url.startswith(key):
return key return key
def matched_folder(self, url): def matched_folder(self, url):
m = self.match(url) m = self.match(url)
if m is not None: if m is not None:
folder = self._folders[m] folder = self._folders[m]
#heading, rest = url[:len(m)], url[len(m):] #heading, rest = url[:len(m)], url[len(m):]
rest = url[len(m):] rest = url[len(m):]
real_folder = os.path.abspath( os.path.join(folder, rest) ) real_folder = os.path.abspath( os.path.join(folder, rest) )
if real_folder.startswith(os.path.abspath(folder)): if real_folder.startswith(os.path.abspath(folder)):
return real_folder return real_folder
else: else:
logging.error('%s does not seem to be a subpath of %s' % (real_folder, folder)) logging.error('%s does not seem to be a subpath of %s' % (real_folder, folder))
def print_folders(self): def print_folders(self):
print '{' print('{')
for k in self._keys: for k in self._keys:
print "'%s': '%s'," % (k, self._folders[k]) print(("'%s': '%s'," % (k, self._folders[k])))
print '}' print('}')
if __name__ == "__main__": if __name__ == "__main__":
# test this: # test this:
import os import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'scon.dj.settings' os.environ['DJANGO_SETTINGS_MODULE'] = 'scon.dj.settings'
f = FolderLibrary({'abc/dab/': 'c:/media', f = FolderLibrary({'abc/dab/': 'c:/media',
'abc': 'd:/abc', 'abc': 'd:/abc',
'abc/dab/tmp': '/tmp', 'abc/dab/tmp': '/tmp',
'uiuiui': 'x:/', 'uiuiui': 'x:/',
'abc/vul/no': 'x:/2', 'abc/vul/no': 'x:/2',
'abc/vul': 'x:/3', 'abc/vul': 'x:/3',
'abc/vul/yes': 'x:/1', 'abc/vul/yes': 'x:/1',
}) })
f.add_folder('abc/dub/', 'c:/dubdub') f.add_folder('abc/dub/', 'c:/dubdub')
f.print_folders() f.print_folders()
print f.matched_folder('abc/dab/okokok/some.png') print((f.matched_folder('abc/dab/okokok/some.png')))

View File

@ -1,198 +1,198 @@
""" """
Qt WebKit Browser for local access to internal Django Views. Qt WebKit Browser for local access to internal Django Views.
""" """
import os, logging import os, logging
from PyQt4 import QtCore, QtGui, QtWebKit, QtNetwork from PyQt4 import QtCore, QtGui, QtWebKit, QtNetwork
from django.test import Client from django.test import Client
from folders import FolderLibrary from .folders import FolderLibrary
from django.http.request import QueryDict from django.http.request import QueryDict
from urlparse import urlparse, parse_qs from urllib.parse import urlparse, parse_qs
import cgi import cgi
from io import BytesIO from io import BytesIO
from django.http.multipartparser import MultiPartParser from django.http.multipartparser import MultiPartParser
class DebugPage(QtWebKit.QWebPage): class DebugPage(QtWebKit.QWebPage):
def sayMyName(self): def sayMyName(self):
return 'DebugPage' return 'DebugPage'
class DejaWebView(QtWebKit.QWebView): class DejaWebView(QtWebKit.QWebView):
''' '''
Optional: Optional:
* folders: FolderLibrary() Instance. * folders: FolderLibrary() Instance.
* page: Initialized QWebPage instance for initial page (default DebugPage()) * page: Initialized QWebPage instance for initial page (default DebugPage())
''' '''
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.folders = kwargs.pop('folders', FolderLibrary()) self.folders = kwargs.pop('folders', FolderLibrary())
page = kwargs.pop('page', DebugPage()) page = kwargs.pop('page', DebugPage())
QtWebKit.QWebView.__init__(self, *args, **kwargs) QtWebKit.QWebView.__init__(self, *args, **kwargs)
#self.oldManager = self.page().networkAccessManager() #self.oldManager = self.page().networkAccessManager()
self.setPage(page) self.setPage(page)
self.page().setNetworkAccessManager(DejaNetworkAccessManager(self)) self.page().setNetworkAccessManager(DejaNetworkAccessManager(self))
self.client = Client() self.client = Client()
#self.client.login(username='admin', password='admin') #self.client.login(username='admin', password='admin')
class DejaNetworkAccessManager(QtNetwork.QNetworkAccessManager): class DejaNetworkAccessManager(QtNetwork.QNetworkAccessManager):
''' '''
The Deja Network Access Manager provides access to two new protocols: The Deja Network Access Manager provides access to two new protocols:
- page:/// tries to resolve a page internally via a django test client. - page:/// tries to resolve a page internally via a django test client.
- res:/// direct access to a resource. - res:/// direct access to a resource.
USE_NETWORK delegates to other network access manager protocols, if False, it will not USE_NETWORK delegates to other network access manager protocols, if False, it will not
allow any requests outside these two protocols allow any requests outside these two protocols
(hopefully disabling network access for your internal browser) (hopefully disabling network access for your internal browser)
Note, if page does not find the page, a res:/// attempt is made automatically. Note, if page does not find the page, a res:/// attempt is made automatically.
This has to be expanded! This has to be expanded!
Note2: not sure if cookies and sessions will work this way! Note2: not sure if cookies and sessions will work this way!
''' '''
USE_NETWORK = False USE_NETWORK = False
def __init__(self, parent=None): def __init__(self, parent=None):
QtNetwork.QNetworkAccessManager.__init__(self, parent=parent) QtNetwork.QNetworkAccessManager.__init__(self, parent=parent)
if parent: if parent:
self.folders = getattr(parent, 'folders', FolderLibrary()) self.folders = getattr(parent, 'folders', FolderLibrary())
def createRequest(self, operation, request, data): def createRequest(self, operation, request, data):
scheme = request.url().scheme() scheme = request.url().scheme()
if scheme != 'page' and scheme != 'res': if scheme != 'page' and scheme != 'res':
if self.USE_NETWORK: if self.USE_NETWORK:
return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data) return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data)
elif scheme == 'page': elif scheme == 'page':
if operation == self.GetOperation: if operation == self.GetOperation:
# Handle page:// URLs separately by creating custom # Handle page:// URLs separately by creating custom
# QNetworkReply objects. # QNetworkReply objects.
reply = PageReply(self, request.url(), self.GetOperation) reply = PageReply(self, request.url(), self.GetOperation)
#print('here') #print('here')
#print reply #print reply
return reply return reply
elif operation == self.PostOperation: elif operation == self.PostOperation:
reply = PageReply(self, request.url(), self.PostOperation, request, data) reply = PageReply(self, request.url(), self.PostOperation, request, data)
return reply return reply
elif scheme == 'res': elif scheme == 'res':
if operation == self.GetOperation: if operation == self.GetOperation:
return ResourceReply(self, request.url(), self.GetOperation) return ResourceReply(self, request.url(), self.GetOperation)
else: else:
if self.USE_NETWORK: if self.USE_NETWORK:
return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data) return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data)
return NoNetworkReply(self, request.url(), self.GetOperation) return NoNetworkReply(self, request.url(), self.GetOperation)
class BasePageReply(QtNetwork.QNetworkReply): class BasePageReply(QtNetwork.QNetworkReply):
content_type = 'text/html; charset=utf-8' content_type = 'text/html; charset=utf-8'
def __init__(self, parent, url, operation, request=None, data=None): def __init__(self, parent, url, operation, request=None, data=None):
QtNetwork.QNetworkReply.__init__(self, parent) QtNetwork.QNetworkReply.__init__(self, parent)
self.data = data self.data = data
self.request = request self.request = request
self.content = self.initialize_content(url, operation) self.content = self.initialize_content(url, operation)
self.offset = 0 self.offset = 0
self.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, self.get_content_type()) self.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, self.get_content_type())
self.setHeader(QtNetwork.QNetworkRequest.ContentLengthHeader, len(self.content)) self.setHeader(QtNetwork.QNetworkRequest.ContentLengthHeader, len(self.content))
QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('readyRead()')) QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('readyRead()'))
QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('finished()')) QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('finished()'))
self.open(self.ReadOnly | self.Unbuffered) self.open(self.ReadOnly | self.Unbuffered)
self.setUrl(url) self.setUrl(url)
def get_content_type(self): def get_content_type(self):
return self.content_type return self.content_type
def initialize_content(self, url, operation): def initialize_content(self, url, operation):
return ''' return '''
<html> <html>
<head><title>Empty Page</title></head> <head><title>Empty Page</title></head>
<body>This is an empty page. If you see this, you need to subclass BasePageReply.</body> <body>This is an empty page. If you see this, you need to subclass BasePageReply.</body>
</html> </html>
''' '''
def abort(self): def abort(self):
pass pass
def bytesAvailable(self): def bytesAvailable(self):
return len(self.content) - self.offset + QtNetwork.QNetworkReply.bytesAvailable(self) return len(self.content) - self.offset + QtNetwork.QNetworkReply.bytesAvailable(self)
def isSequential(self): def isSequential(self):
return True return True
def readData(self, maxSize): def readData(self, maxSize):
if self.offset < len(self.content): if self.offset < len(self.content):
end = min(self.offset + maxSize, len(self.content)) end = min(self.offset + maxSize, len(self.content))
data = self.content[self.offset:end] data = self.content[self.offset:end]
self.offset = end self.offset = end
return data return data
class ResourceReply(BasePageReply): class ResourceReply(BasePageReply):
content_type = 'image/png' content_type = 'image/png'
def determine_content_type(self, path): def determine_content_type(self, path):
return self.content_type return self.content_type
def initialize_content(self, url, operation): def initialize_content(self, url, operation):
# determine folder: # determine folder:
path = unicode(url.path()).lstrip('/') path = str(url.path()).lstrip('/')
folders = getattr(self.parent(), 'folders') folders = getattr(self.parent(), 'folders')
if folders: if folders:
path = folders.matched_folder(path) path = folders.matched_folder(path)
if path: if path:
if os.path.exists(path): if os.path.exists(path):
try: try:
f = open(path, 'rb') f = open(path, 'rb')
return f.read() return f.read()
finally: finally:
f.close() f.close()
else: else:
logging.warning('Path does not exist: %s' % path) logging.warning('Path does not exist: %s' % path)
else: else:
logging.error('Containing Folder not found for %s' % path) logging.error('Containing Folder not found for %s' % path)
else: else:
logging.error('Configuration Error: No Folders found.') logging.error('Configuration Error: No Folders found.')
return '' return ''
class PageReply(ResourceReply): class PageReply(ResourceReply):
content_type = 'text/html' content_type = 'text/html'
def initialize_content(self, url, operation): def initialize_content(self, url, operation):
try: try:
c = self.parent().parent().client c = self.parent().parent().client
except: except:
logging.error('Internal HTTP Client not found. Creating new.') logging.error('Internal HTTP Client not found. Creating new.')
c = Client() c = Client()
logging.info( "Response for %s, method %s" % (url.path(), operation) ) logging.info( "Response for %s, method %s" % (url.path(), operation) )
if operation == DejaNetworkAccessManager.GetOperation: if operation == DejaNetworkAccessManager.GetOperation:
response = c.get(unicode(url.path()), follow=True ) response = c.get(str(url.path()), follow=True )
elif operation == DejaNetworkAccessManager.PostOperation: elif operation == DejaNetworkAccessManager.PostOperation:
ct = str(self.request.rawHeader('Content-Type')) ct = str(self.request.rawHeader('Content-Type'))
cl = str(self.request.rawHeader('Content-Length')) cl = str(self.request.rawHeader('Content-Length'))
s = str(self.data.readAll()) s = str(self.data.readAll())
if ct.startswith('multipart/form-data'): if ct.startswith('multipart/form-data'):
# multipart parsing # multipart parsing
logging.error('Multipart Parsing Try...') logging.error('Multipart Parsing Try...')
b = BytesIO(s) b = BytesIO(s)
q, files = MultiPartParser({'CONTENT_TYPE': ct, q, files = MultiPartParser({'CONTENT_TYPE': ct,
'CONTENT_LENGTH': cl, 'CONTENT_LENGTH': cl,
}, },
b, b,
[]).parse() []).parse()
response = c.post(unicode(url.path()), q, follow=True) response = c.post(str(url.path()), q, follow=True)
else: else:
# assume post data. # assume post data.
q = QueryDict( s ) q = QueryDict( s )
response = c.post(unicode(url.path()), q, follow=True) response = c.post(str(url.path()), q, follow=True)
self.content_type = response.get('Content-Type', self.content_type) self.content_type = response.get('Content-Type', self.content_type)
# response code # response code
#print "Response Status: %s" % response.status_code #print "Response Status: %s" % response.status_code
# note: on a 404, we might need to trigger file response. # note: on a 404, we might need to trigger file response.
if response.status_code == 404: if response.status_code == 404:
return ResourceReply.initialize_content(self, url, DejaNetworkAccessManager.GetOperation) return ResourceReply.initialize_content(self, url, DejaNetworkAccessManager.GetOperation)
if hasattr(response, 'streaming_content'): if hasattr(response, 'streaming_content'):
return ''.join(response.streaming_content) return ''.join(response.streaming_content)
return response.content return response.content
class NoNetworkReply(BasePageReply): class NoNetworkReply(BasePageReply):
def initialize_content(self, url, operation): def initialize_content(self, url, operation):
return ''' return '''
<html> <html>
<head><title>No Network Access.</title></head> <head><title>No Network Access.</title></head>
<body> <body>
Internal access to the network has been disabled. Internal access to the network has been disabled.
</body> </body>
</html> </html>
''' '''

View File

@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
import models from . import models
# Register your models here. # Register your models here.
admin.site.register(models.Crafting) admin.site.register(models.Crafting)
admin.site.register(models.CraftingInput) admin.site.register(models.CraftingInput)

File diff suppressed because it is too large Load Diff

View File

@ -2,8 +2,8 @@
from django.shortcuts import render from django.shortcuts import render
from django.http import HttpResponse from django.http import HttpResponse
from django.template import RequestContext, loader from django.template import RequestContext, loader
import logic from . import logic
import models from . import models
def config(request): def config(request):
t = loader.get_template('scon/config.html') t = loader.get_template('scon/config.html')

View File

@ -1,100 +1,100 @@
def res_to_red(res): def res_to_red(res):
''' calculates reduction % of damage from base resistance ''' calculates reduction % of damage from base resistance
incoming damage is assumed to be 100.0 to get percentages. incoming damage is assumed to be 100.0 to get percentages.
''' '''
if res >= 0: if res >= 0:
fd = 100 / (1.0+res/100.0) fd = 100 / (1.0+res/100.0)
else: else:
fd = 100 / (1.0-res/100.0) fd = 100 / (1.0-res/100.0)
return 100.0 - fd return 100.0 - fd
def dam_res(dam, res): def dam_res(dam, res):
''' calculates damage modified by resistance. ''' calculates damage modified by resistance.
''' '''
if res >= 0: if res >= 0:
fd = dam / (1.0+res/100.0) fd = dam / (1.0+res/100.0)
else: else:
fd = dam / (1.0-res/100.0) fd = dam / (1.0-res/100.0)
return fd return fd
class ShipInstance(object): class ShipInstance(object):
# just testin something. # just testin something.
def __init__(self, def __init__(self,
shields=None, shields=None,
hulls=None, hulls=None,
shield_resis=None, shield_resis=None,
hull_resis=None ): hull_resis=None ):
self.shield_max = shields or 5000 self.shield_max = shields or 5000
self.hull_max = hulls or 5000 self.hull_max = hulls or 5000
shield_resis = shield_resis or (100,100,100) shield_resis = shield_resis or (100,100,100)
hull_resis = hull_resis or (100,100,100) hull_resis = hull_resis or (100,100,100)
self.set_shield_res(*shield_resis) self.set_shield_res(*shield_resis)
self.set_hull_res(*hull_resis) self.set_hull_res(*hull_resis)
def set_shield_res(self, kn, em, th): def set_shield_res(self, kn, em, th):
self.shield_res_kn = kn self.shield_res_kn = kn
self.shield_res_em = em self.shield_res_em = em
self.shield_res_th = th self.shield_res_th = th
def set_hull_res(self, kn, em, th): def set_hull_res(self, kn, em, th):
self.hull_res_kn = kn self.hull_res_kn = kn
self.hull_res_em = em self.hull_res_em = em
self.hull_res_th = th self.hull_res_th = th
def survivability(self): def survivability(self):
# i have no clue how they calc this. # i have no clue how they calc this.
# multiple attempts shows, they are using base pts as measure, but how exactly the calc is? # multiple attempts shows, they are using base pts as measure, but how exactly the calc is?
krs = (self.shield_max/100.0 * self.shield_res_kn) krs = (self.shield_max/100.0 * self.shield_res_kn)
ers = (self.shield_max/100.0 * self.shield_res_em) ers = (self.shield_max/100.0 * self.shield_res_em)
trs = (self.shield_max/100.0 * self.shield_res_th) trs = (self.shield_max/100.0 * self.shield_res_th)
print "Shield.", krs, ers, trs print(("Shield.", krs, ers, trs))
krh = (self.hull_max/100.0 * self.hull_res_kn) krh = (self.hull_max/100.0 * self.hull_res_kn)
erh = (self.hull_max/100.0 * self.hull_res_em) erh = (self.hull_max/100.0 * self.hull_res_em)
trh = (self.hull_max/100.0 * self.hull_res_th) trh = (self.hull_max/100.0 * self.hull_res_th)
print "Hull.", krh, erh, trh print(("Hull.", krh, erh, trh))
#print "?1", ((krs+ers+trs+krh+erh+trh)/6.0)+self.shield_max + self.hull_max #print "?1", ((krs+ers+trs+krh+erh+trh)/6.0)+self.shield_max + self.hull_max
print "?2", ((krs+ers+trs+3*self.shield_max)/3.0)+((krh+erh+trh+3*self.hull_max)/3.0) print(("?2", ((krs+ers+trs+3*self.shield_max)/3.0)+((krh+erh+trh+3*self.hull_max)/3.0)))
# another try: # another try:
""" """
lets assume survivability is really measured through applying 1000 dps for 10 secs. lets assume survivability is really measured through applying 1000 dps for 10 secs.
""" """
print "Assuming dps..." print("Assuming dps...")
shield = self.shield_max shield = self.shield_max
hull = self.hull_max hull = self.hull_max
r1s = shield / (1.0*dam_res(1000, self.shield_res_kn)) r1s = shield / (1.0*dam_res(1000, self.shield_res_kn))
r2s = shield / (1.0*dam_res(1000, self.shield_res_em)) r2s = shield / (1.0*dam_res(1000, self.shield_res_em))
r3s = shield / (1.0*dam_res(1000, self.shield_res_th)) r3s = shield / (1.0*dam_res(1000, self.shield_res_th))
print r1s, r2s, r3s print((r1s, r2s, r3s))
rXs = (r1s+r2s+r3s) / 3.0 rXs = (r1s+r2s+r3s) / 3.0
print "Shield survival time at 1kdps", rXs print(("Shield survival time at 1kdps", rXs))
r1h = hull / (1.0*dam_res(1000, self.hull_res_kn)) r1h = hull / (1.0*dam_res(1000, self.hull_res_kn))
r2h = hull / (1.0*dam_res(1000, self.hull_res_em)) r2h = hull / (1.0*dam_res(1000, self.hull_res_em))
r3h = hull / (1.0*dam_res(1000, self.hull_res_th)) r3h = hull / (1.0*dam_res(1000, self.hull_res_th))
print r1h, r2h, r3h print((r1h, r2h, r3h))
rXh = (r1h+r2h+r3h) / 3.0 rXh = (r1h+r2h+r3h) / 3.0
print "Hull survival time at 1kdps", rXh print(("Hull survival time at 1kdps", rXh))
print "Total survival time ", rXs + rXh, " sec" print(("Total survival time ", rXs + rXh, " sec"))
print "Surv should be ", int(round((rXs+rXh) * 1000)) print(("Surv should be ", int(round((rXs+rXh) * 1000))))
return ((krs+ers+trs)/3.0)+self.shield_max + self.hull_max + ((krh+erh+trh)/3.0) return ((krs+ers+trs)/3.0)+self.shield_max + self.hull_max + ((krh+erh+trh)/3.0)
ship = ShipInstance() ship = ShipInstance()
print ship.survivability() print((ship.survivability()))
print "#" * 80 print(("#" * 80))
mykatanas=ShipInstance(7664, 4296, (70,61,100), (20,80,50)) mykatanas=ShipInstance(7664, 4296, (70,61,100), (20,80,50))
print "We know its 19736... but own calcs say..." print("We know its 19736... but own calcs say...")
print mykatanas.survivability() print((mykatanas.survivability()))

View File

@ -1,444 +1,444 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
## FROM: ## FROM:
# https://github.com/cloudformdesign/cloudtb/blob/master/extra/PyQt/treeview.py # https://github.com/cloudformdesign/cloudtb/blob/master/extra/PyQt/treeview.py
# ****** The Cloud Toolbox v0.1.2****** # ****** The Cloud Toolbox v0.1.2******
# This is the cloud toolbox -- a single module used in several packages # This is the cloud toolbox -- a single module used in several packages
# found at <https://github.com/cloudformdesign> # found at <https://github.com/cloudformdesign>
# For more information see <cloudformdesign.com> # For more information see <cloudformdesign.com>
# #
# This module may be a part of a python package, and may be out of date. # This module may be a part of a python package, and may be out of date.
# This behavior is intentional, do NOT update it. # This behavior is intentional, do NOT update it.
# #
# You are encouraged to use this pacakge, or any code snippets in it, in # You are encouraged to use this pacakge, or any code snippets in it, in
# your own projects. Hopefully they will be helpful to you! # your own projects. Hopefully they will be helpful to you!
# #
# This project is Licenced under The MIT License (MIT) # This project is Licenced under The MIT License (MIT)
# #
# Copyright (c) 2013 Garrett Berg cloudformdesign.com # Copyright (c) 2013 Garrett Berg cloudformdesign.com
# An updated version of this file can be found at: # An updated version of this file can be found at:
# <https://github.com/cloudformdesign/cloudtb> # <https://github.com/cloudformdesign/cloudtb>
# #
# Permission is hereby granted, free of charge, to any person obtaining a # Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"), # copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation # to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense, # the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the # and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions: # Software is furnished to do so, subject to the following conditions:
# #
# The above copyright notice and this permission notice shall be included in # The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software. # all copies or substantial portions of the Software.
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE. # DEALINGS IN THE SOFTWARE.
# #
# http://opensource.org/licenses/MIT # http://opensource.org/licenses/MIT
# import pdb # import pdb
import os import os
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
import sys import sys
#import icons_rc #import icons_rc
# from cloudtb import dbe # from cloudtb import dbe
class Node(object): class Node(object):
'''A general node stucture to be used in treeview '''A general node stucture to be used in treeview
the attrib_dict can store any information your overall treeview the attrib_dict can store any information your overall treeview
needs it to store. needs it to store.
''' '''
def __init__(self, name, parent=None, icon = None, def __init__(self, name, parent=None, icon = None,
attrib_dict = None): attrib_dict = None):
self._name = name self._name = name
self._attrib = attrib_dict self._attrib = attrib_dict
self._children = [] self._children = []
self._parent = parent self._parent = parent
self.icon = icon self.icon = icon
if parent is not None: if parent is not None:
parent.addChild(self) parent.addChild(self)
def addChild(self, child): def addChild(self, child):
self._children.append(child) self._children.append(child)
def insertChild(self, position, child): def insertChild(self, position, child):
if position < 0 or position > len(self._children): if position < 0 or position > len(self._children):
return False return False
self._children.insert(position, child) self._children.insert(position, child)
child._parent = self child._parent = self
return True return True
def removeChild(self, position): def removeChild(self, position):
if position < 0 or position > len(self._children): if position < 0 or position > len(self._children):
return False return False
child = self._children.pop(position) child = self._children.pop(position)
child._parent = None child._parent = None
return True return True
def name(self): def name(self):
return self._name return self._name
def setName(self, name): def setName(self, name):
self._name = name self._name = name
def child(self, row): def child(self, row):
return self._children[row] return self._children[row]
def childCount(self): def childCount(self):
return len(self._children) return len(self._children)
def parent(self): def parent(self):
return self._parent return self._parent
def row(self): def row(self):
if self._parent is not None: if self._parent is not None:
return self._parent._children.index(self) return self._parent._children.index(self)
def log(self, tabLevel=-1): def log(self, tabLevel=-1):
output = "" output = ""
tabLevel += 1 tabLevel += 1
for i in range(tabLevel): for i in range(tabLevel):
output += " " output += " "
output += "|-" + self._name + "\n" output += "|-" + self._name + "\n"
for child in self._children: for child in self._children:
output += child.log(tabLevel) output += child.log(tabLevel)
tabLevel -= 1 tabLevel -= 1
# output += "\n" # output += "\n"
return output return output
def __repr__(self): def __repr__(self):
return self.log() return self.log()
class TreeViewModel(QtCore.QAbstractItemModel): class TreeViewModel(QtCore.QAbstractItemModel):
"""INPUTS: Node, QObject""" """INPUTS: Node, QObject"""
def __init__(self, root, parent=None, header_title = None): def __init__(self, root, parent=None, header_title = None):
super(TreeViewModel, self).__init__(parent) super(TreeViewModel, self).__init__(parent)
self.is_editable = False self.is_editable = False
self.is_selectable = True self.is_selectable = True
self.is_enabled = True self.is_enabled = True
self.set_flags() self.set_flags()
self._rootNode = root self._rootNode = root
self.header_title = header_title self.header_title = header_title
# The following are created functions called in "data" -- which is a # The following are created functions called in "data" -- which is a
# Qt defined funciton. This way of doing things is FAR more pythonic # Qt defined funciton. This way of doing things is FAR more pythonic
# and allows classes to inherit this one and not have to rewrite the # and allows classes to inherit this one and not have to rewrite the
# entire data method # entire data method
# all of them recieve an index and a node # all of them recieve an index and a node
def role_display(self, index, node): def role_display(self, index, node):
if index.column() == 0: if index.column() == 0:
return node.name() return node.name()
def role_edit(self, index, node): def role_edit(self, index, node):
return self.role_display(index, node) return self.role_display(index, node)
def role_tool_tip(self, index, node): def role_tool_tip(self, index, node):
return return
def role_check_state(self, index, node): def role_check_state(self, index, node):
return return
def role_decoration(self, index, node): def role_decoration(self, index, node):
if index.column() == 0: if index.column() == 0:
icon = node.icon icon = node.icon
if icon == None: if icon == None:
return False return False
else: else:
return icon return icon
def role_flags(self, index, node): def role_flags(self, index, node):
'''While not technically a "role" it behaves in much the same way. '''While not technically a "role" it behaves in much the same way.
This method is called by the "flags" method for all indexes with This method is called by the "flags" method for all indexes with
a node''' a node'''
return self.BASE_FLAGS return self.BASE_FLAGS
def set_flags(self, is_editable = None, is_selectable = None, def set_flags(self, is_editable = None, is_selectable = None,
is_enabled = None): is_enabled = None):
''' Sets new flags to the BASE_FLAGS variable''' ''' Sets new flags to the BASE_FLAGS variable'''
if is_editable != None: if is_editable != None:
self.is_editable = is_editable self.is_editable = is_editable
if is_selectable != None: if is_selectable != None:
self.is_selectable = is_selectable self.is_selectable = is_selectable
if is_enabled != None: if is_enabled != None:
self.is_enabled = is_enabled self.is_enabled = is_enabled
self.BASE_FLAGS = QtCore.Qt.ItemFlags( self.BASE_FLAGS = QtCore.Qt.ItemFlags(
(QtCore.Qt.ItemIsEnabled * bool(self.is_enabled)) (QtCore.Qt.ItemIsEnabled * bool(self.is_enabled))
| (QtCore.Qt.ItemIsSelectable * bool(self.is_selectable)) | (QtCore.Qt.ItemIsSelectable * bool(self.is_selectable))
| (QtCore.Qt.ItemIsEditable * bool(self.is_editable)) | (QtCore.Qt.ItemIsEditable * bool(self.is_editable))
) )
"""INPUTS: QModelIndex""" """INPUTS: QModelIndex"""
"""OUTPUT: int""" """OUTPUT: int"""
def rowCount(self, parent): def rowCount(self, parent):
if not parent.isValid(): if not parent.isValid():
parentNode = self._rootNode parentNode = self._rootNode
else: else:
parentNode = parent.internalPointer() parentNode = parent.internalPointer()
return parentNode.childCount() return parentNode.childCount()
"""INPUTS: QModelIndex""" """INPUTS: QModelIndex"""
"""OUTPUT: int""" """OUTPUT: int"""
def columnCount(self, parent): def columnCount(self, parent):
return 1 return 1
"""INPUTS: QModelIndex, int""" """INPUTS: QModelIndex, int"""
"""OUTPUT: QVariant, strings are cast to QString which is a QVariant""" """OUTPUT: QVariant, strings are cast to QString which is a QVariant"""
def data(self, index, role): def data(self, index, role):
'''index is an object that contains a pointer to the item inside '''index is an object that contains a pointer to the item inside
internPointer(). Note that this was set during the insertRows internPointer(). Note that this was set during the insertRows
method call, so you don't need to track them! method call, so you don't need to track them!
''' '''
if not index.isValid(): if not index.isValid():
return None return None
node = index.internalPointer() node = index.internalPointer()
if role == QtCore.Qt.EditRole: if role == QtCore.Qt.EditRole:
return self.role_edit(index, node) return self.role_edit(index, node)
if role == QtCore.Qt.ToolTipRole: if role == QtCore.Qt.ToolTipRole:
return self.role_tool_tip(index, node) return self.role_tool_tip(index, node)
if role == QtCore.Qt.CheckStateRole: if role == QtCore.Qt.CheckStateRole:
return self.role_check_state(index, node) return self.role_check_state(index, node)
if role == QtCore.Qt.DisplayRole: if role == QtCore.Qt.DisplayRole:
return self.role_display(index, node) return self.role_display(index, node)
if role == QtCore.Qt.DecorationRole: if role == QtCore.Qt.DecorationRole:
return self.role_decoration(index, node) return self.role_decoration(index, node)
"""INPUTS: QModelIndex, QVariant, int (flag)""" """INPUTS: QModelIndex, QVariant, int (flag)"""
def setData(self, index, value, role=QtCore.Qt.EditRole): def setData(self, index, value, role=QtCore.Qt.EditRole):
if index.isValid(): if index.isValid():
if role == QtCore.Qt.EditRole: if role == QtCore.Qt.EditRole:
node = index.internalPointer() node = index.internalPointer()
node.setName(value) node.setName(value)
return True return True
return False return False
"""INPUTS: int, Qt::Orientation, int""" """INPUTS: int, Qt::Orientation, int"""
"""OUTPUT: QVariant, strings are cast to QString which is a QVariant""" """OUTPUT: QVariant, strings are cast to QString which is a QVariant"""
def headerData(self, section, orientation, role): def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole: if role == QtCore.Qt.DisplayRole:
if self.header_title != None: if self.header_title != None:
return self.header_title return self.header_title
if section == 0: if section == 0:
return "Scenegraph" return "Scenegraph"
else: else:
return "Typeinfo" return "Typeinfo"
"""INPUTS: QModelIndex""" """INPUTS: QModelIndex"""
"""OUTPUT: int (flag)""" """OUTPUT: int (flag)"""
# def flags(self, index): # def flags(self, index):
# return (QtCore.Qt.ItemIsEnabled | # return (QtCore.Qt.ItemIsEnabled |
# QtCore.Qt.ItemIsSelectable #| # QtCore.Qt.ItemIsSelectable #|
## QtCore.Qt.ItemIsEditable ## QtCore.Qt.ItemIsEditable
# ) # )
def flags(self, index): def flags(self, index):
if not index.isValid(): if not index.isValid():
return self.BASE_FLAGS return self.BASE_FLAGS
node = index.internalPointer() node = index.internalPointer()
return self.role_flags(index, node) return self.role_flags(index, node)
"""INPUTS: QModelIndex""" """INPUTS: QModelIndex"""
"""OUTPUT: QModelIndex""" """OUTPUT: QModelIndex"""
"""Should return the parent of the node with the given QModelIndex""" """Should return the parent of the node with the given QModelIndex"""
def parent(self, index): def parent(self, index):
node = self.getNode(index) node = self.getNode(index)
parentNode = node.parent() parentNode = node.parent()
if parentNode == self._rootNode: if parentNode == self._rootNode:
return QtCore.QModelIndex() return QtCore.QModelIndex()
return self.createIndex(parentNode.row(), 0, parentNode) return self.createIndex(parentNode.row(), 0, parentNode)
"""INPUTS: int, int, QModelIndex""" """INPUTS: int, int, QModelIndex"""
"""OUTPUT: QModelIndex""" """OUTPUT: QModelIndex"""
"""Should return a QModelIndex that corresponds to the given row, """Should return a QModelIndex that corresponds to the given row,
column and parent node""" column and parent node"""
def index(self, row, column, parent): def index(self, row, column, parent):
# This is how Qt creates the nested (tree) list. It knows how many # This is how Qt creates the nested (tree) list. It knows how many
# rows it has because of insertRows, and it uses index and # rows it has because of insertRows, and it uses index and
# createIndex to build the tree. # createIndex to build the tree.
# print 'Index called', row, column # print 'Index called', row, column
parentNode = self.getNode(parent) parentNode = self.getNode(parent)
childItem = parentNode.child(row) childItem = parentNode.child(row)
if childItem: if childItem:
return self.createIndex(row, column, childItem) return self.createIndex(row, column, childItem)
else: else:
return QtCore.QModelIndex() return QtCore.QModelIndex()
"""CUSTOM""" """CUSTOM"""
"""INPUTS: QModelIndex""" """INPUTS: QModelIndex"""
def getNode(self, index): def getNode(self, index):
if index.isValid(): if index.isValid():
node = index.internalPointer() node = index.internalPointer()
if node: if node:
return node return node
return self._rootNode return self._rootNode
"""INPUTS: int, List of Nodes, QModelIndex""" """INPUTS: int, List of Nodes, QModelIndex"""
def insertRows(self, position, rows, parent=QtCore.QModelIndex()): def insertRows(self, position, rows, parent=QtCore.QModelIndex()):
parentNode = self.getNode(parent) parentNode = self.getNode(parent)
self.beginInsertRows(parent, position, position + len(rows) - 1) self.beginInsertRows(parent, position, position + len(rows) - 1)
for i, row in enumerate(rows): for i, row in enumerate(rows):
# childCount = parentNode.childCount() # childCount = parentNode.childCount()
childNode = row childNode = row
success = parentNode.insertChild(position + i, childNode) success = parentNode.insertChild(position + i, childNode)
self.endInsertRows() self.endInsertRows()
return success return success
"""INPUTS: int, int, QModelIndex""" """INPUTS: int, int, QModelIndex"""
def removeRows(self, position, rows, parent=QtCore.QModelIndex()): def removeRows(self, position, rows, parent=QtCore.QModelIndex()):
if rows == 0: if rows == 0:
return return
parentNode = self.getNode(parent) parentNode = self.getNode(parent)
self.beginRemoveRows(parent, position, position + rows - 1) self.beginRemoveRows(parent, position, position + rows - 1)
for row in range(rows): for row in range(rows):
success = parentNode.removeChild(position) success = parentNode.removeChild(position)
# TODO: break if not success? # TODO: break if not success?
self.endRemoveRows() self.endRemoveRows()
return success return success
def clear_rows(self): def clear_rows(self):
return self.removeRows(0, self._rootNode.childCount()) return self.removeRows(0, self._rootNode.childCount())
# TODO: doesn't work. Not sure how to get icons # TODO: doesn't work. Not sure how to get icons
ICON_FOLDER = QtGui.QIcon.fromTheme('folder') ICON_FOLDER = QtGui.QIcon.fromTheme('folder')
def _node_compare(a, b): def _node_compare(a, b):
return b.isdir - a.isdir return b.isdir - a.isdir
def get_file_folder_node(fdata, parent): def get_file_folder_node(fdata, parent):
'''return the node structure of the data. '''return the node structure of the data.
[[(dir_name, path), [[(dir_name, path),
[dir_name, path), [dir_name, path),
[(file, path), [(file, path),
(file, path)]] (file, path)]]
] ]
] ]
''' '''
# TODO: set icons correctly # TODO: set icons correctly
nodes = [] nodes = []
for fobj in fdata: for fobj in fdata:
path = fobj[0] path = fobj[0]
name = os.path.split(path)[1] name = os.path.split(path)[1]
if len(fobj) == 1: if len(fobj) == 1:
fileobj = Node(name, parent = parent, icon = None) fileobj = Node(name, parent = parent, icon = None)
fileobj.full_path = path fileobj.full_path = path
fileobj.isdir = False fileobj.isdir = False
nodes.append(fileobj) nodes.append(fileobj)
continue continue
folderobj = Node(name, parent = parent, icon = ICON_FOLDER, folderobj = Node(name, parent = parent, icon = ICON_FOLDER,
) )
folderobj.full_path = path folderobj.full_path = path
folderobj.isdir = True folderobj.isdir = True
get_file_folder_node(fobj[1], parent = folderobj) get_file_folder_node(fobj[1], parent = folderobj)
nodes.append(folderobj) nodes.append(folderobj)
nodes.sort(cmp = _node_compare) nodes.sort(cmp = _node_compare)
return nodes return nodes
import itertools import itertools
def _get_filelist_nodes(iter_file_list, dir_path = ''): def _get_filelist_nodes(iter_file_list, dir_path = ''):
'''Takes a sorted file list iterator and returns the files in a '''Takes a sorted file list iterator and returns the files in a
format that can be converted''' format that can be converted'''
files = [] files = []
dir_path = os.path.join(dir_path, '') # Put into directory syntax dir_path = os.path.join(dir_path, '') # Put into directory syntax
len_dp = len(dir_path) len_dp = len(dir_path)
while True: while True:
try: try:
fpath = next(iter_file_list) fpath = next(iter_file_list)
except StopIteration: except StopIteration:
break break
if dir_path != fpath[:len_dp]: if dir_path != fpath[:len_dp]:
iter_file_list = itertools.chain((fpath,), iter_file_list) iter_file_list = itertools.chain((fpath,), iter_file_list)
break break
if os.path.isdir(fpath): if os.path.isdir(fpath):
iter_file_list, new_files = _get_filelist_nodes(iter_file_list, iter_file_list, new_files = _get_filelist_nodes(iter_file_list,
dir_path = fpath) dir_path = fpath)
files.append((fpath, new_files)) files.append((fpath, new_files))
else: else:
files.append((fpath,)) files.append((fpath,))
return iter_file_list, files return iter_file_list, files
def get_filelist_nodes(file_list, parent = None): def get_filelist_nodes(file_list, parent = None):
file_list = sorted(file_list) file_list = sorted(file_list)
file_tuples = _get_filelist_nodes(iter(file_list))[1] file_tuples = _get_filelist_nodes(iter(file_list))[1]
return get_file_folder_node(file_tuples, parent) return get_file_folder_node(file_tuples, parent)
def dev_show_file_list(file_objects): def dev_show_file_list(file_objects):
'''For developemnet''' '''For developemnet'''
app = QtGui.QApplication(sys.argv) app = QtGui.QApplication(sys.argv)
rootNode = Node("Rootdir") rootNode = Node("Rootdir")
model = TreeViewModel(rootNode) model = TreeViewModel(rootNode)
treeView = QtGui.QTreeView() treeView = QtGui.QTreeView()
treeView.show() treeView.show()
treeView.setModel(model) treeView.setModel(model)
model.insertRows(0, file_objects, QtCore.QModelIndex()) model.insertRows(0, file_objects, QtCore.QModelIndex())
sys.exit(app.exec_()) sys.exit(app.exec_())
if __name__ == '__main__': if __name__ == '__main__':
from pprint import pprint from pprint import pprint
files = '''/home/user/Projects/Learning/LearningQt/LearningQt.pro.user files = '''/home/user/Projects/Learning/LearningQt/LearningQt.pro.user
/home/user/Projects/Learning/LearningQt/LearningQt.pro /home/user/Projects/Learning/LearningQt/LearningQt.pro
/home/user/Projects/Learning/LearningQt/qmlapplicationviewer/qmlapplicationviewer.h /home/user/Projects/Learning/LearningQt/qmlapplicationviewer/qmlapplicationviewer.h
/home/user/Projects/Learning/LearningQt/qmlapplicationviewer/qmlapplicationviewer.cpp /home/user/Projects/Learning/LearningQt/qmlapplicationviewer/qmlapplicationviewer.cpp
/home/user/Projects/Learning/LearningQt/qmlapplicationviewer/qmlapplicationviewer.pri /home/user/Projects/Learning/LearningQt/qmlapplicationviewer/qmlapplicationviewer.pri
/home/user/Projects/Learning/LearningQt/qmlapplicationviewer /home/user/Projects/Learning/LearningQt/qmlapplicationviewer
/home/user/Projects/Learning/LearningQt/LearningQt64.png /home/user/Projects/Learning/LearningQt/LearningQt64.png
/home/user/Projects/Learning/LearningQt/LearningQt_harmattan.desktop /home/user/Projects/Learning/LearningQt/LearningQt_harmattan.desktop
/home/user/Projects/Learning/LearningQt/LearningQt.svg /home/user/Projects/Learning/LearningQt/LearningQt.svg
/home/user/Projects/Learning/LearningQt/main.cpp /home/user/Projects/Learning/LearningQt/main.cpp
/home/user/Projects/Learning/LearningQt/LearningQt.desktop /home/user/Projects/Learning/LearningQt/LearningQt.desktop
/home/user/Projects/Learning/LearningQt/qml/LearningQt/main.qml /home/user/Projects/Learning/LearningQt/qml/LearningQt/main.qml
/home/user/Projects/Learning/LearningQt/qml/LearningQt /home/user/Projects/Learning/LearningQt/qml/LearningQt
/home/user/Projects/Learning/LearningQt/qml /home/user/Projects/Learning/LearningQt/qml
/home/user/Projects/Learning/LearningQt/LearningQt80.png''' /home/user/Projects/Learning/LearningQt/LearningQt80.png'''
nodes = get_filelist_nodes(files.split('\n')) nodes = get_filelist_nodes(files.split('\n'))
for n in nodes: for n in nodes:
print n print(n)
dev_show_file_list(nodes) dev_show_file_list(nodes)

View File

@ -13,7 +13,7 @@ import sys
from PyQt4 import QtCore, QtGui, QtWebKit, QtNetwork from PyQt4 import QtCore, QtGui, QtWebKit, QtNetwork
from scon.dejaqt.folders import FolderLibrary from scon.dejaqt.folders import FolderLibrary
from scon.dejaqt.qweb import DejaWebView from scon.dejaqt.qweb import DejaWebView
from treeview import TreeViewModel, Node from .treeview import TreeViewModel, Node
class MenuTree(QtGui.QTreeView): class MenuTree(QtGui.QTreeView):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@ -1,89 +1,89 @@
import logging import logging
""" """
Base Class for a Logentry is Log. Stacktrace is an exception, which gets injected if a stacktrace Base Class for a Logentry is Log. Stacktrace is an exception, which gets injected if a stacktrace
is assumed, and swallows all following unrecognized logs. is assumed, and swallows all following unrecognized logs.
-> It gets parsed by a Logstream, like the Logfile, but might also be used to be feeded -> It gets parsed by a Logstream, like the Logfile, but might also be used to be feeded
by live-streams of currently open log files. by live-streams of currently open log files.
-> Logfiles is responsible to read whole packs of files, and -> Logfiles is responsible to read whole packs of files, and
-> Sessions are responsible for reading whole directories. -> Sessions are responsible for reading whole directories.
A Log object usually will expand itself containing "values", and is responsible to retain all dynamic data needed to describe it in unpack() A Log object usually will expand itself containing "values", and is responsible to retain all dynamic data needed to describe it in unpack()
The classmethod is_handler should pre-scan a log, which is usually a dict containing the actual log in log['log'] The classmethod is_handler should pre-scan a log, which is usually a dict containing the actual log in log['log']
but it could be a string aswell. but it could be a string aswell.
clean is called to make a log object independent of its source information, and delete all incoming data, so it becomes sleek. clean is called to make a log object independent of its source information, and delete all incoming data, so it becomes sleek.
reviewed is an internal boolean, which supposed to be saved on successful unpack, unpack should ignore already unpacked logs. reviewed is an internal boolean, which supposed to be saved on successful unpack, unpack should ignore already unpacked logs.
matcher is a regex object to match, or a list of them. matcher is a regex object to match, or a list of them.
trash is a boolean flag to indicate, this log is possibly unknown information or unneeded, and should be removed or ignored. trash is a boolean flag to indicate, this log is possibly unknown information or unneeded, and should be removed or ignored.
-> Note for anyone creating new subclasses for parsing: -> Note for anyone creating new subclasses for parsing:
All classes are to be __slot__-ed so they can be created more efficiently by python. All classes are to be __slot__-ed so they can be created more efficiently by python.
A class without __slot__ will slow down parsing exponentially in CPython. A class without __slot__ will slow down parsing exponentially in CPython.
__slots__ hinder you to add new properties on the fly in the code, but having this immutable class optimizes memory allocation. __slots__ hinder you to add new properties on the fly in the code, but having this immutable class optimizes memory allocation.
This is the reason, the base layout of the log object is explained here. This is the reason, the base layout of the log object is explained here.
""" """
L_CMBT = 'CMBT' L_CMBT = 'CMBT'
L_WARNING = 'WARNING' L_WARNING = 'WARNING'
L_NET = 'NET' # Not supported in near future. L_NET = 'NET' # Not supported in near future.
L_CHAT = 'CHAT' L_CHAT = 'CHAT'
class Log(object): class Log(object):
__slots__ = ['matcher', 'trash', 'reviewed'] __slots__ = ['matcher', 'trash', 'reviewed']
matcher = None matcher = None
trash = False trash = False
reviewed = False reviewed = False
@classmethod @classmethod
def is_handler(cls, log): def is_handler(cls, log):
return False return False
def unpack(self, force=False): def unpack(self, force=False):
''' unpacks this log from its data and saves values ''' ''' unpacks this log from its data and saves values '''
pass pass
def explain(self): def explain(self):
''' returns a String readable by humans explaining this Log ''' ''' returns a String readable by humans explaining this Log '''
return '' return ''
def clean(self): def clean(self):
''' tell the log to forget all non-essential data ''' ''' tell the log to forget all non-essential data '''
pass pass
def append(self, something): def append(self, something):
''' returns true if this logfile wants an unrecognized log appended to it. ''' ''' returns true if this logfile wants an unrecognized log appended to it. '''
return False return False
class Stacktrace(Log): class Stacktrace(Log):
''' Special Log to catch error reports ''' ''' Special Log to catch error reports '''
def __init__(self, values=None): def __init__(self, values=None):
super(Stacktrace, self).__init__() super(Stacktrace, self).__init__()
self.message = values or '' self.message = values or ''
if isinstance(self.message, dict): if isinstance(self.message, dict):
self.message = self.message.get('log', '') self.message = self.message.get('log', '')
#self.trash = True #self.trash = True
@classmethod @classmethod
def is_handler(cls, log): def is_handler(cls, log):
# do i have a system crash report beginning here? # do i have a system crash report beginning here?
if isinstance(log, basestring): if isinstance(log, str):
l = log.strip() l = log.strip()
elif isinstance(log, dict): elif isinstance(log, dict):
l = log.get('log', '').strip() l = log.get('log', '').strip()
else: else:
return False return False
if l.startswith('Stack trace:') or l.startswith('BitStream::DbgLog'): if l.startswith('Stack trace:') or l.startswith('BitStream::DbgLog'):
return True return True
def clean(self): def clean(self):
self.message = '' self.message = ''
def append(self, something): def append(self, something):
''' I take anything! ''' ''' I take anything! '''
logging.debug( "EXC: %s" % something ) logging.debug( "EXC: %s" % something )
self.message = '%s\n%s' % (self.message, something) self.message = '%s\n%s' % (self.message, something)
return True return True

View File

@ -1,206 +1,206 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from logs.base import Log, L_WARNING, Stacktrace from logs.base import Log, L_WARNING, Stacktrace
import re import re
""" """
Responsible for Chat Log. Responsible for Chat Log.
Anything related to chat gets logged here, basicly interesting for chat related stuff mainly. Anything related to chat gets logged here, basicly interesting for chat related stuff mainly.
Channel leaving and joining and connection to the chat server get logged here too. Channel leaving and joining and connection to the chat server get logged here too.
------------------------------- -------------------------------
Maybe add something to create a ColorChart of usernames? Maybe add something to create a ColorChart of usernames?
between 33-33-33 and FF-33 FF-33 FF-33 between 33-33-33 and FF-33 FF-33 FF-33
""" """
class ChatLog(Log): class ChatLog(Log):
__slots__ = ['matcher', 'trash', '_match_id', 'values'] __slots__ = ['matcher', 'trash', '_match_id', 'values']
@classmethod @classmethod
def is_handler(cls, log): def is_handler(cls, log):
if log.get('logtype', None) == 'CHAT': if log.get('logtype', None) == 'CHAT':
return cls._is_handler(log) return cls._is_handler(log)
return False return False
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
return False return False
def __init__(self, values=None): def __init__(self, values=None):
self.values = values or {} self.values = values or {}
self.reviewed = False self.reviewed = False
def unpack(self, force=False): def unpack(self, force=False):
if self.reviewed and not force: if self.reviewed and not force:
return True return True
self._match_id = None self._match_id = None
# unpacks the data from the values. # unpacks the data from the values.
if hasattr(self, 'matcher') and self.matcher: if hasattr(self, 'matcher') and self.matcher:
matchers = self.matcher matchers = self.matcher
if not isinstance(matchers, list): if not isinstance(matchers, list):
matchers = [matchers,] matchers = [matchers,]
for i, matcher in enumerate(matchers): for i, matcher in enumerate(matchers):
m = matcher.match(self.values.get('log', '')) m = matcher.match(self.values.get('log', ''))
if m: if m:
self.values.update(m.groupdict()) self.values.update(m.groupdict())
self._match_id = i self._match_id = i
self.reviewed = True self.reviewed = True
return True return True
# unknown? # unknown?
self.trash = True self.trash = True
def explain(self): def explain(self):
''' returns a String readable by humans explaining this Log ''' ''' returns a String readable by humans explaining this Log '''
return self.values.get('log', 'Unknown Chat Log') return self.values.get('log', 'Unknown Chat Log')
def clean(self): def clean(self):
if 'log' in self.values.keys(): if 'log' in list(self.values.keys()):
del self.values['log'] del self.values['log']
class SystemMessage(ChatLog): class SystemMessage(ChatLog):
matcher = re.compile(r"^<\s+SYSTEM>\s(?P<message>.*)") matcher = re.compile(r"^<\s+SYSTEM>\s(?P<message>.*)")
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').lstrip().startswith('< SYSTEM>'): if log.get('log', '').lstrip().startswith('< SYSTEM>'):
return True return True
return False return False
def explain(self): def explain(self):
return '[SYSTEM]: %(message)s' % self.values return '[SYSTEM]: %(message)s' % self.values
def append(self, something): def append(self, something):
''' System Messages accept appends ''' ''' System Messages accept appends '''
if 'message' in self.values.keys(): if 'message' in list(self.values.keys()):
self.values['message'] = '%s\n%s' % (self.values['message'], something) self.values['message'] = '%s\n%s' % (self.values['message'], something)
return True return True
class PrivateMessageReceived(ChatLog): class PrivateMessageReceived(ChatLog):
matcher = re.compile(r"^<\s\s\s\sPRIVATE From>\[\s*(?P<nickname>[^\]]+)\]\s(?P<message>.*)") matcher = re.compile(r"^<\s\s\s\sPRIVATE From>\[\s*(?P<nickname>[^\]]+)\]\s(?P<message>.*)")
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').lstrip().startswith('< PRIVATE From>'): if log.get('log', '').lstrip().startswith('< PRIVATE From>'):
return True return True
return False return False
def explain(self): def explain(self):
return '[From %(nickname)s]: %(message)s' % self.values return '[From %(nickname)s]: %(message)s' % self.values
def append(self, something): def append(self, something):
''' Private Messages accept appends ''' ''' Private Messages accept appends '''
if 'message' in self.values.keys(): if 'message' in list(self.values.keys()):
self.values['message'] = '%s\n%s' % (self.values['message'], something) self.values['message'] = '%s\n%s' % (self.values['message'], something)
return True return True
class PrivateMessageSent(ChatLog): class PrivateMessageSent(ChatLog):
matcher = re.compile(r"^<\s\s\s\sPRIVATE To\s\s>\[\s*(?P<nickname>[^\]]+)\]\s(?P<message>.*)") matcher = re.compile(r"^<\s\s\s\sPRIVATE To\s\s>\[\s*(?P<nickname>[^\]]+)\]\s(?P<message>.*)")
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').lstrip().startswith('< PRIVATE To >'): if log.get('log', '').lstrip().startswith('< PRIVATE To >'):
return True return True
return False return False
def explain(self): def explain(self):
return '[To %(nickname)s]: %(message)s' % self.values return '[To %(nickname)s]: %(message)s' % self.values
def append(self, something): def append(self, something):
''' Private Messages accept appends ''' ''' Private Messages accept appends '''
if 'message' in self.values.keys(): if 'message' in list(self.values.keys()):
self.values['message'] = '%s\n%s' % (self.values['message'], something) self.values['message'] = '%s\n%s' % (self.values['message'], something)
return True return True
class ChatMessage(ChatLog): class ChatMessage(ChatLog):
matcher = re.compile(r"^<\s*#(?P<channel>[^>]+)>\[\s*(?P<nickname>[^\]]+)\]\s(?P<message>.*)") matcher = re.compile(r"^<\s*#(?P<channel>[^>]+)>\[\s*(?P<nickname>[^\]]+)\]\s(?P<message>.*)")
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').lstrip().startswith('<'): if log.get('log', '').lstrip().startswith('<'):
return True return True
return False return False
def explain(self): def explain(self):
return '[%(channel)s] <%(nickname)s>: %(message)s' % self.values return '[%(channel)s] <%(nickname)s>: %(message)s' % self.values
def append(self, something): def append(self, something):
''' ChatMessages accept appends ''' ''' ChatMessages accept appends '''
if not 'message' in self.values.keys(): if not 'message' in list(self.values.keys()):
print "Missing message? %s" % self.values print(("Missing message? %s" % self.values))
self.values['message'] = '' self.values['message'] = ''
self.values['message'] = '%s\n%s' % (self.values['message'], something) self.values['message'] = '%s\n%s' % (self.values['message'], something)
return True return True
class ChatJoinChannel(ChatLog): class ChatJoinChannel(ChatLog):
matcher = re.compile(r"^Join\schannel\s<\s*#(?P<channel>[^>]+)>") matcher = re.compile(r"^Join\schannel\s<\s*#(?P<channel>[^>]+)>")
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').lstrip().startswith('Join channel'): if log.get('log', '').lstrip().startswith('Join channel'):
return True return True
return False return False
def explain(self): def explain(self):
return '[joined %(channel)s]' % self.values return '[joined %(channel)s]' % self.values
class ChatLeaveChannel(ChatLog): class ChatLeaveChannel(ChatLog):
matcher = re.compile(r"^Leave\schannel\s<\s*#(?P<channel>[^>]+)>") matcher = re.compile(r"^Leave\schannel\s<\s*#(?P<channel>[^>]+)>")
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').lstrip().startswith('Leave channel'): if log.get('log', '').lstrip().startswith('Leave channel'):
return True return True
return False return False
def explain(self): def explain(self):
return '[left %(channel)s]' % self.values return '[left %(channel)s]' % self.values
class ChatServerConnect(ChatLog): class ChatServerConnect(ChatLog):
# 00:12:47.668 CHAT| Connection to chat-server established # 00:12:47.668 CHAT| Connection to chat-server established
matcher = [] matcher = []
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').lstrip().startswith('Connection to'): if log.get('log', '').lstrip().startswith('Connection to'):
return True return True
return False return False
def unpack(self, force=False): def unpack(self, force=False):
self.reviewed = True self.reviewed = True
return True return True
def explain(self): def explain(self):
return '[connected]' return '[connected]'
class ChatServerDisconnect(ChatLog): class ChatServerDisconnect(ChatLog):
# 00:53:03.738 CHAT| Disconnect form chat-server (reason 0) # 00:53:03.738 CHAT| Disconnect form chat-server (reason 0)
matcher = [] matcher = []
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').lstrip().startswith('Disconnect'): if log.get('log', '').lstrip().startswith('Disconnect'):
return True return True
return False return False
def unpack(self, force=False): def unpack(self, force=False):
self.reviewed = True self.reviewed = True
return True return True
def explain(self): def explain(self):
return '[disconnected]' return '[disconnected]'
CHAT_LOGS = [ CHAT_LOGS = [
SystemMessage, SystemMessage,
PrivateMessageReceived, PrivateMessageReceived,
PrivateMessageSent, PrivateMessageSent,
ChatMessage, # private messages need to be before chatmessage. ChatMessage, # private messages need to be before chatmessage.
ChatServerConnect, ChatServerConnect,
ChatServerDisconnect, ChatServerDisconnect,
ChatJoinChannel, ChatJoinChannel,
ChatLeaveChannel, ChatLeaveChannel,
Stacktrace, Stacktrace,
] ]

View File

@ -1,304 +1,304 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Primary Packets for Combat.Log Files. Primary Packets for Combat.Log Files.
This is the most important part for dealing with actual statistics, since every action taken This is the most important part for dealing with actual statistics, since every action taken
in a combat instance gets logged here. in a combat instance gets logged here.
------------------------------------ ------------------------------------
Note: Note:
All logs start with something like All logs start with something like
23:53:29.137 | LOGDATA 23:53:29.137 | LOGDATA
LOGDATA can be quite different depending on the logfile. LOGDATA can be quite different depending on the logfile.
other forms encountered: other forms encountered:
23:54:00.600 WARNING| 23:54:00.600 WARNING|
combat logs: combat logs:
01:04:38.805 CMBT | 01:04:38.805 CMBT |
""" """
import re import re
from base import Log, L_CMBT, Stacktrace from .base import Log, L_CMBT, Stacktrace
import logging import logging
class CombatLog(Log): class CombatLog(Log):
__slots__ = Log.__slots__ + [ '_match_id', 'values'] __slots__ = Log.__slots__ + [ '_match_id', 'values']
@classmethod @classmethod
def _log_handler(cls, log): def _log_handler(cls, log):
if log.startswith(cls.__name__): if log.startswith(cls.__name__):
return True return True
return False return False
@classmethod @classmethod
def is_handler(cls, log): def is_handler(cls, log):
if log.get('logtype', None) == L_CMBT: if log.get('logtype', None) == L_CMBT:
return cls._log_handler(log.get('log', '').strip()) return cls._log_handler(log.get('log', '').strip())
return False return False
def __init__(self, values=None): def __init__(self, values=None):
self.values = values or {} self.values = values or {}
self.reviewed = False self.reviewed = False
def unpack(self, force=False): def unpack(self, force=False):
if self.reviewed and not force: if self.reviewed and not force:
return True return True
self._match_id = None self._match_id = None
# unpacks the data from the values. # unpacks the data from the values.
if hasattr(self, 'matcher') and self.matcher: if hasattr(self, 'matcher') and self.matcher:
matchers = self.matcher matchers = self.matcher
if not isinstance(matchers, list): if not isinstance(matchers, list):
matchers = [matchers,] matchers = [matchers,]
for i, matcher in enumerate(matchers): for i, matcher in enumerate(matchers):
m = matcher.match(self.values.get('log', '')) m = matcher.match(self.values.get('log', ''))
if m: if m:
self.values.update(m.groupdict()) self.values.update(m.groupdict())
self._match_id = i self._match_id = i
self.reviewed = True self.reviewed = True
return True return True
# unknown? # unknown?
if not isinstance(self, UserEvent): if not isinstance(self, UserEvent):
logging.warning('Unknown Packet for %s:\n%s' % (self.__class__.__name__, logging.warning('Unknown Packet for %s:\n%s' % (self.__class__.__name__,
self.values.get('log', ''))) self.values.get('log', '')))
# trash if unknown or no matcher. # trash if unknown or no matcher.
self.trash = True self.trash = True
def explain(self): def explain(self):
''' returns a String readable by humans explaining this Log ''' ''' returns a String readable by humans explaining this Log '''
return self.values.get('log', 'Unknown Combat Log') return self.values.get('log', 'Unknown Combat Log')
def clean(self): def clean(self):
if 'log' in self.values.keys(): if 'log' in list(self.values.keys()):
del self.values['log'] del self.values['log']
# @todo: where does this come from? # @todo: where does this come from?
class Action(CombatLog): class Action(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
pass pass
class Gameplay(CombatLog): class Gameplay(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = [ matcher = [
# usual: team(reason). explained reason. # usual: team(reason). explained reason.
re.compile(r"^Gameplay\sfinished\.\sWinner\steam\:\s+(?P<winner_team>\d+)\((?P<winner_reason>\w+)\)\.\sFinish\sreason\:\s'(?P<reason_verbose>[^']+)'\.\sActual\sgame\stime\s+(?P<game_time>\d+|\d+\.\d+)\ssec"), re.compile(r"^Gameplay\sfinished\.\sWinner\steam\:\s+(?P<winner_team>\d+)\((?P<winner_reason>\w+)\)\.\sFinish\sreason\:\s'(?P<reason_verbose>[^']+)'\.\sActual\sgame\stime\s+(?P<game_time>\d+|\d+\.\d+)\ssec"),
# team, unexplained reason (unknown, Timeout) # team, unexplained reason (unknown, Timeout)
re.compile(r"^Gameplay\sfinished\.\sWinner\steam\:\s+(?P<winner_team>\d+).\sFinish\sreason\:\s'(?P<winner_reason>[^']+)'\.\sActual\sgame\stime\s+(?P<game_time>\d+|\d+\.\d+)\ssec"), re.compile(r"^Gameplay\sfinished\.\sWinner\steam\:\s+(?P<winner_team>\d+).\sFinish\sreason\:\s'(?P<winner_reason>[^']+)'\.\sActual\sgame\stime\s+(?P<game_time>\d+|\d+\.\d+)\ssec"),
] ]
class Apply(CombatLog): # Apply Aura. class Apply(CombatLog): # Apply Aura.
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile(r"^Apply\saura\s'(?P<aura_name>\w+)'\sid\s(?P<id>\d+)\stype\s(?P<aura_type>\w+)\sto\s'(?P<target_name>[^\']+)'") matcher = re.compile(r"^Apply\saura\s'(?P<aura_name>\w+)'\sid\s(?P<id>\d+)\stype\s(?P<aura_type>\w+)\sto\s'(?P<target_name>[^\']+)'")
class Damage(CombatLog): class Damage(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile(r"^Damage\s+(?P<source_name>[^\s]+)\s\->\s+(?P<target_name>[^\s]+)\s+(?P<amount>(?:\d+|\d+\.\d+))(?:\s(?P<module_class>[^\s]+)\s|\s{2,2})(?P<flags>(?:\w|\|)+)") matcher = re.compile(r"^Damage\s+(?P<source_name>[^\s]+)\s\->\s+(?P<target_name>[^\s]+)\s+(?P<amount>(?:\d+|\d+\.\d+))(?:\s(?P<module_class>[^\s]+)\s|\s{2,2})(?P<flags>(?:\w|\|)+)")
class Spawn(CombatLog): class Spawn(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile(r"^Spawn\sSpaceShip\sfor\splayer(?P<player>\d+)\s\((?P<name>[^,]+),\s+(?P<hash>#\w+)\)\.\s+'(?P<ship_class>\w+)'") matcher = re.compile(r"^Spawn\sSpaceShip\sfor\splayer(?P<player>\d+)\s\((?P<name>[^,]+),\s+(?P<hash>#\w+)\)\.\s+'(?P<ship_class>\w+)'")
class Spell(CombatLog): class Spell(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile(r"^Spell\s'(?P<spell_name>\w+)'\sby\s+(?P<source_name>.*)(?:\((?P<module_name>\w+)\)|)\stargets\((?P<target_num>\d+)\)\:(?:\s(?P<targets>.+)|\s*)") matcher = re.compile(r"^Spell\s'(?P<spell_name>\w+)'\sby\s+(?P<source_name>.*)(?:\((?P<module_name>\w+)\)|)\stargets\((?P<target_num>\d+)\)\:(?:\s(?P<targets>.+)|\s*)")
class Reward(CombatLog): class Reward(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = [ matcher = [
# ordinary reward: # ordinary reward:
re.compile(r"^Reward\s+(?P<name>[^\s]+)(?:\s(?P<ship_class>\w+)\s+|\s+)(?P<amount>\d+)\s(?P<reward_type>.*)\s+for\s(?P<reward_reason>.*)"), re.compile(r"^Reward\s+(?P<name>[^\s]+)(?:\s(?P<ship_class>\w+)\s+|\s+)(?P<amount>\d+)\s(?P<reward_type>.*)\s+for\s(?P<reward_reason>.*)"),
# openspace reward (karma): # openspace reward (karma):
re.compile(r"^Reward\s+(?P<name>[^\s]+)(?:\s(?P<ship_class>\w+)\s+|\s+)\s+(?P<karma>[\+\-]\d+)\skarma\spoints\s+for\s(?P<reward_reason>.*)"), re.compile(r"^Reward\s+(?P<name>[^\s]+)(?:\s(?P<ship_class>\w+)\s+|\s+)\s+(?P<karma>[\+\-]\d+)\skarma\spoints\s+for\s(?P<reward_reason>.*)"),
] ]
class Participant(CombatLog): class Participant(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile(r"^\s+Participant\s+(?P<source_name>[^\s]+)(?:\s+(?P<ship_class>\w+)|\s{30,})\s+(?:totalDamage\s(?P<total_damage>(?:\d+|\d+\.\d+));\s+|\s+)(?:mostDamageWith\s'(?P<module_class>[^']+)';\s*(?P<additional>.*)|<(?P<other>\w+)>)") matcher = re.compile(r"^\s+Participant\s+(?P<source_name>[^\s]+)(?:\s+(?P<ship_class>\w+)|\s{30,})\s+(?:totalDamage\s(?P<total_damage>(?:\d+|\d+\.\d+));\s+|\s+)(?:mostDamageWith\s'(?P<module_class>[^']+)';\s*(?P<additional>.*)|<(?P<other>\w+)>)")
""" """
2017-03-29 13:25:49 - Unknown Packet for Rocket: 2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket launch 18912, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', target 'white213mouse' (17894) Rocket launch 18912, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', target 'white213mouse' (17894)
2017-03-29 13:25:49 - Unknown Packet for Rocket: 2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket detonation 18912, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', reason 'auto_detonate', directHit 'white213mouse' Rocket detonation 18912, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', reason 'auto_detonate', directHit 'white213mouse'
2017-03-29 13:25:49 - Unknown Packet for Rocket: 2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket launch 18966, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', target 'white213mouse' (17894) Rocket launch 18966, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', target 'white213mouse' (17894)
2017-03-29 13:25:49 - Unknown Packet for Rocket: 2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket detonation 18966, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', reason 'auto_detonate', directHit 'white213mouse' Rocket detonation 18966, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', reason 'auto_detonate', directHit 'white213mouse'
2017-03-29 13:25:49 - Unknown Packet for Rocket: 2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket detonation 18892, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', reason 'ttl' Rocket detonation 18892, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', reason 'ttl'
2017-03-29 13:25:49 - Unknown Packet for Rocket: 2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket detonation 18931, owner 'optimistik', def 'Weapon_Railgun_Heavy_T5_Epic', reason 'hit' Rocket detonation 18931, owner 'optimistik', def 'Weapon_Railgun_Heavy_T5_Epic', reason 'hit'
2017-03-29 13:25:49 - Unknown Packet for Participant: 2017-03-29 13:25:49 - Unknown Packet for Participant:
Participant white213mouse Ship_Race5_M_ATTACK_Rank15 Participant white213mouse Ship_Race5_M_ATTACK_Rank15
""" """
class Rocket(CombatLog): class Rocket(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile(r"^Rocket\s(?P<event>launch|detonation)\.\sowner\s'(?P<name>[^']+)'(?:,\s(?:def\s'(?P<missile_type>\w+)'|target\s'(?P<target>[^']+)'|reason\s'(?P<reason>\w+)'|directHit\s'(?P<direct_hit>[^']+)'))+") matcher = re.compile(r"^Rocket\s(?P<event>launch|detonation)\.\sowner\s'(?P<name>[^']+)'(?:,\s(?:def\s'(?P<missile_type>\w+)'|target\s'(?P<target>[^']+)'|reason\s'(?P<reason>\w+)'|directHit\s'(?P<direct_hit>[^']+)'))+")
class Heal(CombatLog): class Heal(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = [ matcher = [
# heal by module # heal by module
re.compile(r"^Heal\s+(?P<source_name>[^\s]+)\s\->\s+(?P<target_name>[^\s]+)\s+(?P<amount>(?:\d+|\d+\.\d+))\s(?P<module_class>[^\s]+)"), re.compile(r"^Heal\s+(?P<source_name>[^\s]+)\s\->\s+(?P<target_name>[^\s]+)\s+(?P<amount>(?:\d+|\d+\.\d+))\s(?P<module_class>[^\s]+)"),
# direct heal by source or n/a (global buff) # direct heal by source or n/a (global buff)
re.compile(r"^Heal\s+(?:n/a|(?P<source_name>\w+))\s+\->\s+(?P<target_name>[^\s]+)\s+(?P<amount>(?:\d+|\d+\.\d+))"), re.compile(r"^Heal\s+(?:n/a|(?P<source_name>\w+))\s+\->\s+(?P<target_name>[^\s]+)\s+(?P<amount>(?:\d+|\d+\.\d+))"),
] ]
class Killed(CombatLog): class Killed(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = [ matcher = [
re.compile(r"^Killed\s(?P<target_name>[^\s]+)\s+(?P<ship_class>\w+);\s+killer\s(?P<source_name>[^\s]+)\s*"), re.compile(r"^Killed\s(?P<target_name>[^\s]+)\s+(?P<ship_class>\w+);\s+killer\s(?P<source_name>[^\s]+)\s*"),
re.compile(r"^Killed\s(?P<object>[^\(]+)\((?P<target_name>\w+)\);\s+killer\s(?P<source_name>[^\s]+)\s*"), re.compile(r"^Killed\s(?P<object>[^\(]+)\((?P<target_name>\w+)\);\s+killer\s(?P<source_name>[^\s]+)\s*"),
re.compile(r"^Killed\s(?P<object>[^\;]+);\s+killer\s(?P<source_name>[^\s]+)\s+.*"), re.compile(r"^Killed\s(?P<object>[^\;]+);\s+killer\s(?P<source_name>[^\s]+)\s+.*"),
] ]
class Captured(CombatLog): class Captured(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile(r"^Captured\s'(?P<objective>[^']+)'\(team\s(?P<team>\d+)\)\.(?:\sAttackers\:(?P<attackers>.*)|.*)") matcher = re.compile(r"^Captured\s'(?P<objective>[^']+)'\(team\s(?P<team>\d+)\)\.(?:\sAttackers\:(?P<attackers>.*)|.*)")
class AddStack(CombatLog): class AddStack(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile(r"^AddStack\saura\s'(?P<spell_name>\w+)'\sid\s(?P<id>\d+)\stype\s(?P<type>\w+)\.\snew\sstacks\scount\s(?P<stack_count>\d+)") matcher = re.compile(r"^AddStack\saura\s'(?P<spell_name>\w+)'\sid\s(?P<id>\d+)\stype\s(?P<type>\w+)\.\snew\sstacks\scount\s(?P<stack_count>\d+)")
class Cancel(CombatLog): class Cancel(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile(r"^Cancel\saura\s'(?P<spell_name>\w+)'\sid\s(?P<id>\d+)\stype\s(?P<type>\w+)\sfrom\s'(?P<source_name>[^']*)'") matcher = re.compile(r"^Cancel\saura\s'(?P<spell_name>\w+)'\sid\s(?P<id>\d+)\stype\s(?P<type>\w+)\sfrom\s'(?P<source_name>[^']*)'")
class Scores(CombatLog): class Scores(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile(r"^Scores\s+-\sTeam1\((?P<team1_score>(?:\d+|\d+\.\d+))\)\sTeam2\((?P<team2_score>(?:\d+|\d+\.\d+))\)") matcher = re.compile(r"^Scores\s+-\sTeam1\((?P<team1_score>(?:\d+|\d+\.\d+))\)\sTeam2\((?P<team2_score>(?:\d+|\d+\.\d+))\)")
class Uncaptured(CombatLog): class Uncaptured(CombatLog):
""" """
Variables: Variables:
- objective (which was uncaptured (most likely something like VitalPointXY)) - objective (which was uncaptured (most likely something like VitalPointXY))
- team (number) - team (number)
- attackers (split by space, names of the attackers, contains bots) - attackers (split by space, names of the attackers, contains bots)
""" """
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile(r"^Uncaptured\s'(?P<objective>[^']+)'\(team\s(?P<team>\d+)\)\.(?:\sAttackers\:\s(?P<attackers>.*)|)") matcher = re.compile(r"^Uncaptured\s'(?P<objective>[^']+)'\(team\s(?P<team>\d+)\)\.(?:\sAttackers\:\s(?P<attackers>.*)|)")
# Special classes # Special classes
class GameEvent(CombatLog): class GameEvent(CombatLog):
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = [ matcher = [
# game session identifier. # game session identifier.
re.compile(r"^Connect\sto\sgame\ssession\s+(?P<game_session>\d+)"), re.compile(r"^Connect\sto\sgame\ssession\s+(?P<game_session>\d+)"),
# start gameplay identifier. # start gameplay identifier.
re.compile(r"^Start\sgameplay\s'(?P<gameplay_name>\w+)'\smap\s+'(?P<map_id>\w+)',\slocal\sclient\steam\s(?P<local_team>\d+)"), re.compile(r"^Start\sgameplay\s'(?P<gameplay_name>\w+)'\smap\s+'(?P<map_id>\w+)',\slocal\sclient\steam\s(?P<local_team>\d+)"),
# pve mission identifier. # pve mission identifier.
re.compile(r"^Start\sPVE\smission\s'(?P<pve_name>\w+)'\smap\s+'(?P<map_id>\w+)'"), re.compile(r"^Start\sPVE\smission\s'(?P<pve_name>\w+)'\smap\s+'(?P<map_id>\w+)'"),
] ]
@classmethod @classmethod
def _log_handler(cls, log): def _log_handler(cls, log):
if log.startswith('======='): if log.startswith('======='):
return True return True
return False return False
def unpack(self, force=False): def unpack(self, force=False):
if self.reviewed and not force: if self.reviewed and not force:
return True return True
self._match_id = None self._match_id = None
# unpacks the data from the values. # unpacks the data from the values.
# small override to remove trailing "="s in the matching. # small override to remove trailing "="s in the matching.
if hasattr(self, 'matcher') and self.matcher: if hasattr(self, 'matcher') and self.matcher:
matchers = self.matcher matchers = self.matcher
if not isinstance(matchers, list): if not isinstance(matchers, list):
matchers = [matchers,] matchers = [matchers,]
for i, matcher in enumerate(matchers): for i, matcher in enumerate(matchers):
m = matcher.match(self.values.get('log', '').strip('=').strip()) m = matcher.match(self.values.get('log', '').strip('=').strip())
if m: if m:
self.values.update(m.groupdict()) self.values.update(m.groupdict())
self._match_id = i self._match_id = i
self.reviewed = True self.reviewed = True
return True return True
# unknown? # unknown?
self.trash = True self.trash = True
def clean(self): def clean(self):
if 'log' in self.values.keys(): if 'log' in list(self.values.keys()):
del self.values['log'] del self.values['log']
class PVE_Mission(CombatLog): class PVE_Mission(CombatLog):
""" """
- mission: contains the mission id. - mission: contains the mission id.
- message: contains the pve mission message, like starting rounds, waves, etc. - message: contains the pve mission message, like starting rounds, waves, etc.
""" """
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile("^PVE_Mission:\s'(?P<mission>[^']+)'.\s(?P<message>.*)") # this is very general, but we dont care for pve now. matcher = re.compile("^PVE_Mission:\s'(?P<mission>[^']+)'.\s(?P<message>.*)") # this is very general, but we dont care for pve now.
class Looted(CombatLog): class Looted(CombatLog):
""" """
called on looting in openspace. called on looting in openspace.
- loot contains the loot id. - loot contains the loot id.
- container contains the container looted from. - container contains the container looted from.
""" """
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile("^Looted\s'(?P<loot>[^']+)'\sfrom\s'(?P<container>[^']+)'") matcher = re.compile("^Looted\s'(?P<loot>[^']+)'\sfrom\s'(?P<container>[^']+)'")
class Dropped(CombatLog): class Dropped(CombatLog):
""" """
called on dropping in openspace. called on dropping in openspace.
- loot contains the loot id. it can be '<all>' - loot contains the loot id. it can be '<all>'
""" """
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile("^Dropped\sloot\s'(?P<loot>[^']+)'") matcher = re.compile("^Dropped\sloot\s'(?P<loot>[^']+)'")
class Set(CombatLog): class Set(CombatLog):
""" """
called on setting "relationship" / OpenSpace called on setting "relationship" / OpenSpace
Variables in values: Variables in values:
- what (relationship) - what (relationship)
- name (who do i set?) - name (who do i set?)
- value (to what value?) - value (to what value?)
""" """
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile("^Set\s(?P<what>\w+)\s(?P<name>[^\s]+)\sto\s(?P<value>\w+)") matcher = re.compile("^Set\s(?P<what>\w+)\s(?P<name>[^\s]+)\sto\s(?P<value>\w+)")
class SqIdChange(CombatLog): class SqIdChange(CombatLog):
""" - number: player number """ - number: player number
- name: player name - name: player name
- old_sqid: sqid of player before - old_sqid: sqid of player before
- sqid: new player sqid - sqid: new player sqid
""" """
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile("^Player\s(?P<number>\d+)\((?P<name>[^\)]+)\)\schanged\ssqid\sfrom\s(?P<old_sqid>\d+)\sto\s(?P<sqid>\d+)") matcher = re.compile("^Player\s(?P<number>\d+)\((?P<name>[^\)]+)\)\schanged\ssqid\sfrom\s(?P<old_sqid>\d+)\sto\s(?P<sqid>\d+)")
@classmethod @classmethod
def _log_handler(cls, log): def _log_handler(cls, log):
if log.startswith('Player'): if log.startswith('Player'):
return True return True
return False return False
class Mailed(CombatLog): class Mailed(CombatLog):
""" has no information. only that loot has been mailed """ """ has no information. only that loot has been mailed """
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
matcher = re.compile("Mailed\sloot") matcher = re.compile("Mailed\sloot")
class UserEvent(CombatLog): class UserEvent(CombatLog):
""" special class for combat logs that might be associated with the playing player """ """ special class for combat logs that might be associated with the playing player """
__slots__ = CombatLog.__slots__ __slots__ = CombatLog.__slots__
@classmethod @classmethod
def _log_handler(cls, log): def _log_handler(cls, log):
if log and 'earned medal' in log: if log and 'earned medal' in log:
return True return True
elif log: elif log:
logging.debug('UserEvent saw unknown line:\n%s' % log) logging.debug('UserEvent saw unknown line:\n%s' % log)
return False return False
# Action? # Action?
COMBAT_LOGS = [ Apply, Damage, Spawn, Spell, Reward, Participant, Rocket, Heal, COMBAT_LOGS = [ Apply, Damage, Spawn, Spell, Reward, Participant, Rocket, Heal,
Gameplay, #? Gameplay, #?
Scores, Scores,
Killed, Captured, AddStack, Cancel, Uncaptured, Killed, Captured, AddStack, Cancel, Uncaptured,
# undone openspace: # undone openspace:
PVE_Mission, Looted, Set, Dropped, PVE_Mission, Looted, Set, Dropped,
SqIdChange, Mailed, # unknown if these are important... SqIdChange, Mailed, # unknown if these are important...
# always last: # always last:
GameEvent, UserEvent, GameEvent, UserEvent,
Stacktrace, Stacktrace,
] ]

View File

@ -1,195 +1,195 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from logs.base import Log, L_WARNING, Stacktrace from logs.base import Log, L_WARNING, Stacktrace
import re import re
""" """
This deals with the Game.Log file This deals with the Game.Log file
This file records lots of junk, but is needed to establish actions taken between combat sessions, This file records lots of junk, but is needed to establish actions taken between combat sessions,
or retrieve more detailed information about running instances. or retrieve more detailed information about running instances.
It is also the typical place for a Stacktrace to happen. It is also the typical place for a Stacktrace to happen.
-------------------------------------- --------------------------------------
Interesting Lines: Interesting Lines:
23:16:27.427 | Steam initialized appId 212070, userSteamID 1|1|4c5a01, userName 'G4bOrg' 23:16:27.427 | Steam initialized appId 212070, userSteamID 1|1|4c5a01, userName 'G4bOrg'
23:16:36.214 | ====== starting level: 'levels/mainmenu/mainmenu' ====== 23:16:36.214 | ====== starting level: 'levels/mainmenu/mainmenu' ======
23:16:38.822 | ====== level started: 'levels/mainmenu/mainmenu' success ====== 23:16:38.822 | ====== level started: 'levels/mainmenu/mainmenu' success ======
23:16:44.251 | ====== starting level: 'levels\mainmenu\mm_empire' ====== 23:16:44.251 | ====== starting level: 'levels\mainmenu\mm_empire' ======
23:16:46.464 | ====== level started: 'levels\mainmenu\mm_empire' success ====== 23:16:46.464 | ====== level started: 'levels\mainmenu\mm_empire' success ======
--- Date: 2014-07-18 (Fri Jul 2014) Mitteleuropäische Sommerzeit UTC+01:00 --- Date: 2014-07-18 (Fri Jul 2014) Mitteleuropäische Sommerzeit UTC+01:00
23:55:55.517 | MasterServerSession: connect to dedicated server, session 6777304, at addr 159.253.138.162|35005 23:55:55.517 | MasterServerSession: connect to dedicated server, session 6777304, at addr 159.253.138.162|35005
23:55:55.543 | client: start connecting to 159.253.138.162|35005... 23:55:55.543 | client: start connecting to 159.253.138.162|35005...
23:55:55.683 | client: connected to 159.253.138.162|35005, setting up session... 23:55:55.683 | client: connected to 159.253.138.162|35005, setting up session...
23:55:55.886 | client: ADD_PLAYER 0 (OregyenDuero [OWL], 00039C86) status 6 team 1 group 1178422 23:55:55.886 | client: ADD_PLAYER 0 (OregyenDuero [OWL], 00039C86) status 6 team 1 group 1178422
23:55:55.886 | client: ADD_PLAYER 1 (R0gue, 0012768A) status 6 team 2 group 1178451 23:55:55.886 | client: ADD_PLAYER 1 (R0gue, 0012768A) status 6 team 2 group 1178451
23:55:55.886 | client: ADD_PLAYER 2 (g4borg [OWL], 0003A848) status 1 team 1 group 1178422 23:55:55.886 | client: ADD_PLAYER 2 (g4borg [OWL], 0003A848) status 1 team 1 group 1178422
23:55:55.886 | client: ADD_PLAYER 3 (WladTepes, 001210D8) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 3 (WladTepes, 001210D8) status 6 team 1
23:55:55.886 | client: ADD_PLAYER 4 (oberus [], 000FE9B2) status 6 team 2 23:55:55.886 | client: ADD_PLAYER 4 (oberus [], 000FE9B2) status 6 team 2
23:55:55.886 | client: ADD_PLAYER 5 (TheGuns58, 00121C58) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 5 (TheGuns58, 00121C58) status 6 team 1
23:55:55.886 | client: ADD_PLAYER 6 (Belleraphon, 0004C744) status 2 team 2 23:55:55.886 | client: ADD_PLAYER 6 (Belleraphon, 0004C744) status 2 team 2
23:55:55.886 | client: ADD_PLAYER 7 (TopoL, 00007E1F) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 7 (TopoL, 00007E1F) status 6 team 1
23:55:55.886 | client: ADD_PLAYER 8 (unicoimbraPT, 000C4FAC) status 6 team 2 23:55:55.886 | client: ADD_PLAYER 8 (unicoimbraPT, 000C4FAC) status 6 team 2
23:55:55.886 | client: ADD_PLAYER 9 (AeroBobik [], 00082047) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 9 (AeroBobik [], 00082047) status 6 team 1
23:55:55.886 | client: ADD_PLAYER 10 (Samson4321 [], 000B93AF) status 6 team 2 23:55:55.886 | client: ADD_PLAYER 10 (Samson4321 [], 000B93AF) status 6 team 2
23:55:55.886 | client: ADD_PLAYER 11 (nol [], 00069165) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 11 (nol [], 00069165) status 6 team 1
23:55:55.886 | client: ADD_PLAYER 12 (Pudwoppa, 000334A4) status 2 team 2 23:55:55.886 | client: ADD_PLAYER 12 (Pudwoppa, 000334A4) status 2 team 2
23:55:55.886 | client: ADD_PLAYER 13 (IgorMad [], 000D2AF3) status 6 team 1 23:55:55.886 | client: ADD_PLAYER 13 (IgorMad [], 000D2AF3) status 6 team 1
23:55:55.886 | client: ADD_PLAYER 14 (YokaI, 000F1CC9) status 6 team 2 23:55:55.886 | client: ADD_PLAYER 14 (YokaI, 000F1CC9) status 6 team 2
23:55:55.886 | client: ADD_PLAYER 15 (MrAnyKey [], 0012246C) status 6 team 2 group 1178451 23:55:55.886 | client: ADD_PLAYER 15 (MrAnyKey [], 0012246C) status 6 team 2 group 1178451
23:55:55.886 | client: ADD_PLAYER 30 ((bot)David, 00000000) status 4 team 1 23:55:55.886 | client: ADD_PLAYER 30 ((bot)David, 00000000) status 4 team 1
23:55:55.886 | client: ADD_PLAYER 31 ((bot)George, 00000000) status 4 team 2 23:55:55.886 | client: ADD_PLAYER 31 ((bot)George, 00000000) status 4 team 2
23:55:55.886 | client: server assigned id 2 23:55:55.886 | client: server assigned id 2
23:55:55.886 | client: got level load message 's1340_thar_aliendebris13' 23:55:55.886 | client: got level load message 's1340_thar_aliendebris13'
23:55:55.889 | reset d3d device 23:55:55.889 | reset d3d device
23:55:56.487 | ReplayManager: stopping activity due to map change 23:55:56.487 | ReplayManager: stopping activity due to map change
23:55:56.576 | ====== starting level: 'levels\area2\s1340_thar_aliendebris13' KingOfTheHill client ====== 23:55:56.576 | ====== starting level: 'levels\area2\s1340_thar_aliendebris13' KingOfTheHill client ======
""" """
class GameLog(Log): class GameLog(Log):
__slots__ = ['matcher', 'trash', '_match_id', 'values'] __slots__ = ['matcher', 'trash', '_match_id', 'values']
@classmethod @classmethod
def is_handler(cls, log): def is_handler(cls, log):
if log.get('logtype', None) == '': # we handle only logs with empty logtype. if log.get('logtype', None) == '': # we handle only logs with empty logtype.
return cls._is_handler(log) return cls._is_handler(log)
return False return False
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
return False return False
def __init__(self, values=None): def __init__(self, values=None):
self.values = values self.values = values
self.reviewed = False self.reviewed = False
def clean(self): def clean(self):
if 'log' in self.values.keys(): if 'log' in list(self.values.keys()):
del self.values['log'] del self.values['log']
def unpack(self, force=False): def unpack(self, force=False):
if self.reviewed and not force: if self.reviewed and not force:
return True return True
self._match_id = None self._match_id = None
# unpacks the data from the values. # unpacks the data from the values.
if hasattr(self, 'matcher') and self.matcher: if hasattr(self, 'matcher') and self.matcher:
matchers = self.matcher matchers = self.matcher
if not isinstance(matchers, list): if not isinstance(matchers, list):
matchers = [matchers,] matchers = [matchers,]
for i, matcher in enumerate(matchers): for i, matcher in enumerate(matchers):
m = matcher.match(self.values.get('log', '')) m = matcher.match(self.values.get('log', ''))
if m: if m:
self.values.update(m.groupdict()) self.values.update(m.groupdict())
self._match_id = i self._match_id = i
self.reviewed = True self.reviewed = True
return True return True
# unknown? # unknown?
self.trash = True self.trash = True
def explain(self): def explain(self):
''' returns a String readable by humans explaining this Log ''' ''' returns a String readable by humans explaining this Log '''
return self.values.get('log', 'Unknown Game Log') return self.values.get('log', 'Unknown Game Log')
class WarningLog(Log): class WarningLog(Log):
__slots__ = ['trash',] __slots__ = ['trash',]
trash = True trash = True
@classmethod @classmethod
def is_handler(cls, log): def is_handler(cls, log):
if log.get('logtype', None) == L_WARNING: if log.get('logtype', None) == L_WARNING:
return True return True
return False return False
def __init__(self, values=None): def __init__(self, values=None):
pass pass
######################################################################################################## ########################################################################################################
# Individual logs. # Individual logs.
class SteamInitialization(GameLog): class SteamInitialization(GameLog):
matcher = [ matcher = [
re.compile(r"^Steam\sinitialized\sappId\s(?P<steam_app_id>\d+),\suserSteamID\s(?P<steam_id_universe>\d+)\|(?P<steam_id_type>\d+)\|(?P<steam_id_account_hex>\w+),\suserName\s'(?P<steam_username>[^']+)'"), re.compile(r"^Steam\sinitialized\sappId\s(?P<steam_app_id>\d+),\suserSteamID\s(?P<steam_id_universe>\d+)\|(?P<steam_id_type>\d+)\|(?P<steam_id_account_hex>\w+),\suserName\s'(?P<steam_username>[^']+)'"),
] ]
class MasterServerSession(GameLog): class MasterServerSession(GameLog):
matcher = [ matcher = [
re.compile(r"^MasterServerSession\:\sconnect\sto\sdedicated\sserver(?:,\s|session\s(?P<session_id>\d+)|at addr (?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+))+"), re.compile(r"^MasterServerSession\:\sconnect\sto\sdedicated\sserver(?:,\s|session\s(?P<session_id>\d+)|at addr (?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+))+"),
re.compile(r"^MasterServerSession:\sconnect\sto\sZoneInstance,\ssession\s(?P<session_id>\d+),\sat\saddr\s(?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+),\szoneId\s(?P<zone_id>\d+)"), re.compile(r"^MasterServerSession:\sconnect\sto\sZoneInstance,\ssession\s(?P<session_id>\d+),\sat\saddr\s(?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+),\szoneId\s(?P<zone_id>\d+)"),
] ]
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').startswith('MasterServerSession'): if log.get('log', '').startswith('MasterServerSession'):
return True return True
return False return False
class ClientInfo(GameLog): class ClientInfo(GameLog):
# Note: clinfo holds the subtype of this packet. # Note: clinfo holds the subtype of this packet.
matcher = [ matcher = [
# connecting; addr, port # connecting; addr, port
re.compile(r"^client\:\sstart\s(?P<clinfo>connecting)\sto\s(?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+)\.\.\."), re.compile(r"^client\:\sstart\s(?P<clinfo>connecting)\sto\s(?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+)\.\.\."),
# connected; addr, port # connected; addr, port
re.compile(r"^client\:\s(?P<clinfo>connected)\sto\s(?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+).*"), re.compile(r"^client\:\s(?P<clinfo>connected)\sto\s(?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+).*"),
# ADD_PLAYER; pnr, player, clantag, player_id, status, team, group # ADD_PLAYER; pnr, player, clantag, player_id, status, team, group
re.compile(r"^client\:\s(?P<clinfo>ADD_PLAYER)\s+(?P<pnr>\d+)\s+\((?P<player>[^\s\,]+)(?:\s\[(?P<clantag>\w+)\],|\s\[\],|,)\s(?P<player_id>\w+)\)(?:\s|status\s(?P<status>\d+)|team\s(?P<team>\d+)|group\s(?P<group>\d+))+"), re.compile(r"^client\:\s(?P<clinfo>ADD_PLAYER)\s+(?P<pnr>\d+)\s+\((?P<player>[^\s\,]+)(?:\s\[(?P<clantag>\w+)\],|\s\[\],|,)\s(?P<player_id>\w+)\)(?:\s|status\s(?P<status>\d+)|team\s(?P<team>\d+)|group\s(?P<group>\d+))+"),
# assigned; myid # assigned; myid
re.compile(r"^client\:\sserver\s(?P<clinfo>assigned)\sid\s(?P<myid>\d+)"), re.compile(r"^client\:\sserver\s(?P<clinfo>assigned)\sid\s(?P<myid>\d+)"),
# level; level # level; level
re.compile(r"^client\:\sgot\s(?P<clinfo>level)\sload\smessage\s'(?P<level>[^']+)'"), re.compile(r"^client\:\sgot\s(?P<clinfo>level)\sload\smessage\s'(?P<level>[^']+)'"),
# leave; pnr # leave; pnr
re.compile(r"^client\:\splayer\s(?P<pnr>\d+)\s(?P<clinfo>leave)\sgame"), re.compile(r"^client\:\splayer\s(?P<pnr>\d+)\s(?P<clinfo>leave)\sgame"),
# avgPing; avg_ping, avg_packet_loss, avg_snapshot_loss # avgPing; avg_ping, avg_packet_loss, avg_snapshot_loss
re.compile(r"^client\:\s(?P<clinfo>avgPing)\s(?P<avg_ping>[^;]+)(?:\;|\s|avgPacketLoss\s(?P<avg_packet_loss>[^;]+)|avgSnapshotLoss\s(?P<avg_snapshot_loss>[^;$]+))+"), re.compile(r"^client\:\s(?P<clinfo>avgPing)\s(?P<avg_ping>[^;]+)(?:\;|\s|avgPacketLoss\s(?P<avg_packet_loss>[^;]+)|avgSnapshotLoss\s(?P<avg_snapshot_loss>[^;$]+))+"),
# closed; dr # closed; dr
re.compile(r"^client\:\sconnection\s(?P<clinfo>closed)\.(?:\s|(?P<dr>.*))+"), re.compile(r"^client\:\sconnection\s(?P<clinfo>closed)\.(?:\s|(?P<dr>.*))+"),
# disconnect; addr, port, dr # disconnect; addr, port, dr
re.compile(r"^client\:\s(?P<clinfo>disconnect)\sfrom\sserver\s(?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+)\.(?:\s|(?P<dr>\w+))+"), re.compile(r"^client\:\s(?P<clinfo>disconnect)\sfrom\sserver\s(?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+)\.(?:\s|(?P<dr>\w+))+"),
# ready; # ready;
re.compile(r"^client\:\ssend\s(?P<clinfo>ready)\smessage"), re.compile(r"^client\:\ssend\s(?P<clinfo>ready)\smessage"),
# init; ping # init; ping
re.compile(r"^client\:\sgot\s(?P<clinfo>init)\smessage\s+\(and\s1st\ssnapshot\)\.\sping\s(?P<ping>\d+)"), re.compile(r"^client\:\sgot\s(?P<clinfo>init)\smessage\s+\(and\s1st\ssnapshot\)\.\sping\s(?P<ping>\d+)"),
] ]
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').startswith('client:'): if log.get('log', '').startswith('client:'):
return True return True
return False return False
class StartingLevel(GameLog): class StartingLevel(GameLog):
# level, gametype, unknown_gametype # level, gametype, unknown_gametype
matcher = [ matcher = [
re.compile(r"^======\sstarting\slevel\:\s'(?P<level>[^']+)'(?:\s|client|(?P<gametype>KingOfTheHill)|(?P<unknown_gametype>[^\s]+))+======"), re.compile(r"^======\sstarting\slevel\:\s'(?P<level>[^']+)'(?:\s|client|(?P<gametype>KingOfTheHill)|(?P<unknown_gametype>[^\s]+))+======"),
] ]
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').startswith('====== starting'): if log.get('log', '').startswith('====== starting'):
return True return True
return False return False
class LevelStarted(GameLog): class LevelStarted(GameLog):
matcher = [] matcher = []
@classmethod @classmethod
def _is_handler(cls, log): def _is_handler(cls, log):
if log.get('log', '').startswith('====== level'): if log.get('log', '').startswith('====== level'):
return True return True
return False return False
GAME_LOGS = [#SteamInitialization, GAME_LOGS = [#SteamInitialization,
MasterServerSession, MasterServerSession,
ClientInfo, ClientInfo,
StartingLevel, StartingLevel,
#LevelStarted, #LevelStarted,
Stacktrace, Stacktrace,
] ]

View File

@ -1,49 +1,49 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Resolves Logs. Resolves Logs.
""" """
from logfile import LogFile from .logfile import LogFile
from combat import COMBAT_LOGS from .combat import COMBAT_LOGS
from game import GAME_LOGS from .game import GAME_LOGS
from chat import CHAT_LOGS from .chat import CHAT_LOGS
class LogFileResolver(LogFile): class LogFileResolver(LogFile):
''' dynamic logfile resolver ''' ''' dynamic logfile resolver '''
resolution_classes = COMBAT_LOGS resolution_classes = COMBAT_LOGS
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(LogFileResolver, self).__init__(*args, **kwargs) super(LogFileResolver, self).__init__(*args, **kwargs)
self.resolution_classes = self.resolution_classes or [] self.resolution_classes = self.resolution_classes or []
def resolve(self, line): def resolve(self, line):
for klass in self.resolution_classes: for klass in self.resolution_classes:
if klass.is_handler(line): if klass.is_handler(line):
return klass(line) return klass(line)
return line return line
class CombatLogFile(LogFile): class CombatLogFile(LogFile):
''' Combat Log ''' ''' Combat Log '''
def resolve(self, line): def resolve(self, line):
for klass in COMBAT_LOGS: for klass in COMBAT_LOGS:
if klass.is_handler(line): if klass.is_handler(line):
return klass(line) return klass(line)
return line return line
class GameLogFile(LogFile): class GameLogFile(LogFile):
''' Game Log ''' ''' Game Log '''
def resolve(self, line): def resolve(self, line):
for klass in GAME_LOGS: for klass in GAME_LOGS:
if klass.is_handler(line): if klass.is_handler(line):
return klass(line) return klass(line)
return line return line
class ChatLogFile(LogFile): class ChatLogFile(LogFile):
''' Chat Log ''' ''' Chat Log '''
def resolve(self, line): def resolve(self, line):
for klass in CHAT_LOGS: for klass in CHAT_LOGS:
if klass.is_handler(line): if klass.is_handler(line):
return klass(line) return klass(line)
return line return line

View File

@ -1,144 +1,144 @@
""" """
A LogStream is supposed to: A LogStream is supposed to:
- parse data feeded into it. - parse data feeded into it.
- yield new objects - yield new objects
- remember errors - remember errors
LogStream.Initialize: LogStream.Initialize:
- initialize the logstream in some way. - initialize the logstream in some way.
LogStream.Next: LogStream.Next:
- once initialized, read your stream until you can yield a new class - once initialized, read your stream until you can yield a new class
the next function reads the read-stream ahead. the next function reads the read-stream ahead.
empty lines are omitted empty lines are omitted
it tries to match the data into a new class and yields it it tries to match the data into a new class and yields it
if it runs into trouble, it just outputs the line for now. if it runs into trouble, it just outputs the line for now.
InitializeString: InitializeString:
- init with a data blob - init with a data blob
- nice for trying it on files - nice for trying it on files
@TODO: look at how file streams in python are implemented and find a good generic solution @TODO: look at how file streams in python are implemented and find a good generic solution
combine it with the lookup for "watching files being changed", to create a program which listens to the logs live combine it with the lookup for "watching files being changed", to create a program which listens to the logs live
@see: monitor.py @see: monitor.py
@see: watchdog https://pypi.python.org/pypi/watchdog @see: watchdog https://pypi.python.org/pypi/watchdog
""" """
from .base import Log from .base import Log
import re import re
from logs.base import Stacktrace from logs.base import Stacktrace
import logging import logging
RE_SCLOG = r'^(?P<hh>\d{2,2})\:(?P<mm>\d{2,2})\:(?P<ss>\d{2,2})\.(?P<ns>\d{3,3})\s(?P<logtype>\s*[^\|\s]+\s*|\s+)\|\s(?P<log>.*)' RE_SCLOG = r'^(?P<hh>\d{2,2})\:(?P<mm>\d{2,2})\:(?P<ss>\d{2,2})\.(?P<ns>\d{3,3})\s(?P<logtype>\s*[^\|\s]+\s*|\s+)\|\s(?P<log>.*)'
R_SCLOG = re.compile(RE_SCLOG) R_SCLOG = re.compile(RE_SCLOG)
class LogStream(object): class LogStream(object):
def __init__(self): def __init__(self):
self.lines = [] self.lines = []
self._data = None self._data = None
self._last_object = None self._last_object = None
def add_to_queue(self, line): def add_to_queue(self, line):
# adds a line to the queue # adds a line to the queue
pass pass
def new_packets(self, finish=False): def new_packets(self, finish=False):
# yields new packets. # yields new packets.
# processes the queue a bit. # processes the queue a bit.
# yields new packets, once they are done. # yields new packets, once they are done.
# watch out not to process the last packet until it has a follow up! # watch out not to process the last packet until it has a follow up!
# finish: override and yield all packets to finish. # finish: override and yield all packets to finish.
pass pass
##################################################################### #####################################################################
def has_data(self): def has_data(self):
if self._data: if self._data:
return True return True
def set_data(self, data): def set_data(self, data):
self._data = data self._data = data
def get_data(self): def get_data(self):
return self._data return self._data
def clean(self, remove_log=True): def clean(self, remove_log=True):
# cleans the logs by removing all non parsed packets. # cleans the logs by removing all non parsed packets.
# remove_log: should i remove the raw log entry? # remove_log: should i remove the raw log entry?
lines = [] lines = []
for l in self.lines: for l in self.lines:
if isinstance(l, Log): if isinstance(l, Log):
if l.unpack(): if l.unpack():
if not getattr(l, 'trash', False): if not getattr(l, 'trash', False):
if remove_log: if remove_log:
l.clean() l.clean()
lines.append(l) lines.append(l)
else: else:
print type(l) print((type(l)))
print l print(l)
self.lines = lines self.lines = lines
self._unset_data() self._unset_data()
data = property(set_data, get_data) data = property(set_data, get_data)
def _unset_data(self): def _unset_data(self):
self._data = None self._data = None
def pre_parse_line(self, line): def pre_parse_line(self, line):
if not isinstance(line, basestring): if not isinstance(line, str):
return line return line
elif line.startswith('---'): elif line.startswith('---'):
return None return None
elif line == '' or line == '\n': elif line == '' or line == '\n':
if line == '\n': if line == '\n':
logging.debug('Empty Newline detected.') logging.debug('Empty Newline detected.')
return None return None
else: else:
# get the timecode & logtype # get the timecode & logtype
m = R_SCLOG.match(line) m = R_SCLOG.match(line)
if m: if m:
g = m.groupdict() g = m.groupdict()
if 'logtype' in g.keys(): if 'logtype' in list(g.keys()):
g['logtype'] = g['logtype'].strip() g['logtype'] = g['logtype'].strip()
return g return g
else: else:
return line return line
return None return None
def _parse_line(self, line): def _parse_line(self, line):
# add the line to my lines. # add the line to my lines.
if line is not None: if line is not None:
o = line o = line
if isinstance(line, basestring): if isinstance(line, str):
# Unknown Log? # Unknown Log?
if not line: if not line:
return return
# It might be a stacktrace. inject it./ # It might be a stacktrace. inject it./
if Stacktrace.is_handler(o): if Stacktrace.is_handler(o):
o = Stacktrace(o) o = Stacktrace(o)
self._last_object = o self._last_object = o
else: else:
#if isinstance(self._last_object, Stacktrace) and line.startswith('\t'): #if isinstance(self._last_object, Stacktrace) and line.startswith('\t'):
# logging.debug('Workaround: %s, worked: %s' % (line, self._last_object.append(line))) # logging.debug('Workaround: %s, worked: %s' % (line, self._last_object.append(line)))
# return # return
if self._last_object is not None: if self._last_object is not None:
self._last_object.unpack() self._last_object.unpack()
if self._last_object.append(line): if self._last_object.append(line):
return return
logging.debug('#: %s' % line) logging.debug('#: %s' % line)
o = None o = None
elif isinstance(line, dict): elif isinstance(line, dict):
# Unresolved Log. # Unresolved Log.
o = self.resolve(line) o = self.resolve(line)
self._last_object = o self._last_object = o
else: else:
self._last_object = o self._last_object = o
if o is None: if o is None:
self._last_object = None self._last_object = None
return return
self.lines.append(o) self.lines.append(o)
def parse_line(self, line): def parse_line(self, line):
return self._parse_line(self.pre_parse_line(line)) return self._parse_line(self.pre_parse_line(line))
def resolve(self, gd): def resolve(self, gd):
# gd is a dict. # gd is a dict.
# try to find a class that is responsible for this log. # try to find a class that is responsible for this log.
return gd return gd

View File

@ -1,209 +1,209 @@
""" """
Logging Session. Logging Session.
""" """
import zipfile, logging, os import zipfile, logging, os
from logfiles import CombatLogFile, GameLogFile, ChatLogFile from .logfiles import CombatLogFile, GameLogFile, ChatLogFile
class LogSession(object): class LogSession(object):
""" """
A basic logsession. A basic logsession.
deal with data as it comes along, and output interpretable data for the outside world. deal with data as it comes along, and output interpretable data for the outside world.
""" """
pass pass
class LogFileSession(LogSession): class LogFileSession(LogSession):
""" """
The Log-File-Session is supposed to save one directory of logs. The Log-File-Session is supposed to save one directory of logs.
It can parse its logs, and should become able to build up its internal It can parse its logs, and should become able to build up its internal
structure into Battle Instances etc. structure into Battle Instances etc.
""" """
VALID_FILES = ['combat.log', 'game.log', 'chat.log' ] # extend this to other logs. VALID_FILES = ['combat.log', 'game.log', 'chat.log' ] # extend this to other logs.
def __init__(self, directory): def __init__(self, directory):
''' if directory is a file, it will be handled as a compressed folder ''' ''' if directory is a file, it will be handled as a compressed folder '''
self.battles = [] self.battles = []
self.user = None self.user = None
self.files_parsed = [] self.files_parsed = []
# various logfiles used. # various logfiles used.
self.combat_log = None self.combat_log = None
self.game_log = None self.game_log = None
self.chat_log = None self.chat_log = None
# self.net_log = None # self.net_log = None
self.directory = directory self.directory = directory
self._zip_source = None self._zip_source = None
self.idstr = None # id string to identify this log instance. self.idstr = None # id string to identify this log instance.
self._error = False self._error = False
def clean(self, remove_log=True): def clean(self, remove_log=True):
if self.combat_log: if self.combat_log:
self.combat_log.clean(remove_log) self.combat_log.clean(remove_log)
if self.game_log: if self.game_log:
self.game_log.clean(remove_log) self.game_log.clean(remove_log)
if self.chat_log: if self.chat_log:
self.chat_log.clean(remove_log) self.chat_log.clean(remove_log)
def validate(self, contents=False): def validate(self, contents=False):
""" """
- validates if the logfiles are within this package. - validates if the logfiles are within this package.
- sets the idstr of this object. - sets the idstr of this object.
@todo: in-depth validation of logs, on contents=True. @todo: in-depth validation of logs, on contents=True.
""" """
self._zip_source = os.path.isfile(self.directory) or False self._zip_source = os.path.isfile(self.directory) or False
v = False v = False
try: try:
if self._zip_source: if self._zip_source:
v = self._unzip_validate() v = self._unzip_validate()
if v > 0: if v > 0:
self.idstr = os.path.split(self.directory)[1].replace('.zip', '').lower() self.idstr = os.path.split(self.directory)[1].replace('.zip', '').lower()
else: else:
v = self._validate_files_exist() v = self._validate_files_exist()
if v > 0: if v > 0:
self.idstr = os.path.split(self.directory)[1].lower() self.idstr = os.path.split(self.directory)[1].lower()
except: except:
return False return False
return v return v
def parse_files(self, files=None): def parse_files(self, files=None):
''' parses the logfiles ''' ''' parses the logfiles '''
# perform simple validation. # perform simple validation.
if self._zip_source is None: if self._zip_source is None:
self.validate(False) self.validate(False)
if self._zip_source: if self._zip_source:
self._unzip_logs(files) self._unzip_logs(files)
else: else:
if files is None: if files is None:
files = self.VALID_FILES files = self.VALID_FILES
if 'combat.log' in files and not 'combat.log' in self.files_parsed: if 'combat.log' in files and not 'combat.log' in self.files_parsed:
self.combat_log = CombatLogFile(os.path.join(self.directory, 'combat.log')) self.combat_log = CombatLogFile(os.path.join(self.directory, 'combat.log'))
self.combat_log.read() self.combat_log.read()
self.combat_log.parse() self.combat_log.parse()
self.files_parsed.append('combat.log') self.files_parsed.append('combat.log')
if 'game.log' in files and not 'game.log' in self.files_parsed: if 'game.log' in files and not 'game.log' in self.files_parsed:
self.game_log = GameLogFile(os.path.join(self.directory, 'game.log')) self.game_log = GameLogFile(os.path.join(self.directory, 'game.log'))
self.game_log.read() self.game_log.read()
self.game_log.parse() self.game_log.parse()
self.files_parsed.append('game.log') self.files_parsed.append('game.log')
if 'chat.log' in files and not 'chat.log' in self.files_parsed: if 'chat.log' in files and not 'chat.log' in self.files_parsed:
self.chat_log = ChatLogFile(os.path.join(self.directory, 'chat.log')) self.chat_log = ChatLogFile(os.path.join(self.directory, 'chat.log'))
self.chat_log.read() self.chat_log.read()
self.chat_log.parse() self.chat_log.parse()
self.files_parsed.append('chat.log') self.files_parsed.append('chat.log')
def determine_owner(self): def determine_owner(self):
''' determines the user in the parsed gamelog ''' ''' determines the user in the parsed gamelog '''
pass pass
def parse_battles(self): def parse_battles(self):
''' parses the battles ''' ''' parses the battles '''
pass pass
def _unzip_logs(self, files=None): def _unzip_logs(self, files=None):
z = zipfile.ZipFile(self.directory, "r") z = zipfile.ZipFile(self.directory, "r")
try: try:
for filename in z.namelist(): for filename in z.namelist():
fn = os.path.split(filename)[1] or '' fn = os.path.split(filename)[1] or ''
fn = fn.lower() fn = fn.lower()
if fn: if fn:
if fn == 'combat.log' and (not files or fn in files) and not 'combat.log' in self.files_parsed: if fn == 'combat.log' and (not files or fn in files) and not 'combat.log' in self.files_parsed:
self.combat_log = CombatLogFile(fn) self.combat_log = CombatLogFile(fn)
self.combat_log.set_data(z.read(filename)) self.combat_log.set_data(z.read(filename))
self.combat_log.parse() self.combat_log.parse()
self.files_parsed.append('combat.log') self.files_parsed.append('combat.log')
elif fn == 'game.log' and (not files or fn in files) and not 'game.log' in self.files_parsed: elif fn == 'game.log' and (not files or fn in files) and not 'game.log' in self.files_parsed:
self.game_log = GameLogFile(fn) self.game_log = GameLogFile(fn)
self.game_log.set_data(z.read(filename)) self.game_log.set_data(z.read(filename))
self.game_log.parse() self.game_log.parse()
self.files_parsed.append('game.log') self.files_parsed.append('game.log')
elif fn == 'chat.log' and (not files or fn in files) and not 'chat.log' in self.files_parsed: elif fn == 'chat.log' and (not files or fn in files) and not 'chat.log' in self.files_parsed:
self.chat_log = ChatLogFile(fn) self.chat_log = ChatLogFile(fn)
self.chat_log.set_data(z.read(filename)) self.chat_log.set_data(z.read(filename))
self.chat_log.parse() self.chat_log.parse()
self.files_parsed.append('chat.log') self.files_parsed.append('chat.log')
except: except:
self._error = True self._error = True
return return
finally: finally:
z.close() z.close()
def _unzip_validate(self): def _unzip_validate(self):
z = zipfile.ZipFile(self.directory, "r") z = zipfile.ZipFile(self.directory, "r")
found = 0 found = 0
for filename in z.namelist(): for filename in z.namelist():
fn = os.path.split(filename)[1] or '' fn = os.path.split(filename)[1] or ''
fn = fn.lower() fn = fn.lower()
if fn and fn in self.VALID_FILES: if fn and fn in self.VALID_FILES:
found += 1 found += 1
z.close() z.close()
return found return found
def _validate_files_exist(self): def _validate_files_exist(self):
found = 0 found = 0
for f in self.VALID_FILES: for f in self.VALID_FILES:
if os.path.exists(os.path.join(self.directory, f)): if os.path.exists(os.path.join(self.directory, f)):
found += 1 found += 1
return found return found
class LogSessionCollector(object): class LogSessionCollector(object):
""" """
finds sessions in a directory, a.k.a. you load the log directories finds sessions in a directory, a.k.a. you load the log directories
of SC into sessions. of SC into sessions.
- find_sessions: only find and instantiate sessions. - find_sessions: only find and instantiate sessions.
- collect: validates each found session and returns them as list. - collect: validates each found session and returns them as list.
- collect_unique: instead of a list, a dict is returned, where each - collect_unique: instead of a list, a dict is returned, where each
session can be accessed via its idstr. does not parse/validate sessions. session can be accessed via its idstr. does not parse/validate sessions.
""" """
def __init__(self, directory): def __init__(self, directory):
self.initial_directory = directory self.initial_directory = directory
self.sessions = [] self.sessions = []
self.find_sessions() self.find_sessions()
def find_sessions(self): def find_sessions(self):
for f in os.listdir(self.initial_directory): for f in os.listdir(self.initial_directory):
full_dir = os.path.join(self.initial_directory, f) full_dir = os.path.join(self.initial_directory, f)
if os.path.isdir(full_dir) or full_dir.lower().endswith('.zip'): if os.path.isdir(full_dir) or full_dir.lower().endswith('.zip'):
self.sessions.append(LogFileSession(full_dir)) self.sessions.append(LogFileSession(full_dir))
def collect(self): def collect(self):
sessions = [] sessions = []
for session in self.sessions: for session in self.sessions:
try: try:
if session.validate(): if session.validate():
sessions.append(session) sessions.append(session)
except: except:
continue continue
return sessions return sessions
def collect_unique(self): def collect_unique(self):
''' collects all sessions into a dictionary ordered by their idstr. ''' collects all sessions into a dictionary ordered by their idstr.
sessions without idstr, or already existing (first served) are omitted sessions without idstr, or already existing (first served) are omitted
parsing is not done. parsing is not done.
''' '''
# note this function resets sessions to the working ones. # note this function resets sessions to the working ones.
self.sessions = self.collect() self.sessions = self.collect()
sessions_dict = {} sessions_dict = {}
for session in self.sessions: for session in self.sessions:
if session.idstr and not session.idstr in sessions_dict.keys(): if session.idstr and not session.idstr in list(sessions_dict.keys()):
sessions_dict[session.idstr] = session sessions_dict[session.idstr] = session
return sessions_dict return sessions_dict
def clean(self, remove_log=True): def clean(self, remove_log=True):
for session in self.sessions: for session in self.sessions:
session.clean(remove_log) session.clean(remove_log)
if __name__ == '__main__': if __name__ == '__main__':
l_raw = LogFileSession('D:\\Users\\g4b\\Documents\\My Games\\sc\\2014.05.17 15.50.28') l_raw = LogFileSession('D:\\Users\\g4b\\Documents\\My Games\\sc\\2014.05.17 15.50.28')
l_zip = LogFileSession('D:\\Users\\g4b\\Documents\\My Games\\sc\\2014.05.20 23.49.19.zip') l_zip = LogFileSession('D:\\Users\\g4b\\Documents\\My Games\\sc\\2014.05.20 23.49.19.zip')
l_zip.parse_files() l_zip.parse_files()
print l_zip.combat_log.lines print((l_zip.combat_log.lines))
collector = LogSessionCollector('D:\\Users\\g4b\\Documents\\My Games\\sc\\') collector = LogSessionCollector('D:\\Users\\g4b\\Documents\\My Games\\sc\\')
print collector.collect_unique() print((collector.collect_unique()))

View File

@ -1,161 +1,161 @@
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Monitor a StarConflict Log Directory. Monitor a StarConflict Log Directory.
""" """
import sys, os import sys, os
import time import time
import logging import logging
from watchdog.observers import Observer from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler from watchdog.events import LoggingEventHandler
class SconEventHandler(LoggingEventHandler): class SconEventHandler(LoggingEventHandler):
def __init__(self, monitor, *args, **kwargs): def __init__(self, monitor, *args, **kwargs):
self.monitor = monitor self.monitor = monitor
return super(SconEventHandler, self).__init__(*args, **kwargs) return super(SconEventHandler, self).__init__(*args, **kwargs)
def on_moved(self, event): def on_moved(self, event):
super(SconEventHandler, self).on_moved(event) super(SconEventHandler, self).on_moved(event)
if not event.is_directory: if not event.is_directory:
self.monitor.close(event.src_path) self.monitor.close(event.src_path)
self.monitor.notify_event('moved', {'src': event.src_path, self.monitor.notify_event('moved', {'src': event.src_path,
'is_dir': event.is_directory, 'is_dir': event.is_directory,
'dest': event.dest_path}) 'dest': event.dest_path})
def on_created(self, event): def on_created(self, event):
super(SconEventHandler, self).on_created(event) super(SconEventHandler, self).on_created(event)
if not event.is_directory: if not event.is_directory:
self.monitor.open(event.src_path) self.monitor.open(event.src_path)
self.monitor.notify_event('created', {'src': event.src_path, self.monitor.notify_event('created', {'src': event.src_path,
'is_dir': event.is_directory}) 'is_dir': event.is_directory})
def on_deleted(self, event): def on_deleted(self, event):
super(SconEventHandler, self).on_deleted(event) super(SconEventHandler, self).on_deleted(event)
if not event.is_directory: if not event.is_directory:
self.monitor.close(event.src_path) self.monitor.close(event.src_path)
self.monitor.notify_event('deleted', {'src': event.src_path, self.monitor.notify_event('deleted', {'src': event.src_path,
'is_dir': event.is_directory}) 'is_dir': event.is_directory})
def on_modified(self, event): def on_modified(self, event):
super(SconEventHandler, self).on_modified(event) super(SconEventHandler, self).on_modified(event)
self.monitor.notify_event('modified', {'src': event.src_path, self.monitor.notify_event('modified', {'src': event.src_path,
'is_dir': event.is_directory}) 'is_dir': event.is_directory})
class SconMonitor(object): class SconMonitor(object):
def notify(self, filename, lines): def notify(self, filename, lines):
# notify somebody, filename has a few lines. # notify somebody, filename has a few lines.
# this is basicly the function you want to overwrite. # this is basicly the function you want to overwrite.
# @see: self.run for how to integrate monitor into your main loop. # @see: self.run for how to integrate monitor into your main loop.
if self.notifier is not None: if self.notifier is not None:
self.notifier.notify(filename, lines) self.notifier.notify(filename, lines)
def notify_event(self, event_type, data): def notify_event(self, event_type, data):
if self.notifier is not None: if self.notifier is not None:
self.notifier.notify_event(event_type, data) self.notifier.notify_event(event_type, data)
def __init__(self, path=None, notifier=None): def __init__(self, path=None, notifier=None):
# if you initialize path directly, you lose success boolean. # if you initialize path directly, you lose success boolean.
# albeit atm. this is always true. # albeit atm. this is always true.
if path is not None: if path is not None:
self.initialize(path) self.initialize(path)
self.notifier = notifier self.notifier = notifier
def initialize(self, path): def initialize(self, path):
# initialize the monitor with a path to observe. # initialize the monitor with a path to observe.
self.files = {} self.files = {}
self.event_handler = SconEventHandler(self) self.event_handler = SconEventHandler(self)
self.observer = Observer() self.observer = Observer()
self.observer.schedule(self.event_handler, self.observer.schedule(self.event_handler,
path, path,
recursive=True) recursive=True)
return True return True
def open(self, filename=None): def open(self, filename=None):
# open a logfile and add it to the read-list... # open a logfile and add it to the read-list...
f = open(filename, 'r') f = open(filename, 'r')
new_file = { 'file': f, new_file = { 'file': f,
'cursor': f.tell() } 'cursor': f.tell() }
self.files[filename] = new_file self.files[filename] = new_file
return new_file return new_file
def close(self, filename): def close(self, filename):
# close a single file by key, does not do anything if not found. # close a single file by key, does not do anything if not found.
if filename in self.files.keys(): if filename in list(self.files.keys()):
close_file = self.files.pop(filename) close_file = self.files.pop(filename)
close_file['file'].close() close_file['file'].close()
del close_file del close_file
def close_all(self): def close_all(self):
""" closes all open files in the monitor """ """ closes all open files in the monitor """
for key in self.files.keys(): for key in list(self.files.keys()):
self.close(key) self.close(key)
def read_line(self, afile): def read_line(self, afile):
# read a single line in a file. # read a single line in a file.
f = afile.get('file', None) f = afile.get('file', None)
if f is None: if f is None:
return return
afile['cursor'] = f.tell() afile['cursor'] = f.tell()
line = f.readline() line = f.readline()
if not line: if not line:
f.seek(afile['cursor']) f.seek(afile['cursor'])
return None return None
else: else:
return line return line
def do(self): def do(self):
''' Monitor main task handler, call this in your mainloop in ~1 sec intervals ''' ''' Monitor main task handler, call this in your mainloop in ~1 sec intervals '''
# read all file changes. # read all file changes.
for key, value in self.files.items(): for key, value in list(self.files.items()):
lines = [] lines = []
data = self.read_line(value) data = self.read_line(value)
while data is not None: while data is not None:
lines.append(data) lines.append(data)
data = self.read_line(value) data = self.read_line(value)
if lines: if lines:
self.notify(key, lines) self.notify(key, lines)
# #
def initialize_loop(self): def initialize_loop(self):
''' initializes the main loop for the monitor ''' ''' initializes the main loop for the monitor '''
self.observer.start() self.observer.start()
def break_loop(self): def break_loop(self):
''' call this if you want to break the monitors tasks ''' ''' call this if you want to break the monitors tasks '''
self.observer.stop() self.observer.stop()
def end_loop(self): def end_loop(self):
''' always call this before exiting your main loop ''' ''' always call this before exiting your main loop '''
self.close_all() self.close_all()
self.observer.join() self.observer.join()
def run(self): def run(self):
''' Basic Standalone Main Loop implementation, do not call this in your app. ''' ''' Basic Standalone Main Loop implementation, do not call this in your app. '''
# if you want to run this on its own. # if you want to run this on its own.
# everytime any logfile is updated, print it. # everytime any logfile is updated, print it.
self.initialize_loop() self.initialize_loop()
try: try:
while True: while True:
self.do() self.do()
time.sleep(1) time.sleep(1)
except KeyboardInterrupt: except KeyboardInterrupt:
self.break_loop() self.break_loop()
self.end_loop() self.end_loop()
if __name__ == '__main__': if __name__ == '__main__':
monitor = SconMonitor() monitor = SconMonitor()
logging.basicConfig(level=logging.INFO, logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(message)s', format='%(asctime)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S') datefmt='%Y-%m-%d %H:%M:%S')
path = os.path.join(os.path.expanduser('~'), path = os.path.join(os.path.expanduser('~'),
'Documents', 'Documents',
'My Games', 'My Games',
'StarConflict', 'StarConflict',
'logs' 'logs'
) )
if monitor.initialize(path) is True: if monitor.initialize(path) is True:
monitor.run() monitor.run()

View File

@ -1,103 +1,103 @@
""" """
Main Entry Point / Exe File for QScon, the main log handler / monitor / manager app for log files. Main Entry Point / Exe File for QScon, the main log handler / monitor / manager app for log files.
""" """
import os, sys, logging import os, sys, logging
import sys import sys
import urllib2 import urllib.request, urllib.error, urllib.parse
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from monitor import SconMonitor from .monitor import SconMonitor
from PyQt4.QtCore import QObject, pyqtSignal, pyqtSlot from PyQt4.QtCore import QObject, pyqtSignal, pyqtSlot
class SconMonitorThread(QtCore.QThread): class SconMonitorThread(QtCore.QThread):
updated = pyqtSignal(str, list) updated = pyqtSignal(str, list)
created = pyqtSignal(str, bool) created = pyqtSignal(str, bool)
def __init__(self, path): def __init__(self, path):
QtCore.QThread.__init__(self) QtCore.QThread.__init__(self)
self.path = path self.path = path
def notify(self, filename, lines): def notify(self, filename, lines):
self.updated.emit(filename, lines) self.updated.emit(filename, lines)
#self.mainwindow.notify_filelines(filename, lines) #self.mainwindow.notify_filelines(filename, lines)
#self.list_widget.addItem('%s\n%s' % (filename, ''.join(lines))) #self.list_widget.addItem('%s\n%s' % (filename, ''.join(lines)))
def notify_event(self, event_type, data): def notify_event(self, event_type, data):
if event_type == 'created': if event_type == 'created':
self.created.emit(data['src'], data['is_dir']) self.created.emit(data['src'], data['is_dir'])
def run(self): def run(self):
monitor = SconMonitor(self.path, notifier=self) monitor = SconMonitor(self.path, notifier=self)
#self.list_widget.addItem('Starting to monitor: %s' % self.path) #self.list_widget.addItem('Starting to monitor: %s' % self.path)
monitor.run() monitor.run()
class MainWindow(QtGui.QWidget): class MainWindow(QtGui.QWidget):
def __init__(self): def __init__(self):
super(MainWindow, self).__init__() super(MainWindow, self).__init__()
self.tab_list = QtGui.QTabWidget() self.tab_list = QtGui.QTabWidget()
self.tabs = {} self.tabs = {}
self.button = QtGui.QPushButton("Start") self.button = QtGui.QPushButton("Start")
self.button.clicked.connect(self.start_monitor) self.button.clicked.connect(self.start_monitor)
layout = QtGui.QVBoxLayout() layout = QtGui.QVBoxLayout()
layout.addWidget(self.button) layout.addWidget(self.button)
layout.addWidget(self.tab_list) layout.addWidget(self.tab_list)
self.setLayout(layout) self.setLayout(layout)
def notify_filelines(self, filename, lines): def notify_filelines(self, filename, lines):
if filename not in self.tabs.keys(): if filename not in list(self.tabs.keys()):
new_tab = QtGui.QWidget() new_tab = QtGui.QWidget()
new_tab.list_widget = QtGui.QListWidget() new_tab.list_widget = QtGui.QListWidget()
layout = QtGui.QVBoxLayout(new_tab) layout = QtGui.QVBoxLayout(new_tab)
layout.addWidget(new_tab.list_widget) layout.addWidget(new_tab.list_widget)
self.tabs[filename] = new_tab self.tabs[filename] = new_tab
self.tab_list.addTab(new_tab, "%s" % os.path.split(str(filename))[-1]) self.tab_list.addTab(new_tab, "%s" % os.path.split(str(filename))[-1])
self.tabs[filename].list_widget.addItem(''.join(lines)[:-1]) self.tabs[filename].list_widget.addItem(''.join(lines)[:-1])
def notify_created(self, filename, is_directory): def notify_created(self, filename, is_directory):
if is_directory: if is_directory:
print "Created Directory %s" % filename print(("Created Directory %s" % filename))
else: else:
print "Created File %s" % filename print(("Created File %s" % filename))
def start_monitor(self): def start_monitor(self):
self.button.setDisabled(True) self.button.setDisabled(True)
paths = [os.path.join(os.path.expanduser('~'),'Documents','My Games','StarConflict','logs'), paths = [os.path.join(os.path.expanduser('~'),'Documents','My Games','StarConflict','logs'),
] ]
self.threads = [] self.threads = []
for path in paths: for path in paths:
athread = SconMonitorThread(path) athread = SconMonitorThread(path)
athread.updated.connect(self.notify_filelines) athread.updated.connect(self.notify_filelines)
athread.created.connect(self.notify_created) athread.created.connect(self.notify_created)
self.threads.append(athread) self.threads.append(athread)
athread.start() athread.start()
######################################################################## ########################################################################
def _main(): def _main():
app = QtGui.QApplication(sys.argv) app = QtGui.QApplication(sys.argv)
window = MainWindow() window = MainWindow()
window.resize(640, 480) window.resize(640, 480)
window.show() window.show()
return app.exec_() return app.exec_()
def main(): def main():
r = _main() r = _main()
try: try:
import psutil #@UnresolvedImport import psutil #@UnresolvedImport
except ImportError: except ImportError:
logging.warning('Cannot import PsUtil, terminating without cleaning up threads explicitly.') logging.warning('Cannot import PsUtil, terminating without cleaning up threads explicitly.')
sys.exit(r) sys.exit(r)
def kill_proc_tree(pid, including_parent=True): def kill_proc_tree(pid, including_parent=True):
parent = psutil.Process(pid) parent = psutil.Process(pid)
if including_parent: if including_parent:
parent.kill() parent.kill()
me = os.getpid() me = os.getpid()
kill_proc_tree(me) kill_proc_tree(me)
sys.exit(r) sys.exit(r)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -22,4 +22,4 @@ R_SCLOG = re.compile(RE_SCLOG)
for line in Lines: for line in Lines:
m = R_SCLOG.match(line) m = R_SCLOG.match(line)
if m: if m:
print m.groups() print((m.groups()))