Ab ca. dem 8. Monat kann ein Kind in der Kraxe sitzen. Obwohl der Preis der [Deuter Kid Komfort Pro](deuter Kid Comfort Pro | Profi-Kinderkraxe mit Sonnendach) mehr als saftig ist (wenn auch gerechtfertigt aufgrund der Komplexität), ist sie jeden Cent wert. Das Tragesystem ist hervorragend und die Features durchdacht. Auch sehe ich die typische Deuter Langlebigkeit.
Das Raincover funktioniert auch super – zumindest bei nicht-stürmischen Regen konnte ich es schon 3-mal testen. Allerdings empfiehlt sich ein Regenschirm, damit das Wasser nicht zwischen Kraxe und Rücken runter rinnt. Apropos Regenschirm: Extra einen teuren von „Euroschirm Eberhard Göbel“ gekauft – der original 200 Meter (!!) gehalten hat – bei der ersten Windböe sind zwei Stege gebrochen – obwohl auf der Webseite mit 200km/h Wind getestet wird – aber eben nur frontal von vorne. Zurück zur Kraxe. In manchen Tests hatten die Leute Angst, dass etwas aus der Tasche unten rausfliegt. Noch nie passiert – kleine Utensilien einfach in einen Packsack – wie z.B. Exped Fold Drybag.
Hier und da muss man als Motorradfahrer immer wieder nach Südtirol. Es ist und bleibt das Eldorado. So viele schöne Strecken an einem Fleck – wo gibt’s das sonst noch? Wer allerdings am Wochenende dorthin fährt – selber schuld. Viel Wohnwägen, viele Motorradfahrer. Leider auch viele Egoisten, die glauben, ihnen gehört die Straße alleine. Wie auch immer. Bis auf den Großglockner die Strecke so gefahren, wie geplant. Einziger Planungsfehler: der Pampeago Pass (ganz im Westen) ist gesperrt.
Die freie Wahl, Home-Office zu machen, finde ich genial. An manchen Tagen will man sich die Zeit von 1.5h, die man in Öffis verbringt, einfach sparen. Zeit, sich im Home-Office einzurichten. Da man als ITler rund 9h in einen Monitor schaut war mir eines wichtig: es muss etwas Hochwertiges sein. Ich habe bereits 2 DELL U2711D (11 Jahr alt – damals zu einem stolzen Stückpreis von 700 EUR) im Einsatz und bin sehr zufrieden. Das aktuelle Modell ist der DELL U2722D. Die beiden DELL U2711D werden per DisplayPort an einen Stand-PC angeschlossen. Durch die WQHD Auflösung war das vor 10 Jahren auch nicht trivial – ich habe mir damals eine Nvidia Quadro FX580 besorgen müssen. Heute sind Notebooks aber schon extrem leistungsstark. Mein Notebook: Lenovo Campus ThinkPad T14 20UES00L00:
Die Anreise erfolgte wieder – leider – mit dem Flugzeug. Anschließend kam ich zum ersten Mal in den Genuss des TGV. Man ist in 3h in La Rochelle. La Rochelle ist ein schöner Ort mit einem kleinen Zentrum – aber genügend Restaurants. Ganz ausgelegt für Touristen. Ein Tagesausflug mit dem Bus nach Saint-Martin-de-Ré lohnt sich auch.
Ein Bericht Leider ging es mit dem Flugzeug nach Basel. Leider, weil natürlich das Bewusstsein, dass man etwas umweltschädliches macht, schon an einem nagt. Allerdings muss man 10h Reise 1h gegenüberstellen – was mit einem kleinen Kind natürlich den Ruf nach 1h Reise noch erhöht. Von Basel ging es nach Colmar, wo wir eine Nacht verbrachten. Die Fächerbauten sind sehr beeindruckend und heben den Ort klar von den österreichischen Weinorten ab.
Statussymbol des „Wohlstands“ – der PKW Warum Wohlstand unter Anführungszeichen? Weil ich den Artikel Ein Job, ein Auto, aber nicht genug zu essen sehr interessant fand. Es ist sehr tragisch zu sehen, dass für immer mehr Menschen Essen keine Selbstverständlichkeit mehr ist, ich erlaube mir die Situation trotzdem aus dem Blickwinkel der Umwelt betrachten: Armut und Auto in einem Satz? Wie passt das zusammen?
In einer reichen Gesellschaft, wo fast alle über ein Auto verfügen, ist der, der sich anders fortbewegen muss, relativ arm. (Prof. Christoph Butterwegge)
Ein interessantes Thema – vorallem, weil die Schreibweisen von Adressen oft nicht trivial sind. Auf die Schnelle habe ich folgende zwei Strategien gefunden:
pg_trgm: Dieses Modul erlaubt es, die Ähnlichkeit von Wörtern zu messen. Durch das Anlegen eines Index, ist diese Methode extrem performant. Phonet: Erlaubt einen String auf einen „Aussprache“ String zu projizieren. Ähnlich klingende Strings mappen somit auf den gleichen String. Anschließend Abgleich mit pg_gtrm. Der Vorteil: Aussprachefehler werden „ausgebessert“ und führen zu einem besseren Match. Wann funktioniert das gut? Wenn man die Menge an Möglichkeiten voreinschränkt. Dazu bietet sich natürlich die Postleitzahl an. In Österreich 4-stellig – aber es reicht, wenn man auch nur 1-2 Stellen eingibt. Vorangestellt oder nicht – je nach dem. Ich war mit dem Ergebnis sehr zufrieden.
Health Endpoint eines Services Ich habe eine interessante Problemstellung bearbeitet: Wie könnte ein Health-Endpoint ausschauen? Nach kurzer Recherche im Internet:
the status of the connections to the infrastructure services used by the service instance the status of the host, e.g. disk space application specific logic (Quelle: https://microservices.io/patterns/observability/health-check-api.html)
Meine Meinung dazu:
Punkt 1: zu oberflächlich Punkt 2: aus meiner Sicht sind das Metriken – anderes Thema Punkt 3: ok (obwohl nicht genau gesagt wurde, was hiermit gemeint ist) .NET Core 2.2 hat es native enhalten: Health checks in ASP.NET Core. So wird z.B. bei SQL Server mit SELECT 1 überprüft, ob die Verbindung und die Authentifizierung funktioniert. Wie immer würde ich das gerne mit theoretischen Sichtweisen verschmelzen.
Katalonien hat den Grünen Pass abgeschafft. Da Off-Saison war, hatten wir auch keine Bedenken hinsichtlich Corona. Es war auch nicht sonderlich viel los. In Barcelona die klassischen Sehenswürdigkeiten:
Park Güell Sagrada Família Gute Lokale sind gar nicht leicht zu finden – hier ein paar:
El Nacional Cañete LOKAL BAR Louro - Restaurante do Centro Galego El Bosc de Les Fades An 2 Tagen hieß es wandern. Mit dem Zug „Rodalies“ ging es von hier nach Blanes – ca. 1:30h. Danach folgende Wanderungen:
Dapper Ansich nichts besonderes - trotzdem ein interessantes Thema. Points of Interst:
Wenn die Tabelle leer ist, muss man sich einer Hilfstabelle bedienen Gherkin Tests sollten nicht zu technisch sein - ob man jetzt tasächlich Tabellen prüfen sollte - fraglich. Anderseits ist es ein netter Integrationstest - ob BDD das beste Tool dafür ist, sei dahingestellt. Ich kenne zumindest keinen Business Analysten, der sich solche Tests selber zusammen schraubt. Allerdings kann es beim Verfassen von Akzeptanzkriterien hilfreich sein. using Dapper; using Npgsql; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; namespace PgsqlToGherkinTable { class Program { private static Dictionary<string, string> TableTypes; static void Main(string[] args) { // Check if database has the same one - we assume its allways en-US CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US"); if (args.Length != 5) { Console.WriteLine($"<server> <database> <schema> <userid> <password>"); Environment.Exit(1); } var server = args[0]; var database = args[1]; var tableSchema = args[2]; var user = args[3]; var password = args[4]; using (var connection = new NpgsqlConnection($"Server={server};Database={database};User Id={user};Password={password};")) { Console.WriteLine($"Connecting to {server} ..."); connection.Open(); using (var streamWriter = new StreamWriter("result.txt")) { Console.WriteLine($"Write result to {((FileStream)streamWriter.BaseStream).Name} ..."); InitializeTableTypes(connection, tableSchema); WriteTableContentsToStream(tableSchema, connection, streamWriter); } } } private static void InitializeTableTypes(NpgsqlConnection connection, string schema) { TableTypes = connection.Query<KeyValuePair<string, string>>("SELECT CONCAT(table_schema, '.', table_name, '.', column_name) AS Key, data_type As Value FROM information_schema.columns WHERE table_schema = @schema", new { schema = schema }).ToDictionary(p => p.Key, p => p.Value); } private static void WriteTableContentsToStream(string tableSchema, NpgsqlConnection connection, StreamWriter writer) { string query = "SELECT table_name FROM information_schema.tables WHERE table_schema = @schema AND table_name <> 'pg_stat_statements' ORDER BY table_name"; foreach (var tableName in connection.Query<string>(query, new { schema = tableSchema })) { writer.WriteLine($"And table \"{tableSchema}\".\"{tableName}\" contains rows:"); var result = connection.Query($"SELECT * FROM {tableSchema}.\"{tableName}\""); var rowsAsList = result.Cast<IDictionary<string, object>>().Select(r => ByPassTypeHandling(r, tableSchema, tableName)); if (rowsAsList.Any()) { var length = new Dictionary<string, int>(); var headerBuilder = new StringBuilder("|"); foreach (var column in rowsAsList.First().Keys) { var maxContentLength = rowsAsList.SelectMany(r => r.Where(c => c.Key == column)).Max(r => r.Value.ToString().Length); var maxLength = Math.Max(maxContentLength, column.Length); length[column] = maxLength; headerBuilder.AppendFormat($" {{0,-{maxLength}}} |", column); } writer.WriteLine(headerBuilder.ToString()); foreach (var rows in rowsAsList) { var rowBuilder = new StringBuilder("|"); foreach (var value in rows) { rowBuilder.AppendFormat($" {{0,-{length[value.Key]}}} |", value.Value); } writer.WriteLine(rowBuilder.ToString()); } } else { var columns = connection.Query<string>("SELECT column_name FROM information_schema.columns WHERE table_schema = @schema AND table_name = @table ORDER BY ordinal_position", new { schema = tableSchema, table = tableName }); var headerBuilder = new StringBuilder("|"); foreach (var column in columns) { headerBuilder.Append($" {column} |"); } writer.WriteLine(headerBuilder.ToString()); } writer.WriteLine(); } } private static IDictionary<string, string> ByPassTypeHandling(IDictionary<string, object> rows, string tableSchema, string tableName) { var result = new Dictionary<string, string>(); foreach (var column in rows) { var type = TableTypes[$"{tableSchema}.{tableName}.{column.Key}"]; if (type == "timestamp without time zone" && column.Value != null) { // ISO 8601 result[column.Key] = DateTime.Parse(column.Value.ToString()).ToString("yyyy-MM-ddTHH:mm:ssZ"); } else if (type == "timestamp with time zone" && column.Value != null) { // ISO 8601 result[column.Key] = DateTime.Parse(column.Value.ToString()).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"); } else { result[column.Key] = column.Value == null ? string.Empty : column.Value.ToString(); } } return result; } } }