introduce ResultStore to keep track of game sessions

simu_flags
agp8x 2017-09-18 15:08:38 +02:00
parent c25deecf0b
commit 70f72e6162
8 changed files with 172 additions and 71 deletions

View File

@ -1,15 +1,17 @@
from typing import List
from .analyzer import Analyzer, Result
from .analyzer.biogames import BoardDurationAnalyzer, SimulationRoundsAnalyzer, ActivationSequenceAnalyzer
from .analyzer.default import LogEntryCountAnalyzer, LocationAnalyzer, LogEntrySequenceAnalyzer, ActionSequenceAnalyzer
from .analyzer.biogames import BoardDurationAnalyzer, SimulationRoundsAnalyzer, ActivationSequenceAnalyzer, \
BiogamesCategorizer
from .analyzer.default import LogEntryCountAnalyzer, LocationAnalyzer, LogEntrySequenceAnalyzer, ActionSequenceAnalyzer, \
CategorizerStub
from .analyzer.locomotion import LocomotionActionAnalyzer, CacheSequenceAnalyzer
from .analyzer.mask import MaskSpatials
from .render import Render
from .render.biogames import SimulationRoundsRender, BoardDurationHistRender, BoardDurationBoxRender
from .render.default import PrintRender, JSONRender, TrackRender, HeatMapRender
from .render.locomotion import LocomotionActionRelativeRender, LocomotionActionAbsoluteRender, \
LocomotionActionRatioRender
from .render.biogames import SimulationRoundsRender, BoardDurationHistRender, BoardDurationBoxRender
__FALLBACK__ = PrintRender
__MAPPING__ = {

View File

@ -1,11 +1,14 @@
from analyzers.settings import LogSettings
import logging
from collections import KeysView
from typing import Type, Sized, Collection
log = logging.getLogger(__name__)
from analyzers.settings import LogSettings
log: logging.Logger = logging.getLogger(__name__)
class Result:
def __init__(self, analysis, result):
def __init__(self, analysis: Type, result: Sized):
self.result = result
self.__analysis__ = analysis
log.debug("set" + str(len(self.result)))
@ -18,18 +21,70 @@ class Result:
return self.result
def __repr__(self):
return "<Result " + str(self.__analysis__) + ": " + str(type(self.result)) + " "+str(len(self.result))+">"
return "<Result " + str(self.__analysis__) + ": " + str(type(self.result)) + " " + str(len(self.result)) + ">"
class ResultStore:
"""Store Results"""
def __init__(self, store_entry: Type[Collection] = list, store_action: callable = list.append) -> None:
self.store = {}
self.category = None
self.entry: Type[Collection] = store_entry
self.action: callable = store_action
def new_category(self, key) -> None:
self.category = key
if not key in self.store:
self.store[key] = self.entry()
def add(self, entry: Result) -> None:
self.action(self.store[self.category], entry)
def get_store(self) -> dict:
return dict(self.store)
def get_all(self) -> list:
"""
Throw all categories together
:return:
"""
result = []
for key in self.store:
result += self.store[key]
return result
def get_categories(self) -> KeysView:
return self.store.keys()
def get_category(self, key):
if key not in self.store:
return self.entry
return self.store[key]
def serializable(self):
values = {}
for key in self.store:
values[key] = [{"analysis": str(result.analysis()), "result": result.get()} for result in self.store[key]]
return values
class Analyzer:
def __init__(self, settings: LogSettings):
self.settings = settings
"""Operate on log entries, one at a time"""
def __init__(self, settings: LogSettings) -> None:
self.settings: LogSettings = settings
def process(self, entry: dict) -> bool:
"""
Process an entry
:param entry: Entry to process
:return: True if consumed, False for further analysis
"""
raise NotImplementedError()
def result(self) -> Result:
def result(self, store: ResultStore) -> None:
raise NotImplementedError()
def name(self):
def name(self) -> str:
return self.__name__

View File

@ -1,7 +1,8 @@
import logging
from collections import defaultdict
from . import Result, LogSettings, Analyzer
from . import Result, LogSettings, Analyzer, ResultStore
from .default import CategorizerStub
logger = logging.getLogger(__name__)
@ -15,7 +16,7 @@ class BoardDurationAnalyzer(Analyzer):
def render(self) -> str:
return "\n".join(["{}\t{}".format(entry["active"], entry["id"]) for entry in self.result().get()])
def result(self) -> Result:
def result(self, store: ResultStore) -> None:
result = []
last_timestamp = None
last_board = None
@ -27,7 +28,7 @@ class BoardDurationAnalyzer(Analyzer):
last_timestamp = timestamp
last_board = board_id
# TODO: last board?
return Result(type(self), result)
store.add(Result(type(self), result))
def process(self, entry: dict) -> bool:
entry_type = entry[self.settings.type_field]
@ -54,8 +55,8 @@ class SimulationRoundsAnalyzer(Analyzer):
super().__init__(settings)
self.store = defaultdict(lambda: -1) # TODO verify
def result(self) -> Result:
return Result(type(self), dict(self.store))
def result(self, store: ResultStore) -> None:
store.add(Result(type(self), dict(self.store)))
def process(self, entry: dict) -> bool:
entry_type = entry[self.settings.type_field]
@ -73,8 +74,8 @@ class ActivationSequenceAnalyzer(Analyzer):
super().__init__(settings)
self.store = []
def result(self) -> Result:
return Result(type(self), self.store)
def result(self, store: ResultStore) -> None:
store.add(Result(type(self), self.store))
def process(self, entry: dict) -> bool:
if entry[self.settings.type_field] in self.settings.sequences['start']:
@ -82,3 +83,15 @@ class ActivationSequenceAnalyzer(Analyzer):
self.store.append(entry['cache']['@id'])
else:
logger.error("null cache")
return False
class BiogamesCategorizer(CategorizerStub):
def __init__(self, settings: LogSettings):
super().__init__(settings)
def process(self, entry: dict) -> bool:
if self.key is "default":
if entry[self.settings.type_field] in self.settings.custom['instance_start']:
self.key = entry[self.settings.custom['instance_id']]
return False

View File

@ -1,7 +1,7 @@
import logging
from collections import defaultdict
from . import Result, LogSettings, Analyzer
from . import Result, LogSettings, Analyzer, ResultStore
class LocationAnalyzer(Analyzer):
@ -15,14 +15,14 @@ class LocationAnalyzer(Analyzer):
super().__init__(settings)
self.entries = []
def result(self) -> Result:
def result(self, store: ResultStore) -> None:
self.log.debug(len(self.entries))
return Result(type(self), list(self.entries))
store.add(Result(type(self), list(self.entries)))
def process(self, entry: dict) -> bool:
if entry[self.settings.type_field] in self.settings.spatials:
self.entries.append(entry)
#self.log.debug(len(self.entries))
# self.log.debug(len(self.entries))
return False
@ -32,8 +32,8 @@ class LogEntryCountAnalyzer(Analyzer):
"""
__name__ = "LogEntryCount"
def result(self) -> Result:
return Result(type(self), dict(self.store))
def result(self, store: ResultStore) -> None:
store.add(Result(type(self), dict(self.store)))
def process(self, entry: dict) -> bool:
self.store[entry[self.settings.type_field]] += 1
@ -50,8 +50,8 @@ class LogEntrySequenceAnalyzer(Analyzer):
"""
__name__ = "LogEntrySequence"
def result(self) -> Result:
return Result(type(self), list(self.store))
def result(self, store: ResultStore) -> None:
store.add(Result(type(self), list(self.store)))
def process(self, entry: dict) -> bool:
entry_type = entry[self.settings.type_field]
@ -75,3 +75,21 @@ class ActionSequenceAnalyzer(LogEntrySequenceAnalyzer):
return False
self.store.append(entry_type)
return False
class CategorizerStub(Analyzer):
"""
generate a new Category in a ResultStore
"""
def process(self, entry: dict) -> bool:
raise NotImplementedError()
__name__ = "Categorizer"
def result(self, store: ResultStore) -> None:
store.new_category(self.key)
def __init__(self, settings: LogSettings):
super().__init__(settings)
self.key = "default"

View File

@ -1,5 +1,5 @@
import util
from . import Analyzer, LogSettings, Result
from . import Analyzer, LogSettings, Result, ResultStore
def init_filter(settings: LogSettings, state: str) -> callable:
@ -41,7 +41,7 @@ class LocomotionActionAnalyzer(Analyzer):
self.current_cache = None
self.last = None
def result(self) -> Result:
def result(self, store: ResultStore) -> None:
if self.last is not None:
if self.current_cache is None:
self.locomotion.append(self.last - self.cache_time)
@ -51,7 +51,7 @@ class LocomotionActionAnalyzer(Analyzer):
locomotion = sum(self.locomotion)
action = sum(self.actions)
total = locomotion + action
return Result(type(self), {
store.add(Result(type(self), {
'locomotion_sum': locomotion,
'action_sum': action,
'locomotion': self.locomotion,
@ -60,7 +60,7 @@ class LocomotionActionAnalyzer(Analyzer):
'locomotion_relative': locomotion / total,
'action_relative': action / total,
'locomotion_action_ratio': locomotion / action,
})
}))
def __init__(self, settings: LogSettings):
super().__init__(settings)
@ -86,8 +86,8 @@ class CacheSequenceAnalyzer(Analyzer):
self.store.append((entry['timestamp'], entry['cache']))
return False
def result(self) -> Result:
return Result(type(self), list(self.store))
def result(self, store: ResultStore) -> None:
store.add(Result(type(self), list(self.store)))
def __init__(self, settings: LogSettings):
super().__init__(settings)

View File

@ -1,4 +1,4 @@
from . import Analyzer
from . import Analyzer, Result, ResultStore
class MaskSpatials(Analyzer):
@ -11,5 +11,5 @@ class MaskSpatials(Analyzer):
return True
return False
def result(self) -> int:
return self.masked
def result(self, store: ResultStore) -> None:
store.add(Result(type(self), {"masked": self.masked}))

View File

@ -13,18 +13,22 @@
],
"analyzers": {
"analyzers": [
"LocationAnalyzer"
"BiogamesCategorizer",
"LocomotionActionAnalyzer",
"LogEntryCountAnalyzer"
]
},
"disabled_analyzers":[
"LogEntryCountAnalyzer",
"LogEntrySequenceAnalyzer",
"ActionSequenceAnalyzer",
"BoardDurationAnalyzer",
"LocomotionActionAnalyzer",
"CacheSequenceAnalyzer",
"SimulationRoundsAnalyzer",
"ActivationSequenceAnalyzer"],
"disabled_analyzers": [
"LocationAnalyzer",
"LogEntryCountAnalyzer",
"LogEntrySequenceAnalyzer",
"ActionSequenceAnalyzer",
"BoardDurationAnalyzer",
"LocomotionActionAnalyzer",
"CacheSequenceAnalyzer",
"SimulationRoundsAnalyzer",
"ActivationSequenceAnalyzer"
],
"sequences": {
"start": "de.findevielfalt.games.game2.instance.log.entry.LogEntryCache",
"end": {
@ -32,8 +36,14 @@
"action.@class": "de.findevielfalt.games.game2.instance.action.CacheEnableAction"
}
},
"custom":{
"simulation_rounds": ["de.findevielfalt.games.game2.instance.log.entry.LogEntryQuestion"],
"simu_data": ["de.findevielfalt.games.game2.instance.data.sequence.simulation.SimulationBoardData"]
"custom": {
"simulation_rounds": [
"de.findevielfalt.games.game2.instance.log.entry.LogEntryQuestion"
],
"simu_data": [
"de.findevielfalt.games.game2.instance.data.sequence.simulation.SimulationBoardData"
],
"instance_start": "de.findevielfalt.games.game2.instance.log.entry.LogEntryStartInstance",
"instance_id": "instance_id"
}
}

View File

@ -1,23 +1,25 @@
import json
import logging
from load import LOADERS
from typing import List
from analyzers import get_renderer, Analyzer, render
from analyzers.settings import LogSettings, load_settings
import analyzers
import analyzers
from analyzers import get_renderer, Analyzer, render
from analyzers.analyzer import ResultStore
from analyzers.settings import LogSettings, load_settings
from load import LOADERS
logging.basicConfig(format='%(levelname)s %(name)s:%(message)s', level=logging.DEBUG)
log = logging.getLogger(__name__)
log: logging.Logger = logging.getLogger(__name__)
def process_log(log_id: str, settings: LogSettings) -> List[Analyzer]:
logfile = "data/inst_{id}.{format}".format(id=log_id, format=settings.log_format)
logfile: str = "data/inst_{id}.{format}".format(id=log_id, format=settings.log_format)
loader = LOADERS[settings.log_format]()
try:
loader.load(logfile)
except BaseException as e:
raise RuntimeError(e)
analyzers = []
analyzers: List[Analyzer] = []
log.debug("build analyzers")
for analyzer in settings.analyzers:
analyzers.append(analyzer(settings))
@ -30,36 +32,37 @@ def process_log(log_id: str, settings: LogSettings) -> List[Analyzer]:
if __name__ == '__main__':
settings = load_settings("biogames2.json")
log_ids = [
settings: LogSettings = load_settings("biogames2.json")
log_ids: List[str] = [
"20d4244719404ffab0ca386c76e4b112",
"56d9b64144ab44e7b90bf766f3be32e3",
"dc2cdc28ca074715b905e4aa5badff10",
"e32b16998440475b994ab46d481d3e0c",
]
log_ids = [
log_ids: List[str] = [
"34fecf49dbaca3401d745fb467",
"44ea194de594cd8d63ac0314be",
"57c444470dbf88605433ca935c",
"78e0c545b594e82edfad55bd7f",
"91abfd4b31a5562b1c66be37d9",
"597b704fe9ace475316c345903",
"e01a684aa29dff9ddd9705edf8",
"fbf9d64ae0bdad0de7efa3eec6",
"fe1331481f85560681f86827ec",
# "44ea194de594cd8d63ac0314be",
# "57c444470dbf88605433ca935c",
# "78e0c545b594e82edfad55bd7f",
# "91abfd4b31a5562b1c66be37d9",
# "597b704fe9ace475316c345903",
# "e01a684aa29dff9ddd9705edf8",
# "fbf9d64ae0bdad0de7efa3eec6",
# "fe1331481f85560681f86827ec",
"fec57041458e6cef98652df625", ]
results = []
#TODO: capture session ID, dict
store: ResultStore = ResultStore()
for log_id in log_ids:
for analysis in process_log(log_id, settings):
log.info("* Result for " + analysis.name())
# print(analysis.result())
# print(analysis.render())
results.append(analysis.result())
analysis.result(store)
if False:
for r in get_renderer(analyzers.LocomotionActionAnalyzer):
r().render(results)
render(analyzers.LocationAnalyzer, results)
r().render(store.get_all())
if False:
render(analyzers.LocationAnalyzer, store.get_all())
print(json.dumps(store.serializable(), indent=1))
# for analyzers in analyzers:
# if analyzers.name() in ["LogEntryCount", "ActionSequenceAnalyzer"]: