-
Notifications
You must be signed in to change notification settings - Fork 0
/
wst.py
182 lines (153 loc) · 7.58 KB
/
wst.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
""" See https://github.com/pochmann/wca-statistics-tools """
import mysql.connector, itertools, operator, re, os.path, shutil
from collections import *
from time import time
def __internal(filename):
return os.path.join('internal', filename)
# Load the configuration (if necessary, create it first)
if not os.path.isfile(__internal('config.py')):
shutil.copy(__internal('config.template.py'), __internal('config.py'))
from internal.config import config
#-----------------------------------------------------------------------------
# Database stuff
#-----------------------------------------------------------------------------
# TODO: Proper module structure with main?
# TODO: With python code for forum posts, turn the "wca" in "from wca import" into a link to the github repo
# Maybe like "from [U][URL=foo]wca[/URL][/U] import" or even
# "[/noparse]from [U][URL=foo]wca[/URL][/U][noparse] import
# TODO: Rename to "db" because you might want to support commands?
# Nah, for commands the "rows = " doesn't make sense.
# But do support commands and find good names for both.
def query(q):
# Connect to the database
# TODO: Put it in a config
cnx = mysql.connector.connect(host='localhost', database='wca_export', user='wca_export', password='XXX')
cursor = cnx.cursor()
# Get the raw data
cursor.execute(q)
rows = list(cursor)
# Close
cursor.close()
cnx.close()
return rows
#-----------------------------------------------------------------------------
# Helpers
#-----------------------------------------------------------------------------
#def person_name(id):
# TODO: Use the mysql query params support instead of python's format
#return query("SELECT name FROM Persons WHERE id='{}' and subId=1".format(id))[0][0]
# Make it "name(id)" instead, just like "link(id)"?
# Use cellName where applicable?
# Or id for competitions? (nah, it's good for devs, but "bad" for the public)
def group(rows, index):
return itertools.groupby(rows, operator.itemgetter(index))
#-----------------------------------------------------------------------------
# Formatting
#-----------------------------------------------------------------------------
def person_link(id, addon=None):
if addon is None:
page = 'p.php?i=' + id
elif '#' in addon:
page = 'p.php?i=' + id + addon
elif '@' in addon:
page = 'c.php?byPerson=1&i=' + addon[1:] + '#' + id
return '[url=https://www.worldcubeassociation.org/results/' + page + ']' + person_name[id] + '[/url]'
# Abolish, use link(id) instead
def link(id):
pass
# TODO: Either detect the type of id (person, competition, event, ...) or
# build a dictionary of all links when the module is loaded (and after
# an sql/tsv update) and just access that. In the latter case, you
# could make "link" a dictionary rather than a function, if it's
# accessible in the using script. Although function is cleaner and
# more consistent. Yeah, use a function.
#def link(text, page):
# return '[url=https://www.worldcubeassociation.org/results/{}]{}[/url]'.format(page, text)
def create_post(infile, column_names, rows):
if os.path.isfile(infile):
dirname, basename = os.path.split(infile)
name, ext = os.path.splitext(basename)
outfile = os.path.join(dirname, name + '.out')
#name, sourcetype = infile.split('.')
sourcetype = {'.in': 'SQL', '.py': 'Python'}[ext]
source = open(infile).read().strip()
source_spoiler = '\n\n[SPOILER="' + sourcetype + '"][CODE][NOPARSE]' + source + '[/NOPARSE][/CODE][/SPOILER]'
else:
name = infile
outfile = os.path.join('inout', name + '.out')
source_spoiler = ''
# Prepare a note
export = query('SELECT here FROM export_status')[0][0].split('.')[0]
note = "Using data from [url=https://www.worldcubeassociation.org/results/misc/export.html]" + export + "[/url]" + \
" and Stefan's [url=https://github.com/pochmann/wca-statistics-tools/]WCA Statistics Tools[/url]."
# Produce the out-file
#ctr, rank = 0, None
with open(outfile, 'w', encoding='utf8') as out:
print('[SPOILER="' + name + '"]' + note + '\n\n[TABLE="class:grid,align:left"]', file=out)
print('[TR][TD][B]' + '[/B][/TD][TD][B]'.join(n.split('[')[0] for n in column_names) + '[/B][/TD][/TR]', file=out)
for row in rows:
tr = '[TR]'
for column_name, value in zip(column_names, row):
if type(value) is str and re.match(r'\d{4}[A-Z]{4}\d\d', value):
value = re.sub(r'(\d{4}[A-Z]{4}\d\d)([#@]\w+)?', auto_person, value)
if value in event_name:
eventId = value
value = event_name[value]
if value in competition_name:
value = competition_name[value]
align_right = type(value) is not str or re.match(r'\d+(\.\d+)?%?$', value)
if type(value) is int and column_name == 'Time':
# or recognize by turning a result like 142 into a string like 'r:142'?
# Or with an instruction in the column name?
value = format_value(value)
align_right = True
if type(value) is int and '[R]' in column_name:
value = format_value(value, event_format[eventId])
align_right = True
td = '[TD="align:right"]' if align_right else '[TD]'
tr += td + str(value) + '[/TD]'
print(tr + '[/TR]', file=out)
print('[/TABLE]' + source_spoiler + '[/SPOILER]', file=out)
def auto_person(match):
personId, addon = match.groups()
return person_link(personId, addon)
def format_value(value, measure='time'):
if value < -2: return 'error'
if value == -1: return 'DNF'
if value == -2: return 'DNS'
if value == 0: return ''
if measure == 'number':
return str(value)
if measure == 'time':
if value < 60 * 100: return '{:.2f}'.format(value / 100.0)
if value < 60 * 60 * 100: return '{}:{:05.2f}'.format(value // 6000, value % 6000 / 100.0)
return '{}:{:02}:{:05.2f}'.format(value // 360000, value % 360000 // 6000, value % 6000 / 100.0)
if measure == 'multi':
return format_multi_value(value)
return 'error'
def format_multi_value(value):
# Extract the value parts
if value >= 1000000000:
solved = 99 - value // 10000000 % 100
attempted = value // 100000 % 100
time = value % 100000
else:
difference = 99 - value // 10000000
time = value // 100 % 100000
missed = value % 100
solved = difference + missed
attempted = solved + missed
# Build the time string
if time == 99999:
time = '?:??:??'
elif time < 60 * 60:
time = '{}:{:02}'.format(time//60, time%60)
else:
time = '{}:{:02}:{:02}'.format(time//3600, time//60%60, time%60)
#--- Combine.
return "{}/{} ([SIZE=1]{}[/SIZE])".format(solved, attempted, time)
# Fetch person/event/competition names for automatically turning ids into names/links
person_name = dict(query('SELECT id, name FROM Persons WHERE subId=1'))
event_name = dict(query('SELECT id, cellName FROM Events'))
competition_name = dict(query('SELECT id, cellName FROM Competitions'))
event_format = dict(query('SELECT id, format FROM Events'))