Path: blob/master/Explore/generate_social_media_thumbnails.py
984 views
import os1import numpy as np2import plotly.graph_objects as go3from io import BytesIO4from pathlib import Path5from PIL import Image, ImageDraw, ImageFont6from requests import get7from typing import List89def __get_text_content(url: str) -> str:10return get(url).content.decode('utf-8')1112def __get_json_content(url: str) -> List:13content = __get_text_content(url) \14.replace("null", "None").replace("true", "True").replace("false", "False")15try:16return eval(content)17except:18exit(f'Invalid content for {url}')1920def __get_profile(url: str) -> Image:21if not url or 'icon' in url:22return None2324image = get(url.replace("\\",""), stream=True)25if image.status_code != 200:26return None2728profile = Image.open(image.raw).convert("RGB")2930h,w = profile.size3132# creating luminous image33lum_img = Image.new('L',[h,w] ,0)34draw = ImageDraw.Draw(lum_img)35draw.pieslice([(0,0),(h,w)],0,360,fill=255)36img_arr = np.array(profile)37lum_img_arr = np.array(lum_img)38final_img_arr = np.dstack((img_arr, lum_img_arr))39return Image.fromarray(final_img_arr).resize((80,80))4041def __get_equity_curve(strategy, width=1200, height=400) -> Image:42# Add line43fig = go.Figure()4445x, y = [],[]46statistics = strategy.get('statistics')47if statistics:48for point in statistics.get('sparkline',[]):49x.append(point['time'])50y.append(point['value'])5152fig.add_trace(go.Scatter(x=x, y=y, line=dict(color='#0096FF', width=3)))5354fig.update_layout(55autosize=False,56width=width,57height=height,58paper_bgcolor='rgba(0,0,0,0)',59plot_bgcolor='rgba(0,0,0,0)',60margin=dict(l=0, r=0, t=0, b=0),61xaxis=dict(visible=False),62yaxis=dict(visible=False)63)6465fig_bytes = fig.to_image(format="png")66return Image.open(BytesIO(fig_bytes))6768def __create_landscape(template, strategy, profile) -> Image:69# Create a copy of the template to add elements70copy = template.copy()7172# Add profile picture73copy.paste(profile, (681, 120),mask=profile)7475# Add texts76I1 = ImageDraw.Draw(copy)77name = strategy.get('name')78author_name = strategy.get('authorName')79category = strategy.get('category')80if not category: category = ''81category = category.upper()8283width = name_font.getlength(name)84if width < 530:85I1.text((105,120), name, font=name_font, fill='#313131')86else:87parts = name.split(' ')88for i in reversed(range(len(parts))):89if name_font.getlength(' '.join(parts[:i])) <= 530:90break91I1.text((105,120), ' '.join(parts[:i]), font=name_font, fill='#313131')92I1.text((105,155), ' '.join(parts[i:]), font=name_font, fill='#313131')9394width = author_font.getlength(author_name)95if width < 250:96I1.text((775,157), author_name, font=author_font, fill='#313131')97else:98parts = author_name.split(' ')99for i in reversed(range(len(parts))):100if author_font.getlength(' '.join(parts[:i])) <= 250:101break102if i == 0:103while author_font.getlength(author_name) > 250:104author_name = author_name[:-1]105I1.text((775,157), f'{author_name}...', font=author_font, fill='#313131')106else:107I1.text((775,157), ' '.join(parts[:i]), font=author_font, fill='#313131')108I1.text((775,182), ' '.join(parts[i:]), font=author_font, fill='#313131')109110I1.text((105,200), category, font=category_font,111fill=colors_by_category.get(category, '#313131'))112113fig = __get_equity_curve(strategy)114copy.paste(fig, (0, 210),mask=fig)115return copy116117def __create_square(template, strategy, profile) -> Image:118# Create a copy of the template to add elements119copy = template.copy()120121# Add texts122I1 = ImageDraw.Draw(copy)123name = strategy.get('name')124author_name = strategy.get('authorName')125category = strategy.get('category')126if not category: category = ''127category = category.upper()128129width = name_font.getlength(name)130if width < 650:131I1.text((42,160), name, font=name_font, fill='#313131')132else:133parts = name.split(' ')134for i in reversed(range(len(parts))):135if name_font.getlength(' '.join(parts[:i])) <= 650:136break137I1.text((42,160), ' '.join(parts[:i]), font=name_font, fill='#313131')138I1.text((42,195), ' '.join(parts[i:]), font=name_font, fill='#313131')139140I1.text((42,240), category, font=category_font,141fill=colors_by_category.get(category, '#313131'))142143# Add profile picture144copy.paste(profile, (42, 325),mask=profile)145146I1.text((137, 360), author_name, font=author_font, fill='#313131')147148fig = __get_equity_curve(strategy, 860, 285)149copy.paste(fig, (0, 440),mask=fig)150return copy151152colors_by_category = {153'CRYPTO': '#C39E78',154'EQUITIES': '#BF7C7C',155'US EQUITIES': '#9A74BF',156'ETF': '#B44444',157'FOREX': '#5FB26F',158'FUTURES': '#5D6586'159}160161if __name__ == '__main__':162163os.chdir(os.path.dirname(os.path.abspath(__file__)))164destination_folder = Path('thumbnails')165destination_folder.mkdir(parents=True, exist_ok=True)166167template_landscape = Image.open('template_landscape.png')168template_square = Image.open('template_square.png')169name_font = ImageFont.FreeTypeFont('Inter font/static/Inter-SemiBold.ttf', 35)170category_font = ImageFont.FreeTypeFont('Inter font/static/Inter-Regular.ttf', 20)171author_font = ImageFont.FreeTypeFont('Inter font/static/Inter-Regular.ttf', 24)172default_photo = Image.open('default_photo.png').resize((80,80))173profile_errors = set()174content = __get_json_content('https://www.quantconnect.com/api/v2/sharing/strategies/list/')175strategies = content.get('strategies', [])176177for strategy in strategies:178id = strategy.get('projectId')179if not id:180print(f'No project Id for {strategy}')181continue182183profile_picture = __get_profile(strategy['authorProfile'])184if not profile_picture:185profile_picture = default_photo186profile_errors.add(strategy.get("authorName"))187188landscape = __create_landscape(template_landscape, strategy, profile_picture)189landscape.save(f"thumbnails/{id}.png")190191square = __create_square(template_square, strategy, profile_picture)192square.save(f"thumbnails/{id}_square.png")193194if profile_errors:195print("Cannot download profile images of: " + ','.join(profile_errors))196197