From c7fa5bd40d0e5c9ea50190a90a0ccfee8ad96c25 Mon Sep 17 00:00:00 2001
From: pacien
Date: Thu, 27 Feb 2020 21:05:51 +0100
Subject: viewer: use colon as tag separator instead of dot

For consistency with the query language
and allowing the use of the very common dot in tags.

This also introduces a migration script.

GitHub: closes #164
---
 compiler/ldgallery.1.md                    |  2 +-
 example/src/DSC0001.jpg.yaml               |  4 ++--
 example/src/Ormont-Dessus/DSC0002.jpg.yaml |  6 +++---
 example/src/Ormont-Dessus/DSC0003.jpg.yaml |  6 +++---
 example/src/Ormont-Dessus/_directory.yaml  |  2 +-
 example/src/gallery.yaml                   |  4 ++--
 scripts/migrate_tags_dot_to_colon.py       | 25 +++++++++++++++++++++++++
 viewer/src/components/LdProposition.vue    |  4 ++--
 viewer/src/services/indexfactory.ts        |  2 +-
 9 files changed, 40 insertions(+), 15 deletions(-)
 create mode 100755 scripts/migrate_tags_dot_to_colon.py

diff --git a/compiler/ldgallery.1.md b/compiler/ldgallery.1.md
index 1d42eff..59a5b30 100644
--- a/compiler/ldgallery.1.md
+++ b/compiler/ldgallery.1.md
@@ -107,7 +107,7 @@ description
 
 tags
 : List of tags for the item.
-  Tag groups can be defined using prefixes separated by "." (dot).
+  Tag groups can be defined using prefixes separated by ":" (colon).
   Tags specified in a directory metadata sidecar are applied to all items within that directory.
 
 
diff --git a/example/src/DSC0001.jpg.yaml b/example/src/DSC0001.jpg.yaml
index b946ff7..cca1379 100644
--- a/example/src/DSC0001.jpg.yaml
+++ b/example/src/DSC0001.jpg.yaml
@@ -6,7 +6,7 @@ description: >
   © Philippe NGUYEN
 
 tags:
-  - photographer.nphilou
-  - location.germany.berlin
+  - photographer:nphilou
+  - location:germany:berlin
   - books
   - book-shop
diff --git a/example/src/Ormont-Dessus/DSC0002.jpg.yaml b/example/src/Ormont-Dessus/DSC0002.jpg.yaml
index 5b845b6..a69deb9 100644
--- a/example/src/Ormont-Dessus/DSC0002.jpg.yaml
+++ b/example/src/Ormont-Dessus/DSC0002.jpg.yaml
@@ -6,8 +6,8 @@ description: >
   © Philippe NGUYEN
 
 tags:
-  - photographer.nphilou
-  - time.day
-  - weather.foggy
+  - photographer:nphilou
+  - time:day
+  - weather:foggy
   - catwalk
   - mountain
diff --git a/example/src/Ormont-Dessus/DSC0003.jpg.yaml b/example/src/Ormont-Dessus/DSC0003.jpg.yaml
index 85a88ec..558f921 100644
--- a/example/src/Ormont-Dessus/DSC0003.jpg.yaml
+++ b/example/src/Ormont-Dessus/DSC0003.jpg.yaml
@@ -6,8 +6,8 @@ description: >
   © Philippe NGUYEN
 
 tags:
-  - photographer.nphilou
-  - time.day
-  - weather.foggy
+  - photographer:nphilou
+  - time:day
+  - weather:foggy
   - forest
   - trees
diff --git a/example/src/Ormont-Dessus/_directory.yaml b/example/src/Ormont-Dessus/_directory.yaml
index 75ad01b..5b3e0c7 100644
--- a/example/src/Ormont-Dessus/_directory.yaml
+++ b/example/src/Ormont-Dessus/_directory.yaml
@@ -1,3 +1,3 @@
 # The following tags are applied to all items in the directory
 tags:
-  - location.switzerland.ormont-dessus
+  - location:switzerland:ormont-dessus
diff --git a/example/src/gallery.yaml b/example/src/gallery.yaml
index d2f90aa..c4a5077 100644
--- a/example/src/gallery.yaml
+++ b/example/src/gallery.yaml
@@ -6,14 +6,14 @@
 #excludedDirectories: []
 
 includedFiles:
-  - "*.jpg"
+  - '*.jpg'
 
 #excludedFiles:
 #  - "*.md"
 
 tagsFromDirectories:
   fromParents: 0 # default
-  prefix: ""     # default
+  prefix: ''     # default
 
 thumbnailMaxResolution:
   width: 400  # default
diff --git a/scripts/migrate_tags_dot_to_colon.py b/scripts/migrate_tags_dot_to_colon.py
new file mode 100755
index 0000000..bf56c4c
--- /dev/null
+++ b/scripts/migrate_tags_dot_to_colon.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -i python -p "python3.withPackages (ps: with ps; [ruamel_yaml])"
+
+from argparse import ArgumentParser
+from ruamel.yaml import YAML
+from collections.abc import Iterable
+
+parser = ArgumentParser(description='Converts tag separator from dot to colon in sidecar files, easing migration after GH-164.')
+parser.add_argument('file', type=str, nargs='+', help='YAML sidecar file(s) to migrate.')
+args = parser.parse_args()
+
+yaml = YAML(typ='rt')  # preserve order, style and comments
+yaml.indent(mapping=2, sequence=2, offset=2)
+
+for file_path in args.file:
+  with open(file_path, 'r+') as file:
+    sidecar = yaml.load(file)
+    if not sidecar: continue
+
+    if 'tags' in sidecar and isinstance(sidecar['tags'], Iterable):
+      sidecar['tags'] = [tag.replace('.', ':') for tag in sidecar['tags']]
+
+    file.seek(0)
+    yaml.dump(sidecar, file)
+    file.truncate()
diff --git a/viewer/src/components/LdProposition.vue b/viewer/src/components/LdProposition.vue
index 375c482..3357777 100644
--- a/viewer/src/components/LdProposition.vue
+++ b/viewer/src/components/LdProposition.vue
@@ -74,7 +74,7 @@ export default class LdProposition extends Vue {
     } else {
       // Tags count from the current directory
       this.currentTags
-        .flatMap(tag => tag.split("."))
+        .flatMap(tag => tag.split(":"))
         .map(tag => this.tagsIndex[tag])
         .forEach(tagindex => (propositions[tagindex.tag] = tagindex.items.length));
     }
@@ -89,7 +89,7 @@ export default class LdProposition extends Vue {
   }
 
   rightmost(tag: Gallery.RawTag): Gallery.RawTag {
-    const dot = tag.lastIndexOf(".");
+    const dot = tag.lastIndexOf(":");
     return dot <= 0 ? tag : tag.substr(dot + 1);
   }
 
diff --git a/viewer/src/services/indexfactory.ts b/viewer/src/services/indexfactory.ts
index a6bc865..6fed6cc 100644
--- a/viewer/src/services/indexfactory.ts
+++ b/viewer/src/services/indexfactory.ts
@@ -36,7 +36,7 @@ export default class IndexFactory {
       return; // Directories are not indexed
     }
     for (const tag of item.tags) {
-      const parts = tag.split('.');
+      const parts = tag.split(':');
       let lastPart: string | null = null;
       for (const part of parts) {
         if (!tagsIndex[part]) tagsIndex[part] = { tag: part, tagfiltered: Navigation.normalize(part), items: [], children: {} };
-- 
cgit v1.2.3