Skip to content
Open
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: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,40 @@ create a superuser (admin) account which can be used for Django Admin and Wagtai
uv run python manage.py createsuperuser
```

### 4. Run Development Server
### 4. Generate Test Data (optional but recommended)

To see how blog and events pages look with content,
generate dummy blog posts and events:

```bash
# Generate default test data (25 blog posts, 20 events)
uv run python manage.py create_dummy_posts

# Or specify custom amounts
uv run python manage.py create_dummy_posts --blog-posts=30 --events=25

# Delete any existing dummy data
uv run python manage.py create_dummy_posts --delete
```

This command creates:
- Blog posts distributed across multiple years (for testing year filters)
- Events with both past and upcoming dates
- Random tags, authors, and excerpts
- All posts are automatically published and visible

### 5. Run Development Server

```bash
# Start Django development server
uv run python manage.py runserver
```

## Testing the Homepage Migration
## Available Pages

- **Homepage (Django)**: [http://127.0.0.1:8000](http://127.0.0.1:8000)
- **Blog (Wagtail)**: [http://127.0.0.1:8000/blog/](http://127.0.0.1:8000/blog/)
- **Blog Index**: [http://127.0.0.1:8000/blog/](http://127.0.0.1:8000/blog/)
- **Events Index**: [http://127.0.0.1:8000/events/](http://127.0.0.1:8000/events/)
- **Wagtail Admin**: [http://127.0.0.1:8000/cms/](http://127.0.0.1:8000/cms/)
- **Django Admin**: [http://127.0.0.1:8000/admin/](http://127.0.0.1:8000/admin/)

Expand Down
73 changes: 71 additions & 2 deletions core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,14 @@ def home(request):

# Fetch recent packages from YAML
recent_packages = get_recent_packages(count=3)


# Fetch recent blog posts from Wagtail
recent_blog_posts = (
BlogPage.objects.live()
.select_related('author')
.order_by('-date')[:3]
)

context = {
'page_title': 'Welcome to pyOpenSci',
'hero_title': 'We make it easier for scientists to create, find, maintain, and contribute to reusable code and software.',
Expand All @@ -59,6 +66,8 @@ def home(request):
'recent_contributors': recent_contributors,
# Used for the "Recently Accepted Python Packages" section on the home page
'recent_packages': recent_packages,
# Used for the "Recent blog posts & updates" section on the home page
'recent_blog_posts': recent_blog_posts,
}
return render(request, 'core/home.html', context)

Expand Down Expand Up @@ -203,7 +212,37 @@ def serve_blog_page(request, slug):
HttpResponse
Rendered blog page using Wagtail's serve mechanism
"""
page = get_object_or_404(BlogPage.objects.live().select_related('author'), slug=slug)
page = get_object_or_404(BlogPage.objects.live().select_related('author').prefetch_related('tags'), slug=slug)

# Get related posts based on tag overlap
page_tags = page.tags.all()
related_posts = []

if page_tags:
# Find posts with overlapping tags
from django.db.models import Count
related_posts = (
BlogPage.objects.live()
.select_related('author')
.prefetch_related('tags')
.filter(tags__in=page_tags)
.exclude(pk=page.pk)
.annotate(same_tags=Count('pk'))
.order_by('-same_tags', '-date')[:3]
)

# Fallback to recent posts if no tag matches
if not related_posts:
related_posts = (
BlogPage.objects.live()
.select_related('author')
.exclude(pk=page.pk)
.order_by('-date')[:3]
)

# Add related_posts to the page context
page.related_posts = related_posts

return page.serve(request)


Expand All @@ -224,4 +263,34 @@ def serve_event_page(request, slug):
Rendered event page using Wagtail's serve mechanism
"""
page = get_object_or_404(EventPage.objects.live().select_related('author').prefetch_related('tags'), slug=slug)

# Get related events based on tag overlap
page_tags = page.tags.all()
related_events = []

if page_tags:
# Find events with overlapping tags
from django.db.models import Count
related_events = (
EventPage.objects.live()
.select_related('author')
.prefetch_related('tags')
.filter(tags__in=page_tags)
.exclude(pk=page.pk)
.annotate(same_tags=Count('pk'))
.order_by('-same_tags', '-start_date')[:3]
)

# Fallback to recent events if no tag matches
if not related_events:
related_events = (
EventPage.objects.live()
.select_related('author')
.exclude(pk=page.pk)
.order_by('-start_date')[:3]
)

# Add related_events to the page context
page.related_events = related_events

return page.serve(request)
33 changes: 21 additions & 12 deletions templates/core/blog_index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,28 @@ <h1 class="text-4xl font-bold mb-6">{{ hero_title }}</h1>
<!-- Section Header -->
<div class="text-center mb-12">
<h2 class="text-3xl font-bold text-gray-900 mb-4">Recent pyOpenSci blog posts</h2>
<!-- Year filter buttons -->
<!-- Year filter dropdown -->
{% if available_years %}
<div class="flex flex-wrap justify-center gap-2 mb-8">
<a href="{% url 'core:blog_index' %}"
class="px-4 py-2 rounded-lg text-sm font-medium transition-colors {% if not selected_year %}bg-pyos-deep-purple text-white hover:bg-pyos-dark-purple{% else %}bg-gray-300 text-gray-800 hover:bg-gray-400{% endif %}">
All Years
</a>
{% for year in available_years %}
<a href="{% url 'core:blog_index' %}?year={{ year.year }}"
class="px-4 py-2 rounded-lg text-sm font-medium transition-colors {% if selected_year == year.year|stringformat:'s' %}bg-pyos-deep-purple text-white hover:bg-pyos-dark-purple{% else %}bg-gray-300 text-gray-800 hover:bg-gray-400{% endif %}">
{{ year.year }}
</a>
{% endfor %}
<div class="flex justify-center items-center gap-4 mb-8">
<label for="year-filter" class="text-sm font-medium text-gray-700">Filter by year:</label>
<div class="relative inline-block">
<select id="year-filter"
onchange="window.location.href=this.value"
class="appearance-none bg-white border-2 border-pyos-deep-purple rounded-lg px-4 py-2 pr-12 font-medium text-pyos-deep-purple hover:bg-pyos-light-purple focus:outline-none focus:ring-2 focus:ring-pyos-deep-purple focus:border-pyos-deep-purple transition-all cursor-pointer shadow-sm">
<option value="{% url 'core:blog_index' %}" {% if not selected_year %}selected{% endif %}>All Years</option>
{% for year in available_years %}
<option value="{% url 'core:blog_index' %}?year={{ year.year }}"
{% if selected_year == year.year|stringformat:'s' %}selected{% endif %}>
{{ year.year }}
</option>
{% endfor %}
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-pyos-deep-purple">
<svg class="w-4 h-4 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"/>
</svg>
</div>
</div>
</div>
{% endif %}
</div>
Expand Down
33 changes: 21 additions & 12 deletions templates/core/events_index.html
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,28 @@ <h2 class="text-3xl font-bold text-gray-900 mb-4">Upcoming Events</h2>
<h2 class="text-3xl font-bold text-gray-900 mb-4">Past Events</h2>
<p class="text-gray-600 mb-4">Browse our complete event archive</p>

<!-- Year filter buttons -->
<!-- Year filter dropdown -->
{% if available_years %}
<div class="flex flex-wrap justify-center gap-2 mb-8">
<a href="{% url 'core:events_index' %}#past-events"
class="px-4 py-2 rounded-lg text-sm font-medium transition-colors {% if not selected_year %}bg-pyos-deep-purple text-white hover:bg-pyos-dark-purple{% else %}bg-gray-300 text-gray-800 hover:bg-gray-400{% endif %}">
All Years
</a>
{% for year in available_years %}
<a href="{% url 'core:events_index' %}?year={{ year.year }}#past-events"
class="px-4 py-2 rounded-lg text-sm font-medium transition-colors {% if selected_year == year.year|stringformat:'s' %}bg-pyos-deep-purple text-white hover:bg-pyos-dark-purple{% else %}bg-gray-300 text-gray-800 hover:bg-gray-400{% endif %}">
{{ year.year }}
</a>
{% endfor %}
<div class="flex justify-center items-center gap-4 mb-8">
<label for="year-filter-events" class="text-sm font-medium text-gray-700">Filter by year:</label>
<div class="relative inline-block">
<select id="year-filter-events"
onchange="window.location.href=this.value"
class="appearance-none bg-white border-2 border-pyos-deep-purple rounded-lg px-4 py-2 pr-12 font-medium text-pyos-deep-purple hover:bg-pyos-light-purple focus:outline-none focus:ring-2 focus:ring-pyos-deep-purple focus:border-pyos-deep-purple transition-all cursor-pointer shadow-sm">
<option value="{% url 'core:events_index' %}#past-events" {% if not selected_year %}selected{% endif %}>All Years</option>
{% for year in available_years %}
<option value="{% url 'core:events_index' %}?year={{ year.year }}#past-events"
{% if selected_year == year.year|stringformat:'s' %}selected{% endif %}>
{{ year.year }}
</option>
{% endfor %}
</select>
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-pyos-deep-purple">
<svg class="w-4 h-4 fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"/>
</svg>
</div>
</div>
</div>
{% endif %}
</div>
Expand Down
37 changes: 31 additions & 6 deletions templates/core/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -209,15 +209,40 @@ <h2 class="text-3xl font-bold text-center mb-12 text-pyos-deep-purple font-poppi
<h2 class="text-3xl font-bold text-center mb-8 text-pyos-deep-purple font-poppins">Recent blog posts & updates</h2>

<div class="grid md:grid-cols-3 gap-6 mb-8">
<!-- Placeholder blog post cards -->
{% for i in "123" %}
<div class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300">
{% for post in recent_blog_posts %}
<a href="/blog/{{ post.slug }}/" class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 block">
<!-- Header Image -->
{% if post.header_image %}
<div class="h-40 overflow-hidden">
<img src="{{ post.header_image.url }}"
alt="{{ post.header_image_alt|default:post.title }}"
class="w-full h-full object-cover">
</div>
{% else %}
<div class="h-40 bg-gradient-to-br from-pyos-medium-purple to-pyos-dark-purple"></div>
{% endif %}

<div class="p-6">
<h3 class="font-bold mb-2 text-pyos-deep-purple">Blog Post Title {{ forloop.counter }}</h3>
<p class="text-sm text-gray-600 mb-2">March {{ forloop.counter }}, 2025</p>
<p class="text-gray-700 text-sm">Brief description of the blog post content...</p>
<h3 class="font-bold mb-2 text-pyos-deep-purple hover:text-pyos-dark-purple transition-colors">{{ post.title }}</h3>

<!-- Meta: Date and Author -->
<p class="text-sm text-gray-600 mb-3">
{{ post.date|date:"F j, Y" }}
{% if post.author %}
· {{ post.author.name }}
{% endif %}
</p>

<!-- Excerpt -->
{% if post.excerpt %}
<p class="text-gray-700 text-sm">{{ post.excerpt|truncatewords:20 }}</p>
{% endif %}
</div>
</a>
{% empty %}
<!-- Fallback if no blog posts exist -->
<div class="col-span-3 text-center text-gray-500 py-8">
<p>No blog posts available yet. Check back soon!</p>
</div>
{% endfor %}
</div>
Expand Down
37 changes: 37 additions & 0 deletions templates/publications/blog_page.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,42 @@ <h3 class="text-lg font-semibold mb-4">Comments</h3>
<p class="text-gray-600">Comments system integration placeholder</p>
</div>
{% endif %}

<!-- Related Posts -->
{% if page.related_posts %}
<div class="mt-12">
<h2 class="text-2xl font-bold text-gray-900 mb-6">You May Also Enjoy</h2>
<div class="grid md:grid-cols-3 gap-6">
{% for post in page.related_posts %}
<a href="/blog/{{ post.slug }}/" class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 block flex flex-col">
<!-- Header Image -->
{% if post.header_image %}
<div class="h-40 overflow-hidden flex-shrink-0">
<img src="{{ post.header_image.url }}"
alt="{{ post.header_image_alt|default:post.title }}"
class="w-full h-full object-cover">
</div>
{% else %}
<div class="h-40 bg-gradient-to-br from-pyos-medium-purple to-pyos-dark-purple flex-shrink-0"></div>
{% endif %}

<div class="p-6 flex-grow flex flex-col">
<h3 class="font-bold text-base mb-3 text-pyos-deep-purple hover:text-pyos-dark-purple transition-colors line-clamp-2">{{ post.title }}</h3>

<!-- Meta: Date -->
<p class="text-sm text-gray-600 mb-3">
{{ post.date|date:"F j, Y" }}
</p>

<!-- Excerpt -->
{% if post.excerpt %}
<p class="text-gray-700 text-sm line-clamp-3">{{ post.excerpt|truncatewords:15 }}</p>
{% endif %}
</div>
</a>
{% endfor %}
</div>
</div>
{% endif %}
</div>
{% endblock %}
37 changes: 37 additions & 0 deletions templates/publications/event_page.html
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,42 @@ <h3 class="text-lg font-semibold mb-4">Comments</h3>
<p class="text-gray-600">Comments system integration placeholder</p>
</div>
{% endif %}

<!-- Related Events -->
{% if page.related_events %}
<div class="mt-12">
<h2 class="text-2xl font-bold text-gray-900 mb-6">You May Also Enjoy</h2>
<div class="grid md:grid-cols-3 gap-6">
{% for event in page.related_events %}
<a href="/events/{{ event.slug }}/" class="bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 block flex flex-col">
<!-- Header Image -->
{% if event.header_image %}
<div class="h-40 overflow-hidden flex-shrink-0">
<img src="{{ event.header_image.url }}"
alt="{{ event.header_image_alt|default:event.title }}"
class="w-full h-full object-cover">
</div>
{% else %}
<div class="h-40 bg-gradient-to-br from-pyos-medium-purple to-pyos-dark-purple flex-shrink-0"></div>
{% endif %}

<div class="p-6 flex-grow flex flex-col">
<h3 class="font-bold text-base mb-3 text-pyos-deep-purple hover:text-pyos-dark-purple transition-colors line-clamp-2">{{ event.title }}</h3>

<!-- Meta: Date -->
<p class="text-sm text-gray-600 mb-3">
{{ event.start_date|date:"F j, Y" }}
</p>

<!-- Excerpt -->
{% if event.excerpt %}
<p class="text-gray-700 text-sm line-clamp-3">{{ event.excerpt|truncatewords:15 }}</p>
{% endif %}
</div>
</a>
{% endfor %}
</div>
</div>
{% endif %}
</div>
{% endblock %}