Fakultas Ilmu Komputer UI

Commit 330690cc authored by Bunga Amalia Kurniawati's avatar Bunga Amalia Kurniawati 🌺
Browse files

Merge branch 'bungamaku/15/fix-sonarqube' into 'dev'

Refactor and Fix Pilar-Be SonarQube issues

Closes #15

See merge request !15
parents 6e1eff1f cb2cad6e
Pipeline #78804 passed with stages
in 2 minutes and 25 seconds
......@@ -4,4 +4,8 @@ __pycache__/
.env_var
static
env
.env
\ No newline at end of file
.env
.scannerwork
.vscode
coverage.xml
media
\ No newline at end of file
......@@ -303,7 +303,7 @@ def create_transaction_report(filter_params): # pylint: disable=too-many-locals
return buffer
def create_program_donation_report_CSH(filter_params):
def create_program_donation_report_csh(filter_params):
buffer = io.BytesIO()
workbook = xlsxwriter.Workbook(
buffer,
......@@ -373,7 +373,7 @@ def create_program_donation_report_CSH(filter_params):
workbook.close()
return buffer
def create_program_donation_report_GDS(filter_params):
def create_program_donation_report_gds(filter_params):
buffer = io.BytesIO()
workbook = xlsxwriter.Workbook(
buffer,
......
......@@ -487,7 +487,6 @@ class ProgramProgressSerializer(serializers.ModelSerializer):
def validate(self, attrs):
instance = self.instance
date = attrs.get('date', getattr(instance, 'date', None))
errors = {}
if errors:
raise serializers.ValidationError(errors)
......
from django import http, shortcuts
from django.contrib import auth
from django.db import transaction as db_transaction, utils as db_utils
from django.db.models import deletion
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django_filters import rest_framework
from knox import views as knox_views
from rest_framework import (
exceptions as rest_framework_exceptions, filters as rest_framework_filters, generics,
permissions as rest_framework_permissions, response, status, views as rest_framework_views
)
from rest_framework.authtoken import serializers as authtoken_serializers
from rest_framework.decorators import api_view
from django.views.decorators.csrf import csrf_exempt
from api import (
constants, exceptions as api_exceptions, filters as api_filters, models, paginations,
permissions as api_permissions, reports_writer, schemas, serializers as api_serializers,
utils as api_utils
)
from home_industry import utils as home_industry_utils
class AuthRegister(generics.CreateAPIView):
permission_classes = [api_permissions.IsAnonymousUser]
serializer_class = api_serializers.UserSerializer
class AuthCredLogin(knox_views.LoginView):
permission_classes = [rest_framework_permissions.AllowAny]
serializer_class = authtoken_serializers.AuthTokenSerializer
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)
def post(self, request, format=None): # pylint: disable=redefined-builtin
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
auth.login(request, user)
return super().post(request, format)
class AuthPhoneNumberLogin(rest_framework_views.APIView):
permission_classes = [rest_framework_permissions.AllowAny]
serializer_class = api_serializers.PhoneNumberSerializer
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)
def post(self, request, _format=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = shortcuts.get_object_or_404(
models.User,
phone_number=serializer.validated_data['phone_number']
)
user.otp = api_utils.generate_otp()
user.save()
api_utils.send_otp(str(user.phone_number), user.otp)
token = api_utils.generate_bearer_token(user)
return response.Response({'token': token}, status=status.HTTP_200_OK)
class AuthOTPLogin(knox_views.LoginView):
permission_classes = [rest_framework_permissions.AllowAny]
serializer_class = api_serializers.OTPSerializer
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)
def post(self, request, format=None): # pylint: disable=redefined-builtin
http_authorization = self.request.META.get('HTTP_AUTHORIZATION')
username = api_utils.get_username_from_bearer_token(http_authorization)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = shortcuts.get_object_or_404(models.User, username=username)
if user.otp != serializer.validated_data['otp']:
return response.Response(status=status.HTTP_400_BAD_REQUEST)
auth.login(request, user)
user.otp = ''
user.save()
return super().post(request, format)
class AuthResendOTP(rest_framework_views.APIView):
permission_classes = [rest_framework_permissions.AllowAny]
def post(self, request, _format=None):
http_authorization = self.request.META.get('HTTP_AUTHORIZATION')
username = api_utils.get_username_from_bearer_token(http_authorization)
user = shortcuts.get_object_or_404(models.User, username=username)
user.otp = api_utils.generate_otp()
user.save()
api_utils.send_otp(str(user.phone_number), user.otp)
return response.Response(status=status.HTTP_204_NO_CONTENT)
class CartUpdate(rest_framework_views.APIView):
permission_classes = [rest_framework_permissions.IsAuthenticated]
serializer_class = api_serializers.CartUpdateSerializer
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)
def post(self, request, _format=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = request.user
product = shortcuts.get_object_or_404(
models.Product,
id=serializer.validated_data['product']
)
shopping_cart = models.ShoppingCart.objects.get(user=user)
cart_item, _created = models.CartItem.objects.get_or_create(
product=product,
shopping_cart=shopping_cart
)
if serializer.validated_data['quantity'] == 0:
cart_item.delete()
else:
cart_item.quantity = serializer.validated_data['quantity']
cart_item.save()
return response.Response(status=status.HTTP_204_NO_CONTENT)
class CartOverview(rest_framework_views.APIView):
permission_classes = [rest_framework_permissions.IsAuthenticated]
def get(self, request, _format=None):
user = request.user
shopping_cart = models.ShoppingCart.objects.get(user=user)
item_subtotal = sum(
cart_item.product.price * cart_item.quantity
for cart_item in shopping_cart.cart_items.all()
)
shipping_costs = api_utils.get_shipping_costs(user)
if shipping_costs is None:
return response.Response(
{'item_subtotal': str(item_subtotal)},
status=status.HTTP_200_OK
)
return response.Response(
{'item_subtotal': str(item_subtotal), 'shipping_costs': str(shipping_costs)},
status=status.HTTP_200_OK
)
class CartCheckout(rest_framework_views.APIView):
permission_classes = [rest_framework_permissions.IsAuthenticated]
serializer_class = api_serializers.CartCheckoutSerializer
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)
def post(self, request, _format=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = request.user
shipment_config = home_industry_utils.get_shipment_config()
if user.sub_district.lower() != shipment_config.sub_district.lower():
raise rest_framework_exceptions.ParseError(_( # pylint: disable=no-member
'Cannot process shipment to other sub-districts other than {sub_district}.'
).format(sub_district=shipment_config.sub_district))
shopping_cart = models.ShoppingCart.objects.get(user=user)
cart_items = shopping_cart.cart_items.all()
if not cart_items.exists():
raise rest_framework_exceptions.ParseError(_(
'Unable to checkout because there are no items purchased.'
))
api_utils.validate_product_stock(cart_items)
transaction_status = (
'001' if serializer.validated_data['payment_method'] == 'TRF' else '002'
)
transaction = models.Transaction.objects.create(
user=user,
payment_method=serializer.validated_data['payment_method'],
donation=serializer.validated_data['donation'],
transaction_status=transaction_status,
batch = (
None if serializer.validated_data['payment_method'] == 'TRF'
else models.Batch.objects.filter(
start_date__lte=timezone.now().date(),
end_date__gte=timezone.now().date()).first()
)
)
is_success = True
for cart_item in cart_items:
product = cart_item.product
if product.stock is not None:
try:
with db_transaction.atomic():
product.stock -= cart_item.quantity
product.total_profit += cart_item.quantity*product.profit
product.save()
except db_utils.IntegrityError:
is_success = False
models.TransactionItem.objects.create(
transaction=transaction,
product=product,
quantity=cart_item.quantity
)
cart_item.delete()
if not is_success:
transaction.transaction_status = '007'
transaction.save()
raise rest_framework_exceptions.APIException(_('Checkout failed.'))
return response.Response({'transaction': transaction.id}, status=status.HTTP_200_OK)
class CartUploadPOP(rest_framework_views.APIView):
permission_classes = [rest_framework_permissions.IsAuthenticated]
serializer_class = api_serializers.CartUploadPOPSerializer
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)
def post(self, request, _format=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = request.user
bank_account_transfer_destination = shortcuts.get_object_or_404(
models.BankAccountTransferDestination,
id=serializer.validated_data['bank_account_transfer_destination']
)
transaction = shortcuts.get_object_or_404(
models.Transaction,
id=serializer.validated_data['transaction'],
user=user
)
if transaction.payment_method != 'TRF':
raise rest_framework_exceptions.PermissionDenied(_(
'The payment method for this transaction is not a transfer.'
))
if transaction.transaction_status not in ('001', '002'):
raise rest_framework_exceptions.PermissionDenied(_(
'Cannot upload proof of payment at this stage.'
))
transaction.proof_of_payment = serializer.validated_data['proof_of_payment']
transaction.user_bank_name = serializer.validated_data['user_bank_name']
transaction.user_bank_account_name = serializer.validated_data['user_bank_account_name']
transaction.bank_account_transfer_destination = bank_account_transfer_destination
transaction.update_bank_account_transfer_destination = True
transaction.transaction_status = '002'
transaction.batch = models.Batch.objects.filter(
start_date__lte=timezone.now().date(),
end_date__gte=timezone.now().date()).first()
transaction.save()
return response.Response(status=status.HTTP_204_NO_CONTENT)
class CartCompleteTransaction(rest_framework_views.APIView):
permission_classes = [rest_framework_permissions.IsAuthenticated]
serializer_class = api_serializers.CartCompleteOrCancelTransactionSerializer
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)
def post(self, request, _format=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
transaction = shortcuts.get_object_or_404(
models.Transaction,
id=serializer.validated_data['transaction'],
user=request.user
)
if transaction.transaction_status != '004':
raise rest_framework_exceptions.PermissionDenied(_(
'Transaction cannot be completed unless the status is "Being shipped".'
))
transaction.transaction_status = '005'
transaction.save()
return response.Response(status=status.HTTP_204_NO_CONTENT)
class CartCancelTransaction(rest_framework_views.APIView):
permission_classes = [rest_framework_permissions.IsAuthenticated]
serializer_class = api_serializers.CartCompleteOrCancelTransactionSerializer
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)
def post(self, request, _format=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = request.user
transaction = shortcuts.get_object_or_404(
models.Transaction,
id=serializer.validated_data['transaction'],
user=user
)
if transaction.transaction_status not in ('001', '002'):
raise rest_framework_exceptions.PermissionDenied(_(
'Transaction cannot be canceled at this stage.'
))
transaction_items = transaction.transaction_items.all()
api_utils.return_transaction_items_to_product_stock(transaction_items)
transaction.transaction_status = '006'
transaction.save()
return response.Response(status=status.HTTP_204_NO_CONTENT)
class DonationCreate(rest_framework_views.APIView):
permission_classes = [rest_framework_permissions.IsAuthenticated]
serializer_class = api_serializers.DonationCreateSerializer
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)
def post(self, request, _format=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = request.user
program = shortcuts.get_object_or_404(
models.Program,
id=serializer.validated_data['program']
)
if not program.open_donation:
raise rest_framework_exceptions.PermissionDenied(_(
'This program is currently not accepting donations.'
))
program_donation = None
if serializer.validated_data['donation_type'] == 'CSH':
bank_account_transfer_destination = shortcuts.get_object_or_404(
models.BankAccountTransferDestination,
id=serializer.validated_data['bank_account_transfer_destination']
)
program_donation = models.ProgramDonation.objects.create(
user=user,
program=program,
donation_type='CSH',
amount=serializer.validated_data['amount'],
proof_of_bank_transfer=serializer.validated_data['proof_of_bank_transfer'],
user_bank_name=serializer.validated_data['user_bank_name'],
user_bank_account_name=serializer.validated_data['user_bank_account_name'],
bank_account_transfer_destination=bank_account_transfer_destination
)
else:
if serializer.validated_data['delivery_method'] == 'DLV':
program_donation = models.ProgramDonation.objects.create(
user=user,
program=program,
donation_type='GDS',
goods_quantity=serializer.validated_data['goods_quantity'],
goods_description=serializer.validated_data['goods_description'],
delivery_method=serializer.validated_data['delivery_method'],
delivery_address=None
)
else:
program_donation = models.ProgramDonation.objects.create(
user=user,
program=program,
donation_type='GDS',
goods_quantity=serializer.validated_data['goods_quantity'],
goods_description=serializer.validated_data['goods_description'],
delivery_method=serializer.validated_data['delivery_method'],
delivery_address=serializer.validated_data['delivery_address']
)
return response.Response(
{'program_donation': program_donation.id},
status=status.HTTP_200_OK
)
class DonationReuploadProofOfBankTransfer(rest_framework_views.APIView):
permission_classes = [rest_framework_permissions.IsAuthenticated]
serializer_class = api_serializers.DonationReuploadProofOfBankTransferSerializer
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs)
def post(self, request, _format=None):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = request.user
bank_account_transfer_destination = shortcuts.get_object_or_404(
models.BankAccountTransferDestination,
id=serializer.validated_data['bank_account_transfer_destination']
)
program_donation = shortcuts.get_object_or_404(
models.ProgramDonation,
id=serializer.validated_data['program_donation'],
user=user
)
if program_donation.donation_status not in ('001', '004'):
raise rest_framework_exceptions.PermissionDenied(_(
'Cannot reupload proof of bank transfer at this stage.'
))
if program_donation.donation_type != 'CSH':
raise rest_framework_exceptions.PermissionDenied(_(
'Cannot proof of bank transfer foor good donation.'
))
program_donation.amount = serializer.validated_data['amount']
program_donation.proof_of_bank_transfer = (
serializer.validated_data['proof_of_bank_transfer']
)
program_donation.user_bank_name = serializer.validated_data['user_bank_name']
program_donation.user_bank_account_name = (
serializer.validated_data['user_bank_account_name']
)
program_donation.bank_account_transfer_destination = bank_account_transfer_destination
program_donation.update_bank_account_transfer_destination = True
program_donation.donation_status = '001'
program_donation.save()
return response.Response(status=status.HTTP_204_NO_CONTENT)
class ReportAPIView(rest_framework_views.APIView):
permission_classes = [rest_framework_permissions.IsAdminUser]
report_function = None
serializer_class = None
def get_filename(self, query_params):
raise NotImplementedError
def get_serializer(self, *args, **kwargs):
return self.serializer_class(*args, **kwargs) # pylint: disable=not-callable
def get(self, request, _format=None):
assert self.report_function is not None, (
'{} should include a `report_function` attribute.'.format(self.__class__.__name__)
)
assert self.report_function is not None, (
'{} should include a `serializer_class` attribute.'.format(self.__class__.__name__)
)
serializer = self.get_serializer(data=request.query_params)
serializer.is_valid(raise_exception=True)
buffer = self.report_function.__func__(serializer.validated_data)
buffer.seek(0)
return http.FileResponse(buffer, filename=self.get_filename(serializer.validated_data))
class ReportTransaction(ReportAPIView):
report_function = reports_writer.create_transaction_report
schema = schemas.ReportTransactionSchema()
serializer_class = api_serializers.ReportTransactionSerializer
def get_filename(self, query_params):
filename = '{}.xlsx'.format(_( # pylint: disable=no-member
'Transaction Report from {date_from} to {date_to} for {batch_name}'
).format(
date_from=query_params.get('created_at_date_range_after', '_'),
date_to=query_params.get('created_at_date_range_before', str(timezone.now())[:10]),
batch_name=query_params.get('batch_name', '_')
))
return filename
class ReportProgramDonationCSH(ReportAPIView):
report_function = reports_writer.create_program_donation_report_CSH
schema = schemas.ReportProgramDonationSchema()
serializer_class = api_serializers.ReportProgramDonationSerializer
def get_filename(self, query_params):
filename = '{}.xlsx'.format(_( # pylint: disable=no-member
'Program Donation Report from {date_from} to {date_to}'
).format(
date_from=query_params.get('created_at_date_range_after', '_'),
date_to=query_params.get('created_at_date_range_before', str(timezone.now())[:10])
))
return filename
class ReportProgramDonationGDS(ReportAPIView):
report_function = reports_writer.create_program_donation_report_GDS
schema = schemas.ReportProgramDonationSchema()
serializer_class = api_serializers.ReportProgramDonationSerializer
def get_filename(self, query_params):
filename = '{}.xlsx'.format(_( # pylint: disable=no-member
'Program Donation Report from {date_from} to {date_to}'
).format(
date_from=query_params.get('created_at_date_range_after', '_'),
date_to=query_params.get('created_at_date_range_before', str(timezone.now())[:10])
))
return filename
class UserList(generics.ListCreateAPIView):
filter_backends = [
rest_framework.DjangoFilterBackend,
rest_framework_filters.OrderingFilter,
rest_framework_filters.SearchFilter,
]
filterset_fields = ['username', 'phone_number']
ordering_fields = [
'username', 'full_name', 'phone_number',
'total_transactions', 'total_program_donations_goods', 'total_program_donations_cash'
]
pagination_class = paginations.SmallResultsSetPagination
permission_classes = [rest_framework_permissions.IsAdminUser]
queryset = models.User.objects.all()
search_fields = ['username', 'full_name', 'phone_number']
serializer_class = api_serializers.UserSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [
api_permissions.IsAdminUserOrSelf,
rest_framework_permissions.IsAuthenticated,
]
queryset = models.User.objects.all()
serializer_class = api_serializers.UserSerializer
def get_object(self):
if self.kwargs.get('pk') == 'self':
self.kwargs['pk'] = self.request.user.id
return super().get_object()
class BankAccountTransferDestinationList(generics.ListCreateAPIView):
filter_backends = [rest_framework_filters.SearchFilter]
pagination_class = paginations.SmallResultsSetPagination
permission_classes = [
api_permissions.IsAdminUserOrReadOnly,
rest_framework_permissions.IsAuthenticated,
]
queryset = models.BankAccountTransferDestination.objects.all()
search_fields = ['bank_name', 'bank_account_number', 'bank_account_name']
serializer_class = api_serializers.BankAccountTransferDestinationSerializer
class BankAccountTransferDestinationDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [
api_permissions.IsAdminUserOrReadOnly,
rest_framework_permissions.IsAuthenticated,
]
queryset = models.BankAccountTransferDestination.objects.all()
serializer_class = api_serializers.BankAccountTransferDestinationSerializer
class CategoryList(generics.ListCreateAPIView):
filter_backends = [
rest_framework.DjangoFilterBackend,
rest_framework_filters.OrderingFilter,
rest_framework_filters.SearchFilter,
]
filterset_fields = ['name']
ordering_fields = ['name']
pagination_class = paginations.SmallResultsSetPagination
permission_classes = [
api_permissions.IsAdminUserOrReadOnly,
rest_framework_permissions.IsAuthenticated,
]
queryset = models.Category.objects.all()
search_fields = ['name']
serializer_class = api_serializers.CategorySerializer
class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [
api_permissions.IsAdminUserOrReadOnly,
rest_framework_permissions.IsAuthenticated,
]
queryset = models.Category.objects.all()
serializer_class = api_serializers.CategorySerializer
def destroy(self, request, *_args, **_kwargs):
instance = self.get_object()
try: