wagtail-feathers


Namewagtail-feathers JSON
Version 1.0b1 PyPI version JSON
download
home_pageNone
SummaryA comprehensive Wagtail CMS extension providing theming and foundational features.
upload_time2025-07-29 01:47:57
maintainerNone
docs_urlNone
authorNone
requires_python>=3.11
licenseNone
keywords wagtail django cms content management taxonomy navigation themes
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Wagtail Feathers

A comprehensive Wagtail CMS extension providing foundational functionality for building sophisticated content management systems.

[![Python Version](https://img.shields.io/pypi/pyversions/wagtail-feathers.svg)](https://pypi.org/project/wagtail-feathers/)
[![Django Version](https://img.shields.io/badge/django-5.1%20%7C%205.2-blue.svg)](https://docs.djangoproject.com/en/5.2/)
[![Wagtail Version](https://img.shields.io/badge/wagtail-6.x%20%7C%207.x-orange.svg)](https://wagtail.org/)
[![License](https://img.shields.io/badge/license-BSD--3--Clause-green.svg)](LICENSE)

> [!CAUTION]
> This package is a beta version which means it is **almost** stable and definitely lacks some documentation and tests
> The package is aimed at new installations

## Features

Wagtail Feathers provides a comprehensive set of reusable components and patterns:

### 🏗️ **Advanced Page Architecture**
- **FeatherBasePage**: Foundation for all custom pages with comprehensive SEO capabilities
- **Flexible Content Blocks**: Rich StreamField blocks for dynamic content composition
- **Page Relationships**: Built-in related page functionality and cross-references

### 📂 **Taxonomy & Classification**  
- **Hierarchical Categories**: Tree-structured content classification
- **Flexible Subjects**: Tagging and subject matter organization
- **Custom Attributes**: Extensible metadata and properties system
- **Admin Integration**: Beautiful select widgets and management interface

### 🧭 **Dynamic Navigation**
- **Multiple Menu Types**: Traditional hierarchical and StreamField-based navigation
- **Flexible Menu Items**: Support for both page links and external URLs

### 🔍 **SEO & Metadata**
- **Built on Wagtail SEO**: Extends Wagtail's built-in `seo_title` and `search_description` fields
- **Social Media Ready**: Open Graph and Twitter Card support with dedicated image field
- **Structured Data**: Automatic JSON-LD generation for articles, organizations, and more
- **Smart Fallbacks**: Intelligent content detection for missing SEO fields
- **Search Engine Control**: No-index/no-follow directives and robots meta tags
- **Reading Time**: Automatic reading time calculation with SEO integration

### 🎨 **Theme System**
- **Multi-Site Theming**: Different themes per site
- **Template Discovery**: Automatic theme-based template resolution
- **Asset Management**: Theme-specific CSS/JS handling
- **Configuration**: Site-wide settings and theme preferences


### 🌍 **Internationalization** (Optional)
- **Locale-Aware Ready**: Models ready for i18n when you need it
- **Simple Translation**: Use Wagtail's built-in translation features
- **Advanced Workflows**: Optional wagtail-localize integration (recommended)

### 👥 **Authorship & People**
- **Author Management**: Flexible author attribution system with AuthorType and PageAuthor models
- **People Directory**: Staff and contributor profiles with PersonGroup organization
- **Social Integration**: Social media links and profiles with SocialMediaSettings

### ❌ **Error Pages**
- **Custom Error Pages**: Configurable 404, and 500 error pages (TBC: 400, 403)
- **ErrorPage Model**: Editable error pages with custom messages and images

### ❓ **FAQ System**
- **FAQ Management**: Structured FAQ system with FAQCategory and FAQ models
- **Categorized Content**: Organize frequently asked questions by category

### 🔗 **Page Relationships**
- **Related Content**: RelatedPage, RelatedDocument, and RelatedExternalLink models
- **Cross-References**: Built-in page relationship functionality
- **Geographic Tagging**: PageCountry model for location-based content

## Installation

### Basic Installation (No i18n)
```bash
pip install wagtail-feathers
```

### With Advanced Translation Workflows
```bash
pip install wagtail-feathers[localize]
```

## Quick Start

1. **Add to Django Settings**:

**Basic Setup:**
```python
INSTALLED_APPS = [
    # ... your apps
    "wagtail_feathers",
    "wagtailmarkdown",
    "django_extensions",
    # ... other apps
]
```

**With Simple Translation:**
```python
INSTALLED_APPS = [
    # ... your apps
    "wagtail_feathers",
    "wagtail.contrib.simple_translation",  # Wagtail built-in
    # ... other apps
]
```

**With Advanced Translation:**
```python
INSTALLED_APPS = [
    # ... your apps
    "wagtail_feathers",
    "wagtail_localize",
    "wagtail_localize.locales",
    # ... other apps
]
```

2. **Run Migrations**:
```bash
python manage.py migrate
```

3. **Create Your First Page**:
```python
from wagtail_feathers.models import FeatherBasePage

class MyPage(FeatherBasePage):
    # Your custom fields here
    pass
```

## Advanced Usage

### Custom Page Models

```python
from wagtail_feathers.models import FeatherBasePage, ItemPage
from wagtail_feathers.blocks import CommonContentBlock
from wagtail.fields import StreamField

class ArticlePage(ItemPage):
    body = StreamField([
        ("content", CommonContentBlock()),
    ], use_json_field=True)
    
    content_panels = ItemPage.content_panels + [
        FieldPanel("body"),
    ]
```

### Taxonomy Integration

```python
from wagtail_feathers.models import Category, Classifier, ClassifierGroup

# Create categories
category = Category.objects.create(name="Technology", slug="tech")

# Create classifier groups and classifiers
group = ClassifierGroup.objects.create(name="Topics", max_selections=3)
classifier = Classifier.objects.create(
    name="AI & Machine Learning",
    slug="ai-ml",
    group=group
)
```

### Navigation Menus

```python
from wagtail_feathers.models import Menu, MenuItem, FlatMenu, NestedMenu

# Create a flat menu
flat_menu = FlatMenu.objects.create(name="Main Navigation")

# Create a nested menu
nested_menu = NestedMenu.objects.create(name="Footer Navigation")

# Create menu items
MenuItem.objects.create(
    menu=flat_menu,
    link_page=home_page,
    link_text="Home",
    sort_order=1
)
```

### Error Pages

```python
from wagtail_feathers.models import ErrorPage

# Create custom error pages
error_404 = ErrorPage.objects.create(
    title="404",
    error_code="404",
    heading="Page Not Found",
    message="<p>The page you are looking for does not exist.</p>"
)
```

### FAQ System

```python
from wagtail_feathers.models import FAQCategory, FAQ

# Create FAQ categories and items
category = FAQCategory.objects.create(
    title="General Questions",
    slug="general"
)

faq = FAQ.objects.create(
    question="How do I get started?",
    answer="<p>Follow our quick start guide...</p>",
    category=category
)
```

### SEO & Metadata

```python
from wagtail_feathers.models import FeatherBasePage
from wagtail_feathers.models.seo import SeoContentType, TwitterCardType

class ArticlePage(FeatherBasePage):
    # SEO fields are automatically available via SeoMixin
    pass

# In your templates - add to <head> section:
# {% load seo_tags %}
# {% include "wagtail_feathers/seo/meta.html" %}

# In your templates - add before </body>:
# {% include "wagtail_feathers/seo/structured_data.html" %}

# Programmatic SEO control:
page = ArticlePage.objects.get(pk=1)
page.seo_title = "Custom SEO Title"  # Uses Wagtail's built-in field
page.search_description = "Custom meta description for search engines"  # Uses Wagtail's built-in field
page.seo_content_type = SeoContentType.ARTICLE
page.twitter_card_type = TwitterCardType.SUMMARY_LARGE_IMAGE
page.save()
```

### Reading Time Features

All `ItemPage` models automatically include reading time calculation:

```python
from wagtail_feathers.models import ItemPage

class BlogPost(ItemPage):
    # Reading time is automatically calculated from:
    # - title, introduction, body content
    # - Configurable words-per-minute (default: 200 WPM)
    pass

# In templates:
# {% load reading_time_tags %}
# {% reading_time page %}  # "5 min read"
# {% reading_time page "long" %}  # "5 minutes read"  
# {% reading_time_badge page %}  # Badge with clock icon

# Custom reading time logic:
class TechnicalArticle(ItemPage):
    summary = RichTextField(blank=True)
    
    def get_additional_word_sources(self):
        """Include summary in word count."""
        words = 0
        if self.summary:
            words += self._count_text_words(str(self.summary))
        return words
    
    # Slower reading speed for technical content
    words_per_minute = 150

# Reading time extends SEO automatically:
# - Twitter Cards: "Reading time: 5 min read"
# - Structured Data: "timeRequired": "PT5M"
# - Meta tags: article:reading_time
```

### Extending SEO Features

The SEO functionality in wagtail_feathers is designed to be extensible. Here are several ways to enhance it:

#### 1. Adding Custom SEO Fields

```python
from wagtail_feathers.models import FeatherBasePage, SeoMixin
from wagtail.admin.panels import FieldPanel

class ArticlePage(FeatherBasePage):
    # Add your own SEO-related fields
    meta_keywords = models.CharField(
        max_length=255, 
        blank=True,
        help_text="Comma-separated keywords (optional for modern SEO)"
    )
    
    facebook_app_id = models.CharField(
        max_length=50,
        blank=True,
        help_text="Facebook App ID for Open Graph"
    )
    
    # Extend the SEO panels
    seo_panels = SeoMixin.seo_panels + [
        FieldPanel("meta_keywords"),
        FieldPanel("facebook_app_id"),
    ]
    
    # Override SEO methods for custom behavior
    def get_seo_description(self):
        """Custom SEO description logic."""
        # Try your custom field first
        if hasattr(self, "excerpt") and self.excerpt:
            return self.excerpt[:160]
        
        # Fall back to parent implementation
        return super().get_seo_description()
```

#### 2. Custom Structured Data

```python
import json
from wagtail_feathers.models import SeoMixin

class ProductPage(FeatherBasePage):
    price = models.DecimalField(max_digits=10, decimal_places=2)
    brand = models.CharField(max_length=100)
    sku = models.CharField(max_length=50)
    
    def get_structured_data(self):
        """Override to add Product schema."""
        if self.seo_content_type == "product":
            site = self.get_site()
            base_url = site.root_url if site.root_url else f"https://{site.hostname}"
            
            schema = {
                "@context": "https://schema.org",
                "@type": "Product",
                "name": self.get_seo_title(),
                "description": self.get_seo_description(),
                "sku": self.sku,
                "brand": {
                    "@type": "Brand",
                    "name": self.brand
                },
                "offers": {
                    "@type": "Offer",
                    "price": str(self.price),
                    "priceCurrency": "USD",
                    "availability": "https://schema.org/InStock"
                }
            }
            
            # Add image if available
            if self.get_seo_image():
                schema["image"] = f"{base_url}{self.get_seo_image().file.url}"
                
            return json.dumps(schema, ensure_ascii=False)
        
        # Fall back to parent implementation
        return super().get_structured_data()
```

#### 3. SEO-Focused Page Types

```python
from wagtail_feathers.models import ItemPage
from wagtail_feathers.models.seo import SeoContentType, TwitterCardType

class SEOOptimizedArticle(ItemPage):
    """Article page optimized for SEO best practices."""
    
    # Set SEO defaults
    seo_content_type = SeoContentType.ARTICLE
    twitter_card_type = TwitterCardType.SUMMARY_LARGE_IMAGE
    
    # Additional SEO fields
    focus_keyword = models.CharField(
        max_length=100,
        blank=True,
        help_text="Primary keyword/phrase for this article"
    )
    
    reading_time = models.PositiveIntegerField(
        null=True,
        blank=True,
        help_text="Estimated reading time in minutes"
    )
    
    content_panels = ItemPage.content_panels + [
        FieldPanel("focus_keyword"),
        FieldPanel("reading_time"),
    ]
    
    def get_structured_data(self):
        """Enhanced article schema with reading time."""
        schema_str = super().get_structured_data()
        if schema_str and self.seo_content_type == SeoContentType.ARTICLE:
            schema = json.loads(schema_str)
            
            # Add reading time
            if self.reading_time:
                schema["timeRequired"] = f"PT{self.reading_time}M"
            
            # Add focus keyword as about
            if self.focus_keyword:
                schema["about"] = {
                    "@type": "Thing",
                    "name": self.focus_keyword
                }
                
            return json.dumps(schema, ensure_ascii=False)
        
        return schema_str
```

#### 4. Custom SEO Template Tags

```python
# In your app"s templatetags/custom_seo_tags.py
from django import template
from django.utils.safestring import mark_safe

register = template.Library()

@register.simple_tag
def custom_meta_tags(page):
    """Add custom meta tags based on page type."""
    tags = []
    
    # Add article-specific tags
    if hasattr(page, "focus_keyword") and page.focus_keyword:
        tags.append(f"<meta name='keywords' content='{page.focus_keyword}'>")
    
    # Add Facebook App ID if available
    if hasattr(page, "facebook_app_id") and page.facebook_app_id:
        tags.append(f"<meta property='fb:app_id' content='{page.facebook_app_id}'>")
    
    # Add reading time for articles
    if hasattr(page, "reading_time") and page.reading_time:
        tags.append(f"<meta name='twitter:label1' content="Reading time">")
        tags.append(f"<meta name='twitter:data1' content='{page.reading_time} min read'>")
    
    return mark_safe("\n".join(tags))

# In your templates:
# {% load custom_seo_tags %}
# {% custom_meta_tags page %}
```

#### 5. SEO Analytics Integration

```python
class AnalyticsEnhancedPage(FeatherBasePage):
    """Page with SEO analytics tracking."""
    
    google_analytics_id = models.CharField(
        max_length=20,
        blank=True,
        help_text="Google Analytics tracking ID (e.g., GA-XXXXX-X)"
    )
    
    track_scroll_depth = models.BooleanField(
        default=False,
        help_text="Enable scroll depth tracking for SEO insights"
    )
    
    def get_context(self, request, *args, **kwargs):
        context = super().get_context(request, *args, **kwargs)
        
        # Add SEO tracking context
        context.update({
            "enable_seo_tracking": self.track_scroll_depth,
            "ga_tracking_id": self.google_analytics_id,
        })
        
        return context
```

#### 6. Multi-language SEO

```python
# If using wagtail-localize
from wagtail_feathers.models.seo import SeoMixin
from wagtail_localize.fields import TranslatableField

class MultilingualSEOPage(FeatherBasePage):
    """Page with enhanced multilingual SEO support."""
    
    # Additional translatable SEO fields
    local_keywords = models.CharField(
        max_length=255,
        blank=True,
        help_text="Keywords in local language"
    )
    
    cultural_context = models.TextField(
        blank=True,
        help_text="Cultural context for this market"
    )
    
    # Add to translatable fields
    if SeoMixin.WAGTAIL_LOCALIZE_AVAILABLE:
        translatable_fields = SeoMixin.translatable_fields + [
            TranslatableField("local_keywords"),
            TranslatableField("cultural_context"),
        ]
    
    def get_seo_description(self):
        """Localized SEO description."""
        if self.cultural_context:
            return f"{super().get_seo_description()} {self.cultural_context}"[:160]
        return super().get_seo_description()
```

#### 7. Using Extension Points

The SeoMixin provides several extension points for easy customization:

```python
class BlogArticlePage(ItemPage):
    """Blog article with enhanced SEO using extension points."""
    
    author_twitter = models.CharField(max_length=50, blank=True)
    focus_keyword = models.CharField(max_length=100, blank=True)
    # Note: reading_time is automatically provided by ItemPage
    
    def get_custom_meta_tags(self):
        """Add custom meta tags."""
        tags = {}
        
        if self.focus_keyword:
            tags["keywords"] = self.focus_keyword
        
        # Reading time is automatically added via ReadingTimeMixin
        # but you can customize it here if needed
            
        return tags
    
    def get_custom_twitter_tags(self):
        """Add custom Twitter Card tags."""
        tags = {}
        
        if self.author_twitter:
            tags["twitter:creator"] = f"@{self.author_twitter}"
        
        # Reading time Twitter tags are automatically added via ReadingTimeMixin
        # Additional Twitter tags can be added here
            
        return tags
    
    def get_custom_og_tags(self):
        """Add custom Open Graph tags."""
        tags = {}
        
        if self.author_twitter:
            tags["article:author"] = f"https://twitter.com/{self.author_twitter}"
            
        return tags
    
    def get_sitemap_priority(self):
        """Higher priority for recent articles and longer reads."""
        from django.utils import timezone
        from datetime import timedelta
        
        base_priority = super().get_sitemap_priority()
        
        # Boost recent articles
        if hasattr(self, "publication_date"):
            recent_threshold = timezone.now().date() - timedelta(days=30)
            if self.publication_date >= recent_threshold:
                base_priority += 0.1
        
        # Boost longer articles (more comprehensive content)
        if self.reading_time_minutes and self.reading_time_minutes >= 10:
            base_priority += 0.1
                
        return min(1.0, base_priority)  # Cap at 1.0
```

These examples show how to build upon wagtail_feathers SEO foundation to create powerful, customized SEO solutions for specific use cases.

### People & Authors

```python
from wagtail_feathers.models import Person, PersonGroup, AuthorType, PageAuthor

# Create person groups and people
group = PersonGroup.objects.create(name="Editorial Team")
person = Person.objects.create(
    first_name="John",
    last_name="Doe",
    email="john@example.com"
)
person.groups.add(group)

# Create author types and assign to pages
author_type = AuthorType.objects.create(name="Writer")
PageAuthor.objects.create(
    page=my_page,
    person=person,
    author_type=author_type
)
```

## Requirements

- Python 3.11+
- Django 5.0+  
- Wagtail 5.2+

See `pyproject.toml` for complete dependency list.

## Development

### Setup Development Environment

```bash
# Clone the repository
git clone https://github.com/softquantum/wagtail-feathers.git
cd wagtail-feathers

# Navigate to demo directory
cd demo

# Install demo dependencies (includes editable package install)
pip install -r requirements.txt

# Set up database and demo data
python manage.py migrate
python manage.py setup_demo_data

# Start the demo server
python manage.py runserver
```

```bash
# Run tests
pytest

# Run tests across multiple environments
tox
```

### Try the Demo Site

**Demo Access:**
- **Frontend:** http://localhost:8000
- **Admin:** http://localhost:8000/admin (admin/admin123)

The demo includes (WORK IN PROGRESS):
- Sample pages using wagtail-feathers features
- SEO optimization examples
- Reading time calculation
- Taxonomy and navigation examples
- Live package development (changes reflect immediately)

### Testing

The package includes some tests covering:
- Model functionality and relationships
- Template rendering and themes
- Navigation generation
- Taxonomy management
- Multi-site behavior

```bash
# Run all tests
pytest
```

### Code Quality

```bash
# Lint code
ruff check src/

# Format code  
ruff format src/

# Run via tox
tox -e lint
tox -e format
```

## Architecture

Wagtail Feathers follows a modular architecture:

```
wagtail_feathers/
├── models/           # Core model definitions
│   ├── base.py      # FeatherBasePage and foundation classes
│   ├── author.py    # AuthorType and PageAuthor models
│   ├── errors.py    # ErrorPage model
│   ├── faq.py       # FAQ and FAQCategory models  
│   ├── geographic.py # PageCountry model
│   ├── inline.py    # Related content models
│   ├── navigation.py # Menu and navigation models
│   ├── person.py    # Person and PersonGroup models
│   ├── settings.py  # SiteSettings model
│   ├── social.py    # Social media models
│   ├── specialized_pages.py # Specialized page types
│   ├── taxonomy.py  # Category and Classifier models
│   └── utils.py     # Model utilities
├── blocks.py        # StreamField content blocks
├── templatetags/    # Template tag libraries
├── viewsets/        # Wagtail admin viewsets
├── themes.py        # Theme system
└── management/      # Django management commands
```

## Key Models

Based on the migration, wagtail_feathers provides these core models:

- **Page Foundation**: `FeatherBasePage`, `FeatherBasePageTag`
- **Taxonomy**: `Category`, `Classifier`, `ClassifierGroup`, `PageCategory`, `PageClassifier`
- **Navigation**: `Menu`, `MenuItem`, `FlatMenu`, `NestedMenu`, `Footer`, `FooterNavigation`
- **People & Authors**: `Person`, `PersonGroup`, `AuthorType`, `PageAuthor`
- **Content Organization**: `FAQ`, `FAQCategory`, `ErrorPage`
- **Relationships**: `RelatedPage`, `RelatedDocument`, `RelatedExternalLink`
- **Geography**: `PageCountry`
- **Social**: `SocialMediaSettings`, `SocialMediaLink`
- **Settings**: `SiteSettings`

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Ensure all tests pass
5. Submit a pull request

## License
[BSD-3-Clause](LICENSE)

## Credits & Attributions
- ✅ Wagtail Feathers is Developed by [Maxime Decooman](https://github.com/softquantum).
- ✅ Built on the excellent [Wagtail CMS](https://wagtail.org/) (License [BSD-3-Clause](https://github.com/wagtail/wagtail/blob/main/LICENSE))
- ✅ The icons prefixed "heroicons-" are sourced from version 2.2.0 of [Heroicons](https://github.com/tailwindlabs/heroicons), the beautiful hand-crafted SVG icons library, by the makers of Tailwind CSS (License [MIT](https://github.com/tailwindlabs/heroicons/blob/master/LICENSE)).
            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "wagtail-feathers",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": "Maxime Decooman <maxime@softquantum.com>",
    "keywords": "wagtail, django, cms, content, management, taxonomy, navigation, themes",
    "author": null,
    "author_email": "Maxime Decooman <maxime@softquantum.com>",
    "download_url": "https://files.pythonhosted.org/packages/fb/32/23e795ee48de27def18859fc6965871181c725d6fac473bdd44222498d21/wagtail_feathers-1.0b1.tar.gz",
    "platform": null,
    "description": "# Wagtail Feathers\n\nA comprehensive Wagtail CMS extension providing foundational functionality for building sophisticated content management systems.\n\n[![Python Version](https://img.shields.io/pypi/pyversions/wagtail-feathers.svg)](https://pypi.org/project/wagtail-feathers/)\n[![Django Version](https://img.shields.io/badge/django-5.1%20%7C%205.2-blue.svg)](https://docs.djangoproject.com/en/5.2/)\n[![Wagtail Version](https://img.shields.io/badge/wagtail-6.x%20%7C%207.x-orange.svg)](https://wagtail.org/)\n[![License](https://img.shields.io/badge/license-BSD--3--Clause-green.svg)](LICENSE)\n\n> [!CAUTION]\n> This package is a beta version which means it is **almost** stable and definitely lacks some documentation and tests\n> The package is aimed at new installations\n\n## Features\n\nWagtail Feathers provides a comprehensive set of reusable components and patterns:\n\n### \ud83c\udfd7\ufe0f **Advanced Page Architecture**\n- **FeatherBasePage**: Foundation for all custom pages with comprehensive SEO capabilities\n- **Flexible Content Blocks**: Rich StreamField blocks for dynamic content composition\n- **Page Relationships**: Built-in related page functionality and cross-references\n\n### \ud83d\udcc2 **Taxonomy & Classification**  \n- **Hierarchical Categories**: Tree-structured content classification\n- **Flexible Subjects**: Tagging and subject matter organization\n- **Custom Attributes**: Extensible metadata and properties system\n- **Admin Integration**: Beautiful select widgets and management interface\n\n### \ud83e\udded **Dynamic Navigation**\n- **Multiple Menu Types**: Traditional hierarchical and StreamField-based navigation\n- **Flexible Menu Items**: Support for both page links and external URLs\n\n### \ud83d\udd0d **SEO & Metadata**\n- **Built on Wagtail SEO**: Extends Wagtail's built-in `seo_title` and `search_description` fields\n- **Social Media Ready**: Open Graph and Twitter Card support with dedicated image field\n- **Structured Data**: Automatic JSON-LD generation for articles, organizations, and more\n- **Smart Fallbacks**: Intelligent content detection for missing SEO fields\n- **Search Engine Control**: No-index/no-follow directives and robots meta tags\n- **Reading Time**: Automatic reading time calculation with SEO integration\n\n### \ud83c\udfa8 **Theme System**\n- **Multi-Site Theming**: Different themes per site\n- **Template Discovery**: Automatic theme-based template resolution\n- **Asset Management**: Theme-specific CSS/JS handling\n- **Configuration**: Site-wide settings and theme preferences\n\n\n### \ud83c\udf0d **Internationalization** (Optional)\n- **Locale-Aware Ready**: Models ready for i18n when you need it\n- **Simple Translation**: Use Wagtail's built-in translation features\n- **Advanced Workflows**: Optional wagtail-localize integration (recommended)\n\n### \ud83d\udc65 **Authorship & People**\n- **Author Management**: Flexible author attribution system with AuthorType and PageAuthor models\n- **People Directory**: Staff and contributor profiles with PersonGroup organization\n- **Social Integration**: Social media links and profiles with SocialMediaSettings\n\n### \u274c **Error Pages**\n- **Custom Error Pages**: Configurable 404, and 500 error pages (TBC: 400, 403)\n- **ErrorPage Model**: Editable error pages with custom messages and images\n\n### \u2753 **FAQ System**\n- **FAQ Management**: Structured FAQ system with FAQCategory and FAQ models\n- **Categorized Content**: Organize frequently asked questions by category\n\n### \ud83d\udd17 **Page Relationships**\n- **Related Content**: RelatedPage, RelatedDocument, and RelatedExternalLink models\n- **Cross-References**: Built-in page relationship functionality\n- **Geographic Tagging**: PageCountry model for location-based content\n\n## Installation\n\n### Basic Installation (No i18n)\n```bash\npip install wagtail-feathers\n```\n\n### With Advanced Translation Workflows\n```bash\npip install wagtail-feathers[localize]\n```\n\n## Quick Start\n\n1. **Add to Django Settings**:\n\n**Basic Setup:**\n```python\nINSTALLED_APPS = [\n    # ... your apps\n    \"wagtail_feathers\",\n    \"wagtailmarkdown\",\n    \"django_extensions\",\n    # ... other apps\n]\n```\n\n**With Simple Translation:**\n```python\nINSTALLED_APPS = [\n    # ... your apps\n    \"wagtail_feathers\",\n    \"wagtail.contrib.simple_translation\",  # Wagtail built-in\n    # ... other apps\n]\n```\n\n**With Advanced Translation:**\n```python\nINSTALLED_APPS = [\n    # ... your apps\n    \"wagtail_feathers\",\n    \"wagtail_localize\",\n    \"wagtail_localize.locales\",\n    # ... other apps\n]\n```\n\n2. **Run Migrations**:\n```bash\npython manage.py migrate\n```\n\n3. **Create Your First Page**:\n```python\nfrom wagtail_feathers.models import FeatherBasePage\n\nclass MyPage(FeatherBasePage):\n    # Your custom fields here\n    pass\n```\n\n## Advanced Usage\n\n### Custom Page Models\n\n```python\nfrom wagtail_feathers.models import FeatherBasePage, ItemPage\nfrom wagtail_feathers.blocks import CommonContentBlock\nfrom wagtail.fields import StreamField\n\nclass ArticlePage(ItemPage):\n    body = StreamField([\n        (\"content\", CommonContentBlock()),\n    ], use_json_field=True)\n    \n    content_panels = ItemPage.content_panels + [\n        FieldPanel(\"body\"),\n    ]\n```\n\n### Taxonomy Integration\n\n```python\nfrom wagtail_feathers.models import Category, Classifier, ClassifierGroup\n\n# Create categories\ncategory = Category.objects.create(name=\"Technology\", slug=\"tech\")\n\n# Create classifier groups and classifiers\ngroup = ClassifierGroup.objects.create(name=\"Topics\", max_selections=3)\nclassifier = Classifier.objects.create(\n    name=\"AI & Machine Learning\",\n    slug=\"ai-ml\",\n    group=group\n)\n```\n\n### Navigation Menus\n\n```python\nfrom wagtail_feathers.models import Menu, MenuItem, FlatMenu, NestedMenu\n\n# Create a flat menu\nflat_menu = FlatMenu.objects.create(name=\"Main Navigation\")\n\n# Create a nested menu\nnested_menu = NestedMenu.objects.create(name=\"Footer Navigation\")\n\n# Create menu items\nMenuItem.objects.create(\n    menu=flat_menu,\n    link_page=home_page,\n    link_text=\"Home\",\n    sort_order=1\n)\n```\n\n### Error Pages\n\n```python\nfrom wagtail_feathers.models import ErrorPage\n\n# Create custom error pages\nerror_404 = ErrorPage.objects.create(\n    title=\"404\",\n    error_code=\"404\",\n    heading=\"Page Not Found\",\n    message=\"<p>The page you are looking for does not exist.</p>\"\n)\n```\n\n### FAQ System\n\n```python\nfrom wagtail_feathers.models import FAQCategory, FAQ\n\n# Create FAQ categories and items\ncategory = FAQCategory.objects.create(\n    title=\"General Questions\",\n    slug=\"general\"\n)\n\nfaq = FAQ.objects.create(\n    question=\"How do I get started?\",\n    answer=\"<p>Follow our quick start guide...</p>\",\n    category=category\n)\n```\n\n### SEO & Metadata\n\n```python\nfrom wagtail_feathers.models import FeatherBasePage\nfrom wagtail_feathers.models.seo import SeoContentType, TwitterCardType\n\nclass ArticlePage(FeatherBasePage):\n    # SEO fields are automatically available via SeoMixin\n    pass\n\n# In your templates - add to <head> section:\n# {% load seo_tags %}\n# {% include \"wagtail_feathers/seo/meta.html\" %}\n\n# In your templates - add before </body>:\n# {% include \"wagtail_feathers/seo/structured_data.html\" %}\n\n# Programmatic SEO control:\npage = ArticlePage.objects.get(pk=1)\npage.seo_title = \"Custom SEO Title\"  # Uses Wagtail's built-in field\npage.search_description = \"Custom meta description for search engines\"  # Uses Wagtail's built-in field\npage.seo_content_type = SeoContentType.ARTICLE\npage.twitter_card_type = TwitterCardType.SUMMARY_LARGE_IMAGE\npage.save()\n```\n\n### Reading Time Features\n\nAll `ItemPage` models automatically include reading time calculation:\n\n```python\nfrom wagtail_feathers.models import ItemPage\n\nclass BlogPost(ItemPage):\n    # Reading time is automatically calculated from:\n    # - title, introduction, body content\n    # - Configurable words-per-minute (default: 200 WPM)\n    pass\n\n# In templates:\n# {% load reading_time_tags %}\n# {% reading_time page %}  # \"5 min read\"\n# {% reading_time page \"long\" %}  # \"5 minutes read\"  \n# {% reading_time_badge page %}  # Badge with clock icon\n\n# Custom reading time logic:\nclass TechnicalArticle(ItemPage):\n    summary = RichTextField(blank=True)\n    \n    def get_additional_word_sources(self):\n        \"\"\"Include summary in word count.\"\"\"\n        words = 0\n        if self.summary:\n            words += self._count_text_words(str(self.summary))\n        return words\n    \n    # Slower reading speed for technical content\n    words_per_minute = 150\n\n# Reading time extends SEO automatically:\n# - Twitter Cards: \"Reading time: 5 min read\"\n# - Structured Data: \"timeRequired\": \"PT5M\"\n# - Meta tags: article:reading_time\n```\n\n### Extending SEO Features\n\nThe SEO functionality in wagtail_feathers is designed to be extensible. Here are several ways to enhance it:\n\n#### 1. Adding Custom SEO Fields\n\n```python\nfrom wagtail_feathers.models import FeatherBasePage, SeoMixin\nfrom wagtail.admin.panels import FieldPanel\n\nclass ArticlePage(FeatherBasePage):\n    # Add your own SEO-related fields\n    meta_keywords = models.CharField(\n        max_length=255, \n        blank=True,\n        help_text=\"Comma-separated keywords (optional for modern SEO)\"\n    )\n    \n    facebook_app_id = models.CharField(\n        max_length=50,\n        blank=True,\n        help_text=\"Facebook App ID for Open Graph\"\n    )\n    \n    # Extend the SEO panels\n    seo_panels = SeoMixin.seo_panels + [\n        FieldPanel(\"meta_keywords\"),\n        FieldPanel(\"facebook_app_id\"),\n    ]\n    \n    # Override SEO methods for custom behavior\n    def get_seo_description(self):\n        \"\"\"Custom SEO description logic.\"\"\"\n        # Try your custom field first\n        if hasattr(self, \"excerpt\") and self.excerpt:\n            return self.excerpt[:160]\n        \n        # Fall back to parent implementation\n        return super().get_seo_description()\n```\n\n#### 2. Custom Structured Data\n\n```python\nimport json\nfrom wagtail_feathers.models import SeoMixin\n\nclass ProductPage(FeatherBasePage):\n    price = models.DecimalField(max_digits=10, decimal_places=2)\n    brand = models.CharField(max_length=100)\n    sku = models.CharField(max_length=50)\n    \n    def get_structured_data(self):\n        \"\"\"Override to add Product schema.\"\"\"\n        if self.seo_content_type == \"product\":\n            site = self.get_site()\n            base_url = site.root_url if site.root_url else f\"https://{site.hostname}\"\n            \n            schema = {\n                \"@context\": \"https://schema.org\",\n                \"@type\": \"Product\",\n                \"name\": self.get_seo_title(),\n                \"description\": self.get_seo_description(),\n                \"sku\": self.sku,\n                \"brand\": {\n                    \"@type\": \"Brand\",\n                    \"name\": self.brand\n                },\n                \"offers\": {\n                    \"@type\": \"Offer\",\n                    \"price\": str(self.price),\n                    \"priceCurrency\": \"USD\",\n                    \"availability\": \"https://schema.org/InStock\"\n                }\n            }\n            \n            # Add image if available\n            if self.get_seo_image():\n                schema[\"image\"] = f\"{base_url}{self.get_seo_image().file.url}\"\n                \n            return json.dumps(schema, ensure_ascii=False)\n        \n        # Fall back to parent implementation\n        return super().get_structured_data()\n```\n\n#### 3. SEO-Focused Page Types\n\n```python\nfrom wagtail_feathers.models import ItemPage\nfrom wagtail_feathers.models.seo import SeoContentType, TwitterCardType\n\nclass SEOOptimizedArticle(ItemPage):\n    \"\"\"Article page optimized for SEO best practices.\"\"\"\n    \n    # Set SEO defaults\n    seo_content_type = SeoContentType.ARTICLE\n    twitter_card_type = TwitterCardType.SUMMARY_LARGE_IMAGE\n    \n    # Additional SEO fields\n    focus_keyword = models.CharField(\n        max_length=100,\n        blank=True,\n        help_text=\"Primary keyword/phrase for this article\"\n    )\n    \n    reading_time = models.PositiveIntegerField(\n        null=True,\n        blank=True,\n        help_text=\"Estimated reading time in minutes\"\n    )\n    \n    content_panels = ItemPage.content_panels + [\n        FieldPanel(\"focus_keyword\"),\n        FieldPanel(\"reading_time\"),\n    ]\n    \n    def get_structured_data(self):\n        \"\"\"Enhanced article schema with reading time.\"\"\"\n        schema_str = super().get_structured_data()\n        if schema_str and self.seo_content_type == SeoContentType.ARTICLE:\n            schema = json.loads(schema_str)\n            \n            # Add reading time\n            if self.reading_time:\n                schema[\"timeRequired\"] = f\"PT{self.reading_time}M\"\n            \n            # Add focus keyword as about\n            if self.focus_keyword:\n                schema[\"about\"] = {\n                    \"@type\": \"Thing\",\n                    \"name\": self.focus_keyword\n                }\n                \n            return json.dumps(schema, ensure_ascii=False)\n        \n        return schema_str\n```\n\n#### 4. Custom SEO Template Tags\n\n```python\n# In your app\"s templatetags/custom_seo_tags.py\nfrom django import template\nfrom django.utils.safestring import mark_safe\n\nregister = template.Library()\n\n@register.simple_tag\ndef custom_meta_tags(page):\n    \"\"\"Add custom meta tags based on page type.\"\"\"\n    tags = []\n    \n    # Add article-specific tags\n    if hasattr(page, \"focus_keyword\") and page.focus_keyword:\n        tags.append(f\"<meta name='keywords' content='{page.focus_keyword}'>\")\n    \n    # Add Facebook App ID if available\n    if hasattr(page, \"facebook_app_id\") and page.facebook_app_id:\n        tags.append(f\"<meta property='fb:app_id' content='{page.facebook_app_id}'>\")\n    \n    # Add reading time for articles\n    if hasattr(page, \"reading_time\") and page.reading_time:\n        tags.append(f\"<meta name='twitter:label1' content=\"Reading time\">\")\n        tags.append(f\"<meta name='twitter:data1' content='{page.reading_time} min read'>\")\n    \n    return mark_safe(\"\\n\".join(tags))\n\n# In your templates:\n# {% load custom_seo_tags %}\n# {% custom_meta_tags page %}\n```\n\n#### 5. SEO Analytics Integration\n\n```python\nclass AnalyticsEnhancedPage(FeatherBasePage):\n    \"\"\"Page with SEO analytics tracking.\"\"\"\n    \n    google_analytics_id = models.CharField(\n        max_length=20,\n        blank=True,\n        help_text=\"Google Analytics tracking ID (e.g., GA-XXXXX-X)\"\n    )\n    \n    track_scroll_depth = models.BooleanField(\n        default=False,\n        help_text=\"Enable scroll depth tracking for SEO insights\"\n    )\n    \n    def get_context(self, request, *args, **kwargs):\n        context = super().get_context(request, *args, **kwargs)\n        \n        # Add SEO tracking context\n        context.update({\n            \"enable_seo_tracking\": self.track_scroll_depth,\n            \"ga_tracking_id\": self.google_analytics_id,\n        })\n        \n        return context\n```\n\n#### 6. Multi-language SEO\n\n```python\n# If using wagtail-localize\nfrom wagtail_feathers.models.seo import SeoMixin\nfrom wagtail_localize.fields import TranslatableField\n\nclass MultilingualSEOPage(FeatherBasePage):\n    \"\"\"Page with enhanced multilingual SEO support.\"\"\"\n    \n    # Additional translatable SEO fields\n    local_keywords = models.CharField(\n        max_length=255,\n        blank=True,\n        help_text=\"Keywords in local language\"\n    )\n    \n    cultural_context = models.TextField(\n        blank=True,\n        help_text=\"Cultural context for this market\"\n    )\n    \n    # Add to translatable fields\n    if SeoMixin.WAGTAIL_LOCALIZE_AVAILABLE:\n        translatable_fields = SeoMixin.translatable_fields + [\n            TranslatableField(\"local_keywords\"),\n            TranslatableField(\"cultural_context\"),\n        ]\n    \n    def get_seo_description(self):\n        \"\"\"Localized SEO description.\"\"\"\n        if self.cultural_context:\n            return f\"{super().get_seo_description()} {self.cultural_context}\"[:160]\n        return super().get_seo_description()\n```\n\n#### 7. Using Extension Points\n\nThe SeoMixin provides several extension points for easy customization:\n\n```python\nclass BlogArticlePage(ItemPage):\n    \"\"\"Blog article with enhanced SEO using extension points.\"\"\"\n    \n    author_twitter = models.CharField(max_length=50, blank=True)\n    focus_keyword = models.CharField(max_length=100, blank=True)\n    # Note: reading_time is automatically provided by ItemPage\n    \n    def get_custom_meta_tags(self):\n        \"\"\"Add custom meta tags.\"\"\"\n        tags = {}\n        \n        if self.focus_keyword:\n            tags[\"keywords\"] = self.focus_keyword\n        \n        # Reading time is automatically added via ReadingTimeMixin\n        # but you can customize it here if needed\n            \n        return tags\n    \n    def get_custom_twitter_tags(self):\n        \"\"\"Add custom Twitter Card tags.\"\"\"\n        tags = {}\n        \n        if self.author_twitter:\n            tags[\"twitter:creator\"] = f\"@{self.author_twitter}\"\n        \n        # Reading time Twitter tags are automatically added via ReadingTimeMixin\n        # Additional Twitter tags can be added here\n            \n        return tags\n    \n    def get_custom_og_tags(self):\n        \"\"\"Add custom Open Graph tags.\"\"\"\n        tags = {}\n        \n        if self.author_twitter:\n            tags[\"article:author\"] = f\"https://twitter.com/{self.author_twitter}\"\n            \n        return tags\n    \n    def get_sitemap_priority(self):\n        \"\"\"Higher priority for recent articles and longer reads.\"\"\"\n        from django.utils import timezone\n        from datetime import timedelta\n        \n        base_priority = super().get_sitemap_priority()\n        \n        # Boost recent articles\n        if hasattr(self, \"publication_date\"):\n            recent_threshold = timezone.now().date() - timedelta(days=30)\n            if self.publication_date >= recent_threshold:\n                base_priority += 0.1\n        \n        # Boost longer articles (more comprehensive content)\n        if self.reading_time_minutes and self.reading_time_minutes >= 10:\n            base_priority += 0.1\n                \n        return min(1.0, base_priority)  # Cap at 1.0\n```\n\nThese examples show how to build upon wagtail_feathers SEO foundation to create powerful, customized SEO solutions for specific use cases.\n\n### People & Authors\n\n```python\nfrom wagtail_feathers.models import Person, PersonGroup, AuthorType, PageAuthor\n\n# Create person groups and people\ngroup = PersonGroup.objects.create(name=\"Editorial Team\")\nperson = Person.objects.create(\n    first_name=\"John\",\n    last_name=\"Doe\",\n    email=\"john@example.com\"\n)\nperson.groups.add(group)\n\n# Create author types and assign to pages\nauthor_type = AuthorType.objects.create(name=\"Writer\")\nPageAuthor.objects.create(\n    page=my_page,\n    person=person,\n    author_type=author_type\n)\n```\n\n## Requirements\n\n- Python 3.11+\n- Django 5.0+  \n- Wagtail 5.2+\n\nSee `pyproject.toml` for complete dependency list.\n\n## Development\n\n### Setup Development Environment\n\n```bash\n# Clone the repository\ngit clone https://github.com/softquantum/wagtail-feathers.git\ncd wagtail-feathers\n\n# Navigate to demo directory\ncd demo\n\n# Install demo dependencies (includes editable package install)\npip install -r requirements.txt\n\n# Set up database and demo data\npython manage.py migrate\npython manage.py setup_demo_data\n\n# Start the demo server\npython manage.py runserver\n```\n\n```bash\n# Run tests\npytest\n\n# Run tests across multiple environments\ntox\n```\n\n### Try the Demo Site\n\n**Demo Access:**\n- **Frontend:** http://localhost:8000\n- **Admin:** http://localhost:8000/admin (admin/admin123)\n\nThe demo includes (WORK IN PROGRESS):\n- Sample pages using wagtail-feathers features\n- SEO optimization examples\n- Reading time calculation\n- Taxonomy and navigation examples\n- Live package development (changes reflect immediately)\n\n### Testing\n\nThe package includes some tests covering:\n- Model functionality and relationships\n- Template rendering and themes\n- Navigation generation\n- Taxonomy management\n- Multi-site behavior\n\n```bash\n# Run all tests\npytest\n```\n\n### Code Quality\n\n```bash\n# Lint code\nruff check src/\n\n# Format code  \nruff format src/\n\n# Run via tox\ntox -e lint\ntox -e format\n```\n\n## Architecture\n\nWagtail Feathers follows a modular architecture:\n\n```\nwagtail_feathers/\n\u251c\u2500\u2500 models/           # Core model definitions\n\u2502   \u251c\u2500\u2500 base.py      # FeatherBasePage and foundation classes\n\u2502   \u251c\u2500\u2500 author.py    # AuthorType and PageAuthor models\n\u2502   \u251c\u2500\u2500 errors.py    # ErrorPage model\n\u2502   \u251c\u2500\u2500 faq.py       # FAQ and FAQCategory models  \n\u2502   \u251c\u2500\u2500 geographic.py # PageCountry model\n\u2502   \u251c\u2500\u2500 inline.py    # Related content models\n\u2502   \u251c\u2500\u2500 navigation.py # Menu and navigation models\n\u2502   \u251c\u2500\u2500 person.py    # Person and PersonGroup models\n\u2502   \u251c\u2500\u2500 settings.py  # SiteSettings model\n\u2502   \u251c\u2500\u2500 social.py    # Social media models\n\u2502   \u251c\u2500\u2500 specialized_pages.py # Specialized page types\n\u2502   \u251c\u2500\u2500 taxonomy.py  # Category and Classifier models\n\u2502   \u2514\u2500\u2500 utils.py     # Model utilities\n\u251c\u2500\u2500 blocks.py        # StreamField content blocks\n\u251c\u2500\u2500 templatetags/    # Template tag libraries\n\u251c\u2500\u2500 viewsets/        # Wagtail admin viewsets\n\u251c\u2500\u2500 themes.py        # Theme system\n\u2514\u2500\u2500 management/      # Django management commands\n```\n\n## Key Models\n\nBased on the migration, wagtail_feathers provides these core models:\n\n- **Page Foundation**: `FeatherBasePage`, `FeatherBasePageTag`\n- **Taxonomy**: `Category`, `Classifier`, `ClassifierGroup`, `PageCategory`, `PageClassifier`\n- **Navigation**: `Menu`, `MenuItem`, `FlatMenu`, `NestedMenu`, `Footer`, `FooterNavigation`\n- **People & Authors**: `Person`, `PersonGroup`, `AuthorType`, `PageAuthor`\n- **Content Organization**: `FAQ`, `FAQCategory`, `ErrorPage`\n- **Relationships**: `RelatedPage`, `RelatedDocument`, `RelatedExternalLink`\n- **Geography**: `PageCountry`\n- **Social**: `SocialMediaSettings`, `SocialMediaLink`\n- **Settings**: `SiteSettings`\n\n## Contributing\n\nContributions are welcome! Please:\n\n1. Fork the repository\n2. Create a feature branch\n3. Add tests for new functionality\n4. Ensure all tests pass\n5. Submit a pull request\n\n## License\n[BSD-3-Clause](LICENSE)\n\n## Credits & Attributions\n- \u2705 Wagtail Feathers is Developed by [Maxime Decooman](https://github.com/softquantum).\n- \u2705 Built on the excellent [Wagtail CMS](https://wagtail.org/) (License [BSD-3-Clause](https://github.com/wagtail/wagtail/blob/main/LICENSE))\n- \u2705 The icons prefixed \"heroicons-\" are sourced from version 2.2.0 of [Heroicons](https://github.com/tailwindlabs/heroicons), the beautiful hand-crafted SVG icons library, by the makers of Tailwind CSS (License [MIT](https://github.com/tailwindlabs/heroicons/blob/master/LICENSE)).",
    "bugtrack_url": null,
    "license": null,
    "summary": "A comprehensive Wagtail CMS extension providing theming and foundational features.",
    "version": "1.0b1",
    "project_urls": {
        "Home": "https://github.com/softquantum/wagtail-feathers",
        "Issues": "https://github.com/softquantum/wagtail-feathers/issues"
    },
    "split_keywords": [
        "wagtail",
        " django",
        " cms",
        " content",
        " management",
        " taxonomy",
        " navigation",
        " themes"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "c5001f6dbee32e9764a0007768a77e248e398c68feaebcf3dbe87f092c45c2db",
                "md5": "794fc774ef9546578f43a61ef86b53f9",
                "sha256": "44457274286297cec0ff0d68c6a2f7039d65e6d6b5ac6305b3f78db419a89a40"
            },
            "downloads": -1,
            "filename": "wagtail_feathers-1.0b1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "794fc774ef9546578f43a61ef86b53f9",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 553936,
            "upload_time": "2025-07-29T01:47:56",
            "upload_time_iso_8601": "2025-07-29T01:47:56.122782Z",
            "url": "https://files.pythonhosted.org/packages/c5/00/1f6dbee32e9764a0007768a77e248e398c68feaebcf3dbe87f092c45c2db/wagtail_feathers-1.0b1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "fb3223e795ee48de27def18859fc6965871181c725d6fac473bdd44222498d21",
                "md5": "f6c25e7f4ee0beb9815eae0be78d6766",
                "sha256": "6a8343f302b336881cd290ca4867ce33731cff611b84dbd5a1979778b2a08624"
            },
            "downloads": -1,
            "filename": "wagtail_feathers-1.0b1.tar.gz",
            "has_sig": false,
            "md5_digest": "f6c25e7f4ee0beb9815eae0be78d6766",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 658681,
            "upload_time": "2025-07-29T01:47:57",
            "upload_time_iso_8601": "2025-07-29T01:47:57.960511Z",
            "url": "https://files.pythonhosted.org/packages/fb/32/23e795ee48de27def18859fc6965871181c725d6fac473bdd44222498d21/wagtail_feathers-1.0b1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-29 01:47:57",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "softquantum",
    "github_project": "wagtail-feathers",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "wagtail-feathers"
}
        
Elapsed time: 1.79330s