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
173 changes: 173 additions & 0 deletions tests/test_future_posts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import os
import sys
import json
import shutil
import pytest
from pytest import fixture

from nikola import nikola
from nikola.utils import _reload


try:
from freezegun import freeze_time

_freeze_time = True
except ImportError:
_freeze_time = False
freeze_time = lambda x: lambda y: y

# Define the path to our V8 plugins
from tests import V8_PLUGIN_PATH


class TestCommandFuturePostsBase:
"""Base class for testing the future_posts command."""

@fixture(autouse=True)
def _copy_plugin_to_site(self, _init_site):
"""Copy the future_posts plugin to the test site's plugins directory."""
if not os.path.exists("plugins"):
os.makedirs("plugins")
shutil.copytree(
str(V8_PLUGIN_PATH / "future_posts"),
os.path.join("plugins", "future_posts"),
)

def _add_test_post(self, title):
"""Helper to add a test post with specified date."""
self._run_command(["new_post", "-f", "markdown", "-t", title, "-s"])

def _force_scan(self):
"""Force a rescan of posts."""
self._site._scanned = False
self._site.scan_posts(True)

@fixture(autouse=True)
def _init_site(self, monkeypatch, tmp_path):
"""Initialize a demo site for testing."""
from nikola.plugins.command.init import CommandInit

# Create demo site
monkeypatch.chdir(tmp_path)
command_init = CommandInit()
command_init.execute(options={"demo": True, "quiet": True}, args=["demo"])

# Setup paths and load config
sys.path.insert(0, "")
monkeypatch.chdir(tmp_path / "demo")
with open("conf.py", "a") as f:
f.write(
"SCHEDULE_RULE = 'RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;BYHOUR=7;BYMINUTE=0;BYSECOND=0'\n"
)
import conf

_reload(conf)
sys.path.pop(0)

# Initialize Nikola site
self._site = nikola.Nikola(**conf.__dict__)
# Initialize plugins (our plugin will be discovered from the plugins directory)
self._site.init_plugins()

def _run_command(self, args=[]):
"""Run a Nikola command."""
from nikola.__main__ import main

return main(args)

def _get_command_output(self, args):
"""Run command and capture its output."""
import subprocess

try:
output = subprocess.check_output(args)
return output.decode("utf-8")
except (OSError, subprocess.CalledProcessError):
return ""


@pytest.mark.skipif(not _freeze_time, reason="freezegun package not installed.")
class TestCommandFuturePosts(TestCommandFuturePostsBase):
"""Test the future_posts command functionality."""

@fixture(autouse=True)
def setUp(self):
"""Set up test posts."""
# Add some test posts with future dates
for i in range(3):
self._add_test_post(f"Future Post {i+1}")
self._force_scan()

def test_json_output(self):
"""Test JSON output format."""
output = self._get_command_output(["nikola", "future_posts", "--json"])
posts = json.loads(output)
assert isinstance(posts, list)
assert len(posts) >= 3
assert all(isinstance(post, dict) for post in posts)
assert all("title" in post and "date" in post for post in posts)

def test_calendar_output(self):
"""Test calendar output format."""
output = self._get_command_output(["nikola", "future_posts", "--calendar"])
print(output)
assert "## indicates days with scheduled posts" in output
assert any(month in output for month in ["January", "February", "March"])

def test_details_output(self):
"""Test detailed list output format."""
output = self._get_command_output(["nikola", "future_posts", "--details"])
assert "Found" in output
assert "Future Post" in output
assert "Title:" in output
assert "Date:" in output

def test_html_output(self):
"""Test HTML output format."""
output = self._get_command_output(["nikola", "future_posts", "--html"])
assert "<!DOCTYPE html>" in output
assert "<title>Future Posts</title>" in output
assert "Future Post" in output

def test_no_future_posts(self):
"""Test behavior when no future posts exist."""
# Clear existing posts
for post in os.listdir("posts"):
os.remove(os.path.join("posts", post))
self._force_scan()
output = self._get_command_output(["nikola", "future_posts", "--details"])
assert "No future posts scheduled." in output

@pytest.mark.skipif(not _freeze_time, reason="freezegun package not installed.")
@freeze_time("2025-01-01")
def test_months_parameter(self):
"""Test the --months parameter."""
output = self._get_command_output(
["nikola", "future_posts", "--calendar", "--months", "2"]
)
month_headers = [
line
for line in output.split("\n")
if any(
month in line
for month in [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
]
)
]
assert len(month_headers) == 2
21 changes: 21 additions & 0 deletions v8/future_posts/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2025 Jesper Dramsch

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
157 changes: 157 additions & 0 deletions v8/future_posts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Future Posts Plugin for Nikola

A command plugin for [Nikola](https://getnikola.com) that helps you manage and visualize your scheduled future blog posts. Get an overview of upcoming content through ASCII calendars, detailed lists, JSON data, or HTML output.

## Overview

This plugin adds a `future_posts` command to Nikola that displays information about posts scheduled for future dates. It supports multiple output formats that can be used individually or combined.

## Features

- 📅 ASCII calendar view showing scheduled post dates
- 📋 Detailed list view of upcoming posts
- 🔄 JSON output for integration with other tools
- 🌐 HTML output for web viewing
- 📦 Modular design - mix and match output formats
- 💾 Save output to file or display in terminal

## Installation

You can install this plugin using Nikola's plugin manager:

```bash
nikola plugin -i future_posts
```

Or manually:

1. Create a `future_posts` directory in your Nikola site's plugins directory:

```bash
mkdir -p plugins/future_posts
```

2. Copy `future_posts.plugin` and `future_posts.py` into the new directory

3. Add "future_posts" to your `COMMANDS` list in `conf.py`:
```python
COMMANDS.append('future_posts')
```

## Usage

Basic usage:

```bash
nikola future_posts
```

### Output Formats

You can combine any of these options:

- `--calendar` (`-c`): Show ASCII calendar with marked post dates
- `--details` (`-d`): Show detailed list of posts
- `--json` (`-j`): Output in JSON format
- `--html` (`-h`): Generate HTML output

### Additional Options

- `--months N` (`-m N`): Show N months in calendar view (defaults to all scheduled months + 1)
- `--output FILE` (`-o FILE`): Save output to file instead of stdout

### Examples

Show calendar for scheduled posts:

```bash
nikola future_posts --calendar
```

Show both calendar and detailed list:

```bash
nikola future_posts --calendar --details
```

Generate JSON output:

```bash
nikola future_posts --json
```

Save HTML view to file:

```bash
nikola future_posts --html --output future-posts.html
```

Show calendar for next 6 months:

```bash
nikola future_posts --calendar --months 6
```

## Configuration

Optional configuration settings can be added to your site's `conf.py`:

```python
# Default number of months to show in calendar view
FUTURE_POSTS_DEFAULT_MONTHS = 3

# Default output format
FUTURE_POSTS_DEFAULT_FORMAT = "details"

# Default output file path
FUTURE_POSTS_DEFAULT_OUTPUT = None
```

## Sample Output

### Calendar View

```
February 2025
Mo Tu We Th Fr Sa Su
1 2 3 4
5 6 7 8 9 ## 11
12 13 14 15 16 17 18
19 20 21 22 23 24 ##
26 27 28

## indicates days with scheduled posts
```

### Detailed View

```
Found 2 future posts:
----------------------------------------
Title: My First Future Post
Date: 2025-02-10
Source: posts/first-post.rst
Permalink: /blog/2025/02/first-post/
----------------------------------------
```

## Contributing

Contributions are welcome! Please feel free to:

1. 🐛 Report bugs
2. ✨ Suggest new features
3. 📝 Improve documentation
4. 🔧 Submit pull requests

## License

This project is licensed under the MIT License - see the LICENSE file for details.

## Author

Jesper Dramsch

## Support

If you find any issues or have questions, please file an issue on the [Nikola Plugins repository](https://github.com/getnikola/plugins).
11 changes: 11 additions & 0 deletions v8/future_posts/conf.py.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Options for the future_posts plugin
# No configuration is required by default, but you can customize these settings

# Default number of months to show in calendar view
# FUTURE_POSTS_DEFAULT_MONTHS = 3

# Default output format (calendar, details, json, or html)
# FUTURE_POSTS_DEFAULT_FORMAT = "details"

# Default output file path (if not specified, outputs to stdout)
# FUTURE_POSTS_DEFAULT_OUTPUT = None
13 changes: 13 additions & 0 deletions v8/future_posts/future_posts.plugin
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Core]
Name = future_posts
Module = future_posts

[Nikola]
MinVersion = 8.0.0
PluginCategory = Command

[Documentation]
Author = Jesper Dramsch
Version = 1.0.0
Website = https://plugins.getnikola.com/#future_posts
Description = A command to visualize and manage scheduled future blog posts
Loading
Loading