Skip to content

Commit

Permalink
Use DOMjudge API to fetch images and team names for domlogo
Browse files Browse the repository at this point in the history
  • Loading branch information
nickygerritsen committed Oct 3, 2023
1 parent 313916e commit a356c53
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 24 deletions.
108 changes: 103 additions & 5 deletions domlogo/domlogo.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/python3
#!/usr/bin/env python3
import subprocess

import PySimpleGUI as sg
import glob
Expand All @@ -9,6 +10,29 @@
import platform
import yaml


def download_image(image_type: str, entity_id: str, file: dict):
href = file['href']
filename = file['filename']
photo_head = requests.head(f'{api_url}/{href}', auth=(user, passwd))
etag = photo_head.headers['ETag']
etag_file = f'domlogo-files/{image_type}s/{entity_id}.etag.txt'
temp_file = f'domlogo-files/{image_type}s/temp-{entity_id}-{filename}'
existing_etag = None
if os.path.isfile(etag_file):
with open(etag_file) as f:
existing_etag = f.readline().strip()

if existing_etag != etag:
print(f'Downloading and converting {image_type} for entity with ID {entity_id}...')
with open(temp_file, 'wb') as f:
f.write(requests.get(f'{api_url}/{href}', auth=(user, passwd)).content)

return True, temp_file, etag_file, etag

return False, None, None, None


font = ('Roboto', 14)
mono_font = ('Liberation Mono', 32)
host = platform.node()
Expand Down Expand Up @@ -50,6 +74,73 @@
break
print(f'Using {api_url} as endpoint.')

print('Loading teams and organizations from API')
teams = {team['id']: team for team in requests.get(f'{api_url}/teams', auth=(user, passwd)).json()}
for team_id in teams:
if 'display_name' not in teams[team_id]:
teams[team_id]['display_name'] = teams[team_id]['name']
organizations = {org['id']: org for org in requests.get(f'{api_url}/organizations', auth=(user, passwd)).json()}


print('Downloading any new or changed logos and photos...')
for organization in organizations.values():
if 'logo' in organization:
organization_id = organization['id']
logo = organization['logo'][0]
downloaded, downloaded_to, etag_file, etag = download_image('logo', organization_id, logo)
if downloaded_to:
# Convert to both 64x64 (for sidebar) and 160x160 (for overlay over photo)
target = f'domlogo-files/logos/{organization_id}.png'
command = f'convert {downloaded_to} -resize 64x64 -background none -gravity center -extent 64x64 {target}'
os.system(command)

target = f'domlogo-files/logos/{organization_id}.160.png'
command = f'convert {downloaded_to} -resize 160x160 -background none -gravity center -extent 160x160 {target}'
os.system(command)

with open(etag_file, 'w') as f:
f.write(etag)

os.unlink(downloaded_to)

for team in teams.values():
if 'photo' in team and team['display_name'] != 'DOMjudge':
team_id = team['id']
photo = team['photo'][0]
downloaded, downloaded_to, etag_file, etag = download_image('photo', team_id, photo)
if downloaded_to:
# First convert to a good known size because adding the annotation and logo assumes this
intermediate_target = f'domlogo-files/photos/{team_id}-intermediate.png'
command = f'convert {downloaded_to} -resize 1024x1024 -gravity center {intermediate_target}'
os.system(command)

# Now add logo and team name. We use subprocess.run here to escape the team name
target = f'domlogo-files/photos/{team_id}.png'
organization_id = team['organization_id']
logo_file = f'domlogo-files/logos/{organization_id}.png'
command = [
'convert',
intermediate_target,
'-fill', 'white',
'-undercolor', '#00000080',
'-gravity', 'south',
'-font', 'Ubuntu',
'-pointsize', '30',
'-annotate', '+5+5', f' {team["display_name"]} ',
logo_file,
'-gravity', 'northeast',
'-composite',
target
]

subprocess.run(command)

with open(etag_file, 'w') as f:
f.write(etag)

os.unlink(downloaded_to)
os.unlink(intermediate_target)

latest_logfile = max(glob.glob('output/log/judge.*-2.log'), key=os.path.getctime)
print(f'Checking logfile {latest_logfile}')
with open(latest_logfile, 'r') as logfile:
Expand Down Expand Up @@ -79,7 +170,7 @@
team_id = submission_data['team_id']
last_seen = (submission_id, judging_id, team_id)
new_filename = f'domlogo-files/photos/{team_id}.png'
if not team_id.isdigit():
if not os.path.isfile(new_filename):
new_filename = f'domlogo-files/photos/crew.png'
team_image.update(filename=new_filename)
metadata_text.update(f's{submission_id} / {submission_data["problem_id"]} / {submission_data["language_id"]}')
Expand Down Expand Up @@ -108,9 +199,16 @@
color = 'DeepSkyBlue'
for i in range(len(cache)-1):
cache[i] = cache[i+1]
if not tid.isdigit():
tid = 'DOMjudge'
cache[-1] = (f'domlogo-files/logos/{tid}.png', f's{sid}/j{jid}\n{verdict}', color, jid)
organization_id = None
if tid in teams:
organization_id = teams[tid]['organization_id']
organization_logo = 'domlogo-files/logos/DOMjudge.png'
# Organization ID is null for internal teams so explicitly check for it
if organization_id:
potential_organization_logo = f'domlogo-files/logos/{organization_id}.png'
if os.path.isfile(potential_organization_logo):
organization_logo = potential_organization_logo
cache[-1] = (organization_logo, f's{sid}/j{jid}\n{verdict}', color, jid)
for i in range(len(cache)):
previous_column[i][0].update(filename=cache[i][0])
previous_column[i][1].update(cache[i][1])
Expand Down
31 changes: 14 additions & 17 deletions provision-contest/ansible/roles/domlogo/files/README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
# Generating logos from a Contest Package
# Preparing DOMlogo

```bash
for team in $(cat ~/wf2021/contests/finals/teams.json | jq -r '.[].id'); do
echo $team
ORG_ID=$(cat ~/wf2021/contests/finals/teams.json | jq -r ".[] | select(.id == \"$team\") | .organization_id")
convert ~/wf2021/contests/finals/organizations/$ORG_ID/logo.png -resize 64x64 -background none -gravity center -extent 64x64 $team.png
done
```
First, create the following files:
- `images/logos/DOMjudge.png`, a 64x64 DOMjudge logo with transparent background.
- `images/photos/crew.png`, an image with a width of 1024 (and any height) to show for teams without a photo.
- `images/photos/idle.png`, an image with a width of 1024 (and any height) to show when the judgedaemon is idle.

Next, add the needed Python dependencies to the `lib` folder, within a folder called `python3.8`. You can copy this
folder from a local machine and it should contain the `PySimpleGUI` and `requests` Python packages.

# Generating photos from a Contest package
Optionally you can create a file `images/config.yaml` with something like:

```yaml
host-bg-color: '#013370'
```
```bash
for team in $(cat ~/wf2021/contests/finals/teams.json | jq -r '.[].id'); do
echo $team
ORG_ID=$(cat ~/wf2021/contests/finals/teams.json | jq -r ".[] | select(.id == \"$team\") | .organization_id")
TEAM_NAME=$(cat ~/wf2021/contests/finals/teams.json | jq -r ".[] | select(.id == \"$team\") | .display_name")
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
done
```
DOMlogo will use the DOMjudge API to download logos and photos for all teams, so no further configuration should be needed.
5 changes: 3 additions & 2 deletions provision-contest/ansible/roles/domlogo/tasks/main.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
---
# These tasks install domlogo

- name: Install python3 modules for domlogo
- name: Install python3 modules and imagemagick for domlogo
apt:
state: present
pkg:
- python3-tk
- imagemagick

- name: Install domlogo
copy:
Expand All @@ -17,7 +18,7 @@
loop:
- domlogo

- name: Install domlogo
- name: Install domlogo Python libraries
synchronize:
src: lib
dest: /home/domjudge/.local/
Expand Down

0 comments on commit a356c53

Please sign in to comment.