Repository: vas3k/vas3k.club

Issue:
Баг: неверный формат даты в эндпоинте /feed.json
Клуб поддерживает выдачу постов в формате JSON Feed, но в полях `date_published` и `date_modified` дата лежит в неверном формате.

Спецификация JSON Feed: https://www.jsonfeed.org/version/1/

### Шаги к воспроизведению:
- Дёрнуть https://vas3k.club/feed.json
- Смотрим на массив `items`, у айтема поля `date_published` и `date_modified`

### Текущее поведение:
- Дата формата `yyyy-MM-ddTHH:mm:ss.SSS` (пример `2023-06-24T15:17:25.207755`)

### Ожидаемое поведение:
- Дата в формате стандарта RFC 3339 `yyyy-MM-ddTHH:mm:ssX` (пример `2023-06-24T15:17:25Z` или `2023-06-24T18:17:25+03:00`, как удобнее будет, в целом это один стандарт)
Баг: неверный формат даты в эндпоинте /feed.json
Клуб поддерживает выдачу постов в формате JSON Feed, но в полях `date_published` и `date_modified` дата лежит в неверном формате.

Спецификация JSON Feed: https://www.jsonfeed.org/version/1/

### Шаги к воспроизведению:
- Дёрнуть https://vas3k.club/feed.json
- Смотрим на массив `items`, у айтема поля `date_published` и `date_modified`

### Текущее поведение:
- Дата формата `yyyy-MM-ddTHH:mm:ss.SSS` (пример `2023-06-24T15:17:25.207755`)

### Ожидаемое поведение:
- Дата в формате стандарта RFC 3339 `yyyy-MM-ddTHH:mm:ssX` (пример `2023-06-24T15:17:25Z` или `2023-06-24T18:17:25+03:00`, как удобнее будет, в целом это один стандарт)


Discussion:
Хороший баг, надо фиксить
Прикреплю еще результаты jsonfeed validator:

https://validator.jsonfeed.org/?url=https%3A%2F%2Fvas3k.club%2Ffeed.json

Помимо проблем с датами, иногда content_text = null.
попробую взять на себя ишью, вроде нужно поправить формат конвертации в post.to_dict всего лишь 
Хороший баг, надо фиксить
Прикреплю еще результаты jsonfeed validator:

https://validator.jsonfeed.org/?url=https%3A%2F%2Fvas3k.club%2Ffeed.json

Помимо проблем с датами, иногда content_text = null.
попробую взять на себя ишью, вроде нужно поправить формат конвертации в post.to_dict всего лишь 

Resolving patch:
diff --git a/posts/models/post.py b/posts/models/post.py
--- a/posts/models/post.py
+++ b/posts/models/post.py
@@ -142,9 +142,9 @@ def to_dict(self, including_private=False):
             "id": self.id,
             "url": f"{settings.APP_HOST}{self.get_absolute_url()}",
             "title": self.title,
-            "content_text": self.text if self.is_public or including_private else None,
-            "date_published": self.published_at.isoformat(),
-            "date_modified": self.updated_at.isoformat(),
+            "content_text": self.text if self.is_public or including_private else "🔒",
+            "date_published": self.published_at.astimezone().isoformat(),
+            "date_modified": self.updated_at.astimezone().isoformat(),
             "authors": [
                 {
                     "name": self.author.full_name,
@@ -348,4 +348,3 @@ def delete(self, *args, **kwargs):
     def undelete(self, *args, **kwargs):
         self.deleted_at = None
         self.save()
-


Test patch:
diff --git a/posts/tests/test_api.py b/posts/tests/test_api.py
new file mode 100644
--- /dev/null
+++ b/posts/tests/test_api.py
@@ -0,0 +1,39 @@
+from datetime import datetime
+
+from django.test import TestCase
+
+from posts.tests.test_views import ModelCreator
+
+
+class TestApi(TestCase):
+    def setUp(self):
+        self.creator = ModelCreator()
+        self.user = self.creator.create_user()
+        self.time = datetime.now()
+
+    def test_date_rfc3339(self):
+        post = self.creator.create_post(
+            is_visible=True,
+            is_public=True,
+        )
+        post.published_at = self.time
+        post.updated_at = self.time
+        converted_post = post.to_dict()
+        self.assertIsNotNone(
+            datetime.strptime(
+                converted_post["date_published"], "%Y-%m-%dT%H:%M:%S.%f%z"
+            ).tzinfo,
+        )
+        self.assertIsNotNone(
+            datetime.strptime(
+                converted_post["date_modified"], "%Y-%m-%dT%H:%M:%S.%f%z"
+            ).tzinfo,
+        )
+
+    def test_content_text_is_not_none(self):
+        post = self.creator.create_post(
+            is_visible=True,
+            is_public=False,
+        )
+        converted_post = post.to_dict()
+        self.assertIsNotNone(converted_post["content_text"])