Merge branch 'staging' of https://github.com/WDI-Ideas/goodtimes into feature/module_9_coupons
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import random
|
||||
import requests
|
||||
import googlemaps
|
||||
import tweepy
|
||||
from django.conf import settings
|
||||
from django.core.files.uploadedfile import UploadedFile
|
||||
from django.core.mail import EmailMessage
|
||||
@@ -745,7 +747,7 @@ class GoogleMapsservice:
|
||||
def get_place_id_from_coordinates(self, latitude, longitude):
|
||||
"""
|
||||
Get the place ID of the given coordinates.
|
||||
|
||||
|
||||
:param latitude: Latitude of the location
|
||||
:param longitude: Longitude of the location
|
||||
:return: Place ID
|
||||
@@ -804,16 +806,287 @@ class GoogleMapsservice:
|
||||
if element["status"] == "OK" and element["distance"]["value"] <= radius_km * 1000 # Convert km to meters
|
||||
}
|
||||
|
||||
print(f"distance is {distances} and distances key is {distances.keys()}")
|
||||
|
||||
# Filter the queryset to include only events within the specified radius
|
||||
queryset = queryset.filter(id__in=distances.keys())
|
||||
|
||||
# # Sort the event IDs by their distances in ascending order
|
||||
# event_ids_by_distance = sorted(distances, key=distances.get)
|
||||
print(f"query set after distance filter {queryset}")
|
||||
|
||||
# # Create a Case/When expression to preserve the order of events by distance
|
||||
# preserved_order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(event_ids_by_distance)])
|
||||
# print(f"preserved_order is {preserved_order}")
|
||||
# # Order the queryset based on the preserved order
|
||||
# queryset = queryset.order_by(preserved_order)
|
||||
# Sort the event IDs by their distances in ascending order
|
||||
event_ids_by_distance = sorted(distances, key=distances.get)
|
||||
print(f"sort event by it distance {event_ids_by_distance}")
|
||||
|
||||
# Create a Case/When expression to preserve the order of events by distance
|
||||
preserved_order = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(event_ids_by_distance)])
|
||||
print(f"preserved_order is {preserved_order}")
|
||||
# Order the queryset based on the preserved order
|
||||
queryset = queryset.order_by(preserved_order)
|
||||
for data in queryset:
|
||||
print(f"queryset after preserverd order {data.id}")
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
class TwitterAPI:
|
||||
def __init__(self):
|
||||
self.api_key = settings.TWITTER_API_KEY
|
||||
self.api_secret_key = settings.TWITTER_API_SECRET_KEY
|
||||
self.access_token = settings.TWITTER_ACCESS_TOKEN
|
||||
self.access_token_secret = settings.TWITTER_ACCESS_TOKEN_SECRET
|
||||
self.client, self.api = self._setup_api()
|
||||
|
||||
def _setup_api(self):
|
||||
client = tweepy.Client(
|
||||
consumer_key=self.api_key,
|
||||
consumer_secret=self.api_secret_key,
|
||||
access_token=self.access_token,
|
||||
access_token_secret=self.access_token_secret,
|
||||
)
|
||||
auth = tweepy.OAuth1UserHandler(
|
||||
self.api_key,
|
||||
self.api_secret_key,
|
||||
self.access_token,
|
||||
self.access_token_secret,
|
||||
)
|
||||
api = tweepy.API(auth, wait_on_rate_limit=True)
|
||||
return client, api
|
||||
|
||||
def post_text_tweet(self, caption):
|
||||
tweet = self.client.create_tweet(text=caption)
|
||||
return tweet
|
||||
|
||||
def post_image_with_caption(self, image_url, caption):
|
||||
media = self.api.media_upload(image_url)
|
||||
tweet = self.client.create_tweet(text=caption, media_ids=[media.media_id])
|
||||
return tweet
|
||||
|
||||
|
||||
class TwitterPoster:
|
||||
def __init__(self, twitter_api):
|
||||
self.twitter_api = twitter_api
|
||||
|
||||
def post_text_tweet(self, caption):
|
||||
try:
|
||||
tweet = self.twitter_api.post_text_tweet(caption)
|
||||
return {'success': True, 'message': 'Tweet posted successfully!'}
|
||||
except tweepy.TweepyException as e:
|
||||
return {'success': False, 'message': f'Error posting tweet: {e}'}
|
||||
|
||||
def post_image_with_caption(self, image_url, caption):
|
||||
try:
|
||||
tweet = self.twitter_api.post_image_with_caption(image_url, caption)
|
||||
return {'success': True, 'message': 'Tweet posted successfully!'}
|
||||
except tweepy.TweepyException as e:
|
||||
return {'success': False, 'message': f'Error posting tweet: {e}'}
|
||||
|
||||
class FacebookAPI:
|
||||
def __init__(self):
|
||||
self.app_id = settings.FACEBOOK_APP_ID
|
||||
self.app_secret = settings.FACEBOOK_APP_SECRET
|
||||
self.page_id = settings.FACEBOOK_PAGE_ID
|
||||
self.page_access_token = None
|
||||
|
||||
def _get_short_lived_user_access_token(self):
|
||||
try:
|
||||
url = f"https://graph.facebook.com/oauth/access_token?grant_type=client_credentials&client_id={self.app_id}&client_secret={self.app_secret}"
|
||||
response = requests.get(url)
|
||||
# response.raise_for_status()
|
||||
print(f"short lived token {response.json()}")
|
||||
return response.json()['access_token']
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error getting short-lived user access token: {e}")
|
||||
return None
|
||||
|
||||
def _get_long_lived_user_access_token(self, short_lived_token):
|
||||
try:
|
||||
url = f"https://graph.facebook.com/v20.0/oauth/access_token?grant_type=fb_exchange_token&client_id={self.app_id}&client_secret={self.app_secret}&fb_exchange_token={short_lived_token}"
|
||||
response = requests.get(url)
|
||||
# response.raise_for_status()
|
||||
print(f"long lived access token : {response.json()}")
|
||||
return response.json()['access_token']
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error getting long-lived user access token: {e}")
|
||||
return None
|
||||
|
||||
def _get_page_access_token(self, long_lived_token):
|
||||
url = f"https://graph.facebook.com/{self.page_id}?fields=access_token&access_token={long_lived_token}"
|
||||
response = requests.get(url)
|
||||
# response.raise_for_status()
|
||||
print(f"page access token is {response.json()}")
|
||||
# self.page_access_token = response.json()["access_token"]
|
||||
|
||||
def authenticate(self):
|
||||
# short_lived_token = self._get_short_lived_user_access_token()
|
||||
# if not short_lived_token:
|
||||
# return False
|
||||
# long_lived_token = self._get_long_lived_user_access_token(short_lived_token)
|
||||
# if not long_lived_token:
|
||||
# return False
|
||||
# self._get_page_access_token(short_lived_token)
|
||||
self.page_access_token = settings.FACEBOOK_ACCESS_TOKEN
|
||||
return True
|
||||
|
||||
def post_photo(self, image_url, caption):
|
||||
if not self.page_access_token:
|
||||
print("Page access token not obtained. Call authenticate() first.")
|
||||
return False
|
||||
try:
|
||||
url = f"https://graph.facebook.com/v20.0/{self.page_id}/photos"
|
||||
params = {
|
||||
"message": caption,
|
||||
"url": image_url,
|
||||
"access_token": self.page_access_token,
|
||||
}
|
||||
response = requests.post(url, params=params)
|
||||
# response.raise_for_status()
|
||||
result = response.json()
|
||||
if "id" not in result:
|
||||
print(f"Error posting photo: {result}")
|
||||
return False
|
||||
print(f"Data posted successfully. Post Id: {result['id']}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error posting photo: {e}")
|
||||
return False
|
||||
|
||||
class FacebookPoster:
|
||||
def __init__(self, facebook_api):
|
||||
self.facebook_api = facebook_api
|
||||
|
||||
def post_photo(self, image_url, caption):
|
||||
if not self.facebook_api.authenticate():
|
||||
print("Authentication failed. Please try again.")
|
||||
return {'success': False, 'message': 'Error posting photo. Authenticate failed'}
|
||||
result = self.facebook_api.post_photo(image_url, caption)
|
||||
if not result:
|
||||
return {'success': False, 'message': 'Error posting photo in Facebook'}
|
||||
return {'success': True, 'message': 'Photo posted successfully'}
|
||||
|
||||
|
||||
|
||||
# import requests
|
||||
|
||||
# app_id = "YOUR_APP_ID"
|
||||
# app_secret = "YOUR_APP_SECRET"
|
||||
# page_id = "YOUR_PAGE_ID" # You need to specify the page ID
|
||||
# # Step 1: Get an App Access Token
|
||||
# response = requests.get(f"https://graph.facebook.com/oauth/access_token?client_id={app_id}&client_secret={app_secret}&grant_type=client_credentials")
|
||||
# app_access_token = response.json()["access_token"]
|
||||
|
||||
# # Step 2: Get a Page Access Token
|
||||
# response = requests.get(f"https://graph.facebook.com/{page_id}?fields=access_token&access_token={app_access_token}")
|
||||
# page_access_token = response.json()["access_token"]
|
||||
|
||||
# # Use the Page Access Token to query the Page node
|
||||
# response = requests.get(f"https://graph.facebook.com/{page_id}?access_token={page_access_token}")
|
||||
# page_data = response.json()
|
||||
# print(page_data)
|
||||
class InstagramAPI:
|
||||
def __init__(self):
|
||||
self.app_id = settings.FACEBOOK_APP_ID
|
||||
self.app_secret = settings.FACEBOOK_APP_SECRET
|
||||
self.page_id = settings.INSTAGRAM_PAGE_ID
|
||||
self.page_access_token = None
|
||||
|
||||
def _get_short_lived_user_access_token(self):
|
||||
try:
|
||||
url = f"https://graph.facebook.com/oauth/access_token"
|
||||
params = {
|
||||
"grant_type": "client_credentials",
|
||||
"client_id": self.app_id,
|
||||
"client_secret": self.app_secret
|
||||
}
|
||||
response = requests.get(url, params=params)
|
||||
response.raise_for_status()
|
||||
print(f"Short-lived token: {response.json()}")
|
||||
return response.json()['access_token']
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error getting short-lived user access token: {e}")
|
||||
return None
|
||||
|
||||
def _get_long_lived_user_access_token(self, short_lived_token):
|
||||
try:
|
||||
url = f"https://graph.facebook.com/v20.0/oauth/access_token"
|
||||
params = {
|
||||
"grant_type": "fb_exchange_token",
|
||||
"client_id": self.app_id,
|
||||
"client_secret": self.app_secret,
|
||||
"fb_exchange_token": short_lived_token
|
||||
}
|
||||
response = requests.get(url, params=params)
|
||||
response.raise_for_status()
|
||||
print(f"Long-lived access token: {response.json()}")
|
||||
return response.json()['access_token']
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error getting long-lived user access token: {e}")
|
||||
return None
|
||||
|
||||
def _get_page_access_token(self, long_lived_token):
|
||||
try:
|
||||
url = f"https://graph.facebook.com/{self.page_id}"
|
||||
params = {
|
||||
"fields": "access_token",
|
||||
"access_token": long_lived_token
|
||||
}
|
||||
response = requests.get(url, params=params)
|
||||
response.raise_for_status()
|
||||
print(f"Page access token: {response.json()}")
|
||||
return response.json()["access_token"]
|
||||
except Exception as e:
|
||||
print(f"Error getting page access token: {e}")
|
||||
return None
|
||||
|
||||
def authenticate(self):
|
||||
# short_lived_token = self._get_short_lived_user_access_token()
|
||||
# if not short_lived_token:
|
||||
# return False
|
||||
# long_lived_token = self._get_long_lived_user_access_token(short_lived_token)
|
||||
# if not long_lived_token:
|
||||
# return False
|
||||
# self.page_access_token = self._get_page_access_token(long_lived_token)
|
||||
self.page_access_token = settings.FACEBOOK_ACCESS_TOKEN
|
||||
return True
|
||||
|
||||
def post_image_with_caption(self, image_path, caption):
|
||||
if not self.page_access_token:
|
||||
print("Page access token not obtained. Call Authenticate() first.")
|
||||
return False
|
||||
try:
|
||||
url = f"https://graph.facebook.com/v20.0/{self.page_id}/media"
|
||||
params = {
|
||||
"caption": caption,
|
||||
"image_url": image_path,
|
||||
"access_token": self.page_access_token
|
||||
}
|
||||
response = requests.post(url, data=params)
|
||||
# response.raise_for_status()
|
||||
result = response.json()
|
||||
print(f"Post image with caption result: {result}")
|
||||
|
||||
url = f"https://graph.facebook.com/v20.0/{self.page_id}/media_publish"
|
||||
params = {
|
||||
"creation_id": result["id"],
|
||||
"access_token": self.page_access_token
|
||||
}
|
||||
response = requests.post(url, params=params)
|
||||
# response.raise_for_status()
|
||||
result = response.json()
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error posting photo on instagram: {e}")
|
||||
return False
|
||||
|
||||
|
||||
class InstagramPoster:
|
||||
def __init__(self, instagram_api):
|
||||
self.instagram_api = instagram_api
|
||||
|
||||
def post_image_with_caption(self, image_path, caption):
|
||||
if not self.instagram_api.authenticate():
|
||||
print("Instagram API authentication failed.")
|
||||
return {'success': False, 'message': 'Error posting photo. Authenticate failed'}
|
||||
result = self.instagram_api.post_image_with_caption(image_path, caption)
|
||||
if not result:
|
||||
return {'success': False, 'message': 'Error posting photo in Instagram.'}
|
||||
return {'success': True, 'message': 'Photo posted successfully'}
|
||||
@@ -343,3 +343,18 @@ PLACES_MAPS_API_KEY = env.str("GOOGLE_MAPS_API_KEY")
|
||||
PLACES_MAP_WIDGET_HEIGHT = 480
|
||||
PLACES_MAP_OPTIONS = '{"center": { "lat": 38.971584, "lng": -95.235072 }, "zoom": 10}'
|
||||
PLACES_MARKER_OPTIONS = '{"draggable": true}'
|
||||
|
||||
# twitter keys
|
||||
TWITTER_API_KEY = env.str("TWITTER_API_KEY")
|
||||
TWITTER_API_SECRET_KEY = env.str("TWITTER_API_SECRET_KEY")
|
||||
TWITTER_ACCESS_TOKEN = env.str("TWITTER_ACCESS_TOKEN")
|
||||
TWITTER_ACCESS_TOKEN_SECRET = env.str("TWITTER_ACCESS_TOKEN_SECRET")
|
||||
|
||||
# facebook keys
|
||||
FACEBOOK_APP_ID = env.str("FACEBOOK_APP_ID")
|
||||
FACEBOOK_APP_SECRET = env.str("FACEBOOK_APP_SECRET")
|
||||
FACEBOOK_PAGE_ID = env.str("FACEBOOK_PAGE_ID")
|
||||
FACEBOOK_ACCESS_TOKEN = env.str("FACEBOOK_ACCESS_TOKEN")
|
||||
|
||||
# Instagram Key
|
||||
INSTAGRAM_PAGE_ID = env.str('INSTAGRAM_PAGE_ID')
|
||||
|
||||
@@ -59,8 +59,6 @@ class JsonResponseUtil:
|
||||
response_data["errors"] = errors
|
||||
return JsonResponse(response_data, status=status)
|
||||
|
||||
|
||||
|
||||
class RandomGenerator:
|
||||
@staticmethod
|
||||
def number(start, end):
|
||||
|
||||
@@ -31,4 +31,10 @@ class EventFilter(filters.FilterSet):
|
||||
|
||||
def filter_category(self, queryset, name, value):
|
||||
category = value.split(',')
|
||||
return queryset.filter(category__title__in=category)
|
||||
return queryset.filter(category__title__in=category)
|
||||
|
||||
# def filter_queryset(self, queryset):
|
||||
# queryset = super().filter_queryset(queryset)
|
||||
# if 'price_from' in self.data or 'price_to' in self.data:
|
||||
# queryset = queryset.order_by('entry_fee')
|
||||
# return queryset
|
||||
@@ -50,6 +50,18 @@ class EventCategorySerializer(serializers.ModelSerializer):
|
||||
model = EventCategory
|
||||
fields = ["id", "title", "image", "description", "video_url"]
|
||||
|
||||
def get_image_url(self, obj, field_name, request):
|
||||
image_field = getattr(obj, field_name)
|
||||
if image_field:
|
||||
return request.build_absolute_uri(image_field.url)
|
||||
return ""
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super().to_representation(instance)
|
||||
request = self.context.get("request")
|
||||
data["image"] = self.get_image_url(instance, "image", request)
|
||||
return data
|
||||
|
||||
class AgeGroupsSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = AgeGroups
|
||||
|
||||
@@ -586,7 +586,7 @@ class EventPreferencesView(APIView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""Get all event categories for the authenticated user."""
|
||||
obj = self.model.objects.filter(active=True, deleted=False)
|
||||
serializer = self.serializer_class(obj, many=True)
|
||||
serializer = self.serializer_class(obj, many=True, context={"request": request})
|
||||
return ApiResponse.success(
|
||||
data=serializer.data, message=constants.SUCCESS, status=status.HTTP_200_OK
|
||||
)
|
||||
@@ -1004,65 +1004,77 @@ class AgeGroupListView(APIView):
|
||||
class EventListView(generics.ListAPIView):
|
||||
authentication_classes = [JWTAuthentication]
|
||||
permission_classes = [IsAuthenticated]
|
||||
queryset = Event.objects.filter(active=True, draft=False, deleted=False, end_date__gte=timezone.now().date())
|
||||
serializer_class = EventListSerializer
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
filterset_class = EventFilter
|
||||
|
||||
def apply_popularity_latest(self, queryset, ordering):
|
||||
# Split the ordering fields and remove any leading '-' characters
|
||||
ordering_fields = [field.lstrip("-") for field in ordering.split(",")]
|
||||
|
||||
# Check if 'nearest' is in the ordering fields and remove it as nearest work only for lat and longitude
|
||||
if "nearest" in ordering_fields:
|
||||
ordering.replace("nearest", "")
|
||||
ordering_fields.remove("nearest")
|
||||
|
||||
# Annotate the queryset with popularity if 'popularity' is in the ordering fields
|
||||
if "popularity" in ordering_fields:
|
||||
queryset = queryset.annotate(popularity=Count("interaction_event"))
|
||||
|
||||
# Replace 'latest' with '-created_on' in the ordering fields
|
||||
ordering = ",".join(
|
||||
"start_date" if field == "latest" else f"-{field}"
|
||||
for field in ordering_fields
|
||||
def get_queryset(self):
|
||||
# Base queryset filtering active, non-draft, non-deleted events with end date in the future
|
||||
queryset = Event.objects.filter(
|
||||
active=True,
|
||||
draft=False,
|
||||
deleted=False,
|
||||
end_date__gte=timezone.now().date()
|
||||
)
|
||||
|
||||
# If ordering is empty, set a default ordering
|
||||
if not ordering:
|
||||
ordering = "-start_date"
|
||||
if not self.request.query_params:
|
||||
preferences = PrincipalPreference.objects.get(principal=self.request.user)
|
||||
preferred_categories_ids = preferences.preferred_categories.values_list(
|
||||
"id", flat=True
|
||||
)
|
||||
queryset = queryset.filter(category__in=preferred_categories_ids).order_by("start_date")
|
||||
|
||||
print(f"++++++++++++++++++++++++++++++++ordering data in populatirn flow {ordering}")
|
||||
# Apply the ordering to the queryset
|
||||
return queryset.order_by(*ordering.split(","))
|
||||
return queryset
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
def filter_queryset(self, queryset):
|
||||
queryset = super().filter_queryset(queryset)
|
||||
|
||||
# Sort by nearest location if latitude and longitude are provided
|
||||
latitude = self.request.query_params.get("latitude")
|
||||
longitude = self.request.query_params.get("longitude")
|
||||
ordering = self.request.query_params.get("ordering", "")
|
||||
# Get query parameters
|
||||
latitude = self.request.query_params.get("latitude", "")
|
||||
longitude = self.request.query_params.get("longitude", "")
|
||||
ordering = self.request.query_params.get("sort", "")
|
||||
|
||||
# Handle nearest location sorting
|
||||
if latitude and longitude and "nearest" in ordering:
|
||||
print("latitude fucntion called")
|
||||
gmaps_service = GoogleMapsservice()
|
||||
queryset = gmaps_service.get_nearest_events(queryset, float(latitude), float(longitude))
|
||||
queryset = self.apply_nearest_filter(queryset, latitude, longitude)
|
||||
|
||||
print(f"=======orderring data is {ordering}")
|
||||
# Handle popularity and latest sorting
|
||||
queryset = self.apply_sorting(queryset, ordering)
|
||||
|
||||
return queryset
|
||||
|
||||
def apply_nearest_filter(self, queryset, latitude, longitude):
|
||||
gmaps_service = GoogleMapsservice()
|
||||
return gmaps_service.get_nearest_events(queryset, float(latitude), float(longitude))
|
||||
|
||||
def apply_sorting(self, queryset, ordering):
|
||||
# Split ordering fields and process each field
|
||||
ordering_fields = [field.lstrip("-") for field in ordering.split(",")]
|
||||
|
||||
# Remove 'nearest' from ordering as it's handled separately
|
||||
if "nearest" in ordering_fields:
|
||||
ordering_fields.remove("nearest")
|
||||
ordering = ordering.replace("nearest", "")
|
||||
|
||||
# Annotate with popularity and order it if requested
|
||||
if "popularity" in ordering_fields:
|
||||
queryset = queryset.annotate(popularity=Count("interaction_event")).order_by("-popularity")
|
||||
|
||||
# order latest record and by default sorting
|
||||
if "latest" in ordering_fields:
|
||||
queryset = queryset.order_by("start_date")
|
||||
|
||||
if "price" in ordering_fields:
|
||||
queryset = queryset.order_by('entry_fee')
|
||||
|
||||
# Apply popularity annotation and ordering if requested
|
||||
if ordering:
|
||||
queryset = self.apply_popularity_latest(queryset, ordering)
|
||||
return queryset
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
print(f"getquery set data is {self.get_queryset()}")
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
page = self.paginate_queryset(queryset)
|
||||
if page is not None:
|
||||
serializer = self.get_serializer(page, many=True)
|
||||
data = self.get_paginated_response(serializer.data)
|
||||
return ApiResponse.success(message=constants.SUCCESS, data=data)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
return ApiResponse.success(message=constants.SUCCESS, data=serializer.data)
|
||||
30
manage_events/management/commands/test_facebook_api.py
Normal file
30
manage_events/management/commands/test_facebook_api.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import os
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
from goodtimes.services import FacebookAPI, FacebookPoster
|
||||
from ...models import Event
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Test facebook posting functionality'
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
event = Event.objects.get(id=20)
|
||||
if not event:
|
||||
self.stdout.write(self.style.ERROR("No event found."))
|
||||
|
||||
if not event.image:
|
||||
self.stdout.write(self.style.ERROR("No image found."))
|
||||
|
||||
image_path = f"{settings.BASE_DOMAIN}{event.image.url}"
|
||||
print(f"complete path of image {image_path}")
|
||||
caption = f"{event.title}\nDuration: {event.start_date} to {event.end_date}\nAddress: {event.venue.address}"
|
||||
|
||||
facebook_api = FacebookAPI()
|
||||
facebook_poster = FacebookPoster(facebook_api)
|
||||
|
||||
response = facebook_poster.post_photo(image_path, caption)
|
||||
|
||||
if response['success']:
|
||||
self.stdout.write(self.style.SUCCESS(response['message']))
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR(response['message']))
|
||||
33
manage_events/management/commands/test_instagram_api.py
Normal file
33
manage_events/management/commands/test_instagram_api.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import os
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
from goodtimes.services import InstagramAPI, InstagramPoster
|
||||
from ...models import Event
|
||||
import urllib.request
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Test Instagram posting functionality'
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
event = Event.objects.get(id=20)
|
||||
if not event:
|
||||
self.stdout.write(self.style.ERROR("No event found."))
|
||||
|
||||
if not event.image:
|
||||
self.stdout.write(self.style.ERROR("No image found."))
|
||||
|
||||
image_path = f"{settings.BASE_DOMAIN}{event.image.url}"
|
||||
# image_path = event.image.url
|
||||
print(f"complete path of image {image_path}")
|
||||
|
||||
caption = f"{event.title}\nDuration: {event.start_date} to {event.end_date}\nAddress: {event.venue.address}"
|
||||
|
||||
instagram_api = InstagramAPI()
|
||||
instagram_poster = InstagramPoster(instagram_api)
|
||||
|
||||
response = instagram_poster.post_image_with_caption(image_path, caption)
|
||||
|
||||
if response['success']:
|
||||
self.stdout.write(self.style.SUCCESS(response['message']))
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR(response['message']))
|
||||
28
manage_events/management/commands/test_twitter_api.py
Normal file
28
manage_events/management/commands/test_twitter_api.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import os
|
||||
from django.core.management.base import BaseCommand
|
||||
from goodtimes.services import TwitterAPI, TwitterPoster
|
||||
from ...models import Event
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Test Twitter posting functionality'
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
event = Event.objects.get(id=19)
|
||||
if not event:
|
||||
self.stdout.write(self.style.ERROR("No event found."))
|
||||
|
||||
if not event.image:
|
||||
self.stdout.write(self.style.ERROR("No image found."))
|
||||
|
||||
image_path = event.image.path
|
||||
caption = f"{event.title}\nDuration: {event.start_date} to {event.end_date}\nAddress: {event.venue.address}"
|
||||
|
||||
twitter_api = TwitterAPI()
|
||||
twitter_poster = TwitterPoster(twitter_api)
|
||||
|
||||
response = twitter_poster.post_image_with_caption(image_path, caption)
|
||||
|
||||
if response['success']:
|
||||
self.stdout.write(self.style.SUCCESS(response['message']))
|
||||
else:
|
||||
self.stdout.write(self.style.ERROR(response['message']))
|
||||
@@ -99,4 +99,6 @@ urlpatterns = [
|
||||
views.GenerateEventReportView.as_view(),
|
||||
name="generate_event_report",
|
||||
),
|
||||
|
||||
path("post-to-social-media/<int:id>/<str:platform>/", views.SocialMediaPostView.as_view(), name="social_media_post")
|
||||
]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from accounts import resource_action
|
||||
from goodtimes.services import FacebookAPI, FacebookPoster, InstagramAPI, InstagramPoster, TwitterAPI, TwitterPoster
|
||||
from goodtimes.utils import JsonResponseUtil
|
||||
from manage_events.api.serializers import VenueSerializer, VenueShortSerializer
|
||||
from manage_events.forms import (
|
||||
@@ -532,7 +533,7 @@ class CustomerVenueFilterView(LoginRequiredMixin, generic.View):
|
||||
|
||||
User = get_user_model()
|
||||
from .report import generate_event_report, generate_event_report_pdf_three
|
||||
from django.http import HttpResponse
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
|
||||
|
||||
class GenerateEventReportView(generic.View):
|
||||
@@ -553,3 +554,77 @@ class GenerateEventReportView(generic.View):
|
||||
response["Content-Disposition"] = f'attachment; filename="{filename}"'
|
||||
|
||||
return response
|
||||
|
||||
class SocialMediaPostView(generic.View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
platform = kwargs.get("platform")
|
||||
event_id = kwargs.get("id")
|
||||
print(platform, event_id)
|
||||
errors = []
|
||||
success_messages = []
|
||||
|
||||
try:
|
||||
event = Event.objects.get(id=event_id)
|
||||
except Event.DoesNotExist:
|
||||
errors.append("Event does not exist")
|
||||
return JsonResponse({
|
||||
'message': "Error in posting to social media",
|
||||
'errors': errors,
|
||||
'success_messages': success_messages
|
||||
}, status=400)
|
||||
|
||||
if not event.active:
|
||||
errors.append("Event is not active")
|
||||
return JsonResponse({
|
||||
'message': "Error in posting to social media",
|
||||
'errors': errors,
|
||||
'success_messages': success_messages
|
||||
}, status=400)
|
||||
|
||||
caption = f"{event.title}\nDuration: {event.start_date} to {event.end_date}\nAddress: {event.venue.address}"
|
||||
|
||||
if platform in ['instagram', 'facebook', 'twitter', 'all']:
|
||||
if platform in ['twitter', 'all']:
|
||||
image_url = event.image.path
|
||||
twitter_api = TwitterAPI()
|
||||
twitter_poster = TwitterPoster(twitter_api)
|
||||
result = twitter_poster.post_image_with_caption(image_url, caption)
|
||||
if result['success']:
|
||||
success_messages.append("Posted to Twitter successfully")
|
||||
else:
|
||||
errors.append("Fail to post on Twitter")
|
||||
|
||||
image_url = request.build_absolute_uri(event.image.url)
|
||||
if platform in ['facebook', 'all']:
|
||||
facebook_api = FacebookAPI()
|
||||
facebook_poster = FacebookPoster(facebook_api)
|
||||
result = facebook_poster.post_photo(image_url, caption)
|
||||
if result["success"]:
|
||||
success_messages.append("Posted to Facebook successfully")
|
||||
else:
|
||||
errors.append("Fail to post on Facebook")
|
||||
|
||||
if platform in ['instagram', 'all']:
|
||||
instagram_api = InstagramAPI()
|
||||
instagram_poster = InstagramPoster(instagram_api)
|
||||
result = instagram_poster.post_image_with_caption(image_url, caption)
|
||||
if result["success"]:
|
||||
success_messages.append("Posted to Instagram successfully")
|
||||
else:
|
||||
errors.append("Fail to post on Instagram")
|
||||
|
||||
if not errors:
|
||||
return JsonResponse({'message': 'Post Successful', 'errors': errors, 'success_messages': success_messages})
|
||||
|
||||
if errors and success_messages:
|
||||
return JsonResponse({
|
||||
'message': 'Some posts succeeded while others failed',
|
||||
'errors': errors,
|
||||
'success_messages': success_messages
|
||||
}, status=200)
|
||||
|
||||
return JsonResponse({
|
||||
'message': 'Error in posting to social media',
|
||||
'errors': errors,
|
||||
'success_messages': success_messages
|
||||
}, status=400)
|
||||
@@ -69,8 +69,9 @@ sniffio==1.3.1
|
||||
sqlparse==0.4.4
|
||||
stripe==8.2.0
|
||||
tqdm==4.66.2
|
||||
tweepy==4.14.0
|
||||
Twisted==23.10.0
|
||||
# twisted-iocpsupport==1.0.4
|
||||
twisted-iocpsupport==1.0.4
|
||||
txaio==23.1.1
|
||||
typing_extensions==4.9.0
|
||||
tzdata==2024.1
|
||||
|
||||
BIN
static/img/facebook.png
Normal file
BIN
static/img/facebook.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
BIN
static/img/forward_all_icon.png
Normal file
BIN
static/img/forward_all_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.5 KiB |
BIN
static/img/instagram.png
Normal file
BIN
static/img/instagram.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
BIN
static/img/x_twitter.png
Normal file
BIN
static/img/x_twitter.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -2,12 +2,48 @@
|
||||
{% load static %}
|
||||
{% block stylesheet %}
|
||||
<!-- include required css cdn link through html here -->
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">
|
||||
|
||||
{% include "cdn_through_html/filepond_cdn_css.html" %}
|
||||
{% include "cdn_through_html/quill_cdn_css.html" %}
|
||||
{% include "cdn_through_html/tagify_cdn_css.html" %}
|
||||
{% include "cdn_through_html/sweetalert2_cdn_css.html" %}
|
||||
{{form.media}}
|
||||
|
||||
<style>
|
||||
.social-circle {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
}
|
||||
.logos{
|
||||
text-align:center;
|
||||
margin-bottom:-390px;
|
||||
}
|
||||
.x{
|
||||
margin:5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.x_twitter{
|
||||
width:50px;
|
||||
height:50px;
|
||||
}
|
||||
.forward_all{
|
||||
width:50px;
|
||||
height:50px;
|
||||
}
|
||||
.instalogo{
|
||||
width:50px;
|
||||
height:50px;
|
||||
}
|
||||
.facelogo{
|
||||
width:50px;
|
||||
height:50px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@@ -25,15 +61,41 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card mb-3" style="border-radius: 20px; box-shadow: 0 4px 8px rgba(0,0,0,.1);">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{{ event.brand.title }}</h2>
|
||||
<h3 class="card-title">{{ event.title }}</h3>
|
||||
<p class="card-text"><strong>Description:</strong> {{ event.description }}</p>
|
||||
<p class="card-text"><strong>Created By:</strong> {{ event.created_by }}</p>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card mb-3" style="border-radius: 20px; box-shadow: 0 4px 8px rgba(0,0,0,.1);">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{{ event.brand.title }}</h2>
|
||||
<h3 class="card-title">{{ event.title }}</h3>
|
||||
<p class="card-text"><strong>Description:</strong> {{ event.description }}</p>
|
||||
<p class="card-text"><strong>Created By:</strong> {{ event.created_by }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card mb-3" style="border-radius: 20px; box-shadow: 0 4px 8px rgba(0,0,0,.1);">
|
||||
<div class="card-body">
|
||||
<div class="social-circle">
|
||||
<a data-platform="instagram" class="social-icon" data-toggle="tooltip" title="Post to Instagram">
|
||||
<img src="{% static 'img/instagram.png'%}" class="x instalogo">
|
||||
</a>
|
||||
<a data-platform="facebook" class="social-icon" data-toggle="tooltip" title="Post to Facebook">
|
||||
<img src="{% static 'img/facebook.png'%}" class="x facelogo">
|
||||
</a>
|
||||
<a data-platform="twitter" class="social-icon" data-toggle="tooltip" title="Post to Twitter">
|
||||
<img src="{% static 'img/x_twitter.png'%}" class="x x_twitter">
|
||||
</a>
|
||||
<a data-platform="all" class="social-icon" data-toggle="tooltip" title="Post to All">
|
||||
<img src="{% static 'img/forward_all_icon.png'%}" class="x forward_all">
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-3" style="border-radius: 20px; box-shadow: 0 4px 8px rgba(0,0,0,.1);">
|
||||
@@ -145,4 +207,97 @@
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock content %}
|
||||
{% endblock content %}
|
||||
|
||||
{% block javascript %}
|
||||
<!-- include required css cdn link through html here -->
|
||||
{% include "cdn_through_html/sweetalert2_cdn_js.html" %}
|
||||
<script>
|
||||
|
||||
$(document).ready(function() {
|
||||
// Initialize tooltips
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
$(".social-icon").on("click", function(e) {
|
||||
console.log("social icon clicked");
|
||||
e.preventDefault();
|
||||
var platform = $(this).data("platform");
|
||||
var eventId = "{{ event.id }}";
|
||||
var platform_url = "{% url 'manage_events:social_media_post' id=0 platform='dummy' %}"
|
||||
.replace('/0/', '/' + eventId + '/')
|
||||
.replace('dummy', platform);
|
||||
|
||||
console.log(platform_url);
|
||||
|
||||
Swal.fire({
|
||||
title: "Are you sure you want to post this event on " + platform + "?",
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Yes',
|
||||
cancelButtonText: 'No',
|
||||
allowOutsideClick: false
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
Swal.fire({
|
||||
title: 'Posting...',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: platform_url,
|
||||
type: "GET",
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
Swal.close();
|
||||
|
||||
if (response.success_messages && response.errors && response.errors.length === 0) {
|
||||
let successMessages = response.success_messages.join("<br>");
|
||||
Swal.fire({
|
||||
icon: "success",
|
||||
title: "Success",
|
||||
html: response.message + "<br>" + successMessages
|
||||
});
|
||||
} else if (response.errors && response.success_messages) {
|
||||
let errorMessages = response.errors.join("<br>");
|
||||
let successMessages = response.success_messages.join("<br>");
|
||||
Swal.fire({
|
||||
icon: "warning",
|
||||
title: "Partial Success",
|
||||
html: response.message + "<br>Successes:<br>" + successMessages + "<br>Failures:<br>" + errorMessages
|
||||
});
|
||||
} else if (response.errors) {
|
||||
let errorMessages = response.errors.join("<br>");
|
||||
Swal.fire({
|
||||
icon: "error",
|
||||
title: "Failures",
|
||||
html: response.message + "<br>" + errorMessages
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: "error",
|
||||
title: "Error",
|
||||
text: "Unknown error occurred"
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
Swal.close();
|
||||
var errorMessage = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : "Something went wrong. Please try again later.";
|
||||
Swal.fire({
|
||||
icon: "error",
|
||||
title: "Error",
|
||||
text: errorMessage
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
{%endblock javascript%}
|
||||
Reference in New Issue
Block a user