diff --git a/accounts/migrations/0016_iamprincipalextendeddata_encrypted_pass.py b/accounts/migrations/0016_iamprincipalextendeddata_encrypted_pass.py new file mode 100644 index 0000000..9c4bda1 --- /dev/null +++ b/accounts/migrations/0016_iamprincipalextendeddata_encrypted_pass.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-12-20 12:15 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0015_iamprincipal_twitter_profile'), + ] + + operations = [ + migrations.AddField( + model_name='iamprincipalextendeddata', + name='encrypted_pass', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/accounts/models.py b/accounts/models.py index a7c6968..538369b 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -13,6 +13,7 @@ from django.utils.text import slugify from phonenumber_field.modelfields import PhoneNumberField # from manage_subscriptions.models import Subscription + from goodtimes.utils import RandomGenerator from .resource_action import ( PRINCIPAL_TYPE_EVENT_USER, @@ -333,9 +334,16 @@ class IAmPrincipal(AbstractUser): def __str__(self): return f"{self.email}" + + @staticmethod + def generate_random_password(): + """Generate a password in the format 'GoodTimes@xxxx'.""" + random_number = random.randint(1000, 9999) # Generate a 4-digit random number + return f"GoodTimes@{random_number}" class IAmPrincipalExtendedData(models.Model): + principal = models.OneToOneField( IAmPrincipal, related_name="extended_data", @@ -356,7 +364,7 @@ class IAmPrincipalExtendedData(models.Model): help_text="The date and time when the account was transferred to the user." ) pwd_changed_post_transfer = models.BooleanField(default=False, help_text="Indicates if the user changed their password after the account was transferred.") - + encrypted_pass = models.TextField(blank=True, null=True) class Meta: db_table = "iam_principal_extended_data" @@ -368,6 +376,12 @@ class IAmPrincipalExtendedData(models.Model): self.transferred_on = datetime.datetime.now() super().save(*args, **kwargs) + @property + def decrypted_field(self): + from goodtimes.services import Encryptor + encryptor = Encryptor() + return encryptor.decrypt(self.encrypted_pass) + class IAmPrincipalResourceLink(models.Model): principal = models.ForeignKey( IAmPrincipal, diff --git a/accounts/urls.py b/accounts/urls.py index 99c99c9..369f3be 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -42,6 +42,7 @@ urlpatterns = [ path('principal/role/delete//', views.AppRoleDeleteView.as_view(), name="role_delete"), path('customer/', views.CustomerListView.as_view(), name="customer_list"), + path('customer/get-decrypted-password//', views.GetDecryptedPasswordView.as_view(), name='get_decrypted_password'), path('customer/add/', views.CustomerCreateView.as_view(), name="customer_add"), path('customer/edit//', views.CustomerUpdateView.as_view(), name="customer_edit"), path('customer/detail//', views.CustomerDetailView.as_view(), name="customer_detail"), diff --git a/accounts/views.py b/accounts/views.py index 1f36689..3463ec2 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -24,7 +24,7 @@ from django.utils import timezone import phonenumbers from accounts import permission from goodtimes import constants -from goodtimes.services import EmailService +from goodtimes.services import EmailService, Encryptor from goodtimes.utils import JsonResponseUtil from manage_events.models import EventCategory, PrincipalPreference from manage_referrals.models import ReferralCode @@ -621,6 +621,12 @@ class CustomerCreateView(LoginRequiredMixin, generic.View): try: with transaction.atomic(): + random_password = IAmPrincipal.generate_random_password() + + # Encrypt the password + encryptor = Encryptor() + encrypted_password = encryptor.encrypt(random_password) + # save principal data principal_obj = IAmPrincipal.objects.create( email=form.cleaned_data.get('email'), @@ -628,7 +634,7 @@ class CustomerCreateView(LoginRequiredMixin, generic.View): last_name=form.cleaned_data.get('last_name'), business_name=form.cleaned_data.get('business_name'), phone_no=form.cleaned_data.get('phone_no'), - password=make_password("goodtimes#2024"), + password=make_password(random_password), username=form.cleaned_data.get("email"), email_verified=True, register_complete=True, @@ -651,6 +657,7 @@ class CustomerCreateView(LoginRequiredMixin, generic.View): IAmPrincipalExtendedData.objects.create( principal=principal_obj, is_onboarded=True, + encrypted_pass=encrypted_password, # Save encrypted password ) # save principal preferences record @@ -665,7 +672,6 @@ class CustomerCreateView(LoginRequiredMixin, generic.View): is_paid=True, subscription=free_subscription ) - messages.success(self.request, constants.REGISTRATION_SUCCESS) return redirect(self.success_url) except Exception as e: @@ -862,6 +868,27 @@ class CustomerListView(LoginRequiredMixin, generic.ListView): context = super().get_context_data(**kwargs) context["page_name"] = self.page_name return context + + +class GetDecryptedPasswordView(generic.View): + def get(self, request, customer_id): + try: + # Fetch the extended data for the customer + extended_data = IAmPrincipalExtendedData.objects.get(principal_id=customer_id) + + if not extended_data.encrypted_pass: + return JsonResponse({"success": False, "message": "No password found."}) + + # Use Encryptor to decrypt the password + encryptor = Encryptor() + decrypted_password = encryptor.decrypt(extended_data.encrypted_pass) + + return JsonResponse({"success": True, "decrypted_password": decrypted_password}) + except IAmPrincipalExtendedData.DoesNotExist: + return JsonResponse({"success": False, "message": "Customer not found."}) + except Exception as e: + return JsonResponse({"success": False, "message": str(e)}) + import pandas as pd from openpyxl import Workbook, load_workbook @@ -1022,7 +1049,7 @@ class CustomerTransferView(LoginRequiredMixin, generic.View): # Send the email try: - temp_password = "goodtimes#2024" + temp_password = "GoodTimes@2025" principal_obj.password = make_password(temp_password) principal_obj.save() email_service.load_template( @@ -1159,12 +1186,18 @@ class CustomerImportView(LoginRequiredMixin, generic.View): error_log.append(f"Row {idx}: One or more preferences are invalid.") continue + random_password = IAmPrincipal.generate_random_password() + + # Encrypt the password + encryptor = Encryptor() + encrypted_password = encryptor.encrypt(random_password) + # collect the principals principal = IAmPrincipal( first_name=first_name.strip().capitalize(), last_name=last_name.strip().capitalize(), email=email.strip(), - password=make_password("goodtimes#2024"), + password=make_password(random_password), username=email.strip(), email_verified=True, register_complete=True, @@ -1207,7 +1240,7 @@ class CustomerImportView(LoginRequiredMixin, generic.View): ReferralCode.create_referral_code_for_user_manager(principal=principal, principal_type=principal_type) # Create IAmPrincipalExtendedData record - IAmPrincipalExtendedData.objects.create(principal=principal, is_onboarded=True) + IAmPrincipalExtendedData.objects.create(principal=principal, is_onboarded=True,encrypted_pass=encrypted_password,) # Create PrincipalSubscription record subscription = PrincipalSubscription( diff --git a/goodtimes/services.py b/goodtimes/services.py index 940b66f..792e22c 100644 --- a/goodtimes/services.py +++ b/goodtimes/services.py @@ -726,9 +726,6 @@ class FacebookPoster: 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'} @@ -1130,4 +1127,18 @@ class StripeService: ) return {'success': True, 'data': subscription} except stripe.error.StripeError as e: - return {'success': False, 'message': f'Error cancelling subscription auto-renewal: {e}'} \ No newline at end of file + + return {'success': False, 'message': f'Error cancelling subscription auto-renewal: {e}'} + +from cryptography.fernet import Fernet +class Encryptor: + def __init__(self): + self.key = "paMSf3Ny8KAMs1tRLcVOQQhRxTnInHLwP7WtVdm8O_4=" + self.fernet = Fernet(self.key) + + def encrypt(self, plaintext): + return self.fernet.encrypt(plaintext.encode()).decode() + + def decrypt(self, encrypted_text): + return self.fernet.decrypt(encrypted_text.encode()).decode() + diff --git a/goodtimes/settings/base.py b/goodtimes/settings/base.py index fc658bb..8e96bc9 100644 --- a/goodtimes/settings/base.py +++ b/goodtimes/settings/base.py @@ -77,6 +77,7 @@ THIRD_PARTY_APPS = [ "taggit", "django_quill", "corsheaders", + 'django_extensions', "allauth", "allauth.account", "allauth.socialaccount", @@ -211,6 +212,7 @@ TIME_FORMAT = "H:i p" # otp expire time limit OTP_EXPIRE_TIME = 1 # mins +DEFAULT_CHARSET = 'utf-8' # Default primary key field type diff --git a/manage_events/api/views.py b/manage_events/api/views.py index 3a682ed..99228c2 100644 --- a/manage_events/api/views.py +++ b/manage_events/api/views.py @@ -139,6 +139,7 @@ class CreateVenueApi(APIView): def post(self, request): data = request.data.copy() + print("prindata is ", data) # Convert latitude and longitude to float and round to 8 decimal places data["latitude"] = round(float(data["latitude"]), 8) diff --git a/manage_events/forms.py b/manage_events/forms.py index 3f40650..c17ecfd 100644 --- a/manage_events/forms.py +++ b/manage_events/forms.py @@ -148,6 +148,7 @@ class VenueForm(forms.ModelForm): required=True ) image = forms.ImageField(required=True) + postcode = forms.CharField(required=True, max_length=10) latitude = forms.DecimalField( widget=forms.NumberInput() ) @@ -161,6 +162,7 @@ class VenueForm(forms.ModelForm): "principal", "title", "address", + "postcode", "image", "latitude", "longitude", diff --git a/manage_events/migrations/0017_venue_postcode.py b/manage_events/migrations/0017_venue_postcode.py new file mode 100644 index 0000000..8eb3bef --- /dev/null +++ b/manage_events/migrations/0017_venue_postcode.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.2 on 2024-12-20 09:29 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('manage_events', '0016_freeusagefeaturelimit'), + ] + + operations = [ + migrations.AddField( + model_name='venue', + name='postcode', + field=models.CharField(blank=True, max_length=20, null=True), + ), + ] diff --git a/manage_events/models.py b/manage_events/models.py index 276db10..e0d19f8 100644 --- a/manage_events/models.py +++ b/manage_events/models.py @@ -51,13 +51,14 @@ class Venue(BaseModel): longitude = models.DecimalField( max_digits=14, decimal_places=8, blank=True, null=True ) + postcode = models.CharField(max_length=20, blank=True, null=True) def __str__(self): return self.title class AgeGroups(BaseModel): name = models.CharField(max_length=10, unique=True) - + class Meta: db_table = "age_group" diff --git a/manage_events/views.py b/manage_events/views.py index b3f50ce..d1899a4 100644 --- a/manage_events/views.py +++ b/manage_events/views.py @@ -599,12 +599,17 @@ class SocialMediaPostView(generic.View): 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: + try: + print("facebook is called") + 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") + except Exception as e: + print(f"facebook error {e}") errors.append("Fail to post on Facebook") if platform in ['instagram', 'all']: diff --git a/manage_subscriptions/migrations/0015_alter_subscription_long_description.py b/manage_subscriptions/migrations/0015_alter_subscription_long_description.py new file mode 100644 index 0000000..417f2d0 --- /dev/null +++ b/manage_subscriptions/migrations/0015_alter_subscription_long_description.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0.2 on 2024-12-20 09:29 + +import django_quill.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('manage_subscriptions', '0014_alter_subscription_long_description'), + ] + + operations = [ + migrations.AlterField( + model_name='subscription', + name='long_description', + field=django_quill.fields.QuillField(), + ), + ] diff --git a/requirements.txt b/requirements.txt index 7f10b36..0724efc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -75,7 +75,7 @@ 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 diff --git a/templates/accounts/customer/customer_list.html b/templates/accounts/customer/customer_list.html index caf4463..31b0ffb 100644 --- a/templates/accounts/customer/customer_list.html +++ b/templates/accounts/customer/customer_list.html @@ -1,8 +1,10 @@ {% extends 'layout/base_template.html' %} {% load static %} {% block stylesheet %} - - {% include "cdn_through_html/datatable_cdn_css.html" %} + +{% include "cdn_through_html/datatable_cdn_css.html" %} +{% include "cdn_through_html/modal_cdn_css.html" %} +{% include "cdn_through_html/sweetalert2_cdn_css.html" %} {% endblock %} @@ -19,7 +21,7 @@ Download Excel Template - + Import @@ -27,12 +29,12 @@ Export - + Add Customer - - + +
@@ -46,39 +48,39 @@ Record Id - Image - First Name - Last Name - Email - - Principal Type + Image + First Name + Last Name + Email + # + Principal Type - Email Verified - Referral Count - Onboarded by Admin - Transferred to Customer - Created On - Modified On - Active + Email Verified + Referral Count + Onboarded by Admin + Transferred to Customer + + Created On + Modified On + Active - Action + Action @@ -92,33 +94,43 @@ {{ data_obj.first_name }} {{ data_obj.last_name }} {{ data_obj.email }} - + + {% if data_obj.extended_data %} + + {% endif %} + {{ data_obj.principal_type.name }} {{ data_obj.email_verified }} {{ data_obj.referral_count }} - + {% if data_obj.extended_data %} - {{ data_obj.extended_data.is_onboarded }} + {{ data_obj.extended_data.is_onboarded }} {% else %} - False + False {% endif %} - + {% if data_obj.extended_data %} - {{ data_obj.extended_data.is_transferred }} + {{ data_obj.extended_data.is_transferred }} {% else %} - False + False {% endif %} {{ data_obj.created_on }} {{ data_obj.modified_on }} - + {{ data_obj.is_active }} @@ -129,11 +141,12 @@ --> @@ -159,31 +183,88 @@
+ + + + {% endblock content %} {% block javascript %} - - {% include "cdn_through_html/datatable_cdn_js.html" %} - - + {% endblock %} \ No newline at end of file diff --git a/templates/manage_venues/venue_list.html b/templates/manage_venues/venue_list.html index 31e8a2b..232d291 100644 --- a/templates/manage_venues/venue_list.html +++ b/templates/manage_venues/venue_list.html @@ -45,6 +45,8 @@ aria-sort="ascending" style="width: 69.2656px;"> Title Address + Postcode Latitude {{data_obj.title}} {{data_obj.address}} + {{data_obj.postcode}} {{data_obj.latitude}} {{data_obj.longitude}}