Instantly share code, notes, and snippets.
Last active
April 24, 2017 17:40
-
Save Omochin/35764432bd896b916a1dde6e8b29f2bc to your computer and use it in GitHub Desktop.
This script creates a group photo of players listed in "related" of zKillboard.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# pylint: disable=C0103,W0603 | |
import sys | |
import os | |
import json | |
import math | |
import time | |
import logging | |
import distutils.util | |
import requests | |
from PIL import Image, ImageDraw, ImageFont | |
IMAGES_PATH = 'images' | |
TIMEOUT_SLEEP_TIME = 300 | |
REQUEST_SLEEP_TIME = 10 | |
MAX_REQUEST_RETRY_COUNT = 5 | |
OPTIONS = [ | |
('--portrait_size', 'Size of the portrait to download. default 512(pixel)'), | |
('--aspect_ratio', 'The aspect ratio of the generated image. default 16:9'), | |
('--select_corporations', 'Corporations that you want to select'), | |
('--select_alliances', 'Alliances that you want to select'), | |
('--priority_characters', 'Characters you want to display at the top'), | |
('--ignore_characters', 'Characters you do not want to display'), | |
('--reload_json', 'Always download JSON from API of zKillboard. default True'), | |
] | |
logger = logging.getLogger(__name__) | |
aspect_ratio_vertical = 16 | |
aspect_ratio_horizontal = 9 | |
select_corporations = [] | |
select_alliances = [] | |
priority_characters = [] | |
ignore_characters = [] | |
portrait_size = 512 | |
reload_json = True | |
def initialize(): | |
global aspect_ratio_vertical, aspect_ratio_horizontal | |
global select_corporations, select_alliances | |
global priority_characters | |
global ignore_characters | |
global portrait_size | |
global reload_json | |
logger.setLevel(logging.INFO) | |
stream_handler = logging.StreamHandler(sys.stdout) | |
stream_handler.setLevel(logging.INFO) | |
logger.addHandler(stream_handler) | |
if len(sys.argv) < 2: | |
logger.info(''' | |
usage: python zkillboard_related_image_generator.py api_url [option] | |
api_url: https://zkillboard.com/api/related/<System ID>/<Time>/ | |
Options and arguments:''') | |
for option in OPTIONS: | |
logger.info('{0:<22}: {1}'.format(option[0], option[1])) | |
logger.info('\nPlease separate with ":" if there is more than one') | |
exit() | |
if len(sys.argv) >= 3: | |
for argument in sys.argv[2:]: | |
try: | |
key, value = argument.split('=') | |
if key == '--aspect_ratio': | |
values = value.split(':') | |
if len(values) == 2: | |
vertical = int(values[0]) | |
horizontal = int(values[1]) | |
if vertical and horizontal: | |
aspect_ratio_vertical = int(vertical) | |
aspect_ratio_horizontal = int(horizontal) | |
elif key == '--select_corporations': | |
select_corporations = value.split(':') | |
elif key == '--select_alliances': | |
select_alliances = value.split(':') | |
elif key == '--priority_characters': | |
priority_characters = value.split(':') | |
elif key == '--ignore_characters': | |
ignore_characters = value.split(':') | |
elif key == '--portrait_size': | |
size = int(value) | |
if size: | |
portrait_size = size | |
elif key == '--reload_json': | |
reload_json = distutils.util.strtobool(value) | |
except ValueError: | |
pass | |
def get_sorted_characters(characters): | |
ctickers = [] | |
atickers = [] | |
notickers = [] | |
for character in characters: | |
if 'characterName' not in character: | |
continue | |
if character['characterName'] in ignore_characters: | |
continue | |
get_ticker = lambda name: character[name] if name in character else '' | |
cticker = get_ticker('cticker') | |
aticker = get_ticker('aticker') | |
def append(tickers): | |
tickers.append(( | |
character['characterID'], | |
character['characterName'], | |
cticker, | |
aticker, | |
)) | |
if aticker: | |
append(atickers) | |
elif cticker: | |
append(ctickers) | |
else: | |
append(notickers) | |
return sorted(atickers, key=lambda character: (character[3], character[1])) + \ | |
sorted(ctickers, key=lambda character: (character[2], character[1])) + \ | |
sorted(notickers, key=lambda character: character[1]) | |
def get_selected_characters(characters): | |
selected_characters = [] | |
def append(condition): | |
for character in characters: | |
if condition(character): | |
selected_characters.append(character) | |
for character_name in priority_characters: | |
append(lambda character: character[1] == character_name) | |
for aticker in select_alliances: | |
append(lambda character: character[3] == aticker) | |
for cticker in select_corporations: | |
append(lambda character: character[2] == cticker) | |
if len(select_alliances) == 0 and len(select_corporations) == 0: | |
for character in characters: | |
selected_characters.append(character) | |
return sorted(set(selected_characters), key=selected_characters.index) | |
def get_json(url, path): | |
if not reload_json and os.path.isfile(path): | |
logger.info('PASS - %s' % path) | |
return True | |
request = requests.get(url) | |
if request.status_code == 200: | |
logger.info('SUCCESS - %s' % url) | |
with open('export.json', 'w') as fout: | |
fout.write(request.content.decode()) | |
return True | |
else: | |
logger.error('FAILED - %s' % url) | |
return False | |
def get_image(url, path, count=0): | |
if os.path.isfile(path): | |
logger.info('PASS - %s' % path) | |
return False | |
try: | |
response = requests.get(url, allow_redirects=False, timeout=10) | |
if response.status_code != 200: | |
logger.error('ERROR - status code %d: %s' % (response.status_code, url)) | |
return False | |
content_type = response.headers["content-type"] | |
if 'image' not in content_type: | |
logger.error('ERROR - Content-Type: %s' % content_type) | |
return False | |
with open(path, 'wb') as fout: | |
logger.info('SUCCESS - %s' % path) | |
fout.write(response.content) | |
except requests.exceptions.ConnectTimeout: | |
logger.warning('RETRY %d - %s' % (count, url)) | |
time.sleep(TIMEOUT_SLEEP_TIME) | |
if count < MAX_REQUEST_RETRY_COUNT: | |
return get_image(url, path, count+1) | |
else: | |
return False | |
return True | |
def create_image(characters): | |
length = len(characters) | |
aspect_ratio = aspect_ratio_vertical / aspect_ratio_horizontal | |
aspect_ratio_diff = length | |
vertical = 0 | |
horizontal = 0 | |
for i in range(length): | |
horizontal = length - i | |
vertical = math.ceil(length / horizontal) | |
radio = vertical / horizontal | |
diff = math.fabs(aspect_ratio - radio) | |
if aspect_ratio_diff < diff: | |
break | |
aspect_ratio_diff = diff | |
canvas = Image.new( | |
'RGB', | |
(vertical * portrait_size, horizontal * portrait_size), | |
(255, 255, 255), | |
) | |
if not os.path.isdir(IMAGES_PATH): | |
os.mkdir(IMAGES_PATH) | |
for i, character in enumerate(characters): | |
character_name = character[1] | |
image_path = os.path.join(IMAGES_PATH, '%s_%d.jpg' % (character_name, portrait_size)) | |
image_url = 'https://image.eveonline.com/Character/%d_%d.jpg' \ | |
% (character[0], portrait_size) | |
if get_image(image_url, image_path): | |
time.sleep(REQUEST_SLEEP_TIME) | |
canvas.paste( | |
Image.open(image_path, 'r'), | |
( | |
int(i % vertical) * portrait_size, | |
int(i / vertical) * portrait_size, | |
) | |
) | |
canvas.save('export.jpg', 'JPEG', quality=100, optimize=True) | |
def main(): | |
json_path = 'export.json' | |
logger.info('LOAD - %s' % json_path) | |
if not get_json(sys.argv[1], json_path): | |
logger.error('NOT FOUND - %s' % json_path) | |
with open(json_path) as file: | |
summary = json.load(file)['summary'] | |
characters = get_selected_characters( | |
get_sorted_characters(summary['teamA']['list'] + summary['teamB']['list']) | |
) | |
logger.info('CREATE IMAGE') | |
create_image(characters) | |
with open('export.txt', 'w') as fout: | |
for character in characters: | |
corporation_name = character[2] | |
alliance_name = character[3] | |
text = '' | |
if alliance_name in select_alliances: | |
text = '[%s] ' % alliance_name | |
elif corporation_name in select_corporations: | |
text = '[%s] ' % corporation_name | |
elif alliance_name: | |
text = '[%s] ' % alliance_name | |
elif corporation_name: | |
text = '[%s] ' % corporation_name | |
fout.write('%s%s\n' % (text, character[1])) | |
if __name__ == '__main__': | |
initialize() | |
main() | |
logger.info('FINISHED') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment