Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Website for Python Ireland (python.ie / pycon.ie) community, built with Django 5
- Docker & Docker Compose (for containerized development - recommended)
- [Task](https://taskfile.dev/) (optional but recommended)
- Redis (only for local non-Docker development)
- Environment variables file as per [the instructions here](#environment-variables)

## Quick Start (Docker - Recommended)

Expand All @@ -27,19 +28,24 @@ Website for Python Ireland (python.ie / pycon.ie) community, built with Django 5
task django:migrate
```

4. Create a superuser:
4. Generate sample data (creates pages, navigation, meetups):
```bash
docker compose run --rm web python pythonie/manage.py generate_sample_data --settings=pythonie.settings.dev
```

5. Create a superuser:
```bash
docker compose run --rm web python pythonie/manage.py createsuperuser --settings=pythonie.settings.dev
```

5. Start the development server:
6. Start the development server:
```bash
task run
# or: docker compose run --rm --service-ports web python pythonie/manage.py runserver 0.0.0.0:8000
```

6. Visit http://127.0.0.1:8000/ in your browser
7. Access Wagtail admin at http://127.0.0.1:8000/admin/
7. Visit http://127.0.0.1:8000/ to see the site with sample content
8. Access Wagtail admin at http://127.0.0.1:8000/admin/

## Local Setup (Without Docker)

Expand All @@ -52,11 +58,13 @@ If you prefer to develop without Docker:
5. Activate the virtualenv: `source pythonie-venv/bin/activate`
6. Install dependencies: `pip install -r requirements.txt` (or `uv pip install -r requirements.txt`)
7. Set up the database: `python pythonie/manage.py migrate --settings=pythonie.settings.dev`
8. Create a superuser: `python pythonie/manage.py createsuperuser --settings=pythonie.settings.dev`
9. Install and run Redis server locally: `redis-server`
10. Set Redis environment variable: `export REDISCLOUD_URL=127.0.0.1:6379`
11. Run the server: `python pythonie/manage.py runserver --settings=pythonie.settings.dev`
12. Visit http://127.0.0.1:8000/admin/ to log in
8. Generate sample data: `python pythonie/manage.py generate_sample_data --settings=pythonie.settings.dev`
9. Create a superuser: `python pythonie/manage.py createsuperuser --settings=pythonie.settings.dev`
10. Install and run Redis server locally: `redis-server`
11. Set Redis environment variable: `export REDISCLOUD_URL=127.0.0.1:6379`
12. Run the server: `python pythonie/manage.py runserver --settings=pythonie.settings.dev`
13. Visit http://127.0.0.1:8000/ to see the site with sample content
14. Visit http://127.0.0.1:8000/admin/ to log in to Wagtail admin

## Project Structure

Expand Down Expand Up @@ -87,6 +95,9 @@ task django:migrate # Run migrations
task django:make-migrations # Create new migrations
task django:collect-static # Collect static files

# Sample Data (for development)
python pythonie/manage.py generate_sample_data --settings=pythonie.settings.dev

# Testing
task tests # Run test suite
make docker-tests # Alternative test command
Expand Down
51 changes: 51 additions & 0 deletions pythonie/core/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import factory
from factory.django import DjangoModelFactory
from django.utils import timezone

from core.models import HomePage, SimplePage
from meetups.models import Meetup
from sponsors.models import SponsorshipLevel


class SponsorshipLevelFactory(DjangoModelFactory):
class Meta:
model = SponsorshipLevel
django_get_or_create = ("name",)

level = 100
name = "Bronze"


class MeetupFactory(DjangoModelFactory):
class Meta:
model = Meetup
django_get_or_create = ("id",)

id = factory.Sequence(lambda n: f"meetup-{n}")
name = "Python Ireland Meetup"
description = "Monthly Python meetup in Dublin"
event_url = "https://meetup.com/pythonireland/"
time = factory.LazyFunction(lambda: timezone.now() + timezone.timedelta(days=30))
created = factory.LazyFunction(timezone.now)
rsvps = 50
status = "upcoming"
visibility = "public"


class HomePageFactory(DjangoModelFactory):
class Meta:
model = HomePage

title = "Python Ireland"
slug = "home"
show_meetups = True
body = []


class SimplePageFactory(DjangoModelFactory):
class Meta:
model = SimplePage

title = "Sample Page"
slug = "sample-page"
body = []
Empty file.
Empty file.
128 changes: 128 additions & 0 deletions pythonie/core/management/commands/generate_sample_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
from django.core.management.base import BaseCommand
from wagtail.models import Page, Site

from core.factories import SponsorshipLevelFactory, MeetupFactory, HomePageFactory, SimplePageFactory
from core.models import HomePage, SimplePage


class Command(BaseCommand):
help = "Generate sample data for development"

def handle(self, *args, **options):
self.stdout.write("Generating sample data...")

self._create_sponsorship_levels()
self._create_meetups()
home = self._create_home_page()
self._create_navigation_pages(home)

self.stdout.write(self.style.SUCCESS("\nSample data generated successfully!"))

def _create_sponsorship_levels(self):
levels = [("Bronze", 100), ("Silver", 200), ("Gold", 300), ("Platinum", 400)]
for name, level in levels:
SponsorshipLevelFactory(name=name, level=level)
self.stdout.write(self.style.SUCCESS("Created sponsorship levels"))

def _create_meetups(self):
names = ["Python Ireland Monthly Meetup", "Django Dublin", "PyData Ireland"]
for i, name in enumerate(names):
MeetupFactory(id=f"meetup-{i}", name=name)
self.stdout.write(self.style.SUCCESS("Created meetups"))

def _create_home_page(self):
home = HomePage.objects.first()
if home:
self.stdout.write("Home page already exists")
return home

wagtail_root = Page.objects.get(depth=1)
default_home_exists = Page.objects.filter(slug="home", depth=2).exists()
slug = "python-ireland" if default_home_exists else "home"

home = HomePageFactory.build(
slug=slug,
show_in_menus=True,
body=self._get_home_content(),
)
wagtail_root.add_child(instance=home)
self.stdout.write(self.style.SUCCESS("Created home page"))

site = Site.objects.filter(is_default_site=True).first()
if site:
site.root_page = home
site.save()
self.stdout.write(self.style.SUCCESS("Updated site root page"))

return home

def _create_navigation_pages(self, home):
pycon = self._create_page(home, "PyCon 2025", "pycon-2025")
self._create_page(pycon, "Schedule", "schedule")
self._create_page(pycon, "Speakers", "pycon-speakers")
self._create_page(pycon, "Sponsors", "pycon-sponsors")
self._create_page(pycon, "Venue", "venue")
self._create_page(pycon, "Tickets", "tickets")

self._create_page(home, "Meetups", "meetups", self._get_meetups_content())
self._create_page(home, "Learning Resources", "learning-resources")

previous = self._create_page(home, "Previous PyCons", "previous-pycons")
for year in [2024, 2023, 2022, 2019]:
self._create_page(previous, f"PyCon {year}", f"pycon-{year}")

self._create_page(home, "Coaching program", "coaching-program")
self._create_page(home, "About", "about")

policies = self._create_page(home, "Policies", "policies")
self._create_page(policies, "Code of Conduct", "code-of-conduct")
self._create_page(policies, "Privacy Policy", "privacy-policy")
self._create_page(policies, "Cookie Policy", "cookie-policy")

def _create_page(self, parent, title, slug, body=None):
if SimplePage.objects.filter(slug=slug).exists():
self.stdout.write(f" {title} already exists")
return SimplePage.objects.get(slug=slug)

page = SimplePageFactory.build(title=title, slug=slug, body=body or [], show_in_menus=True)
parent.add_child(instance=page)
self.stdout.write(self.style.SUCCESS(f"Created {title}"))
return page

def _get_home_content(self):
return [
{"type": "heading", "value": "Introduction"},
{"type": "paragraph", "value": (
"<p>Python Ireland is the Irish organisation representing the various chapters of Python users. "
"We organise meet ups and events for software developers, students, academics and anyone who wants "
"to learn the language. One of our aims is to help grow and diversify the Python community in Ireland. "
"We also develop and foster links with other Python based communities overseas.</p>"
)},
{"type": "heading", "value": "PyCon Ireland 2025"},
{"type": "paragraph", "value": (
"<p>We are thrilled to announce PyCon Ireland 2025, taking place in Dublin "
"on November 15th and 16th! Join us at the UCD O'Reilly Hall for this exciting event.</p>"
)},
{"type": "paragraph", "value": (
"<p>PyCon Ireland 2025 will feature two talk tracks and two workshop tracks on both days. "
"Your ticket includes breakfast and lunch. Join us Saturday evening for networking!</p>"
)},
{"type": "paragraph", "value": (
"<p>Please adhere to our <a href='/policies/code-of-conduct/'>Code of Conduct</a>. "
"Check <a href='/pycon-2025/'>Terms and conditions</a> for details.</p>"
)},
{"type": "paragraph", "value": "<p>See you at PyCon Ireland 2025!</p>"},
]

def _get_meetups_content(self):
return [
{"type": "heading", "value": "Python Ireland Meetups"},
{"type": "paragraph", "value": (
"<p>Join us at our regular meetups! We hold events every month.</p>"
"<ul>"
"<li><a href='https://www.meetup.com/pythonireland/events/'>Upcoming Events</a></li>"
"<li><a href='https://www.meetup.com/pythonireland/photos/'>Photos</a></li>"
"<li><a href='https://www.meetup.com/pythonireland/'>Python Ireland on Meetup.com</a></li>"
"</ul>"
)},
]
2 changes: 1 addition & 1 deletion pythonie/core/templatetags/core_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def sponsors(context):
@register.simple_tag(takes_context=False)
def root_page():
site = Site.objects.get(is_default_site=True)
return Page.objects.page(site.root_page).first()
return site.root_page


@register.simple_tag(takes_context=False)
Expand Down
3 changes: 2 additions & 1 deletion requirements/dev.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
-c main.txt
coverage
django-debug-toolbar
factory-boy
fakeredis
isort
model-mommy
model_mommy
pip-audit
pipdeptree
ruff
Expand Down
8 changes: 8 additions & 0 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ django==5.2.8
# model-mommy
django-debug-toolbar==6.1.0
# via -r requirements/dev.in
factory-boy==3.3.3
# via -r requirements/dev.in
faker==39.0.0
# via factory-boy
fakeredis==2.32.1
# via -r requirements/dev.in
filelock==3.20.0
Expand Down Expand Up @@ -103,6 +107,10 @@ sqlparse==0.5.4
# django-debug-toolbar
toml==0.10.2
# via pip-audit
tzdata==2025.2
# via
# -c requirements/main.txt
# faker
urllib3==2.5.0
# via
# -c requirements/main.txt
Expand Down