|
5 | 5 | from django.contrib.auth.decorators import login_required
|
6 | 6 | from django.contrib.sites.models import Site
|
7 | 7 | from django.contrib.sites.shortcuts import get_current_site
|
| 8 | +from django.contrib.auth import get_user_model |
| 9 | +from django.db import transaction |
8 | 10 | from django.http import Http404
|
9 | 11 | from django.middleware import csrf
|
10 | 12 | from django.utils.decorators import method_decorator
|
|
18 | 20 |
|
19 | 21 | from ecommerce.core.constants import ENABLE_SUBSCRIPTIONS_ON_RUNTIME_SWITCH
|
20 | 22 | from ecommerce.core.models import SiteConfiguration
|
| 23 | +from ecommerce.courses.models import Course |
21 | 24 | from ecommerce.extensions.edly_ecommerce_app.api.v1.constants import ERROR_MESSAGES
|
22 | 25 | from ecommerce.extensions.edly_ecommerce_app.helpers import (
|
23 | 26 | user_is_course_creator,
|
|
27 | 30 | validate_site_configurations_for_self_service_api,
|
28 | 31 | validate_site_theme,
|
29 | 32 | )
|
30 |
| -from ecommerce.extensions.edly_ecommerce_app.permissions import CanAccessSiteCreation |
| 33 | +from ecommerce.extensions.edly_ecommerce_app.permissions import CanAccessSiteCreation, CanAccessSiteDeletion |
31 | 34 | from ecommerce.extensions.partner.models import Partner
|
32 | 35 | from ecommerce.theming.models import SiteTheme
|
| 36 | +from ecommerce.extensions.partner.models import StockRecord |
| 37 | +from ecommerce.extensions.order.models import Order, Line |
| 38 | +from ecommerce.extensions.offer.models import ConditionalOffer |
| 39 | +from ecommerce.extensions.order.models import MarkOrdersStatusCompleteConfig |
| 40 | +import logging |
| 41 | + |
| 42 | +logger = logging.getLogger(__name__) |
| 43 | +User = get_user_model() |
33 | 44 |
|
34 | 45 |
|
35 | 46 | class SiteThemesActions(APIView):
|
@@ -205,6 +216,126 @@ def get_oauth2_credentials(self):
|
205 | 216 | )
|
206 | 217 | return oauth2_values
|
207 | 218 |
|
| 219 | +class EdlySiteDeletionViewSet(APIView): |
| 220 | + """ |
| 221 | + Delete the ecommerce site and its data. |
| 222 | + """ |
| 223 | + permission_classes = [IsAuthenticated, CanAccessSiteDeletion] |
| 224 | + |
| 225 | + def post(self, request): |
| 226 | + """ |
| 227 | + POST /edly_ecommerce_api/delete_site/ |
| 228 | + """ |
| 229 | + try: |
| 230 | + with transaction.atomic(): |
| 231 | + self.process_site_deletion(request) |
| 232 | + |
| 233 | + return Response( |
| 234 | + {'success': ERROR_MESSAGES.get('SITE_DELETION_SUCCESS')}, |
| 235 | + status=status.HTTP_200_OK |
| 236 | + ) |
| 237 | + except Exception as e: |
| 238 | + logger.error(f"Site deletion failed: {str(e)}", exc_info=True) |
| 239 | + return Response( |
| 240 | + { |
| 241 | + 'error': ERROR_MESSAGES.get('SITE_DELETION_FAILURE'), |
| 242 | + 'detail': str(e) |
| 243 | + }, |
| 244 | + status=status.HTTP_400_BAD_REQUEST |
| 245 | + ) |
| 246 | + |
| 247 | + def get_current_site(self, request): |
| 248 | + """Get current site value using domain value from request.""" |
| 249 | + site_domain = request.data.get('delete_site_url', '') |
| 250 | + try: |
| 251 | + site = Site.objects.get(domain=site_domain) |
| 252 | + except Site.DoesNotExist: |
| 253 | + site = None |
| 254 | + return site |
| 255 | + |
| 256 | + def get_current_partner(self, request): |
| 257 | + site = self.get_current_site(request) |
| 258 | + try: |
| 259 | + partner = Partner.objects.get(default_site=site) |
| 260 | + except Partner.DoesNotExist: |
| 261 | + partner = None |
| 262 | + return partner |
| 263 | + |
| 264 | + def delete_users(self, request): |
| 265 | + """Delete all the sync user for a given site.""" |
| 266 | + user_emails = request.data.get('emails') |
| 267 | + user_names = request.data.get('usernames') |
| 268 | + if not all([len(user_emails), len(user_names)]): |
| 269 | + logger.info(f"No user deleted for given site : {(str(request.site))}") |
| 270 | + return |
| 271 | + |
| 272 | + users = User.objects.filter( |
| 273 | + email__in=user_emails, |
| 274 | + username__in=user_names |
| 275 | + ).exclude(id=request.user.id) |
| 276 | + |
| 277 | + MarkOrdersStatusCompleteConfig.objects.filter(changed_by__in=users).delete() |
| 278 | + |
| 279 | + users.delete() |
| 280 | + logger.info(f"Successfully deleted user for {request.site}") |
| 281 | + |
| 282 | + def delete_courses(self, request): |
| 283 | + """Delete the courses for the site.""" |
| 284 | + partner = self.get_current_partner(request) |
| 285 | + courses = Course.objects.filter(partner=partner) |
| 286 | + |
| 287 | + course_ids = courses.values_list('id', flat=True) |
| 288 | + |
| 289 | + HistoricalCourse = Course.history.model |
| 290 | + HistoricalCourse.objects.filter(id__in=course_ids).delete() |
| 291 | + courses.delete() |
| 292 | + |
| 293 | + def delete_partner_additional_data(self, request): |
| 294 | + """ |
| 295 | + Delete all related data referencing the partner that is not automatically |
| 296 | + deleted due to on_delete settings. |
| 297 | + """ |
| 298 | + partner = self.get_current_partner(request) |
| 299 | + |
| 300 | + HistoricalConditionalOffer = ConditionalOffer.history.model |
| 301 | + count, _ = HistoricalConditionalOffer.objects.filter(partner=partner).delete() |
| 302 | + logger.info(f"Deleted {count} HistoricalConditionalOffer record(s) for partner {partner}") |
| 303 | + |
| 304 | + HistoricalOrder = Order.history.model |
| 305 | + count, _ = HistoricalOrder.objects.filter(partner=partner).delete() |
| 306 | + logger.info(f"Deleted {count} HistoricalOrder record(s) for partner {partner}") |
| 307 | + |
| 308 | + count, _ = Line.objects.filter(partner=partner).delete() |
| 309 | + logger.info(f"Deleted {count} Line record(s) for partner {partner}") |
| 310 | + HistoricalLine = Line.history.model |
| 311 | + count, _ = HistoricalLine.objects.filter(partner=partner).delete() |
| 312 | + logger.info(f"Deleted {count} HistoricalLine record(s) for partner {partner}") |
| 313 | + |
| 314 | + HistoricalStockRecord = StockRecord.history.model |
| 315 | + count, _ = HistoricalStockRecord.objects.filter(partner=partner).delete() |
| 316 | + logger.info(f"Deleted {count} HistoricalStockRecord record(s) for partner {partner}") |
| 317 | + |
| 318 | + partner.history.all().delete() |
| 319 | + |
| 320 | + |
| 321 | + def delete_site(self, request): |
| 322 | + """Delete the site and partner for a given site.""" |
| 323 | + site = self.get_current_site(request) |
| 324 | + site_partner = self.get_current_partner(request) |
| 325 | + site_partner.delete() |
| 326 | + site.delete() |
| 327 | + logger.info(f"Successfully deleted site {site} and its partner") |
| 328 | + |
| 329 | + def process_site_deletion(self, request): |
| 330 | + """ |
| 331 | + Process deletion of a site. |
| 332 | + """ |
| 333 | + self.delete_courses(request) |
| 334 | + self.delete_partner_additional_data(request) |
| 335 | + self.delete_users(request) |
| 336 | + self.delete_site(request) |
| 337 | + |
| 338 | + |
208 | 339 |
|
209 | 340 | class EdlySiteConfigViewset(APIView):
|
210 | 341 | """
|
|
0 commit comments