Based on your description, you have a subscription-based image generation system in Python with three users:

Daddy: Platinum user
Bob: Gold user
Mary: Regular user
Each subscription level has different features:

Regular:
Receives one image per day automatically created at 7 AM
No logo added to images
One template
Standard image ratio (4:3)
Gold:
Receives two images per day at specified times
Receives their logo added to images
Two templates (e.g., one for regular business, one for specials)
Standard image ratio (4:3)
Can schedule templates on preferred days
Platinum:
Can choose up to three images per day at specified times
Can have three templates running when they choose
Can choose among their logo, neon logo, or random logo
Can have any normal image ratio (4:3, 16:9, 1:1)
Below is the implementation of your system with the users and their specific settings.

1. Define Subscription Plans and Users
First, define the SubscriptionPlan and User classes:


from dataclasses import dataclass, field
from typing import List, Optional
import datetime

@dataclass
class SubscriptionPlan:
    name: str
    max_images_per_day: int
    logo_options: List[str]
    templates_allowed: int
    image_ratios: List[tuple]

@dataclass
class Template:
    name: str
    content: str  # Path to the template image
    days_of_week: Optional[List[str]] = None  # Days when the template is active

@dataclass
class User:
    username: str
    subscription: SubscriptionPlan
    scheduled_times: List[datetime.time] = field(default_factory=list)
    templates: List[Template] = field(default_factory=list)
    logo_choice: str = None
Define Subscription Plans

# Regular Plan
regular_plan = SubscriptionPlan(
    name="Regular",
    max_images_per_day=1,
    logo_options=[],  # No logos
    templates_allowed=1,
    image_ratios=[(4, 3)],  # Standard ratio
)

# Gold Plan
gold_plan = SubscriptionPlan(
    name="Gold",
    max_images_per_day=2,
    logo_options=["default_logo"],  # Their logo added
    templates_allowed=2,
    image_ratios=[(4, 3)],  # Standard ratio
)

# Platinum Plan
platinum_plan = SubscriptionPlan(
    name="Platinum",
    max_images_per_day=3,
    logo_options=["default_logo", "neon_logo", "random"],
    templates_allowed=3,
    image_ratios=[(4, 3), (16, 9), (1, 1)],  # Any normal image ratio
)
Create Users

# Platinum User: Daddy
daddy = User(username="Daddy", subscription=platinum_plan)

# Gold User: Bob
bob = User(username="Bob", subscription=gold_plan)

# Regular User: Mary
mary = User(username="Mary", subscription=regular_plan)
2. Set Up User Preferences
Daddy (Platinum User)

# Scheduled times (up to 3 images per day)
daddy.scheduled_times = [
    datetime.time(9, 0),
    datetime.time(12, 0),
    datetime.time(15, 0)
]

# Logo choice: "default_logo", "neon_logo", or "random"
daddy.logo_choice = "random"

# Templates (up to 3)
daddy.templates = [
    Template(name="Template1", content="template1.png"),
    Template(name="Template2", content="template2.png"),
    Template(name="Template3", content="template3.png"),
]
Bob (Gold User)

# Scheduled times (exactly 2 images per day at specified times)
bob.scheduled_times = [
    datetime.time(10, 0),
    datetime.time(14, 0)
]

# Logo choice: Only "default_logo" for Gold users
bob.logo_choice = "default_logo"

# Templates (2 templates with scheduled days)
bob.templates = [
    Template(
        name="Regular Business",
        content="regular_business_template.png",
        days_of_week=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
    ),
    Template(
        name="Specials",
        content="specials_template.png",
        days_of_week=['Saturday', 'Sunday']
    ),
]
Mary (Regular User)

# Scheduled time (fixed at 7 AM)
mary.scheduled_times = [datetime.time(7, 0)]  # Automatically at 7 AM

# Logo choice: None
mary.logo_choice = None

# Templates (only 1)
mary.templates = [
    Template(name="Regular Template", content="regular_template.png")
]
3. Scheduling Tasks
Install the schedule library if you haven't:


pip install schedule
Set up the scheduling functions:


import schedule
import time
import datetime

def send_image(user: User, template: Template):
    # Function to create and send image to the user
    print(f"Sending image to {user.username} using template {template.name} with logo option '{user.logo_choice}'")

# Regular User Schedule (Mary)
def schedule_regular_user(user: User):
    time_str = user.scheduled_times[0].strftime("%H:%M")
    schedule.every().day.at(time_str).do(send_image, user=user, template=user.templates[0])

# Gold User Schedule (Bob)
def schedule_gold_user(user: User):
    for scheduled_time in user.scheduled_times:
        time_str = scheduled_time.strftime("%H:%M")
        schedule.every().day.at(time_str).do(send_image_gold_user, user=user)

def send_image_gold_user(user: User):
    today = datetime.datetime.now().strftime('%A')
    for template in user.templates:
        if today in template.days_of_week:
            send_image(user, template)
            return
    # Fallback to the first template if no match
    send_image(user, user.templates[0])

# Platinum User Schedule (Daddy)
def schedule_platinum_user(user: User):
    for i, scheduled_time in enumerate(user.scheduled_times):
        time_str = scheduled_time.strftime("%H:%M")
        schedule.every().day.at(time_str).do(send_image_platinum_user, user=user, index=i)

def send_image_platinum_user(user: User, index: int):
    template = user.templates[index % len(user.templates)]
    send_image(user, template)

# Set up schedules based on subscription
def setup_user_schedule(user: User):
    if user.subscription.name == "Regular":
        schedule_regular_user(user)
    elif user.subscription.name == "Gold":
        schedule_gold_user(user)
    elif user.subscription.name == "Platinum":
        schedule_platinum_user(user)

# Set up schedules for all users
setup_user_schedule(daddy)
setup_user_schedule(bob)
setup_user_schedule(mary)
Note: The send_image functions for Gold and Platinum users are adjusted to select the appropriate template based on the day and the user's preferences.

4. Image Processing
Install the Pillow library:


pip install Pillow
Create Image Function

from PIL import Image, ImageDraw, ImageFont
import os
import random

def create_image(user: User, template: Template):
    # Select image ratio (random for Platinum)
    if user.subscription.name == "Platinum":
        ratio = random.choice(user.subscription.image_ratios)
    else:
        ratio = user.subscription.image_ratios[0]  # Standard ratio (4:3)
    
    width, height = ratio[0] * 100, ratio[1] * 100  # Scale for size

    # Load and resize the template image
    template_path = os.path.join('templates', template.content)
    template_image = Image.open(template_path).resize((width, height))

    draw = ImageDraw.Draw(template_image)
    font = ImageFont.load_default()

    # Add personalized text
    draw.text((10, 10), f"Hello {user.username}!", font=font, fill=(255, 255, 0))

    # Add logo if applicable
    if user.logo_choice:
        if user.logo_choice == "random" and user.subscription.name == "Platinum":
            logo_choice = random.choice(['default_logo', 'neon_logo'])
        else:
            logo_choice = user.logo_choice
        logo_path = os.path.join('logos', f"{logo_choice}.png")
        logo = Image.open(logo_path).resize((50, 50)).convert("RGBA")
        # Paste logo onto the image
        template_image.paste(logo, (width - 60, height - 60), logo)

    # Save the image
    timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
    image_filename = f"{user.username}_{template.name}_{timestamp}.png"
    image_path = os.path.join('output_images', image_filename)
    template_image.save(image_path)
    print(f"Created image for {user.username}: {image_path}")

    return image_path
Explanation:

Template Image: Loaded from the templates directory.
Logo: Loaded from the logos directory.
Output Images: Saved to the output_images directory.
Image Ratio: For Platinum users, a random ratio is selected from the allowed ratios.
Send Image Function

def send_image(user: User, template: Template):
    image_path = create_image(user, template)
    # Integrate email or messaging service to send the image
    print(f"Image sent to {user.username}: {image_path}")
5. Running the Scheduler
Ensure that you have the required directories and images:

templates/: Directory containing template images.
logos/: Directory containing logo images (default_logo.png, neon_logo.png).
output_images/: Directory where generated images will be saved.
Run the scheduler:


if __name__ == "__main__":
    print("Scheduler started...")
    while True:
        schedule.run_pending()
        time.sleep(1)
6. Directory Structure
Your project directory should look like this:


project_directory/
├── main.py              # The script with the scheduler and functions
├── templates/
│   ├── template1.png
│   ├── template2.png
│   ├── template3.png
│   ├── regular_template.png
│   ├── regular_business_template.png
│   └── specials_template.png
├── logos/
│   ├── default_logo.png
│   └── neon_logo.png
├── output_images/
7. Additional Considerations
Error Handling: Add try-except blocks to handle file not found errors or other exceptions.
Dynamic Scheduling: If users can update their preferences, incorporate logic to reschedule jobs accordingly.
Database Integration: Use a database to store user data and preferences for persistence.
Email/SMS Integration: Use libraries like smtplib for emails or APIs like Twilio for SMS to send images.
Security: Ensure that paths and user inputs are validated to prevent security issues.
Example: Sending an Email with the Generated Image
Here's an example of how you might send the generated image via email:


import smtplib
from email.message import EmailMessage

def send_image(user: User, template: Template):
    image_path = create_image(user, template)
    print(f"Image created at {image_path}")

    # Email configuration
    EMAIL_ADDRESS = 'your_email@example.com'
    EMAIL_PASSWORD = 'your_email_password'

    msg = EmailMessage()
    msg['Subject'] = f"Your Daily Image, {user.username}"
    msg['From'] = EMAIL_ADDRESS
    msg['To'] = f'{user.username}@example.com'  # User's email address

    msg.set_content(f"Hi {user.username},\n\nHere is your image for today.")

    with open(image_path, 'rb') as img:
        img_data = img.read()
        img_name = os.path.basename(image_path)
        img_type = 'png'
        msg.add_attachment(img_data, maintype='image', subtype=img_type, filename=img_name)

    # Send the email
    with smtplib.SMTP_SSL('smtp.example.com', 465) as smtp:
        smtp.login(EMAIL_ADDRESS, EMAIL_PASSWORD)
        smtp.send_message(msg)

    print(f"Email sent to {user.username}")
Note: Replace 'your_email@example.com', 'your_email_password', and 'smtp.example.com' with your actual email credentials and SMTP server details.

Conclusion
By integrating user preferences and subscription levels, you've built a system that:

Automatically generates images based on user subscriptions.
Schedules tasks according to each user's plan.
Customizes images with templates and logos.
Sends images to users at specified times.
Remember to:

Test thoroughly to ensure scheduling and image generation work as expected.
Handle exceptions and edge cases, such as missing images or invalid scheduled times.
Secure sensitive information, especially when dealing with user data and email credentials.
Let me know if you need further assistance with specific parts of the implementation or have any questions!


Standard image is 1:1


Thank you for the clarification!

You're specifying that the standard image ratio is 1:1 (a square image) instead of the previously used 4:3 ratio.

I'll update the implementation accordingly to reflect the standard image ratio as 1:1 for all relevant parts of the code and explanations.

Updated Implementation with Standard Image Ratio as 1:1
1. Update Subscription Plans
We'll change the image ratios in the subscription plans to reflect the standard ratio as 1:1.


# Regular Plan
regular_plan = SubscriptionPlan(
    name="Regular",
    max_images_per_day=1,
    logo_options=[],  # No logos
    templates_allowed=1,
    image_ratios=[(1, 1)],  # Standard ratio (1:1)
)

# Gold Plan
gold_plan = SubscriptionPlan(
    name="Gold",
    max_images_per_day=2,
    logo_options=["default_logo"],  # Their logo added
    templates_allowed=2,
    image_ratios=[(1, 1)],  # Standard ratio (1:1)
)

# Platinum Plan
platinum_plan = SubscriptionPlan(
    name="Platinum",
    max_images_per_day=3,
    logo_options=["default_logo", "neon_logo", "random"],
    templates_allowed=3,
    image_ratios=[(1, 1), (16, 9), (4, 3)],  # Any normal image ratio
)
Note:

The Regular and Gold plans have the standard image ratio of 1:1.
The Platinum plan can choose from any normal image ratios including 1:1, 16:9, and 4:3.
2. Update Image Processing Function
In the create_image function, adjust the code to account for the standard image ratio being 1:1.

Updated create_image Function

def create_image(user: User, template: Template):
    # Select image ratio
    if user.subscription.name == "Platinum":
        ratio = random.choice(user.subscription.image_ratios)
    else:
        ratio = user.subscription.image_ratios[0]  # Standard ratio (1:1)
    
    width, height = ratio[0] * 100, ratio[1] * 100  # Scale for size
    
    # Load and resize the template image to match the ratio
    template_path = os.path.join('templates', template.content)
    template_image = Image.open(template_path)
    template_image = template_image.resize((width, height))
    
    draw = ImageDraw.Draw(template_image)
    font = ImageFont.load_default()
    
    # Add personalized text
    draw.text((10, 10), f"Hello {user.username}!", font=font, fill=(255, 255, 0))
    
    # Add logo if applicable
    if user.logo_choice:
        if user.logo_choice == "random" and user.subscription.name == "Platinum":
            logo_choice = random.choice(['default_logo', 'neon_logo'])
        else:
            logo_choice = user.logo_choice
        logo_path = os.path.join('logos', f"{logo_choice}.png")
        logo = Image.open(logo_path).resize((50, 50)).convert("RGBA")
        # Paste logo onto the image
        template_image.paste(logo, (width - 60, height - 60), logo)
    
    # Save the image
    timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
    image_filename = f"{user.username}_{template.name}_{timestamp}.png"
    image_path = os.path.join('output_images', image_filename)
    template_image.save(image_path)
    print(f"Created image for {user.username}: {image_path}")
    
    return image_path
Explanation
For Regular and Gold users, the ratio is set to (1, 1), which translates to a square image.
The template image is resized to match the determined width and height based on the chosen ratio.
3. Update Templates and Template Images
Ensure that the template images used for Regular and Gold users are compatible with the 1:1 ratio.

Templates for Regular and Gold Users: The images should be in 1:1 ratio or adaptable to that ratio without distortion.
Templates for Platinum Users: Can use images with ratios 1:1, 16:9, or 4:3.
4. Update Scheduler and User Preferences (No changes required)
The scheduler and user preferences remain the same, except for the potential need to ensure that template images are compatible with the adjusted ratios.

5. Verify Image Outputs
Run the script and verify that images generated for Regular and Gold users are square (1:1 ratio), and that Platinum users receive images in one of the allowed ratios, potentially including 1:1.

Updated Project Directory Structure
Ensure your project directory has the following structure, with templates adjusted for the new standard ratio:


project_directory/
├── main.py              # The script with the scheduler and functions
├── templates/
│   ├── template1.png
│   ├── template2.png
│   ├── template3.png
│   ├── regular_template.png
│   ├── regular_business_template.png
│   └── specials_template.png
├── logos/
│   ├── default_logo.png
│   └── neon_logo.png
├── output_images/
Templates: Ensure that the images in the templates/ folder are suitable for a 1:1 ratio, or can be resized without distortion.
Example Output
As an example, when the create_image function is called for:

Mary (Regular User):
The image will be a square (1:1 ratio).
No logo will be added.
The base template will be resized to the square dimensions.
Bob (Gold User):
The image will be a square (1:1 ratio).
The default_logo will be added to the image.
The template will be one of the two allowed templates and resized accordingly.
Daddy (Platinum User):
The image ratio will be randomly selected from (1:1), (16:9), or (4:3).
A logo will be added based on the logo_choice, which can be "default_logo", "neon_logo", or "random".
The template will be one of the three allowed templates.
Testing the Changes
To test the updates:

Run the Script: Execute the main script (main.py) to start the scheduler.
Monitor the Output: Check the console output for confirmation messages indicating that images have been created and sent.
Verify the Images:
Navigate to the output_images/ directory.
Open the generated images.
Confirm that for Regular and Gold users, images are square (1:1 ratio).
Confirm that Platinum users have images that may be in 1:1, 16:9, or 4:3 ratios.
Additional Notes
Template Compatibility: If your template images are not in the 1:1 ratio, you might want to adjust them or create new ones that are square or can be resized without losing important content.
Image Resizing and Cropping: To maintain the aspect ratio and prevent distortion, consider adding logic to crop images when resizing.

def resize_image_to_ratio(image, target_width, target_height):
    original_width, original_height = image.size
    original_ratio = original_width / original_height
    target_ratio = target_width / target_height

    if original_ratio > target_ratio:
        # Image is wider than target ratio
        new_height = target_height
        new_width = int(original_ratio * new_height)
    else:
        # Image is taller than target ratio
        new_width = target_width
        new_height = int(new_width / original_ratio)

    image = image.resize((new_width, new_height), Image.ANTIALIAS)

    # Crop the image to the target dimensions
    left = (new_width - target_width) / 2
    top = (new_height - target_height) / 2
    right = (new_width + target_width) / 2
    bottom = (new_height + target_height) / 2

    image = image.crop((left, top, right, bottom))
    return image
Use this function within create_image when resizing the template images to ensure that the content is centered and the aspect ratio is maintained.

# Replace this line
template_image = template_image.resize((width, height))

# With this line
template_image = resize_image_to_ratio(template_image, width, height)
Adjusting Logos and Text Positions: Depending on the image size and ratio, you may need to adjust where text and logos are placed on the image. In the draw.text() and template_image.paste() methods, consider calculating positions relative to the image dimensions.

# For text placement
draw.text((width * 0.05, height * 0.05), f"Hello {user.username}!", font=font, fill=(255, 255, 0))

# For logo placement
logo_size = int(min(width, height) * 0.1)  # Logo size relative to image size
logo = logo.resize((logo_size, logo_size)).convert("RGBA")
template_image.paste(logo, (width - logo_size - 10, height - logo_size - 10), logo)
This way, regardless of the image dimensions, the elements are placed proportionally.
Conclusion
By adjusting the standard image ratio to 1:1, we've updated the code and explanations to reflect this change across the implementation:

Subscription Plans: Updated to set the standard image ratio as 1:1 for Regular and Gold users.
Image Processing: Modified the create_image function to create square images for these users.
Templates and Logos: Ensured that templates and logos are compatible with the new ratio.
Please ensure that your template images are suitable for a 1:1 ratio to prevent distortion when resizing. When done and all working will this be worth anything?