Skip to content

Commit

Permalink
chore(feat): add fix
Browse files Browse the repository at this point in the history
  • Loading branch information
ndu committed Dec 18, 2024
1 parent 08aa962 commit b7e0fa5
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 53 deletions.
8 changes: 8 additions & 0 deletions server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ DB_PORT=5432

SITE_URL='http://localhost:8000'

# OpenAI
OPENAI_API_KEY=xxxxxx

# Cloudinary
CLOUDINARY_CLOUD_NAME=xxxx
CLOUDINARY_API_KEY=123456789
CLOUDINARY_API_SECRET=xxxxxxxxxxxxx

# Django smtp
EMAIL_HOST = 'smtp.gmail.com' # Example using Gmail
EMAIL_HOST_USER = 'enter your email'
Expand Down
20 changes: 0 additions & 20 deletions server/apps/research/migrations/0017_alter_article_thumb.py

This file was deleted.

59 changes: 59 additions & 0 deletions server/apps/research/migrations/0018_alter_article_thumb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import apps.research.models.article
import cloudinary.models
import cloudinary.uploader
from django.db import migrations
from django.core.files.storage import default_storage
import os


def transfer_to_cloudinary(apps, schema_editor):
Article = apps.get_model("research", "Article")

for instance in Article.objects.all():
if instance.thumb:
try:
if hasattr(instance.thumb, "public_id"):
continue

if hasattr(instance.thumb, "url"):
file_path = instance.thumb.path

if default_storage.exists(file_path):
with open(file_path, "rb") as file:
upload_result = cloudinary.uploader.upload(
file, folder="coverImage", resource_type="image"
)

instance.thumb = upload_result["public_id"]
instance.save()

# delete the local file
# default_storage.delete(file_path)

except Exception as e:
print(f"Info for Article {instance.id}: {str(e)}")


def reverse_transfer(apps, schema_editor):
pass


class Migration(migrations.Migration):

dependencies = [
("research", "0017_article_gpt_summary_alter_article_summary"),
]

operations = [
migrations.AlterField(
model_name="article",
name="thumb",
field=cloudinary.models.CloudinaryField(
blank=True,
default=apps.research.models.article.get_default_thumb,
max_length=255,
verbose_name="image",
),
),
migrations.RunPython(transfer_to_cloudinary, reverse_transfer),
]
17 changes: 13 additions & 4 deletions server/apps/research/models/article.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from cloudinary.models import CloudinaryField

def get_default_thumb():
return f"{settings.MEDIA_URL}images/2077-Collective.png"
return "v1734517759/v4_article_cover_slashing_hhf6tz"

class Article(BaseModel):
"""Model for articles."""
Expand Down Expand Up @@ -121,7 +121,7 @@ def save(self, *args, **kwargs):
self.slug = self.generate_unique_slug()

"""Override the save method to track slug changes."""
if self.pk: # If this is an existing article
if self.pk:
try:
old_instance = Article.objects.get(pk=self.pk)
# Generate new slug first
Expand All @@ -144,8 +144,17 @@ def save(self, *args, **kwargs):

if self.scheduled_publish_time and self.status == 'draft' and timezone.now() >= self.scheduled_publish_time:
self.status = 'ready'

super().save(*args, **kwargs)

if self.thumb and not hasattr(self.thumb, 'public_id'):
super().save(*args, **kwargs)
elif self.thumb and hasattr(self.thumb, 'public_id'):
try:
if not self.thumb.public_id:
raise ValidationError("Failed to upload image to Cloudinary")
except Exception as e:
raise ValidationError(f"Image upload failed: {str(e)}")
else:
super().save(*args, **kwargs)

def generate_unique_slug(self):
"""Generate a unique slug for the article."""
Expand Down
10 changes: 7 additions & 3 deletions server/apps/research/serializers/article_serializer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import logging
from rest_framework import serializers
from ..models import Article, Author, Category
from .author_serializer import AuthorSerializer
from .category_serializer import CategorySerializer
from django.conf import settings


class RelatedArticleSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -69,7 +71,7 @@ class ArticleSerializer(serializers.ModelSerializer):

def get_thumb(self, obj):
if obj.thumb:
return f"https://res.cloudinary.com/dc2iz5j1c/{obj.thumb}"
return f"{settings.CLOUDINARY_DOMAIN}/{obj.thumb}"
return None

def get_related_articles(self, obj):
Expand Down Expand Up @@ -168,7 +170,8 @@ def create(self, validated_data: dict) -> Article:

return article
except Exception as e:
raise serializers.ValidationError(f"Error creating article: {str(e)}")
logging.error(f"Error creating article: {str(e)}")
raise serializers.ValidationError("An error occurred while creating the article.")

def update(self, instance: Article, validated_data: dict) -> Article:
"""Update an existing article instance."""
Expand All @@ -188,4 +191,5 @@ def update(self, instance: Article, validated_data: dict) -> Article:

return instance
except Exception as e:
raise serializers.ValidationError(f"Error updating article: {str(e)}")
logging.error(f"Error updating article: {str(e)}")
raise serializers.ValidationError("An error occurred while updating the article.")
1 change: 0 additions & 1 deletion server/apps/research/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet
from .redirects_urls import urlpatterns as redirects_urlpatterns
from django.urls import path
from .views import tinymce_upload_image

router = DefaultRouter()
Expand Down
11 changes: 7 additions & 4 deletions server/apps/research/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,17 @@ def tinymce_upload_image(request):
if request.method == "POST" and request.FILES:
try:
file = request.FILES['file']
# Upload to a specific folder in Cloudinary
upload_data = cloudinary.uploader.upload(
file,
folder='article_content' # This will create an article_content folder in Cloudinary
folder='article_content'
)
return JsonResponse({
'location': upload_data['secure_url']
})
except Exception as e:
return JsonResponse({'error': str(e)})
return JsonResponse({'error': 'Invalid request'})
logger.error(f"Error uploading image: {str(e)}")
return JsonResponse(
{'error': 'An error occurred while uploading the image'},
status=500
)
return JsonResponse({'error': 'Invalid request'}, status=400)
22 changes: 1 addition & 21 deletions server/core/config/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
# cloudinary
import cloudinary
import cloudinary.uploader
import cloudinary.api

# third party imports
from .celery_config import (CELERY_BROKER_URL, CELERY_RESULT_BACKEND, CELERY_ACCEPT_CONTENT, CELERY_TASK_SERIALIZER, CELERY_RESULT_SERIALIZER, CELERY_TIMEZONE)
from .mail import (SITE_URL, EMAIL_HOST, EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, DEFAULT_FROM_EMAIL, EMAIL_PORT, EMAIL_USE_TLS, EMAIL_USE_SSL, EMAIL_BACKEND)

load_dotenv()
from decouple import config
from .cloudinary import CLOUDINARY_DOMAIN, CLOUDINARY_STORAGE

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent.parent
Expand Down Expand Up @@ -180,13 +180,6 @@
os.path.join(BASE_DIR, 'static'),
]

# cloudinary storage
CLOUDINARY_STORAGE = {
'CLOUD_NAME': config('CLOUDINARY_CLOUD_NAME'),
'API_KEY': config('CLOUDINARY_API_KEY'),
'API_SECRET': config('CLOUDINARY_API_SECRET')
}

STORAGES = {
"default": {
"BACKEND": "cloudinary_storage.storage.MediaCloudinaryStorage",
Expand All @@ -196,19 +189,6 @@
},
}

TINYMCE_DEFAULT_CONFIG = {
'images_upload_url': '/tinymce/upload/',
'images_upload_base_path': '',
'images_upload_credentials': True,
'file_picker_types': 'image',
'automatic_uploads': True,
'images_file_types': 'jpg,svg,webp,png',
'content_css': 'default',
'plugins': 'image autolink lists media table',
'toolbar1': 'formatselect | bold italic underline | alignleft aligncenter alignright alignjustify | bullist numlist | outdent indent | table | link image media | removeformat',
'width': '100%',
'height': 400
}
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field

Expand Down
9 changes: 9 additions & 0 deletions server/core/config/cloudinary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from decouple import config

CLOUDINARY_DOMAIN = "https://res.cloudinary.com/dc2iz5j1c"

CLOUDINARY_STORAGE = {
'CLOUD_NAME': config('CLOUDINARY_CLOUD_NAME'),
'API_KEY': config('CLOUDINARY_API_KEY'),
'API_SECRET': config('CLOUDINARY_API_SECRET')
}

0 comments on commit b7e0fa5

Please sign in to comment.