Support analog photo dates and correct gallery years
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-18a628ae4ea72d0f9a68de6acfbc698449fe10e49ecf990a8ddced0b2e32949d"
|
title: "photo-18a628ae4ea72d0f9a68de6acfbc698449fe10e49ecf990a8ddced0b2e32949d"
|
||||||
date: "2026-09-19T21:41:03.801419+00:00"
|
date: "2025-09-19T21:42:30+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
anchor: "Geneva"
|
anchor: "Geneva"
|
||||||
---
|
---
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-18a628ae4ea72d0f9a68de6acfbc698449fe10e49ecf990a8ddced0b2e32949d-01"
|
title: "photo-18a628ae4ea72d0f9a68de6acfbc698449fe10e49ecf990a8ddced0b2e32949d-01"
|
||||||
date: "2026-09-19T21:41:02.801419+00:00"
|
date: "2025-09-19T21:42:29+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-18a628ae4ea72d0f9a68de6acfbc698449fe10e49ecf990a8ddced0b2e32949d-02"
|
title: "photo-18a628ae4ea72d0f9a68de6acfbc698449fe10e49ecf990a8ddced0b2e32949d-02"
|
||||||
date: "2026-09-19T21:41:01.801419+00:00"
|
date: "2025-09-19T21:42:28+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-1bb19ce5b6a5570ddda1754bb77583dbfb2b4c534ee88e85a69f511ab47b1a14"
|
title: "photo-1bb19ce5b6a5570ddda1754bb77583dbfb2b4c534ee88e85a69f511ab47b1a14"
|
||||||
date: "2026-09-19T21:29:48.036428+00:00"
|
date: "2026-09-19T12:20:30+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
anchor: "Foto 64"
|
anchor: "Foto 64"
|
||||||
---
|
---
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-1bb19ce5b6a5570ddda1754bb77583dbfb2b4c534ee88e85a69f511ab47b1a14-01"
|
title: "photo-1bb19ce5b6a5570ddda1754bb77583dbfb2b4c534ee88e85a69f511ab47b1a14-01"
|
||||||
date: "2026-09-19T21:29:47.036428+00:00"
|
date: "2026-09-19T12:20:29+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-1bb19ce5b6a5570ddda1754bb77583dbfb2b4c534ee88e85a69f511ab47b1a14-02"
|
title: "photo-1bb19ce5b6a5570ddda1754bb77583dbfb2b4c534ee88e85a69f511ab47b1a14-02"
|
||||||
date: "2026-09-19T21:29:46.036428+00:00"
|
date: "2026-09-19T12:20:28+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-1bb19ce5b6a5570ddda1754bb77583dbfb2b4c534ee88e85a69f511ab47b1a14-03"
|
title: "photo-1bb19ce5b6a5570ddda1754bb77583dbfb2b4c534ee88e85a69f511ab47b1a14-03"
|
||||||
date: "2026-09-19T21:29:45.036428+00:00"
|
date: "2026-09-19T12:20:27+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-260cc99527c13f256d4ea39d0959f5daa942c888ea882852e73fd94f6c30b7db"
|
title: "photo-260cc99527c13f256d4ea39d0959f5daa942c888ea882852e73fd94f6c30b7db"
|
||||||
date: "2026-09-19T12:29:19+00:00"
|
date: "2026-09-19T12:40:26+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-474ab3283ce8801ac6e59b67b65b4169167053873a5a8db4a15b840a3448266c"
|
title: "photo-474ab3283ce8801ac6e59b67b65b4169167053873a5a8db4a15b840a3448266c"
|
||||||
date: "2026-09-19T12:29:27+00:00"
|
date: "2026-09-19T12:40:18+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-00"
|
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d"
|
||||||
date: "2026-09-19T12:29:26+00:00"
|
date: "2026-09-19T12:40:33+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
anchor: "Romania"
|
anchor: "Romania"
|
||||||
---
|
---
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-01"
|
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-01"
|
||||||
date: "2026-09-19T12:29:25+00:00"
|
date: "2026-09-19T12:40:32+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-02"
|
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-02"
|
||||||
date: "2026-09-19T12:29:17+00:00"
|
date: "2026-09-19T12:40:17+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-03"
|
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-03"
|
||||||
date: "2026-09-19T12:29:16+00:00"
|
date: "2026-09-19T12:40:16+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-04"
|
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-04"
|
||||||
date: "2026-09-19T12:29:28+00:00"
|
date: "2026-09-19T12:40:19+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-05"
|
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-05"
|
||||||
date: "2026-09-19T12:29:29+00:00"
|
date: "2026-09-19T12:40:20+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-06"
|
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-06"
|
||||||
date: "2026-09-19T12:29:30+00:00"
|
date: "2026-09-19T12:40:21+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-07"
|
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-07"
|
||||||
date: "2026-09-19T12:29:31+00:00"
|
date: "2026-09-19T12:40:22+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-08"
|
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-08"
|
||||||
date: "2026-09-19T12:29:32+00:00"
|
date: "2026-09-19T12:40:23+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d"
|
title: "photo-7c832afa1b73023d8f2391ff40c221be9ffd468a1a96a6bf4900cbf95595d31d-09"
|
||||||
date: "2026-09-19T12:29:33+00:00"
|
date: "2026-09-19T12:40:24+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
anchor: "Armenia"
|
anchor: "Armenia"
|
||||||
---
|
---
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e"
|
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e"
|
||||||
date: "2026-09-19T21:41:17+00:00"
|
date: "2025-09-19T21:41:17+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-01"
|
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-01"
|
||||||
date: "2026-09-19T21:41:16+00:00"
|
date: "2025-09-19T21:41:16+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-02"
|
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-02"
|
||||||
date: "2026-09-19T21:41:15+00:00"
|
date: "2025-09-19T21:41:15+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-03"
|
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-03"
|
||||||
date: "2026-09-19T21:41:14+00:00"
|
date: "2025-09-19T21:41:14+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-04"
|
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-04"
|
||||||
date: "2026-09-19T21:41:13+00:00"
|
date: "2025-09-19T21:41:13+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-05"
|
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-05"
|
||||||
date: "2026-09-19T21:41:12+00:00"
|
date: "2025-09-19T21:41:12+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-06"
|
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-06"
|
||||||
date: "2026-09-19T21:41:11+00:00"
|
date: "2025-09-19T21:41:11+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-07"
|
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-07"
|
||||||
date: "2026-09-19T21:41:10+00:00"
|
date: "2025-09-19T21:41:10+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-08"
|
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-08"
|
||||||
date: "2026-09-19T21:41:09+00:00"
|
date: "2025-09-19T21:41:09+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-09"
|
title: "photo-85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e-09"
|
||||||
date: "2026-09-19T21:41:08+00:00"
|
date: "2025-09-19T21:41:08+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-b18a2f1c7127077c801160782012cb164759f0b9dd41762205b45e4c9a07d673"
|
title: "photo-b18a2f1c7127077c801160782012cb164759f0b9dd41762205b45e4c9a07d673"
|
||||||
date: "2026-09-19T12:29:18+00:00"
|
date: "2026-09-19T12:40:25+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-bf23ab31ef7b33150e9e99cb31f14b1bc27bc8143293a32bb1d4ac2193a493c6"
|
title: "photo-bf23ab31ef7b33150e9e99cb31f14b1bc27bc8143293a32bb1d4ac2193a493c6"
|
||||||
date: "2026-09-19T21:43:42.048113+00:00"
|
date: "2025-09-19T21:43:42+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
anchor: "Iranian Martyrs\u2019 Memorial Cemetery"
|
anchor: "Iranian Martyrs’ Memorial Cemetery"
|
||||||
---
|
---
|
||||||
|
|
||||||
<p><img src="/img/arts/uploads/bf23ab31ef7b33150e9e99cb31f14b1bc27bc8143293a32bb1d4ac2193a493c6/00.jpg" alt="Martyrs" loading="lazy"></p>
|
<p><img src="/img/arts/uploads/bf23ab31ef7b33150e9e99cb31f14b1bc27bc8143293a32bb1d4ac2193a493c6/00.jpg" alt="Martyrs" loading="lazy"></p>
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-bf23ab31ef7b33150e9e99cb31f14b1bc27bc8143293a32bb1d4ac2193a493c6-01"
|
title: "photo-bf23ab31ef7b33150e9e99cb31f14b1bc27bc8143293a32bb1d4ac2193a493c6-01"
|
||||||
date: "2026-09-19T21:43:41.048113+00:00"
|
date: "2025-09-19T21:43:41+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-c53f20236f166632bff163339c8e789cc09b0d7e63c5b531b1fc20705460975b"
|
title: "photo-c53f20236f166632bff163339c8e789cc09b0d7e63c5b531b1fc20705460975b"
|
||||||
date: "2026-09-19T12:29:24+00:00"
|
date: "2026-09-19T12:40:31+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-c53f20236f166632bff163339c8e789cc09b0d7e63c5b531b1fc20705460975b-01"
|
title: "photo-c53f20236f166632bff163339c8e789cc09b0d7e63c5b531b1fc20705460975b-01"
|
||||||
date: "2026-09-19T12:29:23+00:00"
|
date: "2026-09-19T12:40:30+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-c53f20236f166632bff163339c8e789cc09b0d7e63c5b531b1fc20705460975b-02"
|
title: "photo-c53f20236f166632bff163339c8e789cc09b0d7e63c5b531b1fc20705460975b-02"
|
||||||
date: "2026-09-19T12:29:22+00:00"
|
date: "2026-09-19T12:40:29+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-c53f20236f166632bff163339c8e789cc09b0d7e63c5b531b1fc20705460975b-03"
|
title: "photo-c53f20236f166632bff163339c8e789cc09b0d7e63c5b531b1fc20705460975b-03"
|
||||||
date: "2026-09-19T12:29:21+00:00"
|
date: "2026-09-19T12:40:28+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-c53f20236f166632bff163339c8e789cc09b0d7e63c5b531b1fc20705460975b-04"
|
title: "photo-c53f20236f166632bff163339c8e789cc09b0d7e63c5b531b1fc20705460975b-04"
|
||||||
date: "2026-09-19T12:29:20+00:00"
|
date: "2026-09-19T12:40:27+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f"
|
||||||
date: "2026-09-19T21:41:30+00:00"
|
date: "2025-09-19T21:41:30+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
anchor: "Paris & Lille"
|
anchor: "Paris & Lille"
|
||||||
---
|
---
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-01"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-01"
|
||||||
date: "2026-09-19T21:41:29+00:00"
|
date: "2025-09-19T21:41:29+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-02"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-02"
|
||||||
date: "2026-09-19T21:41:28+00:00"
|
date: "2025-09-19T21:41:28+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-03"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-03"
|
||||||
date: "2026-09-19T21:41:27+00:00"
|
date: "2025-09-19T21:41:27+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-04"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-04"
|
||||||
date: "2026-09-19T21:41:26+00:00"
|
date: "2025-09-19T21:41:26+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-05"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-05"
|
||||||
date: "2026-09-19T21:41:25+00:00"
|
date: "2025-09-19T21:41:25+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-06"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-06"
|
||||||
date: "2026-09-19T21:41:24+00:00"
|
date: "2025-09-19T21:41:24+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-07"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-07"
|
||||||
date: "2026-09-19T21:41:23+00:00"
|
date: "2025-09-19T21:41:23+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-08"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-08"
|
||||||
date: "2026-09-19T21:41:22+00:00"
|
date: "2025-09-19T21:41:22+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-09"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-09"
|
||||||
date: "2026-09-19T21:41:21+00:00"
|
date: "2025-09-19T21:41:21+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-10"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-10"
|
||||||
date: "2026-09-19T21:41:20+00:00"
|
date: "2025-09-19T21:41:20+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-11"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-11"
|
||||||
date: "2026-09-19T21:41:19+00:00"
|
date: "2025-09-19T21:41:19+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: post
|
layout: post
|
||||||
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-12"
|
title: "photo-e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f-12"
|
||||||
date: "2026-09-19T21:41:18+00:00"
|
date: "2025-09-19T21:41:18+00:00"
|
||||||
categories: art
|
categories: art
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+10
-4
@@ -136,11 +136,14 @@ Create **Publish to blog**:
|
|||||||
**ISO 8601**. After **End Repeat**, **Combine Text** from **Repeat Results**
|
**ISO 8601**. After **End Repeat**, **Combine Text** from **Repeat Results**
|
||||||
with **New Lines** and save it as **Photo Dates**. This preserves capture
|
with **New Lines** and save it as **Photo Dates**. This preserves capture
|
||||||
dates even when Shortcuts strips them from the exported JPEG data.
|
dates even when Shortcuts strips them from the exported JPEG data.
|
||||||
5. **Make Archive** from **Shortcut Input**, format **ZIP**. Keep this output in
|
5. Add **Ask for Input**, type **Text**, with the prompt **Date Taken (optional;
|
||||||
|
YYYY, YYYY-MM-DD, or ISO timestamp)**. Save its response as **Date Taken**.
|
||||||
|
This is useful for analog scans; leave it blank for digital photos.
|
||||||
|
6. **Make Archive** from **Shortcut Input**, format **ZIP**. Keep this output in
|
||||||
a variable named **Photo Archive**. This packages every selected image into
|
a variable named **Photo Archive**. This packages every selected image into
|
||||||
one upload; using an image list directly in a File form field can send only
|
one upload; using an image list directly in a File form field can send only
|
||||||
the first image on some Shortcuts versions.
|
the first image on some Shortcuts versions.
|
||||||
6. **Get Contents of URL**: `https://theread.me/api/photos`.
|
7. **Get Contents of URL**: `https://theread.me/api/photos`.
|
||||||
- Method: **POST**.
|
- Method: **POST**.
|
||||||
- Header: `Authorization` = `Bearer YOUR_TOKEN`.
|
- Header: `Authorization` = `Bearer YOUR_TOKEN`.
|
||||||
- Request Body: **Form**.
|
- Request Body: **Form**.
|
||||||
@@ -149,8 +152,9 @@ Create **Publish to blog**:
|
|||||||
- `highlight`: type **Text**, value the highlight-name response. Select each
|
- `highlight`: type **Text**, value the highlight-name response. Select each
|
||||||
prompt's specific output variable so the caption and highlight stay separate.
|
prompt's specific output variable so the caption and highlight stay separate.
|
||||||
- `photo_dates`: type **Text**, value **Photo Dates**.
|
- `photo_dates`: type **Text**, value **Photo Dates**.
|
||||||
|
- `date_taken`: type **Text**, value **Date Taken**.
|
||||||
- Do not set Content-Type; Shortcuts supplies the multipart boundary.
|
- Do not set Content-Type; Shortcuts supplies the multipart boundary.
|
||||||
7. Show the actual response and check its `count` against the number of selected
|
8. Show the actual response and check its `count` against the number of selected
|
||||||
images. Only show “Uploaded; publishing” when there is no `error`. Optionally poll the returned `status_url`
|
images. Only show “Uploaded; publishing” when there is no `error`. Optionally poll the returned `status_url`
|
||||||
with the same header. `pushed` means “GitHub Actions is building/deploying”;
|
with the same header. `pushed` means “GitHub Actions is building/deploying”;
|
||||||
only `published` means the deployed site's receipt contains this album.
|
only `published` means the deployed site's receipt contains this album.
|
||||||
@@ -167,7 +171,9 @@ Select still photos; videos/Live Photo video components aren't supported. The
|
|||||||
service reads EXIF `DateTimeOriginal` automatically when it is present. Because
|
service reads EXIF `DateTimeOriginal` automatically when it is present. Because
|
||||||
iOS Shortcuts can blank that EXIF field while creating the ZIP, `photo_dates`
|
iOS Shortcuts can blank that EXIF field while creating the ZIP, `photo_dates`
|
||||||
supplies the same Photos-library metadata explicitly, one ISO 8601 value per
|
supplies the same Photos-library metadata explicitly, one ISO 8601 value per
|
||||||
image in archive order. An upload without either source is rejected instead of
|
image in archive order. `date_taken` overrides both sources for the whole batch;
|
||||||
|
it accepts a year, calendar date, or ISO timestamp and is intended for analog
|
||||||
|
photos. An upload without any date source is rejected instead of
|
||||||
being placed under the upload year. Re-sharing the same images, caption, and
|
being placed under the upload year. Re-sharing the same images, caption, and
|
||||||
highlight with dates replaces the earlier undated upload. Captions are plain
|
highlight with dates replaces the earlier undated upload. Captions are plain
|
||||||
text and optional.
|
text and optional.
|
||||||
|
|||||||
+29
-7
@@ -6,6 +6,7 @@ import html
|
|||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from collections import Counter
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
@@ -79,14 +80,30 @@ def submitted_photo_dates(raw, count):
|
|||||||
return dates
|
return dates
|
||||||
|
|
||||||
|
|
||||||
|
def submitted_batch_date(raw):
|
||||||
|
"""Parse one optional date override for an entire analog-photo batch."""
|
||||||
|
value = raw.strip()
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
if re.fullmatch(r"\d{4}", value):
|
||||||
|
return datetime(int(value), 1, 1)
|
||||||
|
return datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||||
|
except ValueError as error:
|
||||||
|
raise ValueError("date_taken must be a year, ISO date, or ISO timestamp") from error
|
||||||
|
|
||||||
|
|
||||||
def distinct_photo_dates(dates):
|
def distinct_photo_dates(dates):
|
||||||
"""Keep exact capture times, using earlier seconds only to break true ties."""
|
"""Keep exact capture times, using later seconds only to break true ties."""
|
||||||
result = []
|
result = []
|
||||||
used = set()
|
used = set()
|
||||||
|
remaining = Counter(taken.isoformat() for taken in dates)
|
||||||
for taken in dates:
|
for taken in dates:
|
||||||
candidate = taken
|
key = taken.isoformat()
|
||||||
|
candidate = taken + timedelta(seconds=remaining[key] - 1)
|
||||||
|
remaining[key] -= 1
|
||||||
while candidate.isoformat() in used:
|
while candidate.isoformat() in used:
|
||||||
candidate -= timedelta(seconds=1)
|
candidate += timedelta(seconds=1)
|
||||||
result.append(candidate)
|
result.append(candidate)
|
||||||
used.add(candidate.isoformat())
|
used.add(candidate.isoformat())
|
||||||
return result
|
return result
|
||||||
@@ -321,6 +338,8 @@ def create_app(data_dir=None, token=None):
|
|||||||
try:
|
try:
|
||||||
supplied_dates = (submitted_photo_dates(request.form.get("photo_dates", ""), len(photos))
|
supplied_dates = (submitted_photo_dates(request.form.get("photo_dates", ""), len(photos))
|
||||||
if kind == "photo" else None)
|
if kind == "photo" else None)
|
||||||
|
batch_date = (submitted_batch_date(request.form.get("date_taken", ""))
|
||||||
|
if kind == "photo" else None)
|
||||||
except ValueError as error:
|
except ValueError as error:
|
||||||
abort(400, str(error))
|
abort(400, str(error))
|
||||||
# Ordered byte hashes + caption make retries idempotent, even across restarts.
|
# Ordered byte hashes + caption make retries idempotent, even across restarts.
|
||||||
@@ -339,7 +358,8 @@ def create_app(data_dir=None, token=None):
|
|||||||
try:
|
try:
|
||||||
embedded_date = web_image(raw, temporary / "images" / f"{index:02}.jpg")
|
embedded_date = web_image(raw, temporary / "images" / f"{index:02}.jpg")
|
||||||
if kind == "photo":
|
if kind == "photo":
|
||||||
capture_dates.append(supplied_dates[index] if supplied_dates else embedded_date)
|
capture_dates.append(batch_date or
|
||||||
|
(supplied_dates[index] if supplied_dates else embedded_date))
|
||||||
except (UnidentifiedImageError, OSError, ValueError, SyntaxError,
|
except (UnidentifiedImageError, OSError, ValueError, SyntaxError,
|
||||||
Image.DecompressionBombError, Image.DecompressionBombWarning,
|
Image.DecompressionBombError, Image.DecompressionBombWarning,
|
||||||
ImageCms.PyCMSError):
|
ImageCms.PyCMSError):
|
||||||
@@ -348,14 +368,16 @@ def create_app(data_dir=None, token=None):
|
|||||||
if highlight:
|
if highlight:
|
||||||
digest.update(b"\0highlight\0" + highlight.encode())
|
digest.update(b"\0highlight\0" + highlight.encode())
|
||||||
undated_job = digest.hexdigest()
|
undated_job = digest.hexdigest()
|
||||||
if supplied_dates:
|
if batch_date:
|
||||||
|
digest.update(b"\0date_taken\0" + batch_date.isoformat().encode())
|
||||||
|
elif supplied_dates:
|
||||||
normalized_dates = "\n".join(taken.isoformat() for taken in supplied_dates)
|
normalized_dates = "\n".join(taken.isoformat() for taken in supplied_dates)
|
||||||
digest.update(b"\0photo_dates\0" + normalized_dates.encode())
|
digest.update(b"\0photo_dates\0" + normalized_dates.encode())
|
||||||
job = digest.hexdigest()
|
job = digest.hexdigest()
|
||||||
date = datetime.now(timezone.utc).isoformat()
|
date = datetime.now(timezone.utc).isoformat()
|
||||||
manifest = {"id": job, "date": date, "caption": caption,
|
manifest = {"id": job, "date": date, "caption": caption,
|
||||||
"highlight": highlight, "count": len(photos)}
|
"highlight": highlight, "count": len(photos)}
|
||||||
if (supplied_dates and undated_job != job and
|
if ((batch_date or supplied_dates) and undated_job != job and
|
||||||
(data / "submissions" / undated_job / "manifest.json").is_file()):
|
(data / "submissions" / undated_job / "manifest.json").is_file()):
|
||||||
manifest["replaces"] = undated_job
|
manifest["replaces"] = undated_job
|
||||||
if kind == "snippet":
|
if kind == "snippet":
|
||||||
@@ -363,7 +385,7 @@ def create_app(data_dir=None, token=None):
|
|||||||
atomic_json(temporary / "manifest.json", manifest)
|
atomic_json(temporary / "manifest.json", manifest)
|
||||||
if kind == "photo":
|
if kind == "photo":
|
||||||
if any(taken is None for taken in capture_dates):
|
if any(taken is None for taken in capture_dates):
|
||||||
abort(400, "Photo capture date is missing; send photo_dates from the Photos library")
|
abort(400, "Photo capture date is missing; send date_taken or photo_dates")
|
||||||
capture_dates = distinct_photo_dates(capture_dates)
|
capture_dates = distinct_photo_dates(capture_dates)
|
||||||
manifest["capture_dates"] = [taken.isoformat() for taken in capture_dates]
|
manifest["capture_dates"] = [taken.isoformat() for taken in capture_dates]
|
||||||
atomic_json(temporary / "manifest.json", manifest)
|
atomic_json(temporary / "manifest.json", manifest)
|
||||||
|
|||||||
@@ -195,6 +195,28 @@ def test_photo_dates_can_come_from_photos_library_metadata(service):
|
|||||||
assert "capture date is missing" in missing.json["error"]
|
assert "capture date is missing" in missing.json["error"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_batch_date_taken_overrides_metadata_for_analog_photos(service):
|
||||||
|
_, client, data = service
|
||||||
|
response = client.post("/api/photos", headers=AUTH, data={
|
||||||
|
"photos": [
|
||||||
|
(io.BytesIO(photo(exif=Image.Exif())), "first.jpg"),
|
||||||
|
(io.BytesIO(photo(exif=Image.Exif())), "second.jpg"),
|
||||||
|
],
|
||||||
|
"photo_dates": "2026-01-02T03:04:05+00:00\n2026-01-03T03:04:05+00:00",
|
||||||
|
"date_taken": "2025",
|
||||||
|
})
|
||||||
|
assert response.status_code == 202
|
||||||
|
manifest = json.loads((data / "submissions" / response.json["id"] / "manifest.json").read_text())
|
||||||
|
assert all(value.startswith("2025-01-01T") for value in manifest["capture_dates"])
|
||||||
|
|
||||||
|
invalid = client.post("/api/photos", headers=AUTH, data={
|
||||||
|
"photos": [(io.BytesIO(photo()), "photo.jpg")],
|
||||||
|
"date_taken": "last summer",
|
||||||
|
})
|
||||||
|
assert invalid.status_code == 400
|
||||||
|
assert "year, ISO date, or ISO timestamp" in invalid.json["error"]
|
||||||
|
|
||||||
|
|
||||||
def test_dated_retry_replaces_the_same_undated_upload(service):
|
def test_dated_retry_replaces_the_same_undated_upload(service):
|
||||||
_, client, data = service
|
_, client, data = service
|
||||||
raw = photo()
|
raw = photo()
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ romania = [
|
|||||||
(OLD[4], 2), (OLD[4], 3), (OLD[4], 4), (OLD[0], 0), (OLD[3], 0),
|
(OLD[4], 2), (OLD[4], 3), (OLD[4], 4), (OLD[0], 0), (OLD[3], 0),
|
||||||
]
|
]
|
||||||
ungrouped = [(OLD[2], 2), (OLD[2], 3)]
|
ungrouped = [(OLD[2], 2), (OLD[2], 3)]
|
||||||
ordered = armenia + romania + ungrouped
|
ordered = romania + armenia + ungrouped
|
||||||
assert len(ordered) == len(set(ordered)) == 18
|
assert len(ordered) == len(set(ordered)) == 18
|
||||||
|
|
||||||
collection_groups = [
|
collection_groups = [
|
||||||
@@ -112,13 +112,31 @@ collection_groups = [
|
|||||||
|
|
||||||
paris = "85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e"
|
paris = "85a558a3fcf582e6f0637d6927bb5a074de422f30a389ab9589630b9c163fc0e"
|
||||||
paris_lille = "e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f"
|
paris_lille = "e013523e4dda324d603c89f93ef3e83abbcd82cb11a16f85e53b670e8613d34f"
|
||||||
|
geneva = "18a628ae4ea72d0f9a68de6acfbc698449fe10e49ecf990a8ddced0b2e32949d"
|
||||||
|
iranian_memorial = "bf23ab31ef7b33150e9e99cb31f14b1bc27bc8143293a32bb1d4ac2193a493c6"
|
||||||
|
foto_64 = "1bb19ce5b6a5570ddda1754bb77583dbfb2b4c534ee88e85a69f511ab47b1a14"
|
||||||
uploaded_highlight_groups = [
|
uploaded_highlight_groups = [
|
||||||
|
(
|
||||||
|
"Iranian Martyrs’ Memorial Cemetery",
|
||||||
|
datetime(2025, 9, 19, 21, 43, 42, tzinfo=timezone.utc),
|
||||||
|
[(iranian_memorial, index) for index in range(2)],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"Geneva",
|
||||||
|
datetime(2025, 9, 19, 21, 42, 30, tzinfo=timezone.utc),
|
||||||
|
[(geneva, index) for index in range(3)],
|
||||||
|
),
|
||||||
(
|
(
|
||||||
"Paris & Lille",
|
"Paris & Lille",
|
||||||
datetime(2026, 9, 19, 21, 41, 30, tzinfo=timezone.utc),
|
datetime(2025, 9, 19, 21, 41, 30, tzinfo=timezone.utc),
|
||||||
[(paris_lille, index) for index in range(13)] +
|
[(paris_lille, index) for index in range(13)] +
|
||||||
[(paris, index) for index in range(10)],
|
[(paris, index) for index in range(10)],
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"Foto 64",
|
||||||
|
datetime(2026, 9, 19, 12, 20, 30, tzinfo=timezone.utc),
|
||||||
|
[(foto_64, index) for index in range(4)],
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
anchors = {armenia[0]: "Armenia", romania[0]: "Romania"}
|
anchors = {armenia[0]: "Armenia", romania[0]: "Romania"}
|
||||||
@@ -132,7 +150,7 @@ for job in OLD:
|
|||||||
if batch:
|
if batch:
|
||||||
batch.unlink()
|
batch.unlink()
|
||||||
|
|
||||||
start = datetime(2026, 9, 19, 12, 29, 33, tzinfo=timezone.utc)
|
start = datetime(2026, 9, 19, 12, 40, 33, tzinfo=timezone.utc)
|
||||||
for position, (job, index) in enumerate(ordered):
|
for position, (job, index) in enumerate(ordered):
|
||||||
title = f"photo-{job}-{index:02}"
|
title = f"photo-{job}-{index:02}"
|
||||||
if (job, index) == first_for_job[job]:
|
if (job, index) == first_for_job[job]:
|
||||||
@@ -167,7 +185,7 @@ for folder, group_start, anchor, filenames in collection_groups:
|
|||||||
content = re.sub(r"^anchor:.*\n", "", content, flags=re.MULTILINE)
|
content = re.sub(r"^anchor:.*\n", "", content, flags=re.MULTILINE)
|
||||||
if position == 0:
|
if position == 0:
|
||||||
content = content.replace("categories: art\n", "categories: art\nanchor: " +
|
content = content.replace("categories: art\n", "categories: art\nanchor: " +
|
||||||
json.dumps(anchor) + "\n", 1)
|
json.dumps(anchor, ensure_ascii=False) + "\n", 1)
|
||||||
path.write_text(content)
|
path.write_text(content)
|
||||||
|
|
||||||
for anchor, group_start, photos in uploaded_highlight_groups:
|
for anchor, group_start, photos in uploaded_highlight_groups:
|
||||||
@@ -180,8 +198,8 @@ for anchor, group_start, photos in uploaded_highlight_groups:
|
|||||||
content = re.sub(r"^anchor:.*\n", "", content, flags=re.MULTILINE)
|
content = re.sub(r"^anchor:.*\n", "", content, flags=re.MULTILINE)
|
||||||
if position == 0:
|
if position == 0:
|
||||||
content = content.replace("categories: art\n", "categories: art\nanchor: " +
|
content = content.replace("categories: art\n", "categories: art\nanchor: " +
|
||||||
json.dumps(anchor) + "\n", 1)
|
json.dumps(anchor, ensure_ascii=False) + "\n", 1)
|
||||||
elif index == 0:
|
elif index == 0 and f'id="photo-{job}"' not in content:
|
||||||
# Removing this batch's old highlight must not remove its published
|
# Removing this batch's old highlight must not remove its published
|
||||||
# receipt target from the rendered gallery.
|
# receipt target from the rendered gallery.
|
||||||
content = content.replace("---\n\n", f'---\n\n<div id="photo-{job}"></div>\n\n', 1)
|
content = content.replace("---\n\n", f'---\n\n<div id="photo-{job}"></div>\n\n', 1)
|
||||||
|
|||||||
@@ -82,10 +82,22 @@ class PhotoHighlightTests(unittest.TestCase):
|
|||||||
anchor.group(1) if anchor else None, content))
|
anchor.group(1) if anchor else None, content))
|
||||||
records.sort(reverse=True)
|
records.sort(reverse=True)
|
||||||
grouped = [record for record in records if record[1] in ARMENIA + ROMANIA]
|
grouped = [record for record in records if record[1] in ARMENIA + ROMANIA]
|
||||||
self.assertEqual([record[1] for record in grouped], ARMENIA + ROMANIA)
|
self.assertEqual([record[1] for record in grouped], ROMANIA + ARMENIA)
|
||||||
self.assertEqual(grouped[0][2], "Armenia")
|
self.assertEqual(grouped[0][2], "Romania")
|
||||||
self.assertEqual(grouped[7][2], "Romania")
|
self.assertEqual(grouped[9][2], "Armenia")
|
||||||
self.assertIn("Transmission of Flesh by Erfan Ashourioun", grouped[2][3])
|
self.assertIn("Transmission of Flesh by Erfan Ashourioun", grouped[11][3])
|
||||||
|
|
||||||
|
def test_2026_highlights_follow_supplied_chronology(self):
|
||||||
|
anchors = {}
|
||||||
|
for path in ART.glob("*photo-*.md"):
|
||||||
|
content = path.read_text()
|
||||||
|
anchor = re.search(r'^anchor: "([^"]+)"', content, re.MULTILINE)
|
||||||
|
if anchor and anchor.group(1) in {"Foto 64", "Armenia", "Romania"}:
|
||||||
|
date = datetime.fromisoformat(re.search(
|
||||||
|
r'^date: "([^"]+)"', content, re.MULTILINE).group(1))
|
||||||
|
anchors[anchor.group(1)] = date
|
||||||
|
self.assertLess(anchors["Foto 64"], anchors["Armenia"])
|
||||||
|
self.assertLess(anchors["Armenia"], anchors["Romania"])
|
||||||
|
|
||||||
def test_existing_collection_highlight_starts(self):
|
def test_existing_collection_highlight_starts(self):
|
||||||
for anchor, filenames in COLLECTIONS:
|
for anchor, filenames in COLLECTIONS:
|
||||||
@@ -123,8 +135,36 @@ class PhotoHighlightTests(unittest.TestCase):
|
|||||||
[(PARIS_LILLE, index) for index in range(13)] +
|
[(PARIS_LILLE, index) for index in range(13)] +
|
||||||
[(PARIS, index) for index in range(10)])
|
[(PARIS, index) for index in range(10)])
|
||||||
self.assertEqual(anchors, ["Paris & Lille"])
|
self.assertEqual(anchors, ["Paris & Lille"])
|
||||||
|
self.assertTrue(all(date.year == 2025 for date, _, _ in records))
|
||||||
former_paris = (ART / f"2026-09-19-photo-{PARIS}-00.md").read_text()
|
former_paris = (ART / f"2026-09-19-photo-{PARIS}-00.md").read_text()
|
||||||
self.assertIn(f'<div id="photo-{PARIS}"></div>', former_paris)
|
self.assertEqual(former_paris.count(f'<div id="photo-{PARIS}"></div>'), 1)
|
||||||
|
|
||||||
|
def test_recent_analog_highlights_use_2025_in_supplied_order(self):
|
||||||
|
groups = (
|
||||||
|
("bf23ab31ef7b33150e9e99cb31f14b1bc27bc8143293a32bb1d4ac2193a493c6",
|
||||||
|
2, "Iranian Martyrs’ Memorial Cemetery"),
|
||||||
|
("18a628ae4ea72d0f9a68de6acfbc698449fe10e49ecf990a8ddced0b2e32949d",
|
||||||
|
3, "Geneva"),
|
||||||
|
)
|
||||||
|
newest = []
|
||||||
|
for job, count, anchor in groups:
|
||||||
|
records = []
|
||||||
|
anchors = []
|
||||||
|
for index in range(count):
|
||||||
|
content = (ART / f"2026-09-19-photo-{job}-{index:02}.md").read_text()
|
||||||
|
date = datetime.fromisoformat(re.search(
|
||||||
|
r'^date: "([^"]+)"', content, re.MULTILINE).group(1))
|
||||||
|
records.append(date)
|
||||||
|
anchors.extend(re.findall(r'^anchor: "([^"]+)"', content, re.MULTILINE))
|
||||||
|
self.assertTrue(all(date.year == 2025 for date in records))
|
||||||
|
self.assertEqual(anchors, [anchor])
|
||||||
|
newest.append(max(records))
|
||||||
|
paris_date = datetime.fromisoformat(re.search(
|
||||||
|
r'^date: "([^"]+)"',
|
||||||
|
(ART / f"2026-09-19-photo-{PARIS_LILLE}-00.md").read_text(),
|
||||||
|
re.MULTILINE).group(1))
|
||||||
|
self.assertGreater(newest[0], newest[1])
|
||||||
|
self.assertGreater(newest[1], paris_date)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user