4 Commits

  1. 65
      Jenkinsfile
  2. 6
      apps/hadis/serializers/reference_v2.py
  3. 97
      apps/hadis/serializers/serializers_admin.py
  4. 13
      apps/hadis/views_admin.py

65
Jenkinsfile

@ -1,35 +1,72 @@
pipeline { pipeline {
environment { environment {
develop_server_ip = ''
develop_server_name = ''
production_server_ip = "88.99.212.243"
production_server_name = "newhorizon_germany_001_server"
project_path = "/projects/imam-javad/imam-javad_backend"
version = "master"
production_server_ip = "46.173.16.83"
production_server_name = "4e1f741c-3f7a-407d-904e-6778ec6e6219"
project_path = "/projects/dovodi/dovodi-backend"
gitBranch = "origin/master" gitBranch = "origin/master"
} }
agent any agent any
stages { stages {
stage('deploy'){
steps{
script{
if(gitBranch=="origin/master"){
stage('🚀 Deploy Dovodi Backend (Background Build)') {
when { expression { gitBranch == "origin/master" } }
steps {
script {
try {
withCredentials([usernamePassword(credentialsId: production_server_name, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) { withCredentials([usernamePassword(credentialsId: production_server_name, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
sh 'sshpass -p $PASSWORD ssh -p 1782 $USERNAME@$production_server_ip -o StrictHostKeyChecking=no "cd $project_path && ./runner.sh"'
sh """
sshpass -p \$PASSWORD ssh -o StrictHostKeyChecking=no -o ServerAliveInterval=30 -o ServerAliveCountMax=120 \$USERNAME@${production_server_ip} << 'EOF'
cd ${project_path}
echo "⏳ Starting runner.sh in background to prevent timeout..."
rm -f /tmp/dovodi_backend_deploy_status.txt
rm -f /tmp/dovodi_backend_runner_deploy.log
nohup bash -c './runner.sh && echo "SUCCESS" > /tmp/dovodi_backend_deploy_status.txt || echo "FAIL" > /tmp/dovodi_backend_deploy_status.txt' > /tmp/dovodi_backend_runner_deploy.log 2>&1 &
echo "⏳ Process is running. Waiting for build and deploy to finish..."
while true; do
if [ -f /tmp/dovodi_backend_deploy_status.txt ]; then
STATUS=\$(cat /tmp/dovodi_backend_deploy_status.txt)
if [ "\$STATUS" = "SUCCESS" ]; then
echo "✅ Deployment completed successfully!"
break
else
echo "❌ ERROR: Deployment failed! Last 20 lines of log:"
tail -n 20 /tmp/dovodi_backend_runner_deploy.log
exit 1
fi
fi
sleep 10
done
EOF
"""
}
def lastCommit = sh(script: 'git log -1 --pretty=format:"%h - %s (%an)"', returnStdout: true).trim() def lastCommit = sh(script: 'git log -1 --pretty=format:"%h - %s (%an)"', returnStdout: true).trim()
sh """ sh """
curl -F chat_id=1457670318 \ curl -F chat_id=1457670318 \
-F message_thread_id=6 \ -F message_thread_id=6 \
-F document=@/var/jenkins_home/jobs/${env.JOB_NAME}/builds/${env.BUILD_NUMBER}/log \ -F document=@/var/jenkins_home/jobs/${env.JOB_NAME}/builds/${env.BUILD_NUMBER}/log \
-F caption='Project name: #${env.JOB_NAME} \nBuild status is ${currentBuild.currentResult} \nBuild url: ${BUILD_URL} \nLast Commit: ${lastCommit}' \
-F caption='🚀 Dovodi Backend Deployment: SUCCESS \n📦 Project: #${env.JOB_NAME} \n🔗 Commit: ${lastCommit} \n🌐 URL: ${env.BUILD_URL}' \
https://api.telegram.org/bot7207581748:AAFeymryw7S44D86LYfWqYK-tSNeV3TOwBs/sendDocument https://api.telegram.org/bot7207581748:AAFeymryw7S44D86LYfWqYK-tSNeV3TOwBs/sendDocument
""" """
}
} catch (Exception e) {
currentBuild.result = 'FAILURE'
def lastCommit = sh(script: 'git log -1 --pretty=format:"%h - %s (%an)"', returnStdout: true).trim()
sh """
curl -F chat_id=1457670318 \
-F message_thread_id=6 \
-F document=@/var/jenkins_home/jobs/${env.JOB_NAME}/builds/${env.BUILD_NUMBER}/log \
-F caption='❌ Dovodi Backend Deployment: FAILED \n📦 Project: #${env.JOB_NAME} \n🔗 Commit: ${lastCommit} \n🌐 URL: ${env.BUILD_URL}' \
https://api.telegram.org/bot7207581748:AAFeymryw7S44D86LYfWqYK-tSNeV3TOwBs/sendDocument
"""
throw e
} }
} }
} }
} }
} }
} }

6
apps/hadis/serializers/reference_v2.py

@ -20,15 +20,17 @@ class BookAuthorV2Serializer(serializers.ModelSerializer):
class BookResearcherV2Serializer(serializers.ModelSerializer): class BookResearcherV2Serializer(serializers.ModelSerializer):
name = LocalizedField() name = LocalizedField()
author = BookAuthorV2Serializer(read_only=True)
class Meta: class Meta:
model = BookResearcher model = BookResearcher
fields = ['id', 'name', 'slug']
fields = ['id', 'name', 'slug', 'author']
class BookEditorV2Serializer(serializers.ModelSerializer): class BookEditorV2Serializer(serializers.ModelSerializer):
name = LocalizedField() name = LocalizedField()
author = BookAuthorV2Serializer(read_only=True)
class Meta: class Meta:
model = BookEditor model = BookEditor
fields = ['id', 'name', 'slug']
fields = ['id', 'name', 'slug', 'author']
class BookReferenceV2ListSerializer(serializers.ModelSerializer): class BookReferenceV2ListSerializer(serializers.ModelSerializer):
title = LocalizedField() title = LocalizedField()

97
apps/hadis/serializers/serializers_admin.py

@ -3,7 +3,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile
from utils.image_compression import maybe_compress_uploaded_file from utils.image_compression import maybe_compress_uploaded_file
from ..models import ( from ..models import (
Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, HadisInCollection, HadisTransmitter,
Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, HadisInCollection, HadisTransmitter, HadisReference,
BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, BookEdition, BookVolume, BookResearcher, BookEditor, BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, BookEdition, BookVolume, BookResearcher, BookEditor,
Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, TransmitterOpinion, TransmitterOriginalText, Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, TransmitterOpinion, TransmitterOriginalText,
CorrectionReference, CorrectionReferenceImage, InterpretationReference, InterpretationReferenceImage, CorrectionReference, CorrectionReferenceImage, InterpretationReference, InterpretationReferenceImage,
@ -188,6 +188,12 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer):
required=False, required=False,
) )
collections_detail = serializers.SerializerMethodField() collections_detail = serializers.SerializerMethodField()
book_reference_id = serializers.PrimaryKeyRelatedField(
queryset=BookReference.objects.all(),
required=False,
write_only=True,
allow_null=True,
)
bookmark_count = serializers.SerializerMethodField() bookmark_count = serializers.SerializerMethodField()
@ -214,6 +220,7 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer):
"tags_detail", "tags_detail",
"collections", "collections",
"collections_detail", "collections_detail",
"book_reference_id",
"share_link", "share_link",
"explanation", "explanation",
"bookmark_count", "bookmark_count",
@ -223,6 +230,13 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer):
] ]
read_only_fields = ["id", "slug", "share_link", "bookmark_count", "created_at", "updated_at"] read_only_fields = ["id", "slug", "share_link", "bookmark_count", "created_at", "updated_at"]
def to_representation(self, instance):
ret = super().to_representation(instance)
refs = list(instance.references.all())
ref = refs[0] if refs else None
ret["book_reference"] = ref.book_reference_id if ref else None
return ret
def get_bookmark_count(self, obj): def get_bookmark_count(self, obj):
from apps.bookmark.models.bookmark import Bookmark from apps.bookmark.models.bookmark import Bookmark
return Bookmark.objects.filter(service=Bookmark.ServiceChoices.HADITH, content_id=obj.id, status=True).count() return Bookmark.objects.filter(service=Bookmark.ServiceChoices.HADITH, content_id=obj.id, status=True).count()
@ -232,6 +246,7 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer):
return AdminHadisCollectionSerializer(collections, many=True).data return AdminHadisCollectionSerializer(collections, many=True).data
def create(self, validated_data): def create(self, validated_data):
book_reference = validated_data.pop("book_reference_id", None)
tags = validated_data.pop("tags", []) tags = validated_data.pop("tags", [])
collections = validated_data.pop("collection_items", []) collections = validated_data.pop("collection_items", [])
hadis = super().create(validated_data) hadis = super().create(validated_data)
@ -240,9 +255,13 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer):
if collections: if collections:
for idx, col in enumerate(collections): for idx, col in enumerate(collections):
HadisInCollection.objects.create(hadis=hadis, collection=col, order=idx) HadisInCollection.objects.create(hadis=hadis, collection=col, order=idx)
if book_reference:
HadisReference.objects.create(hadis=hadis, book_reference=book_reference)
return hadis return hadis
def update(self, instance, validated_data): def update(self, instance, validated_data):
book_reference_provided = "book_reference_id" in validated_data
book_reference = validated_data.pop("book_reference_id", None)
tags = validated_data.pop("tags", None) tags = validated_data.pop("tags", None)
collections = validated_data.pop("collection_items", None) collections = validated_data.pop("collection_items", None)
hadis = super().update(instance, validated_data) hadis = super().update(instance, validated_data)
@ -252,6 +271,13 @@ class AdminHadisDetailSerializer(serializers.ModelSerializer):
instance.collection_items.all().delete() instance.collection_items.all().delete()
for idx, col in enumerate(collections): for idx, col in enumerate(collections):
HadisInCollection.objects.create(hadis=hadis, collection=col, order=idx) HadisInCollection.objects.create(hadis=hadis, collection=col, order=idx)
if book_reference_provided:
ref = instance.references.first()
if ref:
ref.book_reference = book_reference
ref.save()
elif book_reference:
HadisReference.objects.create(hadis=hadis, book_reference=book_reference)
return hadis return hadis
@ -1275,22 +1301,90 @@ class AdminBookEditionSerializer(serializers.ModelSerializer):
"updated_at", "updated_at",
] ]
def to_representation(self, instance):
ret = super().to_representation(instance)
res = instance.researchers.first()
ret["researcher_author_id"] = res.author_id if res and res.author_id else None
ed_obj = instance.editors.first()
ret["editor_author_id"] = ed_obj.author_id if ed_obj and ed_obj.author_id else None
return ret
def get_number_of_volumes(self, obj): def get_number_of_volumes(self, obj):
return obj.volumes.count() return obj.volumes.count()
def _handle_author_links(self, edition, validated_data):
request = self.context.get("request")
data = request.data if request else {}
if "researcher_author_id" in data or "researcher_author_id" in validated_data:
res_val = data.get("researcher_author_id") if "researcher_author_id" in data else validated_data.get("researcher_author_id")
if not res_val or res_val in ["none", "", "null", None]:
edition.researchers.all().delete()
else:
try:
author_id = int(res_val)
author_obj = BookAuthor.objects.filter(id=author_id).first()
if author_obj:
res = edition.researchers.first()
if res:
res.author = author_obj
res.name = author_obj.name
res.slug = author_obj.slug
res.save()
else:
BookResearcher.objects.create(
book_edition=edition,
author=author_obj,
name=author_obj.name,
slug=author_obj.slug
)
except (ValueError, TypeError):
pass
if "editor_author_id" in data or "editor_author_id" in validated_data:
ed_val = data.get("editor_author_id") if "editor_author_id" in data else validated_data.get("editor_author_id")
if not ed_val or ed_val in ["none", "", "null", None]:
edition.editors.all().delete()
else:
try:
author_id = int(ed_val)
author_obj = BookAuthor.objects.filter(id=author_id).first()
if author_obj:
ed_obj = edition.editors.first()
if ed_obj:
ed_obj.author = author_obj
ed_obj.name = author_obj.name
ed_obj.slug = author_obj.slug
ed_obj.save()
else:
BookEditor.objects.create(
book_edition=edition,
author=author_obj,
name=author_obj.name,
slug=author_obj.slug
)
except (ValueError, TypeError):
pass
def create(self, validated_data): def create(self, validated_data):
researchers_data = validated_data.pop("researchers", []) researchers_data = validated_data.pop("researchers", [])
editors_data = validated_data.pop("editors", []) editors_data = validated_data.pop("editors", [])
validated_data.pop("researcher_author_id", None)
validated_data.pop("editor_author_id", None)
edition = super().create(validated_data) edition = super().create(validated_data)
for res_data in researchers_data: for res_data in researchers_data:
BookResearcher.objects.create(book_edition=edition, **res_data) BookResearcher.objects.create(book_edition=edition, **res_data)
for ed_data in editors_data: for ed_data in editors_data:
BookEditor.objects.create(book_edition=edition, **ed_data) BookEditor.objects.create(book_edition=edition, **ed_data)
self._handle_author_links(edition, validated_data)
return edition return edition
def update(self, instance, validated_data): def update(self, instance, validated_data):
researchers_data = validated_data.pop("researchers", None) researchers_data = validated_data.pop("researchers", None)
editors_data = validated_data.pop("editors", None) editors_data = validated_data.pop("editors", None)
validated_data.pop("researcher_author_id", None)
validated_data.pop("editor_author_id", None)
edition = super().update(instance, validated_data) edition = super().update(instance, validated_data)
if researchers_data is not None: if researchers_data is not None:
# Delete researchers no longer in the request payload # Delete researchers no longer in the request payload
@ -1326,4 +1420,5 @@ class AdminBookEditionSerializer(serializers.ModelSerializer):
# Create new editor # Create new editor
BookEditor.objects.create(book_edition=instance, **ed_data) BookEditor.objects.create(book_edition=instance, **ed_data)
self._handle_author_links(edition, validated_data)
return edition return edition

13
apps/hadis/views_admin.py

@ -24,6 +24,7 @@ from .serializers.serializers_admin import (
AdminHadisInterpretationSerializer, AdminHadisInterpretationSerializer,
AdminHadisCollectionSerializer, AdminHadisCollectionSerializer,
AdminHadisListSerializer, AdminHadisListSerializer,
AdminHadisBriefSerializer,
AdminHadisStatusSerializer, AdminHadisStatusSerializer,
AdminHadisTagSerializer, AdminHadisTagSerializer,
AdminBookSubjectAreaSerializer, AdminBookSubjectAreaSerializer,
@ -277,11 +278,13 @@ class AdminHadisViewSet(ModelViewSet):
def get_serializer_class(self): def get_serializer_class(self):
if self.action == "list": if self.action == "list":
if self.request.query_params.get("brief") == "true":
return AdminHadisBriefSerializer
return AdminHadisListSerializer return AdminHadisListSerializer
return AdminHadisDetailSerializer return AdminHadisDetailSerializer
def get_queryset(self): def get_queryset(self):
queryset = Hadis.objects.all().select_related("category", "hadis_status")
queryset = Hadis.objects.all().select_related("category__sect", "hadis_status")
search_query = self.request.query_params.get("search") search_query = self.request.query_params.get("search")
if search_query: if search_query:
@ -345,6 +348,10 @@ class AdminHadisCorrectionViewSet(ModelViewSet):
def perform_create(self, serializer): def perform_create(self, serializer):
priority = self.request.data.get("priority", 1) priority = self.request.data.get("priority", 1)
try:
priority = int(priority)
except (ValueError, TypeError):
priority = 1
hadis_id = self.request.data.get("hadis") hadis_id = self.request.data.get("hadis")
if hadis_id: if hadis_id:
from django.db.models import F from django.db.models import F
@ -385,6 +392,10 @@ class AdminHadisInterpretationViewSet(ModelViewSet):
def perform_create(self, serializer): def perform_create(self, serializer):
priority = self.request.data.get("priority", 1) priority = self.request.data.get("priority", 1)
try:
priority = int(priority)
except (ValueError, TypeError):
priority = 1
category_id = self.request.data.get("category") category_id = self.request.data.get("category")
if category_id: if category_id:
from django.db.models import F from django.db.models import F

Loading…
Cancel
Save