Automated Database Populator

Python utility for generating coherent fake books, users, profiles, and cover images for Django development.

Python · Django · Faker · Pillow

README.md
# Automated Database Populator

**Video Demo:** https://youtu.be/enrY6JeYuJE

## Description

Automated Database Populator Python-based tool designed to automatically generate and populate data for a Django Book Management System. This project was developed as part of the CS50 Python course. My goal was to demonstrate the ability to automate tasks in a web-based application, also created by myself.

## Project Files

- `db_populator.py`: It contains functions to connect to the Django database, create genres, books, users, and their profiles. Each function is responsible for generating random but coherent data to populate the database.
    - `connect_to_db()`: Sets up the connection to the Django database.
    - `create_genres()`: Populates the database with a predefined list of book genres.
    - `create_books()`: Generates and adds books with randomly generated titles, authors, and other relevant details.
    - `create_users()`: Creates user accounts along with their profiles, including a default profile picture and a personalized bio.
-`README.md`file: This readme file 
- requirements.txt

## Design Choices

One of the critical design decisions in this project was choosing to generate random data for books and users instead of scraping content from external sources. This approach was taken to avoid any issues related to the collection or generation of sensitive data, ensuring the project remains ethical and privacy-focused.

The project uses the Faker library to create realistic names, titles, and other text-based data. For image generation, the Pillow library is used to create unique book covers with random shapes and colors. This approach provides a diverse range of data, making the book management system feel more authentic for demonstration purposes.

## User and Profile Generation

The `create_users` function also associates each account with a profile. The profile includes a preset bio template that incorporates the user's username to personalize the experience. Additionally, a default profile picture is assigned to each profile, ensuring that every account has a complete set of data.

The Automated Database Populator uses scripting to automate the process of data generation in web applications. It's a practical tool for quickly setting up a rich dataset for a book management system.
db_populator.py
import sys
sys.path.append('/code')

import os
import django
from faker import Faker
from PIL import Image, ImageDraw, ImageFont
import random
import secrets

def connect_to_db():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'library_demo_project.settings')
    django.setup()

def create_genres():
    from library_demo_app.models import Genre  # Import after Django setup
    genres = ["Fantasy", "Science Fiction", "Mystery", "Thriller", "Romance", "Non-Fiction", "Test Genre"]
    for genre_name in genres:
        Genre.objects.get_or_create(name=genre_name)

def generate_linear_gradient(start_color, end_color, width, height):
    """Create a linear gradient from start_color to end_color."""
    base = Image.new('RGB', (width, height), start_color)
    top = Image.new('RGB', (width, height), end_color)
    mask = Image.new('L', (width, height))
    mask_data = []
    for y in range(height):
        mask_data.extend([int(255 * (y / height))] * width)
    mask.putdata(mask_data)
    base.paste(top, (0, 0), mask)
    return base

def generate_cover_image(title, media_directory):
    width, height = 320, 500
    start_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    end_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    img = generate_linear_gradient(start_color, end_color, width, height)
    safe_title = title.replace('/', '_')

    d = ImageDraw.Draw(img)
    font = ImageFont.load_default()

    # Add random shapes
    for _ in range(random.randint(1, 5)):  # Random number of shapes
        shape_type = random.choice(['rectangle', 'ellipse', 'triangle', 'square'])
        x0 = random.randint(0, width)
        y0 = random.randint(0, height)
        x1 = random.randint(x0, width)
        y1 = random.randint(y0, height)
        shape_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        if shape_type == 'rectangle':
            d.rectangle([x0, y0, x1, y1], outline=shape_color, fill=None)
        elif shape_type == 'ellipse':
            d.ellipse([x0, y0, x1, y1], outline=shape_color, fill=None)
        elif shape_type == 'triangle':
            d.polygon([x0, y1, (x0 + x1) // 2, y0, x1, y1], outline=shape_color, fill=None)
        elif shape_type == 'square':
            size = min(x1 - x0, y1 - y0)  
            d.rectangle([x0, y0, x0 + size, y0 + size], outline=shape_color, fill=None)

    max_chars = 25  # Adjust as needed
    displayed_title = title if len(title) <= max_chars else title[:max_chars-3] + '...'
    d.text((10, 50), displayed_title, fill=(255, 255, 255), font=font)

    # Custom message
    d.text((10, 150), "Generated by BookTracker Populator", fill=(255, 255, 255), font=font)

    if not media_directory.endswith('/'):
        media_directory += '/'

    filename = f"{title.replace(' ', '_').replace('/', '_')}.png"
    img_path = os.path.join(media_directory, 'covers', filename)
    img.save(img_path)


    return 'covers/' + filename

def create_books():
    from library_demo_app.models import Book, Genre

    faker = Faker()
    number_of_books = 30  # Any number of books to be generated
    media_directory = ('/code/media')

    all_genres = list(Genre.objects.all())
    random.shuffle(all_genres)

    for _ in range(number_of_books):
        title = faker.catch_phrase().title()
        author = faker.name()
        isbn = faker.isbn13().replace('-', '')  # Remove dashes from the ISBN
        pages = random.randint(50, 1000)
        publication_date = faker.date_between(start_date='-10y', end_date='today')
        summary = faker.text(max_nb_chars=400)
        
        cover_image_filename = generate_cover_image(title, media_directory)

        book, created = Book.objects.get_or_create(
            title=title,
            author=author,
            isbn=isbn,
            defaults={
                'pages': pages,
                'publication_date': publication_date,
                'summary': summary,
                'cover_image': cover_image_filename
            }
        )

        # Randomly assign a few genres
        num_genres_to_assign = random.randint(1, 3)
        selected_genres = all_genres[:num_genres_to_assign]

        for genre in selected_genres:
            book.genres.add(genre)

        random.shuffle(all_genres)

def create_users(num_users=30):
    from django.contrib.auth.models import User
    from library_demo_app.models import Profile
    faker = Faker()
    created_users = []

    for _ in range(num_users):
        username = faker.unique.user_name()
        email = faker.email()
        password = secrets.token_urlsafe()  # random password

        user, created = User.objects.get_or_create(username=username, email=email)
        if created:
            user.set_password(password)
            user.save()

            # Create and associate a profile with the user
            profile, profile_created = Profile.objects.get_or_create(user=user)
            if profile_created:

                bio_template = (
                    f"Hi, I'm {user.username}! 📚 I'm a passionate bookworm with a never-ending love for literature. "
                    "When I'm not nose-deep in a good book, you can find me sipping on a cup of tea, enjoying the great outdoors, "
                    "or curled up with my furry reading companion, Whiskers. I'm a firm believer that books have the power to transport "
                    "us to different worlds and broaden our horizons. My reading journey is a mix of genres, from classic literature to "
                    "contemporary fiction, and I'm always on the lookout for hidden gems."
                )
                profile.bio = bio_template

                profile_photo_path = 'profile_photos/demo.png'  
                profile.profile_photo = profile_photo_path

                profile.location = "Storyville"

                birth_date = faker.date_of_birth(minimum_age=22, maximum_age=52)
                profile.birth_date = birth_date
                
                profile.save()

            created_users.append(user)
    
    return created_users

def main():
    connect_to_db()
    create_genres()
    create_books()
    create_users()

if __name__ == "__main__":
    main()
requirements.txt
# Last update pending

imgkit==1.2.3
Pillow==10.2.0  #  if using higher version from script
sqlparse==0.5.1  # Higher version from script requirements
urllib3==2.2.1

Faker==22.0.0

Back to Home