Templates für Briefe und Rechnungen in RMarkdown erstellen

April 10th, 2021

1 Einleitung

Ich nutze RMarkdown und RStudio schon seit längerem für statistische Auswertungen und Beispielrechnungen in der Lehre. Für meine Briefe und Rechnungen habe ich immer LaTeX verwendet.

Im Dezember habe ich dann dieses Video von Rob Hyndman gesehen:

https://www.youtube.com/watch?v=_D-ux3MqGug

…und dachte mir: das will ich auch!

Zu dieser Anleitung gibt es ein Github-Repo mit dem Template-Paket und der Rmd-Datei dieser Anleitung:
https://github.com/produnis/RMarkdownMyStyle.

Außerdem ist der Arikel bei RPubs.com veröffentlicht, und dort etwas „hübscher“ dargestellt, siehe https://rpubs.com/produnis/RMarkdownTemplates.

1.1 Vorwarnung

Diese Anleitung setzt voraus, dass Sie Ihre Briefe und Rechnungen bereits in LaTeX schreiben, bzw, dass eine Brief- oder Rechnungsvorlage in LaTeX vorliegt. In diesem Tutorial möchte ich zeigen, wie Sie Ihre LaTeX-Vorlagen als Templates in RMarkdown verfügbar machen können, um die Briefe im PDF-Format zu erzeugen. Das heisst, dass letztendlich Ihr RMarkdown wieder über LaTeX nach PDF kompiliert wird. LaTeX muss also installiert sein, und Sie müssen das gewünschte Layout bereits in LaTeX erstellt haben.
Ebenso muss das bookdown-Paket installiert sein.

install.packages("bookdown")

Die Überführung nach Rmarkdowngeht am einfachsten, indem man sie als Templates in einem eigenen R-Paket abpeichert.

2 Ein eigenes R-Paket anlegen

Klicke in RStudio auf File \(\rightarrow\) New Project...

Wählen Sie R Package aus.


Figure 2.1: R-Package wählen

Geben Sie dem Paket einen Namen, z.B. MyStyle, und klicken Sie auf Create Project...


Figure 2.2: Packagenamen wählen

Im Arbeitsverzeichnis des neuen Projekts liegen nun die notwendigen Dateien und grundlegenden Paketordner, wobei wir das meister ignorieren können.

In DESCRIPTION können Sie eine Beschreibung des Pakets vornehmen. Wenn Sie das Paket eh nie veröffentlichen, können Sie sich diesen Schritt sparen.

Im Verzeichnis R werden alle Scriptdateien automatisch ausgeführt, sobald das Paket aktiviert wird. Dort hat RStudio bereits die Dummy-Datei hello.R angelegt. Die dort hinterlegte Funktion hello() steht also nach Aktivierung des Pakets zur Verfügung.

Das können wir direkt testen, indem wir das Paket “bauen und installieren”.

Klicken Sie im Datenfenster (rechter oberer Bereich) auf den Reiter Build.


Figure 2.3: Reiter Build

In diesem Bereich können Sie auf den Knopf Install and Restart klicken.


Figure 2.4: Paket installieren

Dadurch wird Ihr neues Paket installiert und automatisch per library(MyStyle) geladen. Sie können nun alle Funktionen Ihres Pakets in der R-Konsole aufrufen. Probieren wir also die Dummy-Funktion hello() aus, die in der Datei R/hello.R hinterlegt ist:

hello()
## [1] "Hello, world!"

Immer dann, wenn Sie dem Paket neue Funktionen (oder Templates) hinzugefügt haben, können Sie es durch Klick auf Install and Restart aktualisieren.

2.1 Ein Template für Briefe anlegen

Templates fürRMarkdown müssen innerhalb des Pakets im Ordner inst/rmardown/templates/ vorliegen.
Jedes Template erhält einen eigenen Unterordner, in welchem die erforderlichen Dateien abgelegt sind. Auch hier folgend die Dateien einer gewissen Struktur.

Mit der Funktion use_rmarkdown_template() aus dem Paket usethis können wir das “Skelett” des neuen Templates erzeugen.
Beginnen wir mit der Briefvorlage. Das Template soll meinBrief heissen.

install.packages("usethis", dependencies = T)
usethis::use_rmarkdown_template(
  template_name = "meinBrief",
  template_dir = NULL,
  template_description = "Meine private Briefvorlage",
  template_create_dir = FALSE
)

Wie Sie sehen, wurden die Unterordner inst/rmardown/templates/meinbrief angelegt.
Hier liegt die Datei template.yaml. Sie enthält die Informationen, die wir der use_rmarkdown_template()-Funktion übergeben haben.

name: meinBrief
description: >
   Meine private Briefvorlage
create_dir: FALSE

Hier müssen wir nichts ändern.

Im Unterordner skeleton liegt die Datei skeleton.Rmd. In ihr werden Beispieltexte und -code hinterlegt, die später beim Aufruf des Templates als Starttext erzeugt werden. Wir benutzen diese Datei, um unser Template zu testen und die benötigten Variablen zu hinterlegen.
Wenn wir unser Template später in RStudio aus den Vorlagen auswählen, dann werden alle Dateien, die im Unterorder skeleton liegen, in das neu erstellte Projekt kopiert.

2.1.1 LaTeX Vorlage

Nehmen wir als Beispiel die folgende LaTeX-Vorlage für einen Brief.
Das Beispiel ist lauffähig, wenn Sie eine Bilddatei mit dem Namen Unterschrift.png im selben Verzeichnis liegen haben.

\documentclass[DIN,
    fontsize=11pt,          % fontsize
    paper=a4,               % page size a4
    firsthead=on,           % display header on first page
    firstfoot=on,           % display footer on first page
    pagenumber=off,         % position of the page number
    parskip=half,           % Use indent instead of skip, half, false
    enlargefirstpage=on,    % more space on first page
    fromalign=locationright, % placement of name in letter head
    addrfield=on,           % address field for envelope with window, on or true
    subject=titled,         % placement of subject, beforeopening or titled
    foldmarks=on,          % print foldmarks
    numericaldate=off,      % display date in numbers only
    KOMAold]{scrlttr2}
\usepackage[T1]{fontenc}
\usepackage[dvips]{graphicx}
\usepackage[ngerman]{babel}
\usepackage{lipsum}
\usepackage[utf8]{inputenc}
\usepackage{times}
% \usepackage{lmodern}
\usepackage{longtable}
\usepackage{booktabs}
\usepackage{lastpage}
\usepackage[rgb]{xcolor}
\usepackage{hyperref}
\hypersetup{%
    pdfborder=0 0 0,
    pdfauthor={},
    pdftitle={},
    pdfsubject={},
    pdfkeywords={},
    pdfpagemode={UseOutlines},
    bookmarksopen,
    pdfstartview={FitH},
    colorlinks,
    linkcolor={black},
    citecolor={black},
    urlcolor={blue}
  }
\urlstyle{same}

% Randbereiche mit geometry
\usepackage{geometry}
\geometry{a4paper,left=25mm,right=20mm}

\usepackage{lastpage} % letzte Seite anzeigen
\usepackage{marvosym}   
\usepackage{blindtext}

% speichere eigene Variablen für Werte, die häufig drankommen
\newcommand{\myFirstname}{Timm}
\newcommand{\myFamilyname}{Thaler}
\newcommand{\myStreet}{Absenderstr. 7}
\newcommand{\myTown}{47325 Schickensen}

% ---Angaben für das Absenderfeld--------
\setkomavar{fromname}{\flushright\normalfont\textbf{\myFirstname\ \myFamilyname}}
\setkomavar{signature}{\myFirstname\ \myFamilyname}
\setkomavar{fromaddress}{\flushright\normalfont\scriptsize%
Prof. Dr. \myFirstname\ \myFamilyname\\
\myStreet\\\myTown\\%
+49 176 555 984 66\\%
timm@thaler.de\\[2mm]
ING DiBa\\
DE06 505 165 55 5555 4312 64\\[2mm]
Aktenzeichen:\quad\ 2021-4\\
\setkomavar{date}{\today} %datum im Absenderfeld 
Datum:\quad \today}
%---------------------------------------
\renewcommand*\familydefault{\sfdefault}

% Anordnung im Absenderfeld
\setkomavar{firsthead}{}
\setkomavar{location}{%
  \raggedleft
  \usekomavar{fromname}\\
  \usekomavar{fromaddress}
}

% Absender auch über dem Empfängeradressfeld
\setkomavar{backaddress}{\myFamilyname\ | \myStreet\ | \myTown}

% signatur mit Unterschrift
\setkomavar{signature}{\includegraphics[width=25mm]{Unterschrift.png}\\\myFirstname\ \myFamilyname} 
%
% --------------kopf/fusszeile----------------
\usepackage{scrlayer-scrpage}
\clearmainofpairofpagestyles
\cfoot{\flushright\scriptsize\normalfont Seite \thepage\ von \pageref{LastPage} }
\RedeclarePageStyleAlias{empty}{scrheadings}
%----------------------------------------------
%
\setkomavar{date}{} % Datum wieder löschen
\setkomavar{subject}{Dies ist der Betreff}
\setlength{\parindent}{0pt}

% positionierung von Absender und Empfängerboxen
\makeatletter
  \@setplength{backaddrheight}{0pt}% because backaddress=off
  \@setplength{toaddrhpos}{2.5cm}%distance from left
  \@setplength{toaddrvpos}{5cm}%distance from top
  \@setplength{toaddrheight}{3.5cm}%height of the addressbox
  \@setplength{toaddrwidth}{10cm}% width of the addressbox
  \@addtoplength{locwidth}{45mm} % width of ABSENDERbox
  \@setplength{lochpos}{2cm}%distance from top
  \@setplength{locvpos}{32mm}%distance from top
  \@setplength{refvpos}{11cm}% Abstand des Haupttextes von top
\makeatother
%------------------------------------------------------------------------------------

\begin{document}
%
\begin{letter}{Andrea Empfängerin\\
Schloss Schalkenstein\\ 
Adressatenstr. 23\\
22204 Briefkasten
}
%
\opening{Sehr geehrte Frau Empfängerin,}
%
\blindtext[1]

\closing{Mit freundlichen Grüßen,
}
\end{letter}
\end{document}  

Die Datei erzeugt folgendes PDF:


Figure 2.5: PDF-Version des Briefs

Den LaTeX-Code übertragen wir nun in unser Template. Hierfür muss im Ordner inst/markdown/templates/meinbrief der Unterordner
resources angelegt werden. In diesem erzeugen wir die Datei meinbrief.tex und kopieren den LaTeX-Code hinein.

file.edit("inst/markdown/templates/meinbrief/resources/meinbrief.tex")
# kopiere nun den LaTeX Code hierhin.

Als nächstes kopieren wir die Bilddatei für die Unterschrift in den skeleton-Ordner. In unserem Beispiel heisst sie Unterschrift.png.


Figure 2.6: Datei Unterschrift.png

Ab jetzt wechseln wir immer zwischen den Dateien skeleton.Rmd und meinbrief.tex hin und her.

2.1.2 skeleton.Rmd und LaTeX-Datei anpassen

Schauen wir uns zunächst die Datei skeleton.Rmd an.

Hier löschen wir den Beispieltext und belassen nur die Kopfzeile

---
title: "Template Title"
author: "Your Name"
date: "The Date"
output: output_format
---

In der Kopfzeile werden Variablen vergeben. So hat z.B. Variable title derzeit den Wert Template Title,
und date hat den Wert The Date.

Wir können uns dies zu nutze machen. In unserem LaTeX-Code sind all diese Variablen referenzierbar, wenn wir ihren Namen in Dollarzeichen einrahmen, also z.B. $title$.
Wir können im Kopfbereich beliebige neue Variablen erzeugen und diese dann innerhalb der LaTeX-Vorlage nutzen.

Wir gehen also in die Datei meinbrief.tex, und ändern dort alle hardgecodeten Angaben (Name, Adresse, Telefonnummer, usw) in Variablen um. Diese Variablen tragen wir ebenfalls in die Datei skeleton.Rmd ein und hinterlegen dort die Werte.

Die Datei meinbrief.tex sieht danach z.B. so aus:

\documentclass[DIN,
    fontsize=11pt,          % fontsize
    paper=a4,               % page size a4
    firsthead=on,           % display header on first page
    firstfoot=on,           % display footer on first page
    pagenumber=off,         % position of the page number
    parskip=half,           % Use indent instead of skip, half, false
    enlargefirstpage=on,    % more space on first page
    fromalign=locationright, % placement of name in letter head
    addrfield=on,           % address field for envelope with window, on or true
    subject=titled,         % placement of subject, beforeopening or titled
    foldmarks=on,          % print foldmarks
    numericaldate=off,      % display date in numbers only
    KOMAold]{scrlttr2}
\usepackage[T1]{fontenc}
\usepackage[dvips]{graphicx}
\usepackage[ngerman]{babel}
\usepackage{lipsum}
\usepackage[utf8]{inputenc}
\usepackage{times}
% \usepackage{lmodern}
\usepackage{longtable}
\usepackage{booktabs}
\usepackage{lastpage}
\usepackage[rgb]{xcolor}
\usepackage{hyperref}
\hypersetup{%
    pdfborder=0 0 0,
    pdfauthor={},
    pdftitle={},
    pdfsubject={},
    pdfkeywords={},
    pdfpagemode={UseOutlines},
    bookmarksopen,
    pdfstartview={FitH},
    colorlinks,
    linkcolor={black},
    citecolor={black},
    urlcolor={blue}
  }

\urlstyle{same}

% Randbereiche mit geometry
\usepackage{geometry}
\geometry{a4paper,left=25mm,right=20mm}

\usepackage{lastpage} % letzte Seite anzeigen
\usepackage{marvosym}
\usepackage{blindtext}

% speichere eigene Variablen für Werte, die häufig drankommen
\newcommand{\myFirstname}{$myFirstname$}
\newcommand{\myFamilyname}{$myFamilyname$}
\newcommand{\myStreet}{$myStreet$}
\newcommand{\myTown}{$myTown$}

% ---Angaben für das Absenderfeld--------
\setkomavar{fromname}{\flushright\normalfont\textbf{\myFirstname\ \myFamilyname}}
\setkomavar{signature}{\myFirstname\ \myFamilyname}
\setkomavar{fromaddress}{\flushright\normalfont\scriptsize%
$myGrade$ \myFirstname\ \myFamilyname\\
\myStreet\\\myTown\\%
$myTelefon$\\%
$myMail$\\[2mm]
$myBank$\\
$myIBAN$\\[2mm]
Aktenzeichen:\quad\ $myAktenzeichen$\\
\setkomavar{date}{\today} %datum im Absenderfeld
Datum:\quad \today}
%---------------------------------------
\renewcommand*\familydefault{\sfdefault}

% Anordnung im Absenderfeld
\setkomavar{firsthead}{}
\setkomavar{location}{%
  \raggedleft
  \usekomavar{fromname}\\
  \usekomavar{fromaddress}
}

% Absender auch über dem Empfängeradressfeld
\setkomavar{backaddress}{\myFamilyname\ | \myStreet\ | \myTown}

% signatur mit Unterschrift
\setkomavar{signature}{\includegraphics[width=25mm]{Unterschrift.png}\\\myFirstname\ \myFamilyname}
%
% --------------kopf/fusszeile----------------
\usepackage{scrlayer-scrpage}
\clearmainofpairofpagestyles
\cfoot{\flushright\scriptsize\normalfont Seite \thepage\ von \pageref{LastPage} }
\RedeclarePageStyleAlias{empty}{scrheadings}
%----------------------------------------------
%
\setkomavar{date}{} % Datum wieder löschen
\setkomavar{subject}{$Betreff$}
\setlength{\parindent}{0pt}

% positionierung von Absender und Empfängerboxen
\makeatletter
  \@setplength{backaddrheight}{0pt}% because backaddress=off
  \@setplength{toaddrhpos}{2.5cm}%distance from left
  \@setplength{toaddrvpos}{5cm}%distance from top
  \@setplength{toaddrheight}{3.5cm}%height of the addressbox
  \@setplength{toaddrwidth}{10cm}% width of the addressbox
  \@addtoplength{locwidth}{45mm} % width of ABSENDERbox
  \@setplength{lochpos}{2cm}%distance from top
  \@setplength{locvpos}{32mm}%distance from top
  \@setplength{refvpos}{11cm}% Abstand des Haupttextes von top
\makeatother
%------------------------------------------------------------------------------------

\begin{document}
%
\begin{letter}{$anName$\\
$anZusatz$\\
$anStrasse$\\
$anOrt$
}
%
\opening{$opening$,}
%
$body$

\closing{$closing$,
}
\end{letter}
\end{document}

Alle Angaben wurden durch Variablen ersetzt. Die Variable $body$ beinhaltet den gesamten Text des Markdown-Dokuments.

In der Datei skeleton.Rmd wurden die Variablen ebenfalls angelegt, und die Werte entsprechend zugeordnet:

---
title: "Template Title"
author: "Your Name"
date: "The Date"
myFirstname: Timm
myFamilyname: Thaler
myStreet: Absenderstr. 7
myTown: 47325 Schickensen
myGrade: Prof. Dr.
myTelefon: +49 176 555 984 66
myMail: timm@thaler.de
myBank: ING DiBa
myIBAN: DE06 505 165 55 5555 4312 64
Betreff: Dies ist der Betreff
myAktenzeichen: 2021-4
anName: Andrea Empfängerin
anZusatz: Schloss Schalkenstein
anStrasse: Adressatenstr. 23
anOrt: 22204 Briefkasten
opening: Sehr geehrte Frau Empfängerin
closing: Mit freundlichen Grüßen
output: MyStyle::meinbrief
---

dies ist ein Testbrief, um zu schauen, ob es auch in `RMarkdown` funktioniert.

Als letzte Angabe im Kopfbereich wird der output auf unser Template meinbrief im Paket MyStyle gesetzt.

2.1.3 Ausgabeformat bereitstellen

Jetzt sind wir fast schon fertig. Wir müssen noch das eben angegebene Ausgabeformat MyStyle::meinbrief verfügbar machen.
Hierzu erstellen wir im Paketordner “R” die Datei formats.R. Sie wird bei der Aktivierung des Paketes automatisch ausgeführt und soll das Ausgabgeformat bereitstellen.

# erstelle Datei R/formats.R
file.edit("R/formats.R")

Sie erhält folgenden Inhalt:

# mache output "meinbrief" verfügbar
meinbrief <- function(...) {
  template <- system.file("rmarkdown/templates/meinbrief/resources/meinbrief.tex",
                          package="MyStyle")
  bookdown::pdf_document2(...,
                          template = template
  )
}

Wie Sie sehen, verwenden wir letztendlich bookdown::pdf_document2 als Ausgabe.

2.1.4 Ausgabe kontrollieren

Jetzt können wir das Paket neu bauen und installieren, indem wir
Install and Restart klicken.


Figure 2.7: Reiter Build

Wenn alles korrekt eingetragen wurde, können wir nun unsere Beispieldatei skeleton.Rmd knitten. Das erzeugt eine PDF-Datei
im Unterordner skeleton (weil skeleton.Rmd dort liegt). Überprüfen Sie, ob alles so aussieht, wie Sie möchten. Ist alles gut, entfernen Sie die PDF-Datei und bauen Sie das Paket nocheinmal neu.

Fertig.

3 Paket als Vorlage nutzen

Ist das Paket installiert, steht das Template in RStudio zur Auswahl. Klicken Sie auf File \(\rightarrow\) New File \(\rightarrow\) RMarkdown und wählen Sie “From Template”.


Figure 3.1: Template auswählen

Dort steht nun meinBrief zur Auswahl. Wenn Sie auf OK klicken, erhalten Sie den Beispielcode aus skeleton.Rmd sowie die Datei Unterschrift.png. Wenn Sie vergessen haben, die PDF-Datei Ihrer Testreihen zu entfernen, wird diese ebenfalls erzeugt.

4 weitere Templates erstellen

Auf diese Weise können wir beliebig weiterte Templates erstellen. Rob Hyndman hat für seine Fakultät z.B. Vorlagen für Abschlussarbeiten, Berichte und Klausuren erstellt, siehe https://github.com/robjhyndman/MonashEBSTemplates

Wir erzeugen in dieser Anleitung noch eine Vorlage für Rechnungen.

4.1 Vorlage für Rechnungen erzeugen

Für die Rechnungen-Vorlage bedienen wir uns bei unserem Brief und passen einige wenige Felder an.

Zunächst erzeugen wir wieder die Grundstruktur des Templates, welches diesmal meineRechnung heissen soll.

usethis::use_rmarkdown_template(
  template_name = "meineRechnung",
  template_dir = NULL,
  template_description = "Meine private Rechnungsvorlage",
  template_create_dir = FALSE
)

In den Ordner inst/markdown/templates/meinerechnung/skeleton/ kopieren wir zunächst die Datei Unterschrift.png. Dann öffnen wir die Datei skeleton.Rmd und entfernen alles bis auf den Kopfbereich.

---
title: "Template Title"
author: "Your Name"
date: "The Date"
output: output_format
---

Jetzt erstellen wir den Unterordner inst/markdown/templates/meinerechnung/resources/ und darin die Datei meinerechnung.tex.

Die Datei öffnen wir nun, und kopieren den Inhalt unserer Brief-Vorlage hinein.

file.edit("inst/markdown/templates/meinerechnung/resources/meinerechnung.tex")
# kopiere nun den LaTeX Code hierhin.

Jetzt nehmen wir einige Anpassungen vor, z.B. ersetzen wir das alte Aktenzeichen durch eine Rechnungsnummer. Auch fügen wir unsere Steuernummer hinzu.

Die LaTeX-Datei sieht danach so aus:

\documentclass[DIN,
    fontsize=11pt,          % fontsize
    paper=a4,               % page size a4
    firsthead=on,           % display header on first page
    firstfoot=on,           % display footer on first page
    pagenumber=off,         % position of the page number
    parskip=half,           % Use indent instead of skip, half, false
    enlargefirstpage=on,    % more space on first page
    fromalign=locationright, % placement of name in letter head
    addrfield=on,           % address field for envelope with window, on or true
    subject=titled,         % placement of subject, beforeopening or titled
    foldmarks=on,          % print foldmarks
    numericaldate=off,      % display date in numbers only
    KOMAold]{scrlttr2}
\usepackage[T1]{fontenc}
\usepackage[dvips]{graphicx}
\usepackage[ngerman]{babel}
\usepackage{lipsum}
\usepackage[utf8]{inputenc}
\usepackage{times}
% \usepackage{lmodern}
\usepackage{longtable}
\usepackage{booktabs}
\usepackage{lastpage}
\usepackage[rgb]{xcolor}
\usepackage{hyperref}
\hypersetup{%
    pdfborder=0 0 0,
    pdfauthor={},
    pdftitle={},
    pdfsubject={},
    pdfkeywords={},
    pdfpagemode={UseOutlines},
    bookmarksopen,
    pdfstartview={FitH},
    colorlinks,
    linkcolor={black},
    citecolor={black},
    urlcolor={blue}
  }

\urlstyle{same}

% Randbereiche mit geometry
\usepackage{geometry}
\geometry{a4paper,left=25mm,right=20mm}

\usepackage{lastpage} % letzte Seite anzeigen
\usepackage{marvosym}
\usepackage{blindtext}

% speichere eigene Variablen für Werte, die häufig drankommen
\newcommand{\myFirstname}{$myFirstname$}
\newcommand{\myFamilyname}{$myFamilyname$}
\newcommand{\myStreet}{$myStreet$}
\newcommand{\myTown}{$myTown$}

% ---Angaben für das Absenderfeld--------
\setkomavar{fromname}{\flushright\normalfont\textbf{\myFirstname\ \myFamilyname}}
\setkomavar{signature}{\myFirstname\ \myFamilyname}
\setkomavar{fromaddress}{\flushright\normalfont\scriptsize%
$myGrade$ \myFirstname\ \myFamilyname\\
\myStreet\\\myTown\\%
$myTelefon$\\%
$myMail$\\[2mm]
$myBank$\\
$myIBAN$\\
Str-Nr. $myStrNr$\\[2mm]
Rechnungsnummer:\quad\ $myRechnungsnummer$\\
\setkomavar{date}{\today} %datum im Absenderfeld
Datum:\quad \today}
%---------------------------------------
\renewcommand*\familydefault{\sfdefault}

% Anordnung im Absenderfeld
\setkomavar{firsthead}{}
\setkomavar{location}{%
  \raggedleft
  \usekomavar{fromname}\\
  \usekomavar{fromaddress}
}

% Absender auch über dem Empfängeradressfeld
\setkomavar{backaddress}{\myFamilyname\ | \myStreet\ | \myTown}

% signatur mit Unterschrift
\setkomavar{signature}{\includegraphics[width=25mm]{Unterschrift.png}\\\myFirstname\ \myFamilyname}
%
% --------------kopf/fusszeile----------------
\usepackage{scrlayer-scrpage}
\clearmainofpairofpagestyles
\cfoot{\flushright\scriptsize\normalfont Seite \thepage\ von \pageref{LastPage} }
\RedeclarePageStyleAlias{empty}{scrheadings}
%----------------------------------------------
%
\setkomavar{date}{} % Datum wieder löschen
\setkomavar{subject}{$Betreff$}
\setlength{\parindent}{0pt}

% positionierung von Absender und Empfängerboxen
\makeatletter
  \@setplength{backaddrheight}{0pt}% because backaddress=off
  \@setplength{toaddrhpos}{2.5cm}%distance from left
  \@setplength{toaddrvpos}{5cm}%distance from top
  \@setplength{toaddrheight}{3.5cm}%height of the addressbox
  \@setplength{toaddrwidth}{10cm}% width of the addressbox
  \@addtoplength{locwidth}{45mm} % width of ABSENDERbox
  \@setplength{lochpos}{2cm}%distance from top
  \@setplength{locvpos}{32mm}%distance from top
  \@setplength{refvpos}{9cm}% Abstand des Haupttextes von top
\makeatother
%------------------------------------------------------------------------------------

\begin{document}
%
\begin{letter}{$anName$\\
$anZusatz$\\
$anStrasse$\\
$anOrt$
}
%
\opening{$opening$,}
%
$body$

\closing{$closing$,
}
\end{letter}
\end{document}

Die Variablen übertragen wir in die Datei skeleton.Rmd, die dann so aussieht:

---
title: "Template Title"
author: "Your Name"
date: "The Date"
myFirstname: Timm
myFamilyname: Thaler
myStreet: Absenderstr. 7
myTown: 47325 Schickensen
myGrade: Prof. Dr.
myTelefon: +49 176 555 984 66
myMail: timm@thaler.de
myBank: ING DiBa
myIBAN: DE06 505 165 55 5555 4312 64
myStrNr: 313/5555/123
Betreff: RECHNUNG
myRechnungsnummer: 2021-4
anName: Andrea Empfängerin
anZusatz: Schloss Schalkenstein
anStrasse: Adressatenstr. 23
anOrt: 22204 Briefkasten
opening: Sehr geehrte Frau Empfängerin
closing: Mit freundlichen Grüßen
output: MyStyle::meinerechnung
---

Um das Ergebnis testen zu können, müssen wir das Ausgabeformat MyStyle::meinerechnung verfügbar machen.
Hierfür ergänzen wir im Unterordner R in der Datei formats.R wie folgt:

# mache output "meinbrief" verfügbar
meinbrief <- function(...) {
  template <- system.file("rmarkdown/templates/meinbrief/resources/meinbrief.tex",
                          package="MyStyle")
  bookdown::pdf_document2(...,
                          template = template
  )
}

# mache output "meinerechnung" verfügbar
meinerechnung <- function(...) {
  template <- system.file("rmarkdown/templates/meinerechnung/resources/meinerechnung.tex",
                          package="MyStyle")
  bookdown::pdf_document2(...,
                          template = template
  )
}

Wenn Sie jetzt das Paket neu bauen und installieren, sollte das knitten der Datei skeleton.Rmd schon funktionieren, und eine PDF-Datei ausspucken.

Die Rechnungsvorlage kann nun wie folgt erweitert werden:

---
title: "Template Title"
author: "Your Name"
date: "The Date"
myFirstname: Timm
myFamilyname: Thaler
myStreet: Absenderstr. 7
myTown: 47325 Schickensen
myGrade: Prof. Dr.
myTelefon: +49 176 555 984 66
myMail: timm@thaler.de
myBank: ING DiBa
myIBAN: DE06 505 165 55 5555 4312 64
myStrNr: 313/5555/123
Betreff: RECHNUNG
myRechnungsnummer: 2021-4
anName: Andrea Empfängerin
anZusatz: Schloss Schalkenstein
anStrasse: Adressatenstr. 23
anOrt: 22204 Briefkasten
opening: Sehr geehrte Frau Empfängerin
closing: Mit freundlichen Grüßen
output: MyStyle::meinerechnung
---


für die erbrachten Leistungen erlaube ich mir wie folgt in Rechnung zu stellen:



|  **Datum** |                 **Posten**                          | **Betrag**|
|------------|-----------------------------------------------------|----------:|
| 29.06.2021 | Zauberschow für Kinder in der KiTa Regenbogenland   |     300,01|
|            |                                                     |           |
|            |                                         Nettobetrag |     300,01|
|            |                      Umsatzsteuerfrei gem. §19 UstG |       0,00|
|            |                                    **Gesamtbetrag**:| **300,01**|




Bitte überweisen Sie den Betrag unter Angabe der Rechnungsnummer auf mein Konto bei der ING DiBa
mit der IBAN *DE06 505 165 55 5555 4312 64*.

Ich bedanke mich für Ihren Auftrag und verbleibe

Wenn Sie jetzt knitten, sieht die PDF-Datei skeleton.pdf so aus:


Figure 4.1: fertiges PDF-Dokument

Jetzt können Sie noch weitere Anpassungen vornehmen. Wenn Sie fertig sind, entfernen Sie die PDF-Datei skeleton.pdf und bauen und installieren Sie das Paket neu. Ab jetzt steht es in RStudio als Vorlage zur Verfügung.

Klicken Sie auf File \(\rightarrow\) New File \(\rightarrow\) RMarkdown und wählen Sie “From Template”.


Figure 4.2: Vorlagen

5 noch mehr Templates

Noch weitere Templates bietet unter anderem das R-Paket rticles.

install.packages("rticles")

Nach einem Neustart von RStudio stehen die Templates bereit.


Statistik mit R und RStudio

April 4th, 2021

Das Nachschlagewerk liegt nun als Webseite hier: https://www.produnis.de/R/
…und als PDF-File hier: https://www.produnis.de/R/rbuch.pdf

Login bei MediaWiki mit Python

Januar 27th, 2019

Es gibt zwei Möglichkeiten, sich bei MediaWiki einzuloggen,

  • per api.php
  • als „normaler“ User (über die index.php)

api.php

Diese Variante setzt voraus, dass du Special:BotPasswords für deinen Account vergeben hast. Mit der API-Methode ist wirklich nur die api.php erreichbar. Versucht man mit der Session die index.php zu erreichen, erhält man die Meldung „Du musst angemeldet sein!“

Zunächst die Anmeldung, weche wir in einer request.session speichern:

#!/usr/bin/env python3
import requests
username 		= 'produnis'					# Username to login with
botpwd 			= 'myapibot@foobarfoobarfoobars52o8mk8apimpsb8'	# Password of Special:BotPasswords in the style BOTNAME@password
 
api_url   = "https://my.wiki.ltd/api.php"
session = requests.Session()
r1 = session.get(api_url, params={
    'format': 'json',
    'action': 'query',
    'meta': 'tokens',
    'type': 'login',
})
 
r2 = session.post(api_url, data={
    'format': 'json',
    'action': 'login',
    'lgname': username,
    'lgpassword': botpwd,
    'lgtoken': r1.json()['query']['tokens']['logintoken'],
})
if r2.json()['login']['result'] != 'Success':
    raise RuntimeError(r2.json())

Nun kann die API wie folgt aufgerufen und ausgelesen werden:

MyParams = {
	"action": "query",
	"format": "json",
	"prop": "pageprops",
	"titles": "Kategorie:Hauptkategorie"
}
mycontent = session.get(api_url, params=MyParams)
mydata=mycontent.json
print("i got: ", mydata()['query']['pages'])

index.php

Wenn wir die index.php nutzen wollen, zB um die Mpdf-Extension aufzurufen, müssen wir uns anders einloggen:

#!/usr/bin/env python3
import requests
from bs4 import BeautifulSoup as bs
 
username 		= 'produnis'					# Username to login with
userpwd			= 'SuperSecreet'				# Userpassword
botpwd 			= 'myapibot@foobarfoobarfoobars52o8mk8apimpsb8'	# Password of Special:BotPasswords in the style BOTNAME@password
 
api_url   = "https://my.wiki.ltd/api.php"
index_url = "https://my.wiki.ltd/index.php"
 
payload = {
	'wpName': username,
	'wpPassword': userpwd,
	'wploginattempt': 'Log in',
	'wpEditToken': "+\\",
	'title': "Special:UserLogin",
	'authAction': "login",
	'force': "",
	'wpForceHttps': "1",
	'wpFromhttp': "1",
    #'wpLoginToken': '',
	}
 
def get_login_token(raw_resp):
	soup = bs(raw_resp.text, 'lxml')
	token = [n.get('value', '') for n in soup.find_all('input')
		if n.get('name', '') == 'wpLoginToken']
	return token[0]
 
with requests.session() as s:
	resp = s.get(index_url + '?title=Spezial:UserLogin')
	payload['wpLoginToken'] = get_login_token(resp)
	response_post = s.post(index_url + '?title=Spezial:UserLogin&amp;action=submitlogin&amp;type=login', data=payload)

Nun können Seiten ausgerufen werden per:

	response = s.get(index_url + '?title=Special:Watchlist')

Weblinks

  • https://stackoverflow.com/a/38378803/1493264 how to login to Mediawiki with Python
  • https://www.mediawiki.org/wiki/User:Produnis/Login_to_MediaWiki_using_Python
  • https://github.com/produnis/myscripts/blob/master/python/mwc2pdf.py

XFCE Autostart verzögern

September 8th, 2018

Manche Programme, die ich im Autostart betreibe, starten bei mir unter XFCE so schnell, dass ihre Icons nicht im Benachrichtigungs-Panel erscheinen – eben weil das Panel noch nicht bereit ist. Dies betrifft bei mir z.B. Davmail und Nextcloud. Daher möchte ich eine Verzögerung von 5 Sekunden einbauen, bevor diese Programme gestartet werden.

Hier haben wir zwei Möglichkeiten:

  1. wir editieren die Autostartscripte, die unter ~/.config/autostart/ bereit liegen
  2. wir machen es Klicki-Bunti über XFCE-Menu - Einstellungen - Sitzung und Startverhalten - Automatisch gestartete Anwendungen

Für beide gilt folgendes Befehlsschema:

/bin/sh -c "sleep 5 && BEFEHL"

(Es hat mich etwas Zeit gekostet, bis ich herausgefunden habe, dass ein simples sleep 2 && BEFEHL nicht funktioniert, sondern dass man eine Shell angeben muss und der anschließend auszuführende Befehl ebenfalls an diese Shell übergeben werden muss /bin/sh -c "sleep 5 && BEFEHL")

Für davmail sieht das z.B. so aus:

nano ~/.config/autostart/davmail.desktop
[Desktop Entry]
Encoding=UTF-8
Version=0.9.4
Type=Application
Name=davmail
Comment=davmail
Exec=/bin/sh -c "sleep 5 && davmail"
OnlyShowIn=XFCE;
StartupNotify=false
Terminal=false
Hidden=false

Das funktioniert prima… 🙂

Subsonic ist tot, es lebe Airsonic

August 29th, 2018

Seit ein paar Jahren habe ich einen Subsonic-Server für meine Musik. Ich habe sogar die 12,- Euro Zwangsabgabe pro Jahr bezahlt, damit meine Clients auf den Server zugreifen können.

Heute hab ich Subsonic zu Gunsten von Airsonic verworfen. Airsonic ist ein Subsonic-Fork, es ist „frei“ wie in „Bier“ (d.h. die 12,- fallen weg) und funktioniert prima mit meinem Apache-Proxy.

Folgendes Szenario möchte ich realisieren:

  • Airsonic läuft auf einem Port ohne SSL (in meinem Fall 4040)
  • Damit aber auch https-Verbindungen klappen, richte ich einen Apache-Proxy ein. Dieser leitet dann die HTTPS-Anfrage intern an Airsonic weiter. Das hat zB den Vorteil, dass die LetsEncrypt-Zertifikate nur in Apache aktualisiert werden müssen
  • Alle Clients haben von überall Zugriff auf Airsonic

Installation

Die Installation unter Archlinux ist recht einfach:

pacaur -S airsonic  jre8-openjdk-headless

Jetzt muss ich noch sicherstellen, dass auch wirklich jre8 verwendet wird:

archlinux-java status # auf jre8 umstellen

Konfiguration

Für Airsonic habe ich nur meinen Port eingetragen:

sudo nano /etc/airsonic/airsonic.conf
(...)
PORT=4040
CONTEXT_PATH=/airsonic
(...)

Für das Transcoding (also zB das Runterrechnen auf 128kb/s mp3) muss noch ein Symlink auf ffmpeg gesetzt werden:

cd /var/lib/airsonic/transcode
sudo ln -s /usr/bin/ffmpeg .
sudo chown -h airsonic:airsonic ffmpeg

Für Apache wird die httpd.conf wie folgt ergänzt:

sudo nano /etc/httpd/conf/httpd.conf
(...)
RequestHeader set X-Forwarded-Proto "https"
ProxyPass        /airsonic http://127.0.0.1:4040/airsonic
ProxyPassReverse /airsonic http://127.0.0.1:4040/airsonic
(...)

Dies bewirkt, dass Anfragen von http://meinserver.org/airsonic als auch von https://meinserver.org/airsonic intern an den Port 4040, und somit an Airsonic, weitergereicht werden. Ich muss also keine Ports in der Firewall öffnen, und ich kann meine LetsEncrypt-Lösung für SSL verwenden.

Starten…

Nach einem Neustart von Apache kann auch Airsonic gestartet werden:

sudo apachectl graceful
sudo systemctl start airsonic.service

Voila, Airsonic ist nun unter http://IPDESSERVERS:4040 und auch unter http(s)://meinserver.org/airsonic erreichbar.

IPv6 im Congstar Mobilnetz

Juni 29th, 2018

Ich nutze Android8 und habe festgestellt, dass ich mir im Congstar Mobilnetz eine IPv6-Adresse geben kann.
Hierzu gehe ich in den Einstelungen zu VERBINDUNGEN – MOBILE NETZWERKE – ZUGANGSPUNKTE – und wähle „hinzufügen“.

Folgende Felder habe ich eingeragen:

Name: T-Mobile Internet
APN: internet.t-mobile
Benutzername: telekom (oder congstar eintragen)
Kennwort: tm (oder cs eintragen, wenn als Benutzer congstar gewählt wurde)
MCC: 262
MNC: 01
Authentifizierung: CHAP
APN-Typ: default,supl,mms
APN-Protokoll IPv4/IPv6

Alles andere habe ich nicht angegeben/so belassen.

Jetzt noch speichern, und ich kann den neuen ANP auswählen.
Und zack, hat mein Handy eine gültige globale IPv6 Adresse.
Diese sieht man zB unter Einstelungen, Telefoninfo, Status.

Überall per SSH erreichbar mit TOR

Januar 2nd, 2018

Heute möchte ich eine alternative Verwendungsweise von TOR vorstellen:
Wir nutzen das TOR-Netzwerk, um unseren Rechner per SSH erreichbar zu machen. Der Vorteil des TOR-Netzes ist hierbei, dass wir uns keine Gedanken mehr um Firewalls, Port-Forwarding und DynDNS-Adressen machen müssen. Über das TOR-Netz ist unser Rechner direkt erreichbar, (fast) egal, wo wir ihn ans Netz hängen.
Das ist für einen Homeserver interessant, der „nur“ IPv6 hat, oder aber (bei mir zB) für RaspberryPis, die man irgendwo mit hinnimmt, anschließt und stehen lässt.

Vorbereitung

Wir installieren TOR, torsocks und nyx. Unter Archlinux geht das zB per:

pacaur -S tor torsocks nyx

Jetzt legen wir noch ein Verzeichnis für das Logfile an:

sudo mkdir /var/log/tor                   # Logfile
sudo touch /var/log/tor/notices.log       # Logfile
sudo chmod a+rw /var/log/tor/notices.log  # Logfile

Wir machen unseren Port 22 als „hidden service“ verfügbar. Hierfür benötigt TOR noch ein eigenes Verzeichnis:

sudo mkdir /var/lib/tor/hidden_service/   # für Hidden-Service (zB SSH)

Mit dem Programm nyx können wir unseren TOR-Server später kontrollieren. Damit nicht jeder Zugriff auf den Controllport bekommt, erstellen wir ein Passwort-Hash:

tor --hash-password "MEIN PASSWORT"       # Password für nyx festlegen

Dies wirft so einen Hashwert aus: 16:EC846041E12046F96027CDB036371025913DDEC98B66B0AE4D0F19B409. Diesen Hash-Wert benötigen wir noch!

Konfiguration

Die Kofiguration von TOR erfolgt über die Datei /etc/tor/torrc. Tor bringt hier eine Standardeinstellung mit, die unbedingt geändert werden muss. Standardmäßigt ist der TOR-Server nämlich als Exit-Node konfiguriert, und das wollen wir ja nicht!
Folgende Einstellungen reichen für unsere Zwecke aus:

Log notice file /var/log/tor/notices.log
DataDirectory /var/lib/tor
ControlPort 9051            # für nyx
HashedControlPassword 16:EC846041E12046F96027CDB036371025913DDEC98B66B0AE4D0F19B409 #ersetze s.o.
HiddenServiceDir /var/lib/tor/hidden_service/
HiddenServicePort 22 127.0.0.1:22  # Port 22 auch über TOR 
ExitPolicy reject *:*       # no exits allowed !!!

Ihr müsst natürlich den Hash-Wert eures Passwortes entsprechend anpassen. Wichtig ist die letzte Zeile ExitPolicy reject *:*, welche den Exit-Node ausschaltet.

TOR ist jetzt installiert und kann über Port 9051 kontrolliert werden. Es wird KEIN Traffic nach außen geleitet (Exit-Node) und es wird KEIN Traffic hin- und hergeschoben (relay).

Der TOR-Server wird gestartet mit:

sudo systemctl start tor.service

Mit dem Programm nyx kann man nun die Aktivitäten des TOR-Servers begutachten.

nyx

SSH

Nach dem Start des TOR-Servers steht in der Datei /var/lib/tor/hidden_service/hostname die Onion-Adresse, unter welcher der Server nun erreichbar ist.

Der ssh-Aufruf am Client (der ebenfalls mit TOR verbunden sein muss, ist ja klar) erfolgt dann zB per torsocks:

torsocks ssh produnis@ipujtasre32xw656s.onion

Die Onion-Adressen können auch in der .ssh/config wie gewohnt hinterlegt werden.

Weblinks

picture-bots für Friendica und Diaspora*

Dezember 22nd, 2017

Seit vielen Jahren habe ich einen Diaspora-Account, und seit einiger Zeit sogar eine eigene Friendica-Instanz. Im letzten Blogpost hatte ich beschrieben, wie man sich einen Friendica-Account zulegt und Kontakte hinzufügt, siehe hier.

Um nun das Fediversum etwas interessanter zu gestalten, haben manche User RSS- und Feedbots konfiguriert, so dass „angesagte“ Inhalte federiert werden. Hier gibt es zB auch Instagram- und Twitter-Accounts, die ein bestimmtes Thema bedienen („Webfail“, „Ruhrgebiet“, „History“), und dazu in einem bestimmten Takt passende Bilder posten.

Habt ihr auch einen Ordner voller „lustiger Bildchen“ und Memes? Habt ihr schonmal überlegt, diese Bilder über einen eigenen Account zu federieren?

Ich habe zwei Python-Scripte geschrieben, die so etwas ähnlich tun: aus einem definierten Ordner werden die enthaltenen Bilder nach/auf Diaspora, Friendica oder GNUsocial gepostet.
Falls ihr also einen Ordner voller themenbezogener Bilder habt, und diese gerne „botmäßig“ federieren möchtet, schaut euch die Scripte an:

Die Scripte feuern jeweils 1 Bild „pro Runde“, so dass ihr den gewünschten Takt per Cronjob festlegen könnt.

Friendica als Feedreader für RSS, Twitter, Youtube etc. nutzen

November 25th, 2017

Seit einiger Zeit nutze ich Friendica,
eine Art dezentrale Facebook-Alternative, die mit der „Federation“ (Diaspora, GNUscocial, etc.) verbunden ist.
Heute möchte ich kurz zeigen, wie ich Friendica nutze, denn ein gutes Argument ist ja immer:
„Wieso sollte ich mir einen Friendica-Account zulegen, wo doch eh alle anderen bei Facebook oder SnapChat sind“ – denn das stimmt ja auch. Aber ich nutze Friendica eher als aufgemotzten Feed-Reader. Ich hab hier mal irgendwann den Spruch gelesen „if it has a feed, we can track it“.
Und so habe ich alle Webseiten, die ich sonst so täglich abgegrast habe (zB spiegel.de tagesschau.de, planet der ubuntuusers, youtube, twitter) in Friendica als „Kontakte“ eingetragen.
Auf Friendica werden mir dann alle Tweets und Neuigkeiten im Look einer „Facebook-Timeline“ angezeigt.

Da ich meine Kontakte in „Gruppen“ sortieren kann, nehme ich Einfluss auf meine Timeline:
– ich will jetzt nur Funstuff sehen
– ich will nur Nachrichten
– ich will nur Astronomie
– ich will alles sehen

Die Beiträge kann ich dann „liken“, kommentieren und verteilen – denn in der „Federation“ gibt es durchaus auch „echte Menschen“, mit denen man sich verbinden und unterhalten kann.
Ich kann wirklich nur empfehlen, das ganze mal auszuprobieren. Man kann sich entweder selbst einen Friendica-Server aufsetzen (das ist gar nicht sooo schwer), oder einen öffentlichen Server nutzen, siehe https://dir.friendica.social/.

Sobald ihr einen Account habt, könnt ihr Kontakte hinzufügen. Auf eurer Kontaktseite seht ihr links ein Feld „Neuen Kontakt hinzufügen“.
(Ich nutze das Theme „vier“, und alle Screenshots sind vom Theme „vier“.)

Hier tragt ihr die Feedadressen ein. Und die sieht so aus:

  • Twitter: in Form einer Mailadresse USERNAME@twitter.com, also zB realDonaldTrump@twitter.com
  • RSS-Feed: einfach die URL des Feeds eintragen, zB. https://planet.ubuntuusers.de/feeds/full/10/ oder http://feeds.feedburner.com/d0od?format=xml
  • Youtube-Kanäle: ihr benötigt die Channel-ID des Kanals, dem ihr folgen wollt. Für die Kontaktanfrage nutzt man folgende URL, wobei die Channel-ID ergänzt werden muss: https://www.youtube.com/feeds/videos.xml?channel_id=, also zB. https://www.youtube.com/feeds/videos.xml?channel_id=UCtJVZjY6xsZUV-sYdcIFpZw
  • Instagram: Hier wandelt man den Instagram-Stream in einen RSS-Feed um. Die Adresse für die Kontaktanfrage lautet https://websta.me/rss/n/USERNAME also zB https://websta.me/rss/n/s04
  • Diaspora: Einfach die Kontaktadresse, zB comedy@diasp.de eingeben. Funktioniert so auch mit allen anderen Federationsaccounts, zB GNUsocial.
  • Dann klickt man auf „Verbinden“.

Auf der nächsten Seite meckert friendica bei mir, dass „das angefragte Profil nicht vorhanden“ sei (roter Pfeil). Dies kann man ignorieren und einfach auf „Anfrage abschicken“ klicken.

Unter „Kontakteinstellungen“ gibt es die Möglichkeit, „weitere Informationen zu dem Feed“ zu holen (blauer Pfeil).

Hier muss man herumspielen. Manche Feeds zeigen den Inhalt „schöner“, wenn man das Feld „Deaktiviert“, bei anderen ist die Einstellung „Beziehe Informationen“ „schöner“. Bei Youtube-Channels ist grundsätzlich „beziehe informationen“ zu wählen.

Bei zB Blogs, die nicht soooo regelmäßig posten, wie zB linuxundich.de, setze ich den Haken bei „Benachrichtigung bei neuen Beiträgen“. So bekomme ich oben rechts bei der „Glocke“ angezeigt, wenn etwas neues da ist.

Sollte kein Avatar zum Feed angezeigt werden, kann man eines per Bild-URL hinzufügen. Klicke auf „Erweitert“ (s.o., roter Pfeil), und trage in das Feld „Neues Foto von dieser URL“ eine Bild-URL ein.

Friendica kopiert sich dann das Bild von dort.

So erstellt man sich seinen ganz privaten aufgemotzten RSS-Feed-Reader.

Viele „wichtige“ Feeds (z.B. Fefe oder Tagesschau) werden bereits von Bots in die Federation gespiegelt. Es lohnt sich, das Userverzeichnis zu durchforsten. Ich folge diesen Bot-Accounts, ihr könnt die Adressen direkt als Kontaktanfrage nutzen:

aljazeera@squeet.me
arch@friendica.produnis.de
atheism@friendica.produnis.de
bildblog@friendica.produnis.de
derpostillon@gnusocial.de
deutschlandfunk@squeet.me
dlr@squeet.me
earthquake@social.yl.ms
esa@squeet.me
fefesblog@pod.geraspora.de
funstuff@friendica.produnis.de
heiseonline@squeet.me
ey_lou_flynn@pod.geraspora.de
kicker@friendica.produnis.de
Kodi@mastodon.cloud
linuxundich@friendica.produnis.de
nachdenkseiten@friendica.produnis.de
nasa@squeet.me
ncnews@libranet.de
sciencenature@friendica.produnis.de
spon@friendica.produnis.de
tagesschau@squeet.me
taz@pod.geraspora.de
urknall@friendica.produnis.de
wdr@squeet.me
zdfheute@squeet.me

Ja, und zu guter letzt könnt ihr euch auch gerne mit mir verbinden: produnis@friendica.produnis.de

Weblinks

git poor man’s time machine

Februar 9th, 2017

Eine etwas exotischere Verwendungsweise von git ist die einer Time Machine. Das Prinzip einer Time Machine ist, dass für einen Ordner, zB dem eigenen Dokumentenordner, regelmäßige Schnappschüsse erstellt werden. Das Feature ist dann, dass man „durch die Zeit“ blättern kann, um alte Versionen oder gelöschte Dateien zu finden.
Mit git kann man sich soetwas recht einfach erstellen:

Im Terminal wechselt man in das gewünschte Verzeichnis:

cd /Pfad/zum/Verzeichnis

Dort erstellt man ein git-Repository:

git init

Und fügt alle Dateien dem Repository hinzu:

git add *
git commit -m "initial commit"

Jetzt benötigen wir ein Script, welches per Cronjob in den Intervallen ausgeführt wird, die wir gerne für unsere Time Machine Snapshots hätten. Mögliche Cronjobs wären zB.

crontab -e
@hourly /pfad/zu/script.sh
@daily /pfad/zu/script.sh

Die Datei script.sh müssen wir erstellen und ausführbar machen, und ihr folgenden Inhalt geben:

#!/bin/bash
DATE=`date +"%Y-%m-%d_%H-%M-%S"`
cd /path/to/your/folder       # Change to your settings
git checkout -b $DATE
git checkout $DATE
git add *
git commit -m "snapshot of $DATE"
git checkout master
git rebase $DATE

Das Script erstellt einen neuen Branch DATE und führt einen commit auf alle Änderungen im Verzeichnis durch.
Anschließend wird ein rebase des commit auf den Branch „master“ ausgeführt.
Die Änderungen liegen also im Branch DATE als auch im Branch master.

Der Branch DATE wird nicht gelöscht. Er ist der Landepunkt für unsere Zeitmaschine.

Um nun durch die Zeit zu reisen wechselt man im Terminal in das Verzeichnis und gibt ein:

git checkout DATE

wobei DATE ein Zeitstempel ist.
Welche Zeitstempel verfügbar sind zeigt der Befehl

git branch

Der Befehl versetzt euer Verzeichnis in den Zustand, in dem es sich zum Zeitpunkt DATE befunden hat.

Um wieder in die Gegenwart zu wechseln verwendet man den Befehl:

git checkout master

Weblinks