2025
This commit is contained in:
1
src/apps/generate/__init__.py
Normal file
1
src/apps/generate/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from .calendar import app
|
||||
54
src/apps/generate/calendar.py
Normal file
54
src/apps/generate/calendar.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import typer
|
||||
from rich.console import Console
|
||||
from typing import Annotated, List
|
||||
from pathlib import Path
|
||||
from ...utils.sportspress import validate_keys
|
||||
from ...utils.normalize import normalize_header_key, load_config
|
||||
from ...utils.common import read_and_normalize_csv, is_visitor_home_order_reversed, import_gamebygame, parse_datetime, personalize_data_for_team
|
||||
from ...utils.sportspress import write_sportspress_csv
|
||||
from .calendar_utils import generate_calendar
|
||||
from collections import defaultdict
|
||||
import toml
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
@app.command(name="calendar")
|
||||
def generate_calendar_app(
|
||||
input_file: Annotated[List[typer.FileText], typer.Argument(..., help="Path(s) to the CSV file")],
|
||||
):
|
||||
# Read CSV data
|
||||
data = read_and_normalize_csv(input_file)
|
||||
data = personalize_data_for_team(data, "Hounds")
|
||||
# data = parse_datetime(data)
|
||||
|
||||
generate_calendar(data)
|
||||
pass
|
||||
|
||||
@app.command(name="calendar-config")
|
||||
def generate_calendar_configs(
|
||||
input_file: Annotated[List[typer.FileText], typer.Argument(..., help="Path(s) to the CSV file")],
|
||||
output_file: Annotated[Path, typer.Argument(..., help="Path(s) to the output config file")]
|
||||
):
|
||||
data = read_and_normalize_csv(input_file)
|
||||
teams = {row.get('visitor') for row in data}
|
||||
teams.update({row.get('home') for row in data})
|
||||
fields = {row.get('field') for row in data}
|
||||
config = defaultdict(dict)
|
||||
config['fields']['default'] = {
|
||||
'bg_color': (0, 0, 0, 256)
|
||||
}
|
||||
config['teams']['default'] = {
|
||||
'logo': ''
|
||||
}
|
||||
for field in fields:
|
||||
config['fields'][field] = config['fields']['default']
|
||||
for team in teams:
|
||||
config['teams'][team] = config['teams']['default']
|
||||
|
||||
if output_file.is_dir:
|
||||
output_file = output_file.joinpath('calendar_config.toml')
|
||||
|
||||
with output_file.open('w') as f:
|
||||
toml.dump(config, f)
|
||||
|
||||
pass
|
||||
200
src/apps/generate/calendar_utils.py
Normal file
200
src/apps/generate/calendar_utils.py
Normal file
@@ -0,0 +1,200 @@
|
||||
|
||||
from calendar import Calendar
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from typing import Tuple
|
||||
from pathlib import Path
|
||||
|
||||
calendar_cell_size = (400, 500)
|
||||
calendar_cell_width, calendar_cell_height = calendar_cell_size
|
||||
|
||||
def textsize(text, font):
|
||||
im = Image.new(mode="P", size=(0, 0))
|
||||
draw = ImageDraw.Draw(im)
|
||||
_, _, width, height = draw.textbbox((0, 0), text=text, font=font)
|
||||
return width, height
|
||||
|
||||
def corner_image():
|
||||
return Image.new()
|
||||
|
||||
def text_rectangle(text:str, font: str, font_size: int, foreground_color: Tuple[int, int, int, int]=(0,0,0,255), background_color: Tuple[int, int, int, int]=(0,0,0,0), height: int=400, width: int=500) -> Image:
|
||||
font_obj = ImageFont.truetype(font,font_size)
|
||||
img = Image.new('RGBA', (int(width),int(height)), background_color)
|
||||
draw = ImageDraw.Draw(img)
|
||||
text = str(text)
|
||||
text_width, text_height = textsize(text, font=font_obj)
|
||||
x = (width - text_width) / 2
|
||||
y = (height - text_height) / 2
|
||||
text_position = (x,y)
|
||||
draw.text(text_position, text, font=font_obj, fill=foreground_color)
|
||||
return img
|
||||
|
||||
def calendar_cell(
|
||||
height: int=calendar_cell_height, width: int=calendar_cell_width,
|
||||
background_color: Tuple[int, int, int, int]=(0,0,0,0),
|
||||
left_top_corner = None,
|
||||
right_top_corner = None,
|
||||
top_center = None,
|
||||
right_bottom_corner = None,
|
||||
bottom_center = None,
|
||||
left_bottom_corner = None,
|
||||
center = None
|
||||
):
|
||||
# Create a blank rectangle image
|
||||
cell_img = Image.new('RGBA', (width, height), background_color)
|
||||
|
||||
# Left top corner
|
||||
if left_top_corner:
|
||||
paste_position = (0, 0)
|
||||
cell_img.paste(left_top_corner, paste_position, left_top_corner)
|
||||
|
||||
# Right top corner
|
||||
if right_top_corner:
|
||||
paste_position = (width - right_top_corner.width, 0)
|
||||
cell_img.paste(right_top_corner, paste_position, right_top_corner)
|
||||
|
||||
if top_center:
|
||||
raise NotImplementedError
|
||||
|
||||
if right_bottom_corner:
|
||||
paste_position = (width - right_bottom_corner.width, height - right_bottom_corner.height)
|
||||
cell_img.paste(right_bottom_corner, paste_position, right_bottom_corner)
|
||||
|
||||
if bottom_center:
|
||||
paste_position = ((width - bottom_center.width)//2, (height - bottom_center.height))
|
||||
cell_img.paste(bottom_center, paste_position, bottom_center)
|
||||
|
||||
if left_bottom_corner:
|
||||
raise NotImplementedError
|
||||
|
||||
if center:
|
||||
paste_position = ((width - center.width)//2, (height - center.height)//2)
|
||||
cell_img.paste(center, paste_position, center)
|
||||
|
||||
return cell_img
|
||||
|
||||
def generate_calendar(data):
|
||||
result_calendar = Calendar()
|
||||
result_calendar.setfirstweekday(6)
|
||||
baseball_bat = Image.open(f"data/logos/baseball_bat_2.png")
|
||||
baseball_bat = baseball_bat.resize((90, 90))
|
||||
for year, month in {(row['datetime'].year, row['datetime'].month) for row in data}:
|
||||
month_days=list(result_calendar.monthdayscalendar(year, month))
|
||||
month_image = Image.new('RGBA', (calendar_cell_width*7, calendar_cell_height*len(month_days)), (0, 0, 0, 0))
|
||||
first_thursday=(month, [w[4] for w in month_days if w[4] != 0][0])
|
||||
|
||||
colors = {
|
||||
# 'proviso-west': (139, 69, 19, 256),
|
||||
'winnemac': (37, 89, 164, 256),
|
||||
# 'walther': (251, 231, 77, 256),
|
||||
# 'taft': (64, 119, 0, 256),
|
||||
'southwest': (230, 136, 60, 256),
|
||||
# 'maywood': (107, 5, 4, 256),
|
||||
# 'ozinga': (170, 143, 102, 256),
|
||||
# 'simeon':(167,192,226),
|
||||
'Skokie':(72,159,88),
|
||||
# 'comed':(206,45,137),
|
||||
'default': (0, 0, 0, 256),
|
||||
'Loyola': (206,45,137),
|
||||
'Piotrowski': (251, 231, 77, 256),
|
||||
'Baseball Alley': (167,192,226),
|
||||
}
|
||||
|
||||
for week_num, week in enumerate(month_days):
|
||||
for day_num, date in enumerate(week):
|
||||
date_text_image = text_rectangle(date,
|
||||
"data/fonts/refrigerator-deluxe-bold.otf",
|
||||
100,
|
||||
foreground_color='white',
|
||||
height=calendar_cell_height*.25,
|
||||
width=calendar_cell_width*.25)
|
||||
if filtered_data := [row for row in data if row['datetime'].month == month and row['datetime'].day == date]:
|
||||
# Gen square that has one game
|
||||
if len (filtered_data) == 1:
|
||||
game = filtered_data[0]
|
||||
opponent_logo_path = Path(f"data/logos/{game['opponent'].lower()}.png")
|
||||
if opponent_logo_path.exists():
|
||||
opponent_logo = Image.open(f"data/logos/{game['opponent'].lower()}.png")
|
||||
else:
|
||||
opponent_logo = text_rectangle(text=game['opponent'][0].upper(),width=500, height=500, font_size=400, font="data/fonts/college.ttf")
|
||||
is_home_game = game['homevisitor'] == "home"
|
||||
if game.get('wood','').lower() == 'yes':
|
||||
right_bottom_corner = baseball_bat
|
||||
else:
|
||||
right_bottom_corner = None
|
||||
img = calendar_cell(
|
||||
height=calendar_cell_height,
|
||||
width=calendar_cell_width,
|
||||
background_color=colors[game['field']],
|
||||
left_top_corner = text_rectangle('H' if is_home_game else "A",
|
||||
"data/fonts/refrigerator-deluxe-bold.otf",
|
||||
80,
|
||||
foreground_color='black' if is_home_game else 'white',
|
||||
background_color='white' if is_home_game else 'black',
|
||||
height=calendar_cell_height*.2,
|
||||
width=calendar_cell_width*.2),
|
||||
right_top_corner = date_text_image,
|
||||
center = opponent_logo.resize((int(opponent_logo.width*.5), int(opponent_logo.height*.5))),
|
||||
bottom_center = text_rectangle(f"{game['time']:%-I:%M}",
|
||||
"data/fonts/refrigerator-deluxe-bold.otf",
|
||||
120,
|
||||
foreground_color='white',
|
||||
height=calendar_cell_height*.25,
|
||||
width=calendar_cell_width),
|
||||
right_bottom_corner=right_bottom_corner
|
||||
)
|
||||
# img.show()
|
||||
elif len(filtered_data) == 2:
|
||||
game1, game2 = filtered_data[:2]
|
||||
img = calendar_cell(
|
||||
height=calendar_cell_height,
|
||||
width=calendar_cell_width,
|
||||
background_color='red',
|
||||
left_top_corner = text_rectangle('DH',
|
||||
"data/fonts/refrigerator-deluxe-bold.otf",
|
||||
80,
|
||||
foreground_color='black',
|
||||
background_color='white',
|
||||
height=calendar_cell_height*.2,
|
||||
width=calendar_cell_width*.2),
|
||||
right_top_corner = date_text_image,
|
||||
center = opponent_logo.resize((int(opponent_logo.width*.5), int(opponent_logo.height*.5))),
|
||||
bottom_center = text_rectangle(f"{game1['time']:%-I:%M} & {game2['time']:%-I:%M}",
|
||||
"data/fonts/refrigerator-deluxe-bold.otf",
|
||||
80,
|
||||
foreground_color='white',
|
||||
height=calendar_cell_height*.2,
|
||||
width=calendar_cell_width),
|
||||
)
|
||||
pass
|
||||
else:
|
||||
img=calendar_cell(
|
||||
height=calendar_cell_height,
|
||||
width=calendar_cell_width,
|
||||
background_color=(204,204,204,int(256*.85)),
|
||||
right_top_corner = text_rectangle(date,
|
||||
"data/fonts/refrigerator-deluxe-bold.otf",
|
||||
100,
|
||||
foreground_color='black',
|
||||
height=calendar_cell_height*.25,
|
||||
width=calendar_cell_width*.25)
|
||||
)
|
||||
pass
|
||||
|
||||
if date: month_image.paste(img, (img.size[0]*day_num, img.size[1]*week_num), img)
|
||||
month_image.save(f'data/output/{year}-{month}.png')
|
||||
|
||||
# if (month, date) in games_lookup.keys() and not (month, date) == first_thursday:
|
||||
# background_image = game_square([g for g in games if g['dtstart'].month==month and g['dtstart'].day==date])
|
||||
# elif (month, date) in games_lookup.keys() and (month, date) == first_thursday:
|
||||
# background_image = game_square(
|
||||
# [g for g in games if g['dtstart'].month == month and g['dtstart'].day == date], special='open-mic')
|
||||
# elif (month, date) == first_thursday:
|
||||
# background_image = openmic_square(date)
|
||||
# else:
|
||||
# background_image = blank_square(date)
|
||||
|
||||
# if date: month_image.paste(background_image, (background_image.size[0]*day_num, background_image.size[1]*week_num), background_image)
|
||||
|
||||
# month_image.thumbnail((1000,1000))
|
||||
# month_image.save(f'output/{year}-{month}.png')
|
||||
# month_image.show()
|
||||
Reference in New Issue
Block a user