diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d5e9805df6150d952951b54120b55f3680bbf73f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*~ +Pipfile.lock +.venv \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000000000000000000000000000000000000..59a91f72a208550f80d273d4655ef3283ed2e4f6 --- /dev/null +++ b/Pipfile @@ -0,0 +1,14 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +pelican = "*" +Markdown = "*" +typogrify = "*" + +[requires] +python_version = "3" diff --git a/plugins/tag_cloud/README.rst b/plugins/tag_cloud/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..4619969b304cb1fe0d33392fab4bcffa9142beae --- /dev/null +++ b/plugins/tag_cloud/README.rst @@ -0,0 +1,98 @@ +tag_cloud +========= + +This plugin generates a tag-cloud. + +Installation +------------ + +In order to use to use this plugin, you have to edit(*) or create(+) the following files:: + + blog/ + ├── pelicanconf.py * + ├── content + ├── plugins + + │ └── tag_cloud.py + + └── themes + └── mytheme + ├── templates + │ └── base.html * + └── static + └── css + └── style.css * + +In **pelicanconf.py** you have to activate the plugin:: + + PLUGIN_PATHS = ["plugins"] + PLUGINS = ["tag_cloud"] + +Into your **plugins** folder, you should add tag_cloud.py (from this repository). + +In your theme files, you should change **base.html** to apply formats (and sizes) defined in **style.css**, as specified in "Settings", below. + +Settings +-------- + +================================================ ===================================================== +Setting name (followed by default value) What does it do? +================================================ ===================================================== +``TAG_CLOUD_STEPS = 4`` Count of different font sizes in the tag + cloud. +``TAG_CLOUD_MAX_ITEMS = 100`` Maximum number of tags in the cloud. +``TAG_CLOUD_SORTING = 'random'`` The tag cloud ordering scheme. Valid values: + random, alphabetically, alphabetically-rev, size and + size-rev +``TAG_CLOUD_BADGE = True`` Optionnal setting : can bring **badges**, which mean + say : display the number of each tags present + on all articles. +================================================ ===================================================== + +The default theme does not include a tag cloud, but it is pretty easy to add one:: + + + +You should then also define CSS styles with appropriate classes (tag-1 to tag-N, +where N matches ``TAG_CLOUD_STEPS``), tag-1 being the most frequent, and +define a ``ul.tagcloud`` class with appropriate list-style to create the cloud. +You should copy/paste this **badge** CSS rule ``ul.tagcloud .list-group-item .badge`` +if you're using ``TAG_CLOUD_BADGE`` setting. (this rule, potentially long , is suggested to avoid +conflicts with CSS libs as twitter Bootstrap) + +For example:: + + ul.tagcloud { + list-style: none; + padding: 0; + } + + ul.tagcloud li { + display: inline-block; + } + + li.tag-1 { + font-size: 150%; + } + + li.tag-2 { + font-size: 120%; + } + + /* ... add li.tag-3 etc, as much as needed */ + + ul.tagcloud .list-group-item span.badge { + background-color: grey; + color: white; + } + +By default the tags in the cloud are sorted randomly, but if you prefers to have it alphabetically use the `alphabetically` (ascending) and `alphabetically-rev` (descending). Also is possible to sort the tags by it's size (number of articles with this specific tag) using the values `size` (ascending) and `size-rev` (descending). diff --git a/plugins/tag_cloud/__init__.py b/plugins/tag_cloud/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a7004f504c8bb1f1c168b97633231099752a0f56 --- /dev/null +++ b/plugins/tag_cloud/__init__.py @@ -0,0 +1,2 @@ +from .tag_cloud import * + diff --git a/plugins/tag_cloud/tag_cloud.py b/plugins/tag_cloud/tag_cloud.py new file mode 100644 index 0000000000000000000000000000000000000000..bf91055effe3fe33478283cb1dac6ce3bfeb65de --- /dev/null +++ b/plugins/tag_cloud/tag_cloud.py @@ -0,0 +1,90 @@ +''' +tag_cloud +=================================== + +This plugin generates a tag cloud from available tags +''' +from __future__ import unicode_literals + +from collections import defaultdict +from operator import itemgetter + +import logging +import math +import random + +from pelican import signals + +logger = logging.getLogger(__name__) + + +def set_default_settings(settings): + settings.setdefault('TAG_CLOUD_STEPS', 4) + settings.setdefault('TAG_CLOUD_MAX_ITEMS', 100) + settings.setdefault('TAG_CLOUD_SORTING', 'random') + settings.setdefault('TAG_CLOUD_BADGE', False) + + +def init_default_config(pelican): + from pelican.settings import DEFAULT_CONFIG + set_default_settings(DEFAULT_CONFIG) + if(pelican): + set_default_settings(pelican.settings) + + +def generate_tag_cloud(generator): + tag_cloud = defaultdict(int) + for article in generator.articles: + for tag in getattr(article, 'tags', []): + tag_cloud[tag] += 1 + + tag_cloud = sorted(tag_cloud.items(), key=itemgetter(1), reverse=True) + tag_cloud = tag_cloud[:generator.settings.get('TAG_CLOUD_MAX_ITEMS')] + + tags = list(map(itemgetter(1), tag_cloud)) + if tags: + max_count = tags[0] + min_count = tags[-1] + steps = generator.settings.get('TAG_CLOUD_STEPS') + + # calculate word sizes + def generate_tag(tag, count): + tag = ( + tag, + int(math.floor(steps - (steps - 1) * math.log(count - min_count + 1) + / (math.log(max_count - min_count + 1) or 1))) + ) + if generator.settings.get('TAG_CLOUD_BADGE'): + tag += (count,) + return tag + + tag_cloud = [ + generate_tag(tag, count) + for tag, count in tag_cloud + ] + + sorting = generator.settings.get('TAG_CLOUD_SORTING') + + if sorting == 'alphabetically': + tag_cloud.sort(key=lambda elem: elem[0].name) + elif sorting == 'alphabetically-rev': + tag_cloud.sort(key=lambda elem: elem[0].name, reverse=True) + elif sorting == 'size': + tag_cloud.sort(key=lambda elem: elem[1]) + elif sorting == 'size-rev': + tag_cloud.sort(key=lambda elem: elem[1], reverse=True) + elif sorting == 'random': + random.shuffle(tag_cloud) + else: + logger.warning("setting for TAG_CLOUD_SORTING not recognized: %s, " + "falling back to 'random'", sorting) + random.shuffle(tag_cloud) + + # make available in context + generator.tag_cloud = tag_cloud + generator._update_context(['tag_cloud']) + + +def register(): + signals.initialized.connect(init_default_config) + signals.article_generator_finalized.connect(generate_tag_cloud) diff --git a/plugins/tag_cloud/test_data/article_1.md b/plugins/tag_cloud/test_data/article_1.md new file mode 100644 index 0000000000000000000000000000000000000000..bb44efefdcbfd8eb718ccc36eeaa2cc4796b621f --- /dev/null +++ b/plugins/tag_cloud/test_data/article_1.md @@ -0,0 +1,4 @@ +Title: Article1 +tags: fun, pelican, plugins + +content, yeah! \ No newline at end of file diff --git a/plugins/tag_cloud/test_data/article_2.md b/plugins/tag_cloud/test_data/article_2.md new file mode 100644 index 0000000000000000000000000000000000000000..74dbb6346f3acce2570ba870e4656b13727f8375 --- /dev/null +++ b/plugins/tag_cloud/test_data/article_2.md @@ -0,0 +1,5 @@ +Title: Article2 +tags: pelican, plugins, python + +content2, yeah! + diff --git a/plugins/tag_cloud/test_data/article_3.md b/plugins/tag_cloud/test_data/article_3.md new file mode 100644 index 0000000000000000000000000000000000000000..bc0cd5aa1f9ff807ceec20bf7a10aa5d3362c920 --- /dev/null +++ b/plugins/tag_cloud/test_data/article_3.md @@ -0,0 +1,5 @@ +Title: Article3 +tags: pelican, plugins + +content3, yeah! + diff --git a/plugins/tag_cloud/test_data/article_4.md b/plugins/tag_cloud/test_data/article_4.md new file mode 100644 index 0000000000000000000000000000000000000000..9a3132062427e4420ce692b7e5722a1be0d6ab2f --- /dev/null +++ b/plugins/tag_cloud/test_data/article_4.md @@ -0,0 +1,5 @@ +Title: Article4 +tags: pelican, fun + +content4, yeah! + diff --git a/plugins/tag_cloud/test_data/article_5.md b/plugins/tag_cloud/test_data/article_5.md new file mode 100644 index 0000000000000000000000000000000000000000..1d3f2ffa72d378575489bf9c01674859ecef514a --- /dev/null +++ b/plugins/tag_cloud/test_data/article_5.md @@ -0,0 +1,5 @@ +Title: Article5 +tags: plugins, pelican, fun + +content5, yeah! + diff --git a/plugins/tag_cloud/test_tag_cloud.py b/plugins/tag_cloud/test_tag_cloud.py new file mode 100644 index 0000000000000000000000000000000000000000..22fbf0ae35943b4fe2ed03111ba2b7b59188df76 --- /dev/null +++ b/plugins/tag_cloud/test_tag_cloud.py @@ -0,0 +1,114 @@ +import unittest +import os +import six +import tag_cloud +from shutil import rmtree +from tempfile import mkdtemp + +from pelican.generators import ArticlesGenerator +from pelican.tests.support import get_settings +from pelican.urlwrappers import Tag + +CUR_DIR = os.path.dirname(__file__) +CONTENT_DIR = os.path.join(CUR_DIR, 'test_data') + + +class TestTagCloudGeneration(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.temp_path = mkdtemp(prefix='pelicantests.') + + cls._settings = get_settings(filenames={}) + cls._settings['DEFAULT_CATEGORY'] = 'Default' + cls._settings['DEFAULT_DATE'] = (1970, 1, 1) + cls._settings['READERS'] = {'asc': None} + cls._settings['CACHE_CONTENT'] = False + tag_cloud.set_default_settings(cls._settings) + + context = cls._settings.copy() + context['generated_content'] = dict() + context['static_links'] = set() + cls.generator = ArticlesGenerator( + context=context, settings=cls._settings, + path=CONTENT_DIR, theme=cls._settings['THEME'], output_path=cls.temp_path) + cls.generator.generate_context() + + @classmethod + def tearDownClass(cls): + rmtree(cls.temp_path) + + def test_tag_cloud_random(self): + self.generator.settings['TAG_CLOUD_STEPS'] = 10 + self.generator.settings['TAG_CLOUD_BADGE'] = False + tag_cloud.generate_tag_cloud(self.generator) + expected = [ + (Tag('pelican', self._settings), 1), + (Tag('plugins', self._settings), 2), + (Tag('fun', self._settings), 3), + (Tag('python', self._settings), 10) + ] + six.assertCountEqual(self, self.generator.tag_cloud, expected) + + def test_tag_cloud_badge(self): + self.generator.settings['TAG_CLOUD_STEPS'] = 10 + self.generator.settings['TAG_CLOUD_BADGE'] = True + tag_cloud.generate_tag_cloud(self.generator) + expected = [ + (Tag('pelican', self._settings), 1, 5), + (Tag('plugins', self._settings), 2, 4), + (Tag('fun', self._settings), 3, 3), + (Tag('python', self._settings), 10, 1) + ] + six.assertCountEqual(self, self.generator.tag_cloud, expected) + + def test_tag_cloud_alphabetical(self): + self.generator.settings['TAG_CLOUD_STEPS'] = 10 + self.generator.settings['TAG_CLOUD_SORTING'] = 'alphabetically' + tag_cloud.generate_tag_cloud(self.generator) + expected = [ + (Tag('fun', self._settings), 3), + (Tag('pelican', self._settings), 1), + (Tag('plugins', self._settings), 2), + (Tag('python', self._settings), 10) + ] + self.assertEqual(self.generator.tag_cloud, expected) + + def test_tag_cloud_alphabetical_rev(self): + self.generator.settings['TAG_CLOUD_STEPS'] = 10 + self.generator.settings['TAG_CLOUD_SORTING'] = 'alphabetically-rev' + tag_cloud.generate_tag_cloud(self.generator) + expected = [ + (Tag('python', self._settings), 10), + (Tag('plugins', self._settings), 2), + (Tag('pelican', self._settings), 1), + (Tag('fun', self._settings), 3) + ] + self.assertEqual(self.generator.tag_cloud, expected) + + def test_tag_cloud_size(self): + self.generator.settings['TAG_CLOUD_STEPS'] = 10 + self.generator.settings['TAG_CLOUD_SORTING'] = 'size' + tag_cloud.generate_tag_cloud(self.generator) + expected = [ + (Tag('pelican', self._settings), 1), + (Tag('plugins', self._settings), 2), + (Tag('fun', self._settings), 3), + (Tag('python', self._settings), 10) + ] + self.assertEqual(self.generator.tag_cloud, expected) + + def test_tag_cloud_size_rev(self): + self.generator.settings['TAG_CLOUD_STEPS'] = 10 + self.generator.settings['TAG_CLOUD_SORTING'] = 'size-rev' + tag_cloud.generate_tag_cloud(self.generator) + expected = [ + (Tag('python', self._settings), 10), + (Tag('fun', self._settings), 3), + (Tag('plugins', self._settings), 2), + (Tag('pelican', self._settings), 1) + ] + self.assertEqual(self.generator.tag_cloud, expected) + +if __name__ == "__main__": + unittest.main() diff --git a/projects/SEFM2020/Makefile b/projects/SEFM2020/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..dabfa0e547cdff085b1f55fff1b0e3d09030e840 --- /dev/null +++ b/projects/SEFM2020/Makefile @@ -0,0 +1,84 @@ +PY?=python3 +PELICAN?=pelican +PELICANOPTS= + +BASEDIR=$(CURDIR) +INPUTDIR=$(BASEDIR)/content +OUTPUTDIR=$(BASEDIR)/output +CONFFILE=$(BASEDIR)/pelicanconf.py +PUBLISHCONF=$(BASEDIR)/publishconf.py + +SSH_HOST=eosproxey +SSH_PORT=22 +SSH_USER=eos +SSH_TARGET_DIR=/var/www/SEFM2020 + +DEBUG ?= 0 +ifeq ($(DEBUG), 1) + PELICANOPTS += -D +endif + +RELATIVE ?= 0 +ifeq ($(RELATIVE), 1) + PELICANOPTS += --relative-urls +endif + +help: + @echo 'Makefile for a pelican Web site ' + @echo ' ' + @echo 'Usage: ' + @echo ' make html (re)generate the web site ' + @echo ' make clean remove the generated files ' + @echo ' make regenerate regenerate files upon modification ' + @echo ' make publish generate using production settings ' + @echo ' make serve [PORT=8000] serve site at http://localhost:8000' + @echo ' make serve-global [SERVER=0.0.0.0] serve (as root) to $(SERVER):80 ' + @echo ' make devserver [PORT=8000] serve and regenerate together ' + @echo ' make ssh_upload upload the web site via SSH ' + @echo ' make rsync_upload upload the web site via rsync+ssh ' + @echo ' ' + @echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html ' + @echo 'Set the RELATIVE variable to 1 to enable relative urls ' + @echo ' ' + +html: + $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) + +clean: + [ ! -d $(OUTPUTDIR) ] || rm -rf $(OUTPUTDIR) + +regenerate: + $(PELICAN) -r $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) + +serve: +ifdef PORT + $(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) +else + $(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) +endif + +serve-global: +ifdef SERVER + $(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b $(SERVER) +else + $(PELICAN) -l $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) -b 0.0.0.0 +endif + + +devserver: +ifdef PORT + $(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) -p $(PORT) +else + $(PELICAN) -lr $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS) +endif + +publish: + $(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(PUBLISHCONF) $(PELICANOPTS) + +ssh_upload: publish + scp -P $(SSH_PORT) -r $(OUTPUTDIR)/* $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) + +rsync_upload: publish + rsync -var --chmod="o+r" --delete $(OUTPUTDIR)/ $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) + +.PHONY: html help clean regenerate serve serve-global devserver publish ssh_upload rsync_upload diff --git a/projects/SEFM2020/content/extras/favicon.ico b/projects/SEFM2020/content/extras/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..a7e4434b00ff6f7aa3568963d1d678f27d192064 GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x$s0z4t0&$6Exh#P@81c+sU7>{a9 zDWLiwApVbpp8(lwfw&Zi?SPmEQx`-8D8~TAM}QbvKRWvtQ2rbcPY2?7Al3k4CXfk0 z+8l_{)f2;i1l7NiO#PpL`V@fpHwF58$