Skip to content

Commit ac63cc7

Browse files
nickygerritsenvmcj
authored andcommitted
Use DOMjudge API to fetch images and team names for domlogo
1 parent df92d64 commit ac63cc7

File tree

3 files changed

+121
-24
lines changed

3 files changed

+121
-24
lines changed

domlogo/domlogo.py

Lines changed: 104 additions & 5 deletions
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,73 @@
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+
print('Downloading any new or changed logos and photos...')
86+
for organization in organizations.values():
87+
if 'logo' in organization:
88+
organization_id = organization['id']
89+
logo = organization['logo'][0]
90+
downloaded, downloaded_to, etag_file, etag = download_image('logo', organization_id, logo)
91+
if downloaded_to:
92+
# Convert to both 64x64 (for sidebar) and 160x160 (for overlay over photo)
93+
downloaded_to_escaped = shlex.quote(downloaded_to)
94+
target = shlex.quote(f'domlogo-files/logos/{organization_id}.png')
95+
command = f'convert {downloaded_to_escaped} -resize 64x64 -background none -gravity center -extent 64x64 {target}'
96+
os.system(command)
97+
98+
target = shlex.quote(f'domlogo-files/logos/{organization_id}.160.png')
99+
command = f'convert {downloaded_to_escaped} -resize 160x160 -background none -gravity center -extent 160x160 {target}'
100+
os.system(command)
101+
102+
with open(etag_file, 'w') as f:
103+
f.write(etag)
104+
105+
os.unlink(downloaded_to)
106+
107+
for team in teams.values():
108+
if 'photo' in team and team['display_name'] != 'DOMjudge':
109+
team_id = team['id']
110+
photo = team['photo'][0]
111+
downloaded, downloaded_to, etag_file, etag = download_image('photo', team_id, photo)
112+
if downloaded_to:
113+
# First convert to a good known size because adding the annotation and logo assumes this
114+
intermediate_target = f'domlogo-files/photos/{team_id}-intermediate.png'
115+
command = f'convert {downloaded_to} -resize 1024x1024 -gravity center {intermediate_target}'
116+
os.system(command)
117+
118+
# Now add logo and team name. We use subprocess.run here to escape the team name
119+
target = f'domlogo-files/photos/{team_id}.png'
120+
organization_id = team['organization_id']
121+
logo_file = f'domlogo-files/logos/{organization_id}.png'
122+
command = [
123+
'convert',
124+
intermediate_target,
125+
'-fill', 'white',
126+
'-undercolor', '#00000080',
127+
'-gravity', 'south',
128+
'-font', 'Ubuntu',
129+
'-pointsize', '30',
130+
'-annotate', '+5+5', f' {team["display_name"]} ',
131+
logo_file,
132+
'-gravity', 'northeast',
133+
'-composite',
134+
target
135+
]
136+
137+
subprocess.run(command)
138+
139+
with open(etag_file, 'w') as f:
140+
f.write(etag)
141+
142+
os.unlink(downloaded_to)
143+
os.unlink(intermediate_target)
144+
53145
latest_logfile = max(glob.glob('output/log/judge.*-2.log'), key=os.path.getctime)
54146
print(f'Checking logfile {latest_logfile}')
55147
with open(latest_logfile, 'r') as logfile:
@@ -79,7 +171,7 @@
79171
team_id = submission_data['team_id']
80172
last_seen = (submission_id, judging_id, team_id)
81173
new_filename = f'domlogo-files/photos/{team_id}.png'
82-
if not team_id.isdigit():
174+
if not os.path.isfile(new_filename):
83175
new_filename = f'domlogo-files/photos/crew.png'
84176
team_image.update(filename=new_filename)
85177
metadata_text.update(f's{submission_id} / {submission_data["problem_id"]} / {submission_data["language_id"]}')
@@ -108,9 +200,16 @@
108200
color = 'DeepSkyBlue'
109201
for i in range(len(cache)-1):
110202
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)
203+
organization_id = None
204+
if tid in teams:
205+
organization_id = teams[tid]['organization_id']
206+
organization_logo = 'domlogo-files/logos/DOMjudge.png'
207+
# Organization ID is null for internal teams so explicitly check for it
208+
if organization_id:
209+
potential_organization_logo = f'domlogo-files/logos/{organization_id}.png'
210+
if os.path.isfile(potential_organization_logo):
211+
organization_logo = potential_organization_logo
212+
cache[-1] = (organization_logo, f's{sid}/j{jid}\n{verdict}', color, jid)
114213
for i in range(len(cache)):
115214
previous_column[i][0].update(filename=cache[i][0])
116215
previous_column[i][1].update(cache[i][1])
Lines changed: 14 additions & 17 deletions
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

Lines changed: 3 additions & 2 deletions
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:
@@ -15,7 +16,7 @@
1516
group: domjudge
1617
mode: 0755
1718

18-
- name: Install domlogo
19+
- name: Install domlogo Python libraries
1920
synchronize:
2021
src: lib
2122
dest: /home/domjudge/.local/

0 commit comments

Comments
 (0)