Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 82fabe69a7 | |||
| 88bffcad8a | |||
| c7212316ee | |||
| ee63871e1c | |||
| bb10d5fa06 | |||
| 36fa6a6dca | |||
| aaa311ee03 | |||
| 366f496ba3 | |||
| 66d687d9f0 | |||
| 1d69d9fe8a | |||
| 6f11554c18 | |||
| 03fed1e1e4 | |||
| be5ac238bc |
@@ -1,6 +1,6 @@
|
||||
[Application]
|
||||
name=Solo Auswertung
|
||||
version=0.9.4
|
||||
version=0.9.7
|
||||
# How to launch the app - this calls the 'main' function from the 'myapp' package:
|
||||
entry_point=main:main
|
||||
# icon=myapp.ico
|
||||
|
||||
@@ -16,7 +16,6 @@ def main():
|
||||
cli = solo_turnier.cli.Cli(l)
|
||||
|
||||
batchWorker = solo_turnier.batch.BatchWorker(cli)
|
||||
batchWorker.prepare()
|
||||
|
||||
if cli.showGUI():
|
||||
raise Exception('Not yet implemented')
|
||||
@@ -24,7 +23,9 @@ def main():
|
||||
solo_turnier.flask.startFlask(
|
||||
batchWorker,
|
||||
debug=cli.getLogLevel() > 0,
|
||||
port=cli.getPort()
|
||||
port=cli.getPort(),
|
||||
showOnlyFinalists=not cli.showAllParticipants(),
|
||||
externalDebugger=cli.externalDebugger
|
||||
)
|
||||
else:
|
||||
combinedData = batchWorker.run(
|
||||
|
||||
@@ -5,8 +5,6 @@ import os
|
||||
import pprint
|
||||
|
||||
import tabulate
|
||||
import tkinter.filedialog
|
||||
import tkinter.simpledialog
|
||||
|
||||
class BatchWorker:
|
||||
def __init__(
|
||||
@@ -15,33 +13,16 @@ class BatchWorker:
|
||||
):
|
||||
self.l = logging.getLogger('solo_turnier.batch')
|
||||
self.config = config
|
||||
self.importPath = None
|
||||
|
||||
def prepare(self):
|
||||
self.importPath = self.config.importHtmlPath()
|
||||
if self.importPath is None:
|
||||
self.l.debug('No HTML import path was provided.')
|
||||
self.importPath = tkinter.filedialog.askdirectory(mustexist=True, title='HMTL Export auswählen')
|
||||
|
||||
if self.importPath is None or len(self.importPath) == 0:
|
||||
self.l.critical('Import path was not selected. Aborting.')
|
||||
tkinter.simpledialog.messagebox.showerror(
|
||||
title='Invalid HTML path selected',
|
||||
message='You did not select an appropriate folder. Aborting now.'
|
||||
)
|
||||
exit(1)
|
||||
|
||||
self.l.debug('Using import path %s', self.importPath)
|
||||
|
||||
def run(self, removeFilteredParicipants=True):
|
||||
self.l.debug(self.config.__dict__)
|
||||
|
||||
locator = solo_turnier.html_locator.HtmlLocator()
|
||||
self.l.info('Checking for feasible preview HTML export files in "%s"', self.importPath)
|
||||
htmlCandidatesPreview = locator.findPreviewRoundCandidates(self.importPath)
|
||||
self.l.info('Checking for feasible preview HTML export files in "%s"', self.config.importHtmlPath())
|
||||
htmlCandidatesPreview = locator.findPreviewRoundCandidates(self.config.importHtmlPath())
|
||||
self.l.debug('Found HTML file candidates for preview rounds: %s', htmlCandidatesPreview)
|
||||
|
||||
htmlResultFiles = locator.findCandidates(self.importPath)
|
||||
htmlResultFiles = locator.findCandidates(self.config.importHtmlPath())
|
||||
self.l.debug('Using HTML result files for result extraction: %s', htmlResultFiles)
|
||||
|
||||
worker = solo_turnier.worker.Worker()
|
||||
|
||||
@@ -10,7 +10,7 @@ class Cli:
|
||||
parser.add_argument('--no-flask', action='store_false', dest='flask', help='Disable the internal flask web server')
|
||||
parser.add_argument('--port', help='The port to listen for incoming requests', default='8082')
|
||||
|
||||
parser.add_argument('html', help='The path from where to look for HTML export files', nargs='?', default=None)
|
||||
parser.add_argument('html', help='The path from where to look for HTML export files', nargs=1, default=['.'])
|
||||
parser.add_argument('-o', '--output', help='Set the output path of the script', nargs=1, default=[None])
|
||||
parser.add_argument('--all-participants', '-a', action='store_true', help='Show all participants not only finalists')
|
||||
|
||||
@@ -22,6 +22,8 @@ class Cli:
|
||||
debugpy.listen(5678)
|
||||
debugpy.wait_for_client()
|
||||
|
||||
self.externalDebugger = self.__args.debug
|
||||
|
||||
map = {
|
||||
0: logging.ERROR,
|
||||
1: logging.WARN,
|
||||
@@ -40,7 +42,7 @@ class Cli:
|
||||
return self.__args.flask
|
||||
|
||||
def importHtmlPath(self):
|
||||
return self.__args.html
|
||||
return self.__args.html[0]
|
||||
|
||||
def importCSVPath(self):
|
||||
return self.__args.import_from[0]
|
||||
|
||||
@@ -44,6 +44,11 @@ class CompetitionClassParser:
|
||||
self.namesPreview = [
|
||||
'Sichtung'
|
||||
]
|
||||
self.mapShortNames = {
|
||||
'N': self.NEWC,
|
||||
'B': self.BEG,
|
||||
'A': self.ADV,
|
||||
}
|
||||
|
||||
def parseClass(self, cls: str, allowPreview: bool = False) -> Class_t:
|
||||
if allowPreview and cls in self.namesPreview:
|
||||
@@ -57,6 +62,9 @@ class CompetitionClassParser:
|
||||
else:
|
||||
return self.mapNames[cls]
|
||||
|
||||
def parseAbbreviatedClass(self, cls: str) -> Class_t:
|
||||
return self.mapShortNames[cls]
|
||||
|
||||
def isPureClass(self, cls: str, allowPreview: bool = False) -> bool:
|
||||
parsedClass = self.parseClass(cls, allowPreview)
|
||||
return isinstance(parsedClass, CompetitionClass)
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import flask
|
||||
import solo_turnier
|
||||
import logging
|
||||
|
||||
_l = logging.getLogger(__name__)
|
||||
|
||||
def startFlask(
|
||||
batchWorker: solo_turnier.batch.BatchWorker,
|
||||
debug: bool = False,
|
||||
port: int = 8082,
|
||||
showOnlyFinalists: bool = True
|
||||
showOnlyFinalists: bool = True,
|
||||
externalDebugger: bool = False,
|
||||
):
|
||||
app = flask.Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
combinedData = batchWorker.run(False)
|
||||
_l.debug('Show only finalists %s', showOnlyFinalists)
|
||||
|
||||
return flask.render_template('index.html', data=combinedData)
|
||||
return flask.render_template('index.html', data=combinedData, onlyFinalists=showOnlyFinalists)
|
||||
|
||||
@app.get('/custom.css')
|
||||
def css():
|
||||
@@ -23,4 +28,5 @@ def startFlask(
|
||||
)
|
||||
return flask.Response(ret, mimetype='text/css')
|
||||
|
||||
app.run(host='0.0.0.0', port=port, debug=debug)
|
||||
useReloader = debug and not externalDebugger
|
||||
app.run(host='0.0.0.0', port=port, debug=debug, use_reloader=useReloader)
|
||||
|
||||
@@ -8,6 +8,10 @@ from .types import HtmlPreviewImport as HtmlImport, HtmlResultImport
|
||||
from .group import GroupParser
|
||||
from .competition_class import CompetitionClassParser
|
||||
|
||||
class IncompleteRoundException(Exception):
|
||||
def __init__(self, *args):
|
||||
super(IncompleteRoundException, self).__init__(*args)
|
||||
|
||||
class HtmlParser:
|
||||
|
||||
def __init__(self, text: str, fileName: str = None):
|
||||
@@ -53,12 +57,17 @@ class HtmlParser:
|
||||
if len(tds) != 2:
|
||||
return
|
||||
|
||||
if tds[1].contents[0].startswith('Alle Starter weiter genommen.'):
|
||||
self.l.info('No excluded starters found.')
|
||||
return
|
||||
|
||||
regex = re.compile('(.*) \\(([0-9]+)\\)')
|
||||
|
||||
place = tds[0].contents[0]
|
||||
|
||||
match = regex.fullmatch(tds[1].contents[0])
|
||||
if match is None:
|
||||
self.l.error('Could not match %s to regex search pattern', str(tds))
|
||||
raise Exception(f'Could not match {tds} to regex search pattern')
|
||||
name = match.group(1)
|
||||
number = match.group(2)
|
||||
@@ -73,7 +82,8 @@ class HtmlParser:
|
||||
def __parseFirstTable(table):
|
||||
roundName = table.tr.td.contents[0]
|
||||
if roundName != 'Endrunde':
|
||||
raise Exception('Could not parse HTML file')
|
||||
self.l.warning('Found table with round name %s.', roundName)
|
||||
raise IncompleteRoundException('Could not parse HTML file')
|
||||
|
||||
__parseRows(table.find_all('tr')[2:], True)
|
||||
|
||||
@@ -82,10 +92,14 @@ class HtmlParser:
|
||||
__parseRows(table.find_all('tr'), False)
|
||||
|
||||
tables = self.soup.find('div', class_='extract').find_all('table')
|
||||
if len(tables) > 0:
|
||||
__parseFirstTable(tables[0])
|
||||
|
||||
try:
|
||||
if len(tables) > 0:
|
||||
__parseFirstTable(tables[0])
|
||||
|
||||
__parseRemainingTables(tables[1:])
|
||||
__parseRemainingTables(tables[1:])
|
||||
except IncompleteRoundException:
|
||||
pass
|
||||
|
||||
# title = self.soup.find('div', class_='eventhead').table.tr.td.contents[0]
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
{% if not participant.finalist %}
|
||||
{% set rowCls = "no-finalist" %}
|
||||
{% endif %}
|
||||
{% if participant.finalist or not onlyFinalists %}
|
||||
<tr class="{{ rowCls }}">
|
||||
<td>{{ participant.name }} ({{ participant.id }})</td>
|
||||
{% for dance in data.results[group].dances %}
|
||||
@@ -47,6 +48,7 @@
|
||||
{% endblock %}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
@@ -159,7 +159,10 @@ class PreviewWorker:
|
||||
|
||||
for file in parsers:
|
||||
parser = parsers[file]
|
||||
self.__extractPersonsFromSinglePreview(parser)
|
||||
try:
|
||||
self.__extractPersonsFromSinglePreview(parser)
|
||||
except:
|
||||
self.l.error('Failed to parse preview round in file %s. Skipping this file\'s content.', parser.fileName)
|
||||
|
||||
return types.HtmlPreviewImport(self.participants, self.previewResults)
|
||||
|
||||
@@ -177,10 +180,14 @@ class ResultExtractor:
|
||||
for filePair in files:
|
||||
with open(filePair[0], 'r') as fp:
|
||||
text = fp.read()
|
||||
with open(filePair[1], 'r') as fp:
|
||||
textTab = fp.read()
|
||||
parser = html_parser.HtmlParser(text, filePair[0])
|
||||
parserTab = html_parser.HtmlParser(textTab, filePair[1])
|
||||
|
||||
if filePair[1] is None:
|
||||
parserTab = None
|
||||
else:
|
||||
with open(filePair[1], 'r') as fp:
|
||||
textTab = fp.read()
|
||||
parserTab = html_parser.HtmlParser(textTab, filePair[1])
|
||||
|
||||
try:
|
||||
data = parser.guessDataFromHtmlTitle()
|
||||
@@ -247,8 +254,11 @@ class ResultExtractor:
|
||||
self.l.debug('Extracting data from file %s', fileName)
|
||||
self._analyzeSingleParser(parsers[fileNameTuple][0], ret)
|
||||
|
||||
self.l.debug('Fetching individual result of combined competitions in %s', fileName)
|
||||
self._analyzeIndividualResults(parsers[fileNameTuple][1], ret)
|
||||
if parsers[fileNameTuple][1] is None:
|
||||
self.l.info('Skipping extraction of individual result as class is not yet finished.')
|
||||
else:
|
||||
self.l.debug('Fetching individual result of combined competitions in %s', fileName)
|
||||
self._analyzeIndividualResults(parsers[fileNameTuple][1], ret)
|
||||
|
||||
return ret
|
||||
|
||||
@@ -460,11 +470,11 @@ class Worker:
|
||||
self.l.log(5, 'Obtained result %s', resultsOfParticipant)
|
||||
results[participant] = resultsOfParticipant
|
||||
|
||||
self.l.log(5, 'Result before native fixing: %s', (results))
|
||||
self.l.log(5, 'Result before native fixing: %s', pformat(results))
|
||||
# self._fixNativePlaces(dances, results)
|
||||
self._fixNativePlacesFromTable(dances, results, importedData.htmlResults)
|
||||
# self.l.log(5, 'Result after native fixing: %s', pformat(results))
|
||||
self.l.log(5,'Data %s', results)
|
||||
self._fixNativeDataFromTable(dances, results, importedData.htmlResults)
|
||||
self.l.log(5, 'Result after native fixing: %s', pformat(results))
|
||||
# self.l.log(5,'Fixed data %s', results)
|
||||
|
||||
totalResult[group] = types.TotalGroupResult(dances, results)
|
||||
|
||||
@@ -540,7 +550,9 @@ class Worker:
|
||||
raise Exception('Multiple results found with same key')
|
||||
rawResult = rawResult[0]
|
||||
|
||||
nativeClass = previewResults.results[participant][dance]
|
||||
nativeClass = key[2]
|
||||
# nativeClass = previewResults.results[participant][dance]
|
||||
# nativeClass = key[2]
|
||||
|
||||
# self.l.log(5, 'Result %s => %s', key, rawResult)
|
||||
ret = types.SingleParticipantResult(
|
||||
@@ -555,13 +567,15 @@ class Worker:
|
||||
|
||||
return results
|
||||
|
||||
def _fixNativePlacesFromTable(
|
||||
def _fixNativeDataFromTable(
|
||||
self,
|
||||
dances: list[str],
|
||||
data: dict[types.HtmlPreviewParticipant, list[types.SingleParticipantResult]],
|
||||
importedData: types.HtmlCompetitionTotalResults
|
||||
):
|
||||
rePlace = re.compile('([0-9]+)(?:-([0-9]+))?')
|
||||
classParser = competition_class.CompetitionClassParser()
|
||||
|
||||
for participant in data.keys():
|
||||
self.l.log(5, 'fixing participant %s', participant)
|
||||
results = data[participant]
|
||||
@@ -588,6 +602,9 @@ class Worker:
|
||||
result.placeNative = matcher.group(1)
|
||||
result.placeNativeTo = matcher.group(2)
|
||||
|
||||
if raw[1] is not None:
|
||||
result.nativeClass = classParser.parseAbbreviatedClass(raw[1])
|
||||
|
||||
pass
|
||||
|
||||
def _fixNativePlaces(
|
||||
|
||||
Reference in New Issue
Block a user