Skip to content

Commit

Permalink
Merge pull request #1 from shazz/patch-readtime
Browse files Browse the repository at this point in the history
Update pelicanconf.py
  • Loading branch information
shazz authored Aug 17, 2024
2 parents 251214a + 9ca5051 commit ca9c4b7
Show file tree
Hide file tree
Showing 13 changed files with 1,161 additions and 2 deletions.
39 changes: 39 additions & 0 deletions pelican-plugins/Contributing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Contributing a plugin
=====================

Details regarding how to write a plugin are explained in the Pelican `docs`_.

If you want to contribute, **please be sure** to read our general contributing
`guidelines`_ first. Then you can fork this repository, create a new branch,
make your changes, squash your commits, and issue your pull request from your
new branch (i.e., **not** the ``master`` branch).

Make sure that your plugin follows the structure below::

my_plugin
├── __init__.py
├── my_plugin.py
├── test_my_plugin.py
└── ReadMe.rst / ReadMe.md

``my_plugin.py`` is the actual plugin implementation. Include a brief
explanation of what the plugin does as a module docstring. Put any further
explanations and usage details into the ``ReadMe`` file.

``__init__.py`` should contain a single line with ``from .my_plugin import *``.

Place tests for your plugin in the same folder inside ``test_my_plugin.py``.
If you need content or templates in your tests, you can use the main
``test_data`` folder for that purpose.

**Note:** Each plugin can contain a LICENSE file stating the license it's
released under. If there is an absence of LICENSE then it defaults to the
*GNU AFFERO GENERAL PUBLIC LICENSE Version 3*. Please refer to the ``LICENSE``
file for the full text of the license.

Before making your initial commit, please be sure to add an entry to the repo's
top-level ``ReadMe`` file, adding your plugin to the list (in alphabetical
order) and providing a brief description.

.. _guidelines: http://docs.getpelican.com/en/latest/contribute.html#using-git-and-github
.. _docs: http://docs.getpelican.com/en/latest/plugins.html#how-to-create-plugins
665 changes: 665 additions & 0 deletions pelican-plugins/LICENSE

Large diffs are not rendered by default.

349 changes: 349 additions & 0 deletions pelican-plugins/Readme.rst

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pelican-plugins/latex
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
render_math/
41 changes: 41 additions & 0 deletions pelican-plugins/readtime/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# 'pelican-readtime' - A Read Time Plugin for Pelican Static Site Generator

An article estimated read time plugin for Pelican static site generator. After Pelican generated the content of each page, the plugin read through the generated HTML content and strip all the tags, count all the word, then utilize average human read speed to calculate the read time of each article. The read time is passed over to the 'content' object of the article so Jinja template can use it to display the read time on wherever appropriate.


Demo
-----
The plugin can be embedded into your site's template and look like this:<br>
![Pelican Read Time Demo](./demo.png )<br>
An example can be found in my own blog [here](https://wayofnumbers.github.io/).<br>

Usage
-----

This plugin only uses standard modules(re, html, math, etc), so no extra module installation like BeautifuSoup. To use it, just add the plugin name to the **pelicanconf.py** file.
```python
PLUGINS=[ ... , 'pelican-readtime']
```
Then you can put the following code in whichever template you what, like *article.html*.
```html
{% if article.readtime %}
<div><b>Read in {{article.readtime.minutes}} min.</b></div>
{% endif %}
```

You can also add some styling to it like so:
```html
{% if article.readtime %}
<span><p style="text-align:right; color:#aaaaaa; ">&nbsp Estimated read time: {{article.readtime.minutes}} min.</p></span>
{% endif %}
```

Credits
-----
This is a revised version of [jmaister's readtime plugin](https://github.com/jmaister/readtime). I added some more comments and tweaked the code so it runs smoothly on Python 3.6


Reference
-----
[1] Wikipedia - [Words per minute](https://en.wikipedia.org/wiki/Words_per_minute) <br>
[2] Medium - [Read Time](https://help.medium.com/hc/en-us/articles/214991667-Read-time) <br>
1 change: 1 addition & 0 deletions pelican-plugins/readtime/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .readtime import *
Binary file not shown.
Binary file not shown.
Binary file added pelican-plugins/readtime/demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions pelican-plugins/readtime/readtime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import re
import math

from pelican import signals
from html.parser import HTMLParser #use html.parser for Python 3.6


# http://en.wikipedia.org/wiki/Words_per_minute
WPM = 230.0


class MLStripper(HTMLParser):
def __init__(self):
super().__init__() # subclassing HTMLParser, also need to calling
# super class's '__init__' method
self.reset()
self.fed = []

#this method is called whenever a 'data' is encountered.
def handle_data(self, d):
self.fed.append(d)

# join all content word into one long sentence for further processing
def get_data(self):
return ''.join(self.fed)


def strip_tags(html):
s = MLStripper()
s.feed(html) # Feed the class with html content, get the fed list
return s.get_data()


def calculate_readtime(content_object):
if content_object._content is not None:
content = content_object._content # get the content html from Pelican

text = strip_tags(content) #strip tags and get long sentence
words = re.split(r'[^0-9A-Za-z]+', text) # split the long sentence into list of words

num_words = len(words) # count the words
minutes = int(math.ceil(num_words / WPM)) #calculate the minutes

#set minimum read time to 1 minutes.
if minutes == 0:
minutes = 1

content_object.readtime = {
"minutes": minutes,
}


def register():
signals.content_object_init.connect(calculate_readtime) # connect with 'content_object_init' signal.

4 changes: 4 additions & 0 deletions pelican-plugins/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Markdown
nose
nose-exclude
nose-summary-report
2 changes: 1 addition & 1 deletion pelicanconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
THEME = "themes/pelican-chunk"

PLUGIN_PATHS = ["pelican-plugins"]
PLUGINS = ["readtime", "neighbors"]
PLUGINS = ["pelican-readtime"]

PATH = "content"

Expand Down
6 changes: 5 additions & 1 deletion themes/pelican-chunk/templates/post.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
<span class="cat-links"><a href="{{ SITEURL }}/{{ article.category.url }}"
title="View all posts in {{ article.category }}" rel="category tag">{{ article.category }}</a></span>
{% endif %}

{% if article.readtime %}
<span class="cat-links">Read in {{article.readtime.minutes}} min.</span>
{% endif %}
</div> <!-- /#entry-meta -->
<div class="main">
<h2 class="entry-title">
Expand All @@ -37,4 +41,4 @@ <h2 class="entry-title">
</span>
{% endif %}
</div> <!--/#main-->
</div> <!--/#post-->
</div> <!--/#post-->

0 comments on commit ca9c4b7

Please sign in to comment.