Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
8 changes: 6 additions & 2 deletions blog/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

class BlogIndexPage(Page):
"""
Index page for blog posts
Index page for blog posts.

Wagtail page model for the main blog listing page.
"""
intro = RichTextField(blank=True)

Expand All @@ -20,7 +22,9 @@ class Meta:

class BlogPage(Page):
"""
Individual blog post page
Individual blog post page.

Wagtail page model for individual blog posts with date, intro, and content.
"""
date = models.DateField("Post date")
intro = models.CharField(max_length=250)
Expand Down
4 changes: 2 additions & 2 deletions core/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from django.contrib import admin

from django.contrib import admin
# Register your models here.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment supposed to be here? Also, whenever possible, it would be best to add an extra empty line at the end of each file (that will get rid of the red symbols you see here)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those comments are there in the starter files when you install Django. I should have cleared them after I added my code. I will do that in my next commit.

I have always wondered why many files I see in repos online have an extra empty line at the bottom. Thanks for pointing this out to me😅

100 changes: 97 additions & 3 deletions core/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,97 @@
from django.db import models

# Create your models here.
from django.db import models
from typing import Optional


class Contributor(models.Model):
"""
Django model representing a pyOpenSci contributor.

This model mirrors the PersonModel from pyosMeta for future database migration.
Currently, contributor data is read directly from YAML files.
"""

# Basic information
name = models.CharField(max_length=255, null=True, blank=True)
github_username = models.CharField(max_length=100, unique=True)
github_image_id = models.IntegerField(null=True, blank=True)
bio = models.TextField(null=True, blank=True)
organization = models.CharField(max_length=255, null=True, blank=True)
location = models.CharField(max_length=255, null=True, blank=True)
email = models.EmailField(null=True, blank=True)

# Dates
date_added = models.DateField(null=True, blank=True)

# Role flags
deia_advisory = models.BooleanField(default=False)
editorial_board = models.BooleanField(default=False)
emeritus_editor = models.BooleanField(default=False)
advisory = models.BooleanField(default=False)
emeritus_advisory = models.BooleanField(default=False)
board = models.BooleanField(default=False)

# Social media and external links
twitter = models.CharField(max_length=50, null=True, blank=True)
mastodon = models.URLField(null=True, blank=True)
orcidid = models.CharField(max_length=50, null=True, blank=True)
website = models.URLField(null=True, blank=True)

# JSON fields for lists (SQLite compatible)
title = models.JSONField(default=list, blank=True)
partners = models.JSONField(default=list, blank=True)
contributor_type = models.JSONField(default=list, blank=True)
packages_eic = models.JSONField(default=list, blank=True)
packages_editor = models.JSONField(default=list, blank=True)
packages_submitted = models.JSONField(default=list, blank=True)
packages_reviewed = models.JSONField(default=list, blank=True)

# Metadata
sort = models.IntegerField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

class Meta:
ordering = ['-date_added', 'sort', 'name']
verbose_name = "Contributor"
verbose_name_plural = "Contributors"

def __str__(self) -> str:
return self.display_name

@property
def display_name(self) -> str:
"""
Return name if available, otherwise GitHub username.

Returns
-------
str
The contributor's display name.
"""
return self.name or f"@{self.github_username}"

@property
def github_avatar_url(self) -> Optional[str]:
"""
Generate GitHub avatar URL from image ID.

Returns
-------
str or None
GitHub avatar URL if image ID exists, None otherwise.
"""
if self.github_image_id:
return f"https://avatars.githubusercontent.com/u/{self.github_image_id}?s=400&v=4"
return None

@property
def github_profile_url(self) -> str:
"""
Generate GitHub profile URL.

Returns
-------
str
GitHub profile URL for the contributor.
"""
return f"https://github.com/{self.github_username}"
51 changes: 34 additions & 17 deletions core/templatetags/image_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,21 @@ def responsive_image(image_path, alt_text="", css_classes="", sizes="100vw"):
"""
Generate a responsive picture element with WebP and PNG fallback.

Args:
image_path: Path to the image without extension (e.g., 'headers/pyopensci-sprints')
alt_text: Alt text for accessibility
css_classes: CSS classes to apply to the img element
sizes: Sizes attribute for responsive images
Parameters
----------
image_path : str
Path to the image without extension (e.g., 'headers/pyopensci-sprints').
alt_text : str, default ""
Alt text for accessibility.
css_classes : str, default ""
CSS classes to apply to the img element.
sizes : str, default "100vw"
Sizes attribute for responsive images.

Returns:
HTML picture element with WebP and PNG fallback
Returns
-------
str
HTML picture element with WebP and PNG fallback.
"""
webp_path = static(f"images/{image_path}.webp")
png_path = static(f"images/{image_path}.png")
Expand All @@ -37,12 +44,17 @@ def hero_image(image_path, alt_text=""):
"""
Generate a hero image with WebP support and optimized loading.

Args:
image_path: Path to the image without extension
alt_text: Alt text for accessibility
Parameters
----------
image_path : str
Path to the image without extension.
alt_text : str, default ""
Alt text for accessibility.

Returns:
HTML picture element optimized for hero sections
Returns
-------
str
HTML picture element optimized for hero sections.
"""
return responsive_image(
image_path=image_path,
Expand All @@ -57,12 +69,17 @@ def card_image(image_path, alt_text=""):
"""
Generate a card image with WebP support.

Args:
image_path: Path to the image without extension
alt_text: Alt text for accessibility
Parameters
----------
image_path : str
Path to the image without extension.
alt_text : str, default ""
Alt text for accessibility.

Returns:
HTML picture element optimized for card layouts
Returns
-------
str
HTML picture element optimized for card layouts.
"""
return responsive_image(
image_path=image_path,
Expand Down
Loading