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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,84 +1,84 @@
import logging, os
try:
from django.conf import settings
except:
logging.error('Django Settings could not be loaded. Maybe Django has not been initialized?')
settings = None
class FolderLibrary(object):
def __init__(self, folders=None):
self._folders = {}
try:
if settings:
self.folders.update( getattr(settings, 'DEJAQT_DIRS', {}) )
except:
logging.error('DEJAQT_DIRS in django settings threw error.')
import traceback
traceback.print_exc()
if folders:
# no try here: if this fails, you got yourself a programming error.
self.folders.update(folders)
self._keys = []
self.build_keycache()
def get_folders(self):
return self._folders
def set_folders(self, folders):
self._folders = folders
self.build_keycache()
folders = property(get_folders, set_folders)
def build_keycache(self):
self._keys = self._folders.keys()
self._keys.sort(key=lambda item: (-len(item), item))
def add_folder(self, url, folder):
if not url:
url = ''
self._folders[url] = folder
self.build_keycache()
def match(self, url):
# run down our keycache, first match wins.
for key in self._keys:
if url.startswith(key):
return key
def matched_folder(self, url):
m = self.match(url)
if m is not None:
folder = self._folders[m]
#heading, rest = url[:len(m)], url[len(m):]
rest = url[len(m):]
real_folder = os.path.abspath( os.path.join(folder, rest) )
if real_folder.startswith(os.path.abspath(folder)):
return real_folder
else:
logging.error('%s does not seem to be a subpath of %s' % (real_folder, folder))
def print_folders(self):
print '{'
for k in self._keys:
print "'%s': '%s'," % (k, self._folders[k])
print '}'
if __name__ == "__main__":
# test this:
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'scon.dj.settings'
f = FolderLibrary({'abc/dab/': 'c:/media',
'abc': 'd:/abc',
'abc/dab/tmp': '/tmp',
'uiuiui': 'x:/',
'abc/vul/no': 'x:/2',
'abc/vul': 'x:/3',
'abc/vul/yes': 'x:/1',
})
f.add_folder('abc/dub/', 'c:/dubdub')
f.print_folders()
print f.matched_folder('abc/dab/okokok/some.png')
import logging, os
try:
from django.conf import settings
except:
logging.error('Django Settings could not be loaded. Maybe Django has not been initialized?')
settings = None
class FolderLibrary(object):
def __init__(self, folders=None):
self._folders = {}
try:
if settings:
self.folders.update( getattr(settings, 'DEJAQT_DIRS', {}) )
except:
logging.error('DEJAQT_DIRS in django settings threw error.')
import traceback
traceback.print_exc()
if folders:
# no try here: if this fails, you got yourself a programming error.
self.folders.update(folders)
self._keys = []
self.build_keycache()
def get_folders(self):
return self._folders
def set_folders(self, folders):
self._folders = folders
self.build_keycache()
folders = property(get_folders, set_folders)
def build_keycache(self):
self._keys = list(self._folders.keys())
self._keys.sort(key=lambda item: (-len(item), item))
def add_folder(self, url, folder):
if not url:
url = ''
self._folders[url] = folder
self.build_keycache()
def match(self, url):
# run down our keycache, first match wins.
for key in self._keys:
if url.startswith(key):
return key
def matched_folder(self, url):
m = self.match(url)
if m is not None:
folder = self._folders[m]
#heading, rest = url[:len(m)], url[len(m):]
rest = url[len(m):]
real_folder = os.path.abspath( os.path.join(folder, rest) )
if real_folder.startswith(os.path.abspath(folder)):
return real_folder
else:
logging.error('%s does not seem to be a subpath of %s' % (real_folder, folder))
def print_folders(self):
print('{')
for k in self._keys:
print(("'%s': '%s'," % (k, self._folders[k])))
print('}')
if __name__ == "__main__":
# test this:
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'scon.dj.settings'
f = FolderLibrary({'abc/dab/': 'c:/media',
'abc': 'd:/abc',
'abc/dab/tmp': '/tmp',
'uiuiui': 'x:/',
'abc/vul/no': 'x:/2',
'abc/vul': 'x:/3',
'abc/vul/yes': 'x:/1',
})
f.add_folder('abc/dub/', 'c:/dubdub')
f.print_folders()
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.
"""
import os, logging
from PyQt4 import QtCore, QtGui, QtWebKit, QtNetwork
from django.test import Client
from folders import FolderLibrary
from django.http.request import QueryDict
from urlparse import urlparse, parse_qs
import cgi
from io import BytesIO
from django.http.multipartparser import MultiPartParser
class DebugPage(QtWebKit.QWebPage):
def sayMyName(self):
return 'DebugPage'
class DejaWebView(QtWebKit.QWebView):
'''
Optional:
* folders: FolderLibrary() Instance.
* page: Initialized QWebPage instance for initial page (default DebugPage())
'''
def __init__(self, *args, **kwargs):
self.folders = kwargs.pop('folders', FolderLibrary())
page = kwargs.pop('page', DebugPage())
QtWebKit.QWebView.__init__(self, *args, **kwargs)
#self.oldManager = self.page().networkAccessManager()
self.setPage(page)
self.page().setNetworkAccessManager(DejaNetworkAccessManager(self))
self.client = Client()
#self.client.login(username='admin', password='admin')
class DejaNetworkAccessManager(QtNetwork.QNetworkAccessManager):
'''
The Deja Network Access Manager provides access to two new protocols:
- page:/// tries to resolve a page internally via a django test client.
- res:/// direct access to a resource.
USE_NETWORK delegates to other network access manager protocols, if False, it will not
allow any requests outside these two protocols
(hopefully disabling network access for your internal browser)
Note, if page does not find the page, a res:/// attempt is made automatically.
This has to be expanded!
Note2: not sure if cookies and sessions will work this way!
'''
USE_NETWORK = False
def __init__(self, parent=None):
QtNetwork.QNetworkAccessManager.__init__(self, parent=parent)
if parent:
self.folders = getattr(parent, 'folders', FolderLibrary())
def createRequest(self, operation, request, data):
scheme = request.url().scheme()
if scheme != 'page' and scheme != 'res':
if self.USE_NETWORK:
return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data)
elif scheme == 'page':
if operation == self.GetOperation:
# Handle page:// URLs separately by creating custom
# QNetworkReply objects.
reply = PageReply(self, request.url(), self.GetOperation)
#print('here')
#print reply
return reply
elif operation == self.PostOperation:
reply = PageReply(self, request.url(), self.PostOperation, request, data)
return reply
elif scheme == 'res':
if operation == self.GetOperation:
return ResourceReply(self, request.url(), self.GetOperation)
else:
if self.USE_NETWORK:
return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data)
return NoNetworkReply(self, request.url(), self.GetOperation)
class BasePageReply(QtNetwork.QNetworkReply):
content_type = 'text/html; charset=utf-8'
def __init__(self, parent, url, operation, request=None, data=None):
QtNetwork.QNetworkReply.__init__(self, parent)
self.data = data
self.request = request
self.content = self.initialize_content(url, operation)
self.offset = 0
self.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, self.get_content_type())
self.setHeader(QtNetwork.QNetworkRequest.ContentLengthHeader, len(self.content))
QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('readyRead()'))
QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('finished()'))
self.open(self.ReadOnly | self.Unbuffered)
self.setUrl(url)
def get_content_type(self):
return self.content_type
def initialize_content(self, url, operation):
return '''
<html>
<head><title>Empty Page</title></head>
<body>This is an empty page. If you see this, you need to subclass BasePageReply.</body>
</html>
'''
def abort(self):
pass
def bytesAvailable(self):
return len(self.content) - self.offset + QtNetwork.QNetworkReply.bytesAvailable(self)
def isSequential(self):
return True
def readData(self, maxSize):
if self.offset < len(self.content):
end = min(self.offset + maxSize, len(self.content))
data = self.content[self.offset:end]
self.offset = end
return data
class ResourceReply(BasePageReply):
content_type = 'image/png'
def determine_content_type(self, path):
return self.content_type
def initialize_content(self, url, operation):
# determine folder:
path = unicode(url.path()).lstrip('/')
folders = getattr(self.parent(), 'folders')
if folders:
path = folders.matched_folder(path)
if path:
if os.path.exists(path):
try:
f = open(path, 'rb')
return f.read()
finally:
f.close()
else:
logging.warning('Path does not exist: %s' % path)
else:
logging.error('Containing Folder not found for %s' % path)
else:
logging.error('Configuration Error: No Folders found.')
return ''
class PageReply(ResourceReply):
content_type = 'text/html'
def initialize_content(self, url, operation):
try:
c = self.parent().parent().client
except:
logging.error('Internal HTTP Client not found. Creating new.')
c = Client()
logging.info( "Response for %s, method %s" % (url.path(), operation) )
if operation == DejaNetworkAccessManager.GetOperation:
response = c.get(unicode(url.path()), follow=True )
elif operation == DejaNetworkAccessManager.PostOperation:
ct = str(self.request.rawHeader('Content-Type'))
cl = str(self.request.rawHeader('Content-Length'))
s = str(self.data.readAll())
if ct.startswith('multipart/form-data'):
# multipart parsing
logging.error('Multipart Parsing Try...')
b = BytesIO(s)
q, files = MultiPartParser({'CONTENT_TYPE': ct,
'CONTENT_LENGTH': cl,
},
b,
[]).parse()
response = c.post(unicode(url.path()), q, follow=True)
else:
# assume post data.
q = QueryDict( s )
response = c.post(unicode(url.path()), q, follow=True)
self.content_type = response.get('Content-Type', self.content_type)
# response code
#print "Response Status: %s" % response.status_code
# note: on a 404, we might need to trigger file response.
if response.status_code == 404:
return ResourceReply.initialize_content(self, url, DejaNetworkAccessManager.GetOperation)
if hasattr(response, 'streaming_content'):
return ''.join(response.streaming_content)
return response.content
class NoNetworkReply(BasePageReply):
def initialize_content(self, url, operation):
return '''
<html>
<head><title>No Network Access.</title></head>
<body>
Internal access to the network has been disabled.
</body>
</html>
'''
"""
Qt WebKit Browser for local access to internal Django Views.
"""
import os, logging
from PyQt4 import QtCore, QtGui, QtWebKit, QtNetwork
from django.test import Client
from .folders import FolderLibrary
from django.http.request import QueryDict
from urllib.parse import urlparse, parse_qs
import cgi
from io import BytesIO
from django.http.multipartparser import MultiPartParser
class DebugPage(QtWebKit.QWebPage):
def sayMyName(self):
return 'DebugPage'
class DejaWebView(QtWebKit.QWebView):
'''
Optional:
* folders: FolderLibrary() Instance.
* page: Initialized QWebPage instance for initial page (default DebugPage())
'''
def __init__(self, *args, **kwargs):
self.folders = kwargs.pop('folders', FolderLibrary())
page = kwargs.pop('page', DebugPage())
QtWebKit.QWebView.__init__(self, *args, **kwargs)
#self.oldManager = self.page().networkAccessManager()
self.setPage(page)
self.page().setNetworkAccessManager(DejaNetworkAccessManager(self))
self.client = Client()
#self.client.login(username='admin', password='admin')
class DejaNetworkAccessManager(QtNetwork.QNetworkAccessManager):
'''
The Deja Network Access Manager provides access to two new protocols:
- page:/// tries to resolve a page internally via a django test client.
- res:/// direct access to a resource.
USE_NETWORK delegates to other network access manager protocols, if False, it will not
allow any requests outside these two protocols
(hopefully disabling network access for your internal browser)
Note, if page does not find the page, a res:/// attempt is made automatically.
This has to be expanded!
Note2: not sure if cookies and sessions will work this way!
'''
USE_NETWORK = False
def __init__(self, parent=None):
QtNetwork.QNetworkAccessManager.__init__(self, parent=parent)
if parent:
self.folders = getattr(parent, 'folders', FolderLibrary())
def createRequest(self, operation, request, data):
scheme = request.url().scheme()
if scheme != 'page' and scheme != 'res':
if self.USE_NETWORK:
return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data)
elif scheme == 'page':
if operation == self.GetOperation:
# Handle page:// URLs separately by creating custom
# QNetworkReply objects.
reply = PageReply(self, request.url(), self.GetOperation)
#print('here')
#print reply
return reply
elif operation == self.PostOperation:
reply = PageReply(self, request.url(), self.PostOperation, request, data)
return reply
elif scheme == 'res':
if operation == self.GetOperation:
return ResourceReply(self, request.url(), self.GetOperation)
else:
if self.USE_NETWORK:
return QtNetwork.QNetworkAccessManager.createRequest(self, operation, request, data)
return NoNetworkReply(self, request.url(), self.GetOperation)
class BasePageReply(QtNetwork.QNetworkReply):
content_type = 'text/html; charset=utf-8'
def __init__(self, parent, url, operation, request=None, data=None):
QtNetwork.QNetworkReply.__init__(self, parent)
self.data = data
self.request = request
self.content = self.initialize_content(url, operation)
self.offset = 0
self.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader, self.get_content_type())
self.setHeader(QtNetwork.QNetworkRequest.ContentLengthHeader, len(self.content))
QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('readyRead()'))
QtCore.QTimer.singleShot(0, self, QtCore.SIGNAL('finished()'))
self.open(self.ReadOnly | self.Unbuffered)
self.setUrl(url)
def get_content_type(self):
return self.content_type
def initialize_content(self, url, operation):
return '''
<html>
<head><title>Empty Page</title></head>
<body>This is an empty page. If you see this, you need to subclass BasePageReply.</body>
</html>
'''
def abort(self):
pass
def bytesAvailable(self):
return len(self.content) - self.offset + QtNetwork.QNetworkReply.bytesAvailable(self)
def isSequential(self):
return True
def readData(self, maxSize):
if self.offset < len(self.content):
end = min(self.offset + maxSize, len(self.content))
data = self.content[self.offset:end]
self.offset = end
return data
class ResourceReply(BasePageReply):
content_type = 'image/png'
def determine_content_type(self, path):
return self.content_type
def initialize_content(self, url, operation):
# determine folder:
path = str(url.path()).lstrip('/')
folders = getattr(self.parent(), 'folders')
if folders:
path = folders.matched_folder(path)
if path:
if os.path.exists(path):
try:
f = open(path, 'rb')
return f.read()
finally:
f.close()
else:
logging.warning('Path does not exist: %s' % path)
else:
logging.error('Containing Folder not found for %s' % path)
else:
logging.error('Configuration Error: No Folders found.')
return ''
class PageReply(ResourceReply):
content_type = 'text/html'
def initialize_content(self, url, operation):
try:
c = self.parent().parent().client
except:
logging.error('Internal HTTP Client not found. Creating new.')
c = Client()
logging.info( "Response for %s, method %s" % (url.path(), operation) )
if operation == DejaNetworkAccessManager.GetOperation:
response = c.get(str(url.path()), follow=True )
elif operation == DejaNetworkAccessManager.PostOperation:
ct = str(self.request.rawHeader('Content-Type'))
cl = str(self.request.rawHeader('Content-Length'))
s = str(self.data.readAll())
if ct.startswith('multipart/form-data'):
# multipart parsing
logging.error('Multipart Parsing Try...')
b = BytesIO(s)
q, files = MultiPartParser({'CONTENT_TYPE': ct,
'CONTENT_LENGTH': cl,
},
b,
[]).parse()
response = c.post(str(url.path()), q, follow=True)
else:
# assume post data.
q = QueryDict( s )
response = c.post(str(url.path()), q, follow=True)
self.content_type = response.get('Content-Type', self.content_type)
# response code
#print "Response Status: %s" % response.status_code
# note: on a 404, we might need to trigger file response.
if response.status_code == 404:
return ResourceReply.initialize_content(self, url, DejaNetworkAccessManager.GetOperation)
if hasattr(response, 'streaming_content'):
return ''.join(response.streaming_content)
return response.content
class NoNetworkReply(BasePageReply):
def initialize_content(self, url, operation):
return '''
<html>
<head><title>No Network Access.</title></head>
<body>
Internal access to the network has been disabled.
</body>
</html>
'''

View File

@ -1,5 +1,5 @@
from django.contrib import admin
import models
from . import models
# Register your models here.
admin.site.register(models.Crafting)
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.http import HttpResponse
from django.template import RequestContext, loader
import logic
import models
from . import logic
from . import models
def config(request):
t = loader.get_template('scon/config.html')

View File

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

View File

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

View File

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

View File

@ -1,89 +1,89 @@
import logging
"""
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.
-> 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.
-> Logfiles is responsible to read whole packs of files, and
-> 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()
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.
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.
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.
-> Note for anyone creating new subclasses for parsing:
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.
__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.
"""
L_CMBT = 'CMBT'
L_WARNING = 'WARNING'
L_NET = 'NET' # Not supported in near future.
L_CHAT = 'CHAT'
class Log(object):
__slots__ = ['matcher', 'trash', 'reviewed']
matcher = None
trash = False
reviewed = False
@classmethod
def is_handler(cls, log):
return False
def unpack(self, force=False):
''' unpacks this log from its data and saves values '''
pass
def explain(self):
''' returns a String readable by humans explaining this Log '''
return ''
def clean(self):
''' tell the log to forget all non-essential data '''
pass
def append(self, something):
''' returns true if this logfile wants an unrecognized log appended to it. '''
return False
class Stacktrace(Log):
''' Special Log to catch error reports '''
def __init__(self, values=None):
super(Stacktrace, self).__init__()
self.message = values or ''
if isinstance(self.message, dict):
self.message = self.message.get('log', '')
#self.trash = True
@classmethod
def is_handler(cls, log):
# do i have a system crash report beginning here?
if isinstance(log, basestring):
l = log.strip()
elif isinstance(log, dict):
l = log.get('log', '').strip()
else:
return False
if l.startswith('Stack trace:') or l.startswith('BitStream::DbgLog'):
return True
def clean(self):
self.message = ''
def append(self, something):
''' I take anything! '''
logging.debug( "EXC: %s" % something )
self.message = '%s\n%s' % (self.message, something)
import logging
"""
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.
-> 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.
-> Logfiles is responsible to read whole packs of files, and
-> 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()
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.
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.
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.
-> Note for anyone creating new subclasses for parsing:
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.
__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.
"""
L_CMBT = 'CMBT'
L_WARNING = 'WARNING'
L_NET = 'NET' # Not supported in near future.
L_CHAT = 'CHAT'
class Log(object):
__slots__ = ['matcher', 'trash', 'reviewed']
matcher = None
trash = False
reviewed = False
@classmethod
def is_handler(cls, log):
return False
def unpack(self, force=False):
''' unpacks this log from its data and saves values '''
pass
def explain(self):
''' returns a String readable by humans explaining this Log '''
return ''
def clean(self):
''' tell the log to forget all non-essential data '''
pass
def append(self, something):
''' returns true if this logfile wants an unrecognized log appended to it. '''
return False
class Stacktrace(Log):
''' Special Log to catch error reports '''
def __init__(self, values=None):
super(Stacktrace, self).__init__()
self.message = values or ''
if isinstance(self.message, dict):
self.message = self.message.get('log', '')
#self.trash = True
@classmethod
def is_handler(cls, log):
# do i have a system crash report beginning here?
if isinstance(log, str):
l = log.strip()
elif isinstance(log, dict):
l = log.get('log', '').strip()
else:
return False
if l.startswith('Stack trace:') or l.startswith('BitStream::DbgLog'):
return True
def clean(self):
self.message = ''
def append(self, something):
''' I take anything! '''
logging.debug( "EXC: %s" % something )
self.message = '%s\n%s' % (self.message, something)
return True

View File

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

View File

@ -1,304 +1,304 @@
# -*- coding: utf-8 -*-
"""
Primary Packets for Combat.Log Files.
This is the most important part for dealing with actual statistics, since every action taken
in a combat instance gets logged here.
------------------------------------
Note:
All logs start with something like
23:53:29.137 | LOGDATA
LOGDATA can be quite different depending on the logfile.
other forms encountered:
23:54:00.600 WARNING|
combat logs:
01:04:38.805 CMBT |
"""
import re
from base import Log, L_CMBT, Stacktrace
import logging
class CombatLog(Log):
__slots__ = Log.__slots__ + [ '_match_id', 'values']
@classmethod
def _log_handler(cls, log):
if log.startswith(cls.__name__):
return True
return False
@classmethod
def is_handler(cls, log):
if log.get('logtype', None) == L_CMBT:
return cls._log_handler(log.get('log', '').strip())
return False
def __init__(self, values=None):
self.values = values or {}
self.reviewed = False
def unpack(self, force=False):
if self.reviewed and not force:
return True
self._match_id = None
# unpacks the data from the values.
if hasattr(self, 'matcher') and self.matcher:
matchers = self.matcher
if not isinstance(matchers, list):
matchers = [matchers,]
for i, matcher in enumerate(matchers):
m = matcher.match(self.values.get('log', ''))
if m:
self.values.update(m.groupdict())
self._match_id = i
self.reviewed = True
return True
# unknown?
if not isinstance(self, UserEvent):
logging.warning('Unknown Packet for %s:\n%s' % (self.__class__.__name__,
self.values.get('log', '')))
# trash if unknown or no matcher.
self.trash = True
def explain(self):
''' returns a String readable by humans explaining this Log '''
return self.values.get('log', 'Unknown Combat Log')
def clean(self):
if 'log' in self.values.keys():
del self.values['log']
# @todo: where does this come from?
class Action(CombatLog):
__slots__ = CombatLog.__slots__
pass
class Gameplay(CombatLog):
__slots__ = CombatLog.__slots__
matcher = [
# 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"),
# 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"),
]
class Apply(CombatLog): # Apply Aura.
__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>[^\']+)'")
class Damage(CombatLog):
__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|\|)+)")
class Spawn(CombatLog):
__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+)'")
class Spell(CombatLog):
__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*)")
class Reward(CombatLog):
__slots__ = CombatLog.__slots__
matcher = [
# 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>.*)"),
# 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>.*)"),
]
class Participant(CombatLog):
__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+)>)")
"""
2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket launch 18912, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', target 'white213mouse' (17894)
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'
2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket launch 18966, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', target 'white213mouse' (17894)
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'
2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket detonation 18892, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', reason 'ttl'
2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket detonation 18931, owner 'optimistik', def 'Weapon_Railgun_Heavy_T5_Epic', reason 'hit'
2017-03-29 13:25:49 - Unknown Packet for Participant:
Participant white213mouse Ship_Race5_M_ATTACK_Rank15
"""
class Rocket(CombatLog):
__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>[^']+)'))+")
class Heal(CombatLog):
__slots__ = CombatLog.__slots__
matcher = [
# 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]+)"),
# 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+))"),
]
class Killed(CombatLog):
__slots__ = CombatLog.__slots__
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<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+.*"),
]
class Captured(CombatLog):
__slots__ = CombatLog.__slots__
matcher = re.compile(r"^Captured\s'(?P<objective>[^']+)'\(team\s(?P<team>\d+)\)\.(?:\sAttackers\:(?P<attackers>.*)|.*)")
class AddStack(CombatLog):
__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+)")
class Cancel(CombatLog):
__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>[^']*)'")
class Scores(CombatLog):
__slots__ = CombatLog.__slots__
matcher = re.compile(r"^Scores\s+-\sTeam1\((?P<team1_score>(?:\d+|\d+\.\d+))\)\sTeam2\((?P<team2_score>(?:\d+|\d+\.\d+))\)")
class Uncaptured(CombatLog):
"""
Variables:
- objective (which was uncaptured (most likely something like VitalPointXY))
- team (number)
- attackers (split by space, names of the attackers, contains bots)
"""
__slots__ = CombatLog.__slots__
matcher = re.compile(r"^Uncaptured\s'(?P<objective>[^']+)'\(team\s(?P<team>\d+)\)\.(?:\sAttackers\:\s(?P<attackers>.*)|)")
# Special classes
class GameEvent(CombatLog):
__slots__ = CombatLog.__slots__
matcher = [
# game session identifier.
re.compile(r"^Connect\sto\sgame\ssession\s+(?P<game_session>\d+)"),
# 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+)"),
# pve mission identifier.
re.compile(r"^Start\sPVE\smission\s'(?P<pve_name>\w+)'\smap\s+'(?P<map_id>\w+)'"),
]
@classmethod
def _log_handler(cls, log):
if log.startswith('======='):
return True
return False
def unpack(self, force=False):
if self.reviewed and not force:
return True
self._match_id = None
# unpacks the data from the values.
# small override to remove trailing "="s in the matching.
if hasattr(self, 'matcher') and self.matcher:
matchers = self.matcher
if not isinstance(matchers, list):
matchers = [matchers,]
for i, matcher in enumerate(matchers):
m = matcher.match(self.values.get('log', '').strip('=').strip())
if m:
self.values.update(m.groupdict())
self._match_id = i
self.reviewed = True
return True
# unknown?
self.trash = True
def clean(self):
if 'log' in self.values.keys():
del self.values['log']
class PVE_Mission(CombatLog):
"""
- mission: contains the mission id.
- message: contains the pve mission message, like starting rounds, waves, etc.
"""
__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.
class Looted(CombatLog):
"""
called on looting in openspace.
- loot contains the loot id.
- container contains the container looted from.
"""
__slots__ = CombatLog.__slots__
matcher = re.compile("^Looted\s'(?P<loot>[^']+)'\sfrom\s'(?P<container>[^']+)'")
class Dropped(CombatLog):
"""
called on dropping in openspace.
- loot contains the loot id. it can be '<all>'
"""
__slots__ = CombatLog.__slots__
matcher = re.compile("^Dropped\sloot\s'(?P<loot>[^']+)'")
class Set(CombatLog):
"""
called on setting "relationship" / OpenSpace
Variables in values:
- what (relationship)
- name (who do i set?)
- value (to what value?)
"""
__slots__ = CombatLog.__slots__
matcher = re.compile("^Set\s(?P<what>\w+)\s(?P<name>[^\s]+)\sto\s(?P<value>\w+)")
class SqIdChange(CombatLog):
""" - number: player number
- name: player name
- old_sqid: sqid of player before
- sqid: new player sqid
"""
__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+)")
@classmethod
def _log_handler(cls, log):
if log.startswith('Player'):
return True
return False
class Mailed(CombatLog):
""" has no information. only that loot has been mailed """
__slots__ = CombatLog.__slots__
matcher = re.compile("Mailed\sloot")
class UserEvent(CombatLog):
""" special class for combat logs that might be associated with the playing player """
__slots__ = CombatLog.__slots__
@classmethod
def _log_handler(cls, log):
if log and 'earned medal' in log:
return True
elif log:
logging.debug('UserEvent saw unknown line:\n%s' % log)
return False
# Action?
COMBAT_LOGS = [ Apply, Damage, Spawn, Spell, Reward, Participant, Rocket, Heal,
Gameplay, #?
Scores,
Killed, Captured, AddStack, Cancel, Uncaptured,
# undone openspace:
PVE_Mission, Looted, Set, Dropped,
SqIdChange, Mailed, # unknown if these are important...
# always last:
GameEvent, UserEvent,
Stacktrace,
]
# -*- coding: utf-8 -*-
"""
Primary Packets for Combat.Log Files.
This is the most important part for dealing with actual statistics, since every action taken
in a combat instance gets logged here.
------------------------------------
Note:
All logs start with something like
23:53:29.137 | LOGDATA
LOGDATA can be quite different depending on the logfile.
other forms encountered:
23:54:00.600 WARNING|
combat logs:
01:04:38.805 CMBT |
"""
import re
from .base import Log, L_CMBT, Stacktrace
import logging
class CombatLog(Log):
__slots__ = Log.__slots__ + [ '_match_id', 'values']
@classmethod
def _log_handler(cls, log):
if log.startswith(cls.__name__):
return True
return False
@classmethod
def is_handler(cls, log):
if log.get('logtype', None) == L_CMBT:
return cls._log_handler(log.get('log', '').strip())
return False
def __init__(self, values=None):
self.values = values or {}
self.reviewed = False
def unpack(self, force=False):
if self.reviewed and not force:
return True
self._match_id = None
# unpacks the data from the values.
if hasattr(self, 'matcher') and self.matcher:
matchers = self.matcher
if not isinstance(matchers, list):
matchers = [matchers,]
for i, matcher in enumerate(matchers):
m = matcher.match(self.values.get('log', ''))
if m:
self.values.update(m.groupdict())
self._match_id = i
self.reviewed = True
return True
# unknown?
if not isinstance(self, UserEvent):
logging.warning('Unknown Packet for %s:\n%s' % (self.__class__.__name__,
self.values.get('log', '')))
# trash if unknown or no matcher.
self.trash = True
def explain(self):
''' returns a String readable by humans explaining this Log '''
return self.values.get('log', 'Unknown Combat Log')
def clean(self):
if 'log' in list(self.values.keys()):
del self.values['log']
# @todo: where does this come from?
class Action(CombatLog):
__slots__ = CombatLog.__slots__
pass
class Gameplay(CombatLog):
__slots__ = CombatLog.__slots__
matcher = [
# 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"),
# 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"),
]
class Apply(CombatLog): # Apply Aura.
__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>[^\']+)'")
class Damage(CombatLog):
__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|\|)+)")
class Spawn(CombatLog):
__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+)'")
class Spell(CombatLog):
__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*)")
class Reward(CombatLog):
__slots__ = CombatLog.__slots__
matcher = [
# 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>.*)"),
# 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>.*)"),
]
class Participant(CombatLog):
__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+)>)")
"""
2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket launch 18912, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', target 'white213mouse' (17894)
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'
2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket launch 18966, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', target 'white213mouse' (17894)
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'
2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket detonation 18892, owner 'LOSNAR', def 'SpaceMissile_Barrage_T5_Mk3', reason 'ttl'
2017-03-29 13:25:49 - Unknown Packet for Rocket:
Rocket detonation 18931, owner 'optimistik', def 'Weapon_Railgun_Heavy_T5_Epic', reason 'hit'
2017-03-29 13:25:49 - Unknown Packet for Participant:
Participant white213mouse Ship_Race5_M_ATTACK_Rank15
"""
class Rocket(CombatLog):
__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>[^']+)'))+")
class Heal(CombatLog):
__slots__ = CombatLog.__slots__
matcher = [
# 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]+)"),
# 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+))"),
]
class Killed(CombatLog):
__slots__ = CombatLog.__slots__
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<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+.*"),
]
class Captured(CombatLog):
__slots__ = CombatLog.__slots__
matcher = re.compile(r"^Captured\s'(?P<objective>[^']+)'\(team\s(?P<team>\d+)\)\.(?:\sAttackers\:(?P<attackers>.*)|.*)")
class AddStack(CombatLog):
__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+)")
class Cancel(CombatLog):
__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>[^']*)'")
class Scores(CombatLog):
__slots__ = CombatLog.__slots__
matcher = re.compile(r"^Scores\s+-\sTeam1\((?P<team1_score>(?:\d+|\d+\.\d+))\)\sTeam2\((?P<team2_score>(?:\d+|\d+\.\d+))\)")
class Uncaptured(CombatLog):
"""
Variables:
- objective (which was uncaptured (most likely something like VitalPointXY))
- team (number)
- attackers (split by space, names of the attackers, contains bots)
"""
__slots__ = CombatLog.__slots__
matcher = re.compile(r"^Uncaptured\s'(?P<objective>[^']+)'\(team\s(?P<team>\d+)\)\.(?:\sAttackers\:\s(?P<attackers>.*)|)")
# Special classes
class GameEvent(CombatLog):
__slots__ = CombatLog.__slots__
matcher = [
# game session identifier.
re.compile(r"^Connect\sto\sgame\ssession\s+(?P<game_session>\d+)"),
# 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+)"),
# pve mission identifier.
re.compile(r"^Start\sPVE\smission\s'(?P<pve_name>\w+)'\smap\s+'(?P<map_id>\w+)'"),
]
@classmethod
def _log_handler(cls, log):
if log.startswith('======='):
return True
return False
def unpack(self, force=False):
if self.reviewed and not force:
return True
self._match_id = None
# unpacks the data from the values.
# small override to remove trailing "="s in the matching.
if hasattr(self, 'matcher') and self.matcher:
matchers = self.matcher
if not isinstance(matchers, list):
matchers = [matchers,]
for i, matcher in enumerate(matchers):
m = matcher.match(self.values.get('log', '').strip('=').strip())
if m:
self.values.update(m.groupdict())
self._match_id = i
self.reviewed = True
return True
# unknown?
self.trash = True
def clean(self):
if 'log' in list(self.values.keys()):
del self.values['log']
class PVE_Mission(CombatLog):
"""
- mission: contains the mission id.
- message: contains the pve mission message, like starting rounds, waves, etc.
"""
__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.
class Looted(CombatLog):
"""
called on looting in openspace.
- loot contains the loot id.
- container contains the container looted from.
"""
__slots__ = CombatLog.__slots__
matcher = re.compile("^Looted\s'(?P<loot>[^']+)'\sfrom\s'(?P<container>[^']+)'")
class Dropped(CombatLog):
"""
called on dropping in openspace.
- loot contains the loot id. it can be '<all>'
"""
__slots__ = CombatLog.__slots__
matcher = re.compile("^Dropped\sloot\s'(?P<loot>[^']+)'")
class Set(CombatLog):
"""
called on setting "relationship" / OpenSpace
Variables in values:
- what (relationship)
- name (who do i set?)
- value (to what value?)
"""
__slots__ = CombatLog.__slots__
matcher = re.compile("^Set\s(?P<what>\w+)\s(?P<name>[^\s]+)\sto\s(?P<value>\w+)")
class SqIdChange(CombatLog):
""" - number: player number
- name: player name
- old_sqid: sqid of player before
- sqid: new player sqid
"""
__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+)")
@classmethod
def _log_handler(cls, log):
if log.startswith('Player'):
return True
return False
class Mailed(CombatLog):
""" has no information. only that loot has been mailed """
__slots__ = CombatLog.__slots__
matcher = re.compile("Mailed\sloot")
class UserEvent(CombatLog):
""" special class for combat logs that might be associated with the playing player """
__slots__ = CombatLog.__slots__
@classmethod
def _log_handler(cls, log):
if log and 'earned medal' in log:
return True
elif log:
logging.debug('UserEvent saw unknown line:\n%s' % log)
return False
# Action?
COMBAT_LOGS = [ Apply, Damage, Spawn, Spell, Reward, Participant, Rocket, Heal,
Gameplay, #?
Scores,
Killed, Captured, AddStack, Cancel, Uncaptured,
# undone openspace:
PVE_Mission, Looted, Set, Dropped,
SqIdChange, Mailed, # unknown if these are important...
# always last:
GameEvent, UserEvent,
Stacktrace,
]

View File

@ -1,195 +1,195 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from logs.base import Log, L_WARNING, Stacktrace
import re
"""
This deals with the Game.Log file
This file records lots of junk, but is needed to establish actions taken between combat sessions,
or retrieve more detailed information about running instances.
It is also the typical place for a Stacktrace to happen.
--------------------------------------
Interesting Lines:
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:38.822 | ====== level started: 'levels/mainmenu/mainmenu' success ======
23:16:44.251 | ====== starting level: 'levels\mainmenu\mm_empire' ======
23:16:46.464 | ====== level started: 'levels\mainmenu\mm_empire' success ======
--- 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.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.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 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 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 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 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 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 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 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 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: server assigned id 2
23:55:55.886 | client: got level load message 's1340_thar_aliendebris13'
23:55:55.889 | reset d3d device
23:55:56.487 | ReplayManager: stopping activity due to map change
23:55:56.576 | ====== starting level: 'levels\area2\s1340_thar_aliendebris13' KingOfTheHill client ======
"""
class GameLog(Log):
__slots__ = ['matcher', 'trash', '_match_id', 'values']
@classmethod
def is_handler(cls, log):
if log.get('logtype', None) == '': # we handle only logs with empty logtype.
return cls._is_handler(log)
return False
@classmethod
def _is_handler(cls, log):
return False
def __init__(self, values=None):
self.values = values
self.reviewed = False
def clean(self):
if 'log' in self.values.keys():
del self.values['log']
def unpack(self, force=False):
if self.reviewed and not force:
return True
self._match_id = None
# unpacks the data from the values.
if hasattr(self, 'matcher') and self.matcher:
matchers = self.matcher
if not isinstance(matchers, list):
matchers = [matchers,]
for i, matcher in enumerate(matchers):
m = matcher.match(self.values.get('log', ''))
if m:
self.values.update(m.groupdict())
self._match_id = i
self.reviewed = True
return True
# unknown?
self.trash = True
def explain(self):
''' returns a String readable by humans explaining this Log '''
return self.values.get('log', 'Unknown Game Log')
class WarningLog(Log):
__slots__ = ['trash',]
trash = True
@classmethod
def is_handler(cls, log):
if log.get('logtype', None) == L_WARNING:
return True
return False
def __init__(self, values=None):
pass
########################################################################################################
# Individual logs.
class SteamInitialization(GameLog):
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>[^']+)'"),
]
class MasterServerSession(GameLog):
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\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
def _is_handler(cls, log):
if log.get('log', '').startswith('MasterServerSession'):
return True
return False
class ClientInfo(GameLog):
# Note: clinfo holds the subtype of this packet.
matcher = [
# connecting; addr, port
re.compile(r"^client\:\sstart\s(?P<clinfo>connecting)\sto\s(?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+)\.\.\."),
# connected; addr, port
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
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
re.compile(r"^client\:\sserver\s(?P<clinfo>assigned)\sid\s(?P<myid>\d+)"),
# level; level
re.compile(r"^client\:\sgot\s(?P<clinfo>level)\sload\smessage\s'(?P<level>[^']+)'"),
# leave; pnr
re.compile(r"^client\:\splayer\s(?P<pnr>\d+)\s(?P<clinfo>leave)\sgame"),
# 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>[^;$]+))+"),
# closed; dr
re.compile(r"^client\:\sconnection\s(?P<clinfo>closed)\.(?:\s|(?P<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+))+"),
# ready;
re.compile(r"^client\:\ssend\s(?P<clinfo>ready)\smessage"),
# init; ping
re.compile(r"^client\:\sgot\s(?P<clinfo>init)\smessage\s+\(and\s1st\ssnapshot\)\.\sping\s(?P<ping>\d+)"),
]
@classmethod
def _is_handler(cls, log):
if log.get('log', '').startswith('client:'):
return True
return False
class StartingLevel(GameLog):
# level, gametype, unknown_gametype
matcher = [
re.compile(r"^======\sstarting\slevel\:\s'(?P<level>[^']+)'(?:\s|client|(?P<gametype>KingOfTheHill)|(?P<unknown_gametype>[^\s]+))+======"),
]
@classmethod
def _is_handler(cls, log):
if log.get('log', '').startswith('====== starting'):
return True
return False
class LevelStarted(GameLog):
matcher = []
@classmethod
def _is_handler(cls, log):
if log.get('log', '').startswith('====== level'):
return True
return False
GAME_LOGS = [#SteamInitialization,
MasterServerSession,
ClientInfo,
StartingLevel,
#LevelStarted,
Stacktrace,
#!/usr/bin/python
# -*- coding: utf-8 -*-
from logs.base import Log, L_WARNING, Stacktrace
import re
"""
This deals with the Game.Log file
This file records lots of junk, but is needed to establish actions taken between combat sessions,
or retrieve more detailed information about running instances.
It is also the typical place for a Stacktrace to happen.
--------------------------------------
Interesting Lines:
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:38.822 | ====== level started: 'levels/mainmenu/mainmenu' success ======
23:16:44.251 | ====== starting level: 'levels\mainmenu\mm_empire' ======
23:16:46.464 | ====== level started: 'levels\mainmenu\mm_empire' success ======
--- 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.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.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 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 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 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 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 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 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 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 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: server assigned id 2
23:55:55.886 | client: got level load message 's1340_thar_aliendebris13'
23:55:55.889 | reset d3d device
23:55:56.487 | ReplayManager: stopping activity due to map change
23:55:56.576 | ====== starting level: 'levels\area2\s1340_thar_aliendebris13' KingOfTheHill client ======
"""
class GameLog(Log):
__slots__ = ['matcher', 'trash', '_match_id', 'values']
@classmethod
def is_handler(cls, log):
if log.get('logtype', None) == '': # we handle only logs with empty logtype.
return cls._is_handler(log)
return False
@classmethod
def _is_handler(cls, log):
return False
def __init__(self, values=None):
self.values = values
self.reviewed = False
def clean(self):
if 'log' in list(self.values.keys()):
del self.values['log']
def unpack(self, force=False):
if self.reviewed and not force:
return True
self._match_id = None
# unpacks the data from the values.
if hasattr(self, 'matcher') and self.matcher:
matchers = self.matcher
if not isinstance(matchers, list):
matchers = [matchers,]
for i, matcher in enumerate(matchers):
m = matcher.match(self.values.get('log', ''))
if m:
self.values.update(m.groupdict())
self._match_id = i
self.reviewed = True
return True
# unknown?
self.trash = True
def explain(self):
''' returns a String readable by humans explaining this Log '''
return self.values.get('log', 'Unknown Game Log')
class WarningLog(Log):
__slots__ = ['trash',]
trash = True
@classmethod
def is_handler(cls, log):
if log.get('logtype', None) == L_WARNING:
return True
return False
def __init__(self, values=None):
pass
########################################################################################################
# Individual logs.
class SteamInitialization(GameLog):
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>[^']+)'"),
]
class MasterServerSession(GameLog):
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\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
def _is_handler(cls, log):
if log.get('log', '').startswith('MasterServerSession'):
return True
return False
class ClientInfo(GameLog):
# Note: clinfo holds the subtype of this packet.
matcher = [
# connecting; addr, port
re.compile(r"^client\:\sstart\s(?P<clinfo>connecting)\sto\s(?P<addr>\d+\.\d+\.\d+\.\d+)\|(?P<port>\d+)\.\.\."),
# connected; addr, port
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
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
re.compile(r"^client\:\sserver\s(?P<clinfo>assigned)\sid\s(?P<myid>\d+)"),
# level; level
re.compile(r"^client\:\sgot\s(?P<clinfo>level)\sload\smessage\s'(?P<level>[^']+)'"),
# leave; pnr
re.compile(r"^client\:\splayer\s(?P<pnr>\d+)\s(?P<clinfo>leave)\sgame"),
# 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>[^;$]+))+"),
# closed; dr
re.compile(r"^client\:\sconnection\s(?P<clinfo>closed)\.(?:\s|(?P<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+))+"),
# ready;
re.compile(r"^client\:\ssend\s(?P<clinfo>ready)\smessage"),
# init; ping
re.compile(r"^client\:\sgot\s(?P<clinfo>init)\smessage\s+\(and\s1st\ssnapshot\)\.\sping\s(?P<ping>\d+)"),
]
@classmethod
def _is_handler(cls, log):
if log.get('log', '').startswith('client:'):
return True
return False
class StartingLevel(GameLog):
# level, gametype, unknown_gametype
matcher = [
re.compile(r"^======\sstarting\slevel\:\s'(?P<level>[^']+)'(?:\s|client|(?P<gametype>KingOfTheHill)|(?P<unknown_gametype>[^\s]+))+======"),
]
@classmethod
def _is_handler(cls, log):
if log.get('log', '').startswith('====== starting'):
return True
return False
class LevelStarted(GameLog):
matcher = []
@classmethod
def _is_handler(cls, log):
if log.get('log', '').startswith('====== level'):
return True
return False
GAME_LOGS = [#SteamInitialization,
MasterServerSession,
ClientInfo,
StartingLevel,
#LevelStarted,
Stacktrace,
]

View File

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

View File

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

View File

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

View File

@ -1,161 +1,161 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Monitor a StarConflict Log Directory.
"""
import sys, os
import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler
class SconEventHandler(LoggingEventHandler):
def __init__(self, monitor, *args, **kwargs):
self.monitor = monitor
return super(SconEventHandler, self).__init__(*args, **kwargs)
def on_moved(self, event):
super(SconEventHandler, self).on_moved(event)
if not event.is_directory:
self.monitor.close(event.src_path)
self.monitor.notify_event('moved', {'src': event.src_path,
'is_dir': event.is_directory,
'dest': event.dest_path})
def on_created(self, event):
super(SconEventHandler, self).on_created(event)
if not event.is_directory:
self.monitor.open(event.src_path)
self.monitor.notify_event('created', {'src': event.src_path,
'is_dir': event.is_directory})
def on_deleted(self, event):
super(SconEventHandler, self).on_deleted(event)
if not event.is_directory:
self.monitor.close(event.src_path)
self.monitor.notify_event('deleted', {'src': event.src_path,
'is_dir': event.is_directory})
def on_modified(self, event):
super(SconEventHandler, self).on_modified(event)
self.monitor.notify_event('modified', {'src': event.src_path,
'is_dir': event.is_directory})
class SconMonitor(object):
def notify(self, filename, lines):
# notify somebody, filename has a few lines.
# this is basicly the function you want to overwrite.
# @see: self.run for how to integrate monitor into your main loop.
if self.notifier is not None:
self.notifier.notify(filename, lines)
def notify_event(self, event_type, data):
if self.notifier is not None:
self.notifier.notify_event(event_type, data)
def __init__(self, path=None, notifier=None):
# if you initialize path directly, you lose success boolean.
# albeit atm. this is always true.
if path is not None:
self.initialize(path)
self.notifier = notifier
def initialize(self, path):
# initialize the monitor with a path to observe.
self.files = {}
self.event_handler = SconEventHandler(self)
self.observer = Observer()
self.observer.schedule(self.event_handler,
path,
recursive=True)
return True
def open(self, filename=None):
# open a logfile and add it to the read-list...
f = open(filename, 'r')
new_file = { 'file': f,
'cursor': f.tell() }
self.files[filename] = new_file
return new_file
def close(self, filename):
# close a single file by key, does not do anything if not found.
if filename in self.files.keys():
close_file = self.files.pop(filename)
close_file['file'].close()
del close_file
def close_all(self):
""" closes all open files in the monitor """
for key in self.files.keys():
self.close(key)
def read_line(self, afile):
# read a single line in a file.
f = afile.get('file', None)
if f is None:
return
afile['cursor'] = f.tell()
line = f.readline()
if not line:
f.seek(afile['cursor'])
return None
else:
return line
def do(self):
''' Monitor main task handler, call this in your mainloop in ~1 sec intervals '''
# read all file changes.
for key, value in self.files.items():
lines = []
data = self.read_line(value)
while data is not None:
lines.append(data)
data = self.read_line(value)
if lines:
self.notify(key, lines)
#
def initialize_loop(self):
''' initializes the main loop for the monitor '''
self.observer.start()
def break_loop(self):
''' call this if you want to break the monitors tasks '''
self.observer.stop()
def end_loop(self):
''' always call this before exiting your main loop '''
self.close_all()
self.observer.join()
def run(self):
''' Basic Standalone Main Loop implementation, do not call this in your app. '''
# if you want to run this on its own.
# everytime any logfile is updated, print it.
self.initialize_loop()
try:
while True:
self.do()
time.sleep(1)
except KeyboardInterrupt:
self.break_loop()
self.end_loop()
if __name__ == '__main__':
monitor = SconMonitor()
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
path = os.path.join(os.path.expanduser('~'),
'Documents',
'My Games',
'StarConflict',
'logs'
)
if monitor.initialize(path) is True:
monitor.run()
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Monitor a StarConflict Log Directory.
"""
import sys, os
import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler
class SconEventHandler(LoggingEventHandler):
def __init__(self, monitor, *args, **kwargs):
self.monitor = monitor
return super(SconEventHandler, self).__init__(*args, **kwargs)
def on_moved(self, event):
super(SconEventHandler, self).on_moved(event)
if not event.is_directory:
self.monitor.close(event.src_path)
self.monitor.notify_event('moved', {'src': event.src_path,
'is_dir': event.is_directory,
'dest': event.dest_path})
def on_created(self, event):
super(SconEventHandler, self).on_created(event)
if not event.is_directory:
self.monitor.open(event.src_path)
self.monitor.notify_event('created', {'src': event.src_path,
'is_dir': event.is_directory})
def on_deleted(self, event):
super(SconEventHandler, self).on_deleted(event)
if not event.is_directory:
self.monitor.close(event.src_path)
self.monitor.notify_event('deleted', {'src': event.src_path,
'is_dir': event.is_directory})
def on_modified(self, event):
super(SconEventHandler, self).on_modified(event)
self.monitor.notify_event('modified', {'src': event.src_path,
'is_dir': event.is_directory})
class SconMonitor(object):
def notify(self, filename, lines):
# notify somebody, filename has a few lines.
# this is basicly the function you want to overwrite.
# @see: self.run for how to integrate monitor into your main loop.
if self.notifier is not None:
self.notifier.notify(filename, lines)
def notify_event(self, event_type, data):
if self.notifier is not None:
self.notifier.notify_event(event_type, data)
def __init__(self, path=None, notifier=None):
# if you initialize path directly, you lose success boolean.
# albeit atm. this is always true.
if path is not None:
self.initialize(path)
self.notifier = notifier
def initialize(self, path):
# initialize the monitor with a path to observe.
self.files = {}
self.event_handler = SconEventHandler(self)
self.observer = Observer()
self.observer.schedule(self.event_handler,
path,
recursive=True)
return True
def open(self, filename=None):
# open a logfile and add it to the read-list...
f = open(filename, 'r')
new_file = { 'file': f,
'cursor': f.tell() }
self.files[filename] = new_file
return new_file
def close(self, filename):
# close a single file by key, does not do anything if not found.
if filename in list(self.files.keys()):
close_file = self.files.pop(filename)
close_file['file'].close()
del close_file
def close_all(self):
""" closes all open files in the monitor """
for key in list(self.files.keys()):
self.close(key)
def read_line(self, afile):
# read a single line in a file.
f = afile.get('file', None)
if f is None:
return
afile['cursor'] = f.tell()
line = f.readline()
if not line:
f.seek(afile['cursor'])
return None
else:
return line
def do(self):
''' Monitor main task handler, call this in your mainloop in ~1 sec intervals '''
# read all file changes.
for key, value in list(self.files.items()):
lines = []
data = self.read_line(value)
while data is not None:
lines.append(data)
data = self.read_line(value)
if lines:
self.notify(key, lines)
#
def initialize_loop(self):
''' initializes the main loop for the monitor '''
self.observer.start()
def break_loop(self):
''' call this if you want to break the monitors tasks '''
self.observer.stop()
def end_loop(self):
''' always call this before exiting your main loop '''
self.close_all()
self.observer.join()
def run(self):
''' Basic Standalone Main Loop implementation, do not call this in your app. '''
# if you want to run this on its own.
# everytime any logfile is updated, print it.
self.initialize_loop()
try:
while True:
self.do()
time.sleep(1)
except KeyboardInterrupt:
self.break_loop()
self.end_loop()
if __name__ == '__main__':
monitor = SconMonitor()
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
path = os.path.join(os.path.expanduser('~'),
'Documents',
'My Games',
'StarConflict',
'logs'
)
if monitor.initialize(path) is True:
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.
"""
import os, sys, logging
import sys
import urllib2
from PyQt4 import QtCore, QtGui
from monitor import SconMonitor
from PyQt4.QtCore import QObject, pyqtSignal, pyqtSlot
class SconMonitorThread(QtCore.QThread):
updated = pyqtSignal(str, list)
created = pyqtSignal(str, bool)
def __init__(self, path):
QtCore.QThread.__init__(self)
self.path = path
def notify(self, filename, lines):
self.updated.emit(filename, lines)
#self.mainwindow.notify_filelines(filename, lines)
#self.list_widget.addItem('%s\n%s' % (filename, ''.join(lines)))
def notify_event(self, event_type, data):
if event_type == 'created':
self.created.emit(data['src'], data['is_dir'])
def run(self):
monitor = SconMonitor(self.path, notifier=self)
#self.list_widget.addItem('Starting to monitor: %s' % self.path)
monitor.run()
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.tab_list = QtGui.QTabWidget()
self.tabs = {}
self.button = QtGui.QPushButton("Start")
self.button.clicked.connect(self.start_monitor)
layout = QtGui.QVBoxLayout()
layout.addWidget(self.button)
layout.addWidget(self.tab_list)
self.setLayout(layout)
def notify_filelines(self, filename, lines):
if filename not in self.tabs.keys():
new_tab = QtGui.QWidget()
new_tab.list_widget = QtGui.QListWidget()
layout = QtGui.QVBoxLayout(new_tab)
layout.addWidget(new_tab.list_widget)
self.tabs[filename] = new_tab
self.tab_list.addTab(new_tab, "%s" % os.path.split(str(filename))[-1])
self.tabs[filename].list_widget.addItem(''.join(lines)[:-1])
def notify_created(self, filename, is_directory):
if is_directory:
print "Created Directory %s" % filename
else:
print "Created File %s" % filename
def start_monitor(self):
self.button.setDisabled(True)
paths = [os.path.join(os.path.expanduser('~'),'Documents','My Games','StarConflict','logs'),
]
self.threads = []
for path in paths:
athread = SconMonitorThread(path)
athread.updated.connect(self.notify_filelines)
athread.created.connect(self.notify_created)
self.threads.append(athread)
athread.start()
########################################################################
def _main():
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.resize(640, 480)
window.show()
return app.exec_()
def main():
r = _main()
try:
import psutil #@UnresolvedImport
except ImportError:
logging.warning('Cannot import PsUtil, terminating without cleaning up threads explicitly.')
sys.exit(r)
def kill_proc_tree(pid, including_parent=True):
parent = psutil.Process(pid)
if including_parent:
parent.kill()
me = os.getpid()
kill_proc_tree(me)
sys.exit(r)
if __name__ == "__main__":
main()
"""
Main Entry Point / Exe File for QScon, the main log handler / monitor / manager app for log files.
"""
import os, sys, logging
import sys
import urllib.request, urllib.error, urllib.parse
from PyQt4 import QtCore, QtGui
from .monitor import SconMonitor
from PyQt4.QtCore import QObject, pyqtSignal, pyqtSlot
class SconMonitorThread(QtCore.QThread):
updated = pyqtSignal(str, list)
created = pyqtSignal(str, bool)
def __init__(self, path):
QtCore.QThread.__init__(self)
self.path = path
def notify(self, filename, lines):
self.updated.emit(filename, lines)
#self.mainwindow.notify_filelines(filename, lines)
#self.list_widget.addItem('%s\n%s' % (filename, ''.join(lines)))
def notify_event(self, event_type, data):
if event_type == 'created':
self.created.emit(data['src'], data['is_dir'])
def run(self):
monitor = SconMonitor(self.path, notifier=self)
#self.list_widget.addItem('Starting to monitor: %s' % self.path)
monitor.run()
class MainWindow(QtGui.QWidget):
def __init__(self):
super(MainWindow, self).__init__()
self.tab_list = QtGui.QTabWidget()
self.tabs = {}
self.button = QtGui.QPushButton("Start")
self.button.clicked.connect(self.start_monitor)
layout = QtGui.QVBoxLayout()
layout.addWidget(self.button)
layout.addWidget(self.tab_list)
self.setLayout(layout)
def notify_filelines(self, filename, lines):
if filename not in list(self.tabs.keys()):
new_tab = QtGui.QWidget()
new_tab.list_widget = QtGui.QListWidget()
layout = QtGui.QVBoxLayout(new_tab)
layout.addWidget(new_tab.list_widget)
self.tabs[filename] = new_tab
self.tab_list.addTab(new_tab, "%s" % os.path.split(str(filename))[-1])
self.tabs[filename].list_widget.addItem(''.join(lines)[:-1])
def notify_created(self, filename, is_directory):
if is_directory:
print(("Created Directory %s" % filename))
else:
print(("Created File %s" % filename))
def start_monitor(self):
self.button.setDisabled(True)
paths = [os.path.join(os.path.expanduser('~'),'Documents','My Games','StarConflict','logs'),
]
self.threads = []
for path in paths:
athread = SconMonitorThread(path)
athread.updated.connect(self.notify_filelines)
athread.created.connect(self.notify_created)
self.threads.append(athread)
athread.start()
########################################################################
def _main():
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.resize(640, 480)
window.show()
return app.exec_()
def main():
r = _main()
try:
import psutil #@UnresolvedImport
except ImportError:
logging.warning('Cannot import PsUtil, terminating without cleaning up threads explicitly.')
sys.exit(r)
def kill_proc_tree(pid, including_parent=True):
parent = psutil.Process(pid)
if including_parent:
parent.kill()
me = os.getpid()
kill_proc_tree(me)
sys.exit(r)
if __name__ == "__main__":
main()

View File

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