Embed Mastodon toots with Nikola

Reading the post Mastodon Embed Shortcode for hugo I became envious and thought 'We Nikola users might want that, too'. The Nikola handbook plainly admits to have 'adopted' the shortcode feature from Hugo so how hard can it be?

Here are three ways of embedding toots in your Nikola site using reStructuredText.

The raw directive

This directive enables you to pass content to the output without being processed. See reStructuredText Directives documentation for details.

raw.txt (Source)

.. raw:: html

    <iframe src="https://mastodon.social/@kevingimbel/100745593232538751/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="600" height="333"</iframe>

The raw shortcode

This is part of the built-in shortcodes. It works like the directive, you only need to use shortcode tags.

raw.txt (Source)

{{% raw %}}
<iframe src="https://mastodon.social/@kevingimbel/100745593232538751/embed" class="mastodon-embed" style="max-width: 100%; border: 0" width="600" height="333"</iframe>
{{% /raw %}}

Create your own shortcode

A separate shortcode can easily be realized with a template-based shortcode.

All you have to do is create a template and save it as your_shortcode_name.tmpl in the shortcodes folder of your Nikola site. If you do not use any custom shortcodes yet you will probably have to create the folder.

Depending on the template engine used by your theme you have to provide shortcode templates for Jinja2 or Mako:

The shortcode can be used as intended, pass a status and optionally width and height.

raw.txt (Source)

{{% mastodon status=https://mastodon.social/@kevingimbel/100745593232538751 %}}
{{% mastodon status=https://mastodon.social/@kevingimbel/100745593232538751 width=300 height=600 %}}

This is working for me but if the shortcode fails you should use the shortcode role to pass the text unaltered (consult the handbook for more information):

raw.txt (Source)

:sc:`{{% mastodon status=https://mastodon.social/@kevingimbel/100745593232538751 %}}`

tfbrief - LaTeX-Briefvorlage

Manche raunen, Briefe mit LaTeX zu verfassen, wäre mit Kanonen auf Spatzen zu feuern. Die kennen die Dokumentenklasse tfbrief nicht.

tfbrief fiel mir einst vor Jahren zu und begeistert ob seiner Einfachheit nutzte ich es eifrig und verbreitete es fleißig unter Interessierten. Die Originalquelle ist inzwischen versiegt, doch ich konnte eine einsame Kopie auf GitHub ausfindig machen. Dabei stellte ich fest, dass die Dokumentenklasse quasi unverändert und weitgehend unter Ausschluss der Öffentlichkeit existierte.

Meine Änderungen:
  • obsoleten Code entfernen (fixltx2e package, tocityshort-Attribut)
  • die Option german lädt ngerman, wenn babel geladen wird
  • Typos (v.a. der Kommafehler in Abschlussformel (im Deutschen nicht vorhanden))
  • Betreffzeile serifenlos
  • eine weitere Beispieldatei
  • PDF-Outputs der Beispiele
  • ausführliche Dokumentation

Link zum Repository: GitHub: andre-lehnert/latex-letter

Packing GTK+ applications with setuptools

The only reason this page exists is to remove the links in my setup.py file.

Dependencies

from setuptools import setup

REQUIRED = ["PyGObject", ...]

setup(
    ...
    install_requires=REQUIRED,
    ...
    )

Pass the PyPI package name, for GTK+ applications you will need at least the GObject Python bindings to successfully run the import gi command.

Non-code files

from setuptools import setup

PACKAGES = ["my_package"]
PACKAGE_DIR = {"my_package": "my_package_dir"}
PACKAGE_DATA = {"my_package": ["ui/*", "logging.yaml"]}

setup(
    ...
    packages=PACKAGES,
    package_dir=PACKAGE_DIR,
    package_data=PACKAGE_DATA,
    ...
    )

The package "my_package" will be built from the files in "my_package_dir" folder into the "my_package" folder.

Non-code files like Glade files are passed as a list to the package_data option. In the example all files of the subfolder "ui" and the file "logging.yaml" are integrated into the package.

Desktop files

The locations of .desktop files and icons are defined by the Freedesktop specifications. Desktop entries for example are located in

/usr/share/applications
/usr/local/share/applications
~/.local/share/applications

While running install the suitable prefix will be identified so only relative paths are required to be passed to the data_files option.

from setuptools import setup

rel_app_path = "share/applications"
rel_icon_path = "share/icons/hicolor/scalable/apps"

DATAFILES = [
            (destination_dir, ["list", "of", "files"]),
            (rel_app_path, ["my_app.desktop"]),
            (rel_icon_path, ["my_appicon.svg"]),
            ]

setup(
    ...
    data_files=DATAFILES,
    ...
    )

This step only copies the files into the specific directories. The correct path declaration WITHIN the .desktop file has to be customized during the install command which will be accomplished by a custom function.

Customizing existing commands

The .desktop file includes information about the program to be executed as well as a corresponding icon, keywords etc. Because the target installation location may vary the file has to be adapted during the installation process.

To run own methods in existing commands you will have to create an instance of the specific command class (install, build, bdist etc.) and customize the "run" method.

In setuptools this information is passed to the cmd_class option.

from setuptools import setup
from setuptools.command.install import install

class CustomInstall(install):

    def run(self):
        self.my_function(args, go, here)
        install.run(self)

    def my_function(self, *args):
        try:
            do_some_shit()
        except:
            pass

setup(
    ...
    cmdclass={"install": CustomInstall}
    ...
    )

A list of available commands can be obtained by

$ python setup.py --help-commands

Creating new commands

Setuptools enables you to simply create your own commands. It may be useful to create an 'uninstall' command to get rid of all the files dumped to the system during installation to avoid to do that manually.

from setuptools import setup, Command

class UnInstall(Command):

    description = "description shown by setup.py --help-commands"
    user_options = [("myoption",
                     "m",
                     "myoption description shown by setup.py cmd --help")]

    def initialize_options(self):
        # method must exist
        # define all options with default value
        self.myoption = None

    def finalize_options(self):
        # method must exist
        pass

    def run(self):
        # method must exist
        # code to be executed goes here
        print("This is a custom command.")

setup(
    ...
    cmdclass={"uninstall": UnInstall}
    ...
    )

NoN: jetzt noch toller

Knights of Ni - sag niemals nie

Nach den kürzlichen Änderungen an der Oberfläche gibt es nun erfreuliche Fortschritte darunter zu vermelden.

Neuigkeiten

  1. Man kann NoN jetzt richtig (mit Hilfe von setuptools) installieren und deinstallieren.
  2. Vom Programm generierte Dateien werden nun wie üblich im eigenen Verzeichnis im Homeverzeichnis gespeichert.
  3. Der Programmablauf wurde beschleunigt und die Dateizugriffe massiv reduziert, indem die Posts-/Pages-Informationen für den wiederholten Abruf geparst und in einer JSON-Datei abgelegt werden.

Installation via setuptools

Bei der Installation via setuptools werden sowohl das Programm inklusive der UI als auch ein Desktopeintrag installiert. Nach der Installation lässt sich das Programm in GNOME also bequem über [super] + [n] (... [o] ... [n]) + [enter] starten.

Um dies alles auch wieder loszuwerden, gibt es ein zusätzliches uninstall-Kommando. Beide Kommandos sind mit der Option --user möglich:

# (De-/)Installation im lokalen Nutzerverzeichnis
$ python setup.py install --user
$ python setup.py uninstall --user

# systemweite (De-/)Installation
$ sudo python setup.py install
$ sudo python setup.py uninstall

Dateistruktur

Die Nutzerdaten liegen jetzt in ~/.non. Dort befinden sich

  1. die Konfigurationsdatei config.yaml, in der der Pfad zur aktuellen/letzten Nikola-Seite und die Bookmarks gespeichert sind,
  2. die Logdatei non.log sowie
  3. die geparsten Daten der aufgerufenen Nikola-Seiten jeweils als JSON-Datei.

Speicherung von (Meta-)Daten

Für die Anzeige im TreeStore wurden die Daten bisher sowieso intern als Dictionary gespeichert. Das Ineffiziente daran war allerdings, dass die Daten bei jedem Refresh immer neu abgerufen wurden. Dies ist nicht notwendig und mit zunehmender Größe der Seite auch sehr zäh.

Die Lösung bestand also darin, die Daten des Dictionary zum einen für den späteren Gebrauch in einer Datei zu speichern und zum anderen es nur durch eine Update-Funktion auf aktualisierte Inhalte zu überprüfen.

Auf diese Weise werden beim ersten Aufruf einer Nikola-Konfiguration alle Posts/Pages, Tags und Kategorien oder bei erneutem Aufruf die Daten aus der entsprechenden JSON-Datei eingelesen. Die Aktualisierung erfolgt über den Abgleich der letzten Dateimodifikation. Die JSON-Datei wird beim Wechsel in eine andere Nikola-Seite (z.B. über ein Bookmark) oder bei Programmende gespeichert.

Lost in the rabbit hole of Google Takeout

Backup your data. They said. All of it. They said. And then came Google Takeout

/images/import_gplus_post.png

G+ post HTML file from Takeout

I get the 4, 20 and Google+

Takeout is Google's user data archive system for numerous products. My primary target me was to download an archive of my Google+ activities.

The approach is quite simple: choose the product in Takeout and wait until the archive(s) has/have been generated. The downloadable archive will be valid for a week but you can generate new archives at any time.

Lesson 1:
Choose zip as filetype if you use umlauts, there could be encoding issues in tgz files.

I remembered that there was an import plugin for Nikola and I imagined to throw in the archive and to get a usable local site in return. At this point of the article the reader may speculate that this didn't work in the slightest way.

Unpacking presents

The first inspection reveals:

  1. All G+ posts are located in Google+ stream/Posts as HTML files. These files appear usable.
  2. Image links just point to filenames. The path is missing so only images in the same directory are shown but
  3. Images are scattered among different directories (in Posts and Photos and their subfolders). The majority of image files are stored in Photos of posts in date corresponding subfolders.
  4. There are different date formats in peaceful co-existence:
Photos of posts/
 ├── 02.06.14
 ├── 02.06.16
 ├── 22. Juli 2013
 ├── 23.01.17
 ├── 2011-08-14
 └── 2012-03-13
  1. There is a corresponding JSON file for every image but not for HTML files.
  2. Strucure of HTML files:
/images/import_gplus_inspector.thumbnail.png

Dumdidumdumdum...Inspektor Gadget

Lesson 2
You can open only single posts, there are a lot of deadlinks in image posts, but share and reaction information are displayed (public/private/collection/community post, +1, reshares and comments).

Your entry: Nikola

With low expectations I install the import plugin for Nikola and see what happens. Nothing. The posts once were provided as JSON files but not in recent days.

I brachiate through the files, importing HTML files first. The import plugin instantiates a new Nikola site, so I can just trial and error like hell. Then I care about deadlinks, then titles, it kepps getting better with every build.

The result is a static website of my Google+ stream including +1's and comments and a link to the original post.

Theming

In general the import is independent from any theme. I personally recommend hyde which even can be improved by the custom.css that is included in the archive.

Wishlist

  • local search function
  • filter posts by share status

Attention!

In case you consider a publicly accessible stream backup you have to keep in mind that the imported data also includes all privately shared posts.

Conclusion

As a long-term heavy Google+ user you are used to inconsistencies and improvementent constantly getting worse so a Takeout archive is no more than a sparring partner to train with. It is only a matter of time until my version of the import plugin will go the way of all those Google messengers before.

/images/takeout_gplus_slow.gif

static Google+ Nikola site (hyde theme)

Update

The end of Google+ has been recently announced and the issue of backing up and presentation of the data has become more relevant.

You can download the plugin now from GitHub: encarsia/gplus_nikola_import.

The associated article to the plugin: Nikola-Import-Plugin für Google+ (currently only in German but there is a detailed README file in the repository on how to get the plugin work).


Comment on