Skip to content

Commit e48d00e

Browse files
Use DOMjudge API to fetch images and team names for domlogo
1 parent 313916e commit e48d00e

File tree

3 files changed

+122
-24
lines changed

3 files changed

+122
-24
lines changed

domlogo/domlogo.py

+105-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
#!/usr/bin/python3
1+
#!/usr/bin/env python3
2+
import subprocess
23

34
import PySimpleGUI as sg
45
import glob
@@ -7,8 +8,32 @@
78
import re
89
import time
910
import platform
11+
import shlex
1012
import yaml
1113

14+
15+
def download_image(image_type: str, entity_id: str, file: dict):
16+
href = file['href']
17+
filename = file['filename']
18+
photo_head = requests.head(f'{api_url}/{href}', auth=(user, passwd))
19+
etag = photo_head.headers['ETag']
20+
etag_file = f'domlogo-files/{image_type}s/{entity_id}.etag.txt'
21+
temp_file = f'domlogo-files/{image_type}s/temp-{entity_id}-{filename}'
22+
existing_etag = None
23+
if os.path.isfile(etag_file):
24+
with open(etag_file) as f:
25+
existing_etag = f.readline().strip()
26+
27+
if existing_etag != etag:
28+
print(f'Downloading and converting {image_type} for entity with ID {entity_id}...')
29+
with open(temp_file, 'wb') as f:
30+
f.write(requests.get(f'{api_url}/{href}', auth=(user, passwd)).content)
31+
32+
return True, temp_file, etag_file, etag
33+
34+
return False, None, None, None
35+
36+
1237
font = ('Roboto', 14)
1338
mono_font = ('Liberation Mono', 32)
1439
host = platform.node()
@@ -50,6 +75,74 @@
5075
break
5176
print(f'Using {api_url} as endpoint.')
5277

78+
print('Loading teams and organizations from API')
79+
teams = {team['id']: team for team in requests.get(f'{api_url}/teams', auth=(user, passwd)).json()}
80+
for team_id in teams:
81+
if 'display_name' not in teams[team_id]:
82+
teams[team_id]['display_name'] = teams[team_id]['name']
83+
organizations = {org['id']: org for org in requests.get(f'{api_url}/organizations', auth=(user, passwd)).json()}
84+
85+
86+
print('Downloading any new or changed logos and photos...')
87+
for organization in organizations.values():
88+
if 'logo' in organization:
89+
organization_id = organization['id']
90+
logo = organization['logo'][0]
91+
downloaded, downloaded_to, etag_file, etag = download_image('logo', organization_id, logo)
92+
if downloaded_to:
93+
# Convert to both 64x64 (for sidebar) and 160x160 (for overlay over photo)
94+
downloaded_to_escaped = shlex.quote(downloaded_to)
95+
target = shlex.quote(f'domlogo-files/logos/{organization_id}.png')
96+
command = f'convert {downloaded_to_escaped} -resize 64x64 -background none -gravity center -extent 64x64 {target}'
97+
os.system(command)
98+
99+
target = shlex.quote(f'domlogo-files/logos/{organization_id}.160.png')
100+
command = f'convert {downloaded_to_escaped} -resize 160x160 -background none -gravity center -extent 160x160 {target}'
101+
os.system(command)
102+
103+
with open(etag_file, 'w') as f:
104+
f.write(etag)
105+
106+
os.unlink(downloaded_to)
107+
108+
for team in teams.values():
109+
if 'photo' in team and team['display_name'] != 'DOMjudge':
110+
team_id = team['id']
111+
photo = team['photo'][0]
112+
downloaded, downloaded_to, etag_file, etag = download_image('photo', team_id, photo)
113+
if downloaded_to:
114+
# First convert to a good known size because adding the annotation and logo assumes this
115+
intermediate_target = f'domlogo-files/photos/{team_id}-intermediate.png'
116+
command = f'convert {downloaded_to} -resize 1024x1024 -gravity center {intermediate_target}'
117+
os.system(command)
118+
119+
# Now add logo and team name. We use subprocess.run here to escape the team name
120+
target = f'domlogo-files/photos/{team_id}.png'
121+
organization_id = team['organization_id']
122+
logo_file = f'domlogo-files/logos/{organization_id}.png'
123+
command = [
124+
'convert',
125+
intermediate_target,
126+
'-fill', 'white',
127+
'-undercolor', '#00000080',
128+
'-gravity', 'south',
129+
'-font', 'Ubuntu',
130+
'-pointsize', '30',
131+
'-annotate', '+5+5', f' {team["display_name"]} ',
132+
logo_file,
133+
'-gravity', 'northeast',
134+
'-composite',
135+
target
136+
]
137+
138+
subprocess.run(command)
139+
140+
with open(etag_file, 'w') as f:
141+
f.write(etag)
142+
143+
os.unlink(downloaded_to)
144+
os.unlink(intermediate_target)
145+
53146
latest_logfile = max(glob.glob('output/log/judge.*-2.log'), key=os.path.getctime)
54147
print(f'Checking logfile {latest_logfile}')
55148
with open(latest_logfile, 'r') as logfile:
@@ -79,7 +172,7 @@
79172
team_id = submission_data['team_id']
80173
last_seen = (submission_id, judging_id, team_id)
81174
new_filename = f'domlogo-files/photos/{team_id}.png'
82-
if not team_id.isdigit():
175+
if not os.path.isfile(new_filename):
83176
new_filename = f'domlogo-files/photos/crew.png'
84177
team_image.update(filename=new_filename)
85178
metadata_text.update(f's{submission_id} / {submission_data["problem_id"]} / {submission_data["language_id"]}')
@@ -108,9 +201,16 @@
108201
color = 'DeepSkyBlue'
109202
for i in range(len(cache)-1):
110203
cache[i] = cache[i+1]
111-
if not tid.isdigit():
112-
tid = 'DOMjudge'
113-
cache[-1] = (f'domlogo-files/logos/{tid}.png', f's{sid}/j{jid}\n{verdict}', color, jid)
204+
organization_id = None
205+
if tid in teams:
206+
organization_id = teams[tid]['organization_id']
207+
organization_logo = 'domlogo-files/logos/DOMjudge.png'
208+
# Organization ID is null for internal teams so explicitly check for it
209+
if organization_id:
210+
potential_organization_logo = f'domlogo-files/logos/{organization_id}.png'
211+
if os.path.isfile(potential_organization_logo):
212+
organization_logo = potential_organization_logo
213+
cache[-1] = (organization_logo, f's{sid}/j{jid}\n{verdict}', color, jid)
114214
for i in range(len(cache)):
115215
previous_column[i][0].update(filename=cache[i][0])
116216
previous_column[i][1].update(cache[i][1])
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
1-
# Generating logos from a Contest Package
1+
# Preparing DOMlogo
22

3-
```bash
4-
for team in $(cat ~/wf2021/contests/finals/teams.json | jq -r '.[].id'); do
5-
echo $team
6-
ORG_ID=$(cat ~/wf2021/contests/finals/teams.json | jq -r ".[] | select(.id == \"$team\") | .organization_id")
7-
convert ~/wf2021/contests/finals/organizations/$ORG_ID/logo.png -resize 64x64 -background none -gravity center -extent 64x64 $team.png
8-
done
9-
```
3+
First, create the following files:
4+
- `images/logos/DOMjudge.png`, a 64x64 DOMjudge logo with transparent background.
5+
- `images/photos/crew.png`, an image with a width of 1024 (and any height) to show for teams without a photo.
6+
- `images/photos/idle.png`, an image with a width of 1024 (and any height) to show when the judgedaemon is idle.
7+
8+
Next, add the needed Python dependencies to the `lib` folder, within a folder called `python3.8`. You can copy this
9+
folder from a local machine and it should contain the `PySimpleGUI` and `requests` Python packages.
1010

11-
# Generating photos from a Contest package
11+
Optionally you can create a file `images/config.yaml` with something like:
12+
13+
```yaml
14+
host-bg-color: '#013370'
15+
```
1216
13-
```bash
14-
for team in $(cat ~/wf2021/contests/finals/teams.json | jq -r '.[].id'); do
15-
echo $team
16-
ORG_ID=$(cat ~/wf2021/contests/finals/teams.json | jq -r ".[] | select(.id == \"$team\") | .organization_id")
17-
TEAM_NAME=$(cat ~/wf2021/contests/finals/teams.json | jq -r ".[] | select(.id == \"$team\") | .display_name")
18-
convert ~/wf2021/contests/finals/teams/$team/photo.jpg -fill white -undercolor '#00000080' -gravity south -font 'Ubuntu' -pointsize 30 -annotate +5+5 " $TEAM_NAME " ~/wf2021/contests/finals/organizations/$ORG_ID/logo.160x160.png -gravity northeast -composite -resize 1024x1024 $team.png
19-
done
20-
```
17+
DOMlogo will use the DOMjudge API to download logos and photos for all teams, so no further configuration should be needed.

provision-contest/ansible/roles/domlogo/tasks/main.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
---
22
# These tasks install domlogo
33

4-
- name: Install python3 modules for domlogo
4+
- name: Install python3 modules and imagemagick for domlogo
55
apt:
66
state: present
77
pkg:
88
- python3-tk
9+
- imagemagick
910

1011
- name: Install domlogo
1112
copy:
@@ -17,7 +18,7 @@
1718
loop:
1819
- domlogo
1920

20-
- name: Install domlogo
21+
- name: Install domlogo Python libraries
2122
synchronize:
2223
src: lib
2324
dest: /home/domjudge/.local/

0 commit comments

Comments
 (0)