Commit 90b5400b authored by John Red Medrano's avatar John Red Medrano

initial commit

parent aac7447f
set password "t!r\$0lut10ns"
cd /var/www/devrms.oneberrysystem.com/api-main-service
spawn git pull "http://administrator@42.61.118.105:7990/scm/rms/api-main-service.git" SIT
expect "Password for"
send "$password\n"
echo "================================================================";
echo "======================REMOVING API SERVICES=====================";
docker-compose -f api-main-service/docker-compose.yml down
echo "================================================================";
cd /var/www/red-ci-cd/
echo "================================================================";
echo "======================BUILDING API SERVICES=====================";
docker-compose -f api-main-service/docker-compose.yml up -d --build
echo "================================================================";
FROM python:3.6
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
ADD . /code
COPY . /code
WORKDIR /code
RUN pip install -r requirements/local.txt
EXPOSE 1000
RUN python manage.py migrate
File added
set password "t!r\$0lut10ns"
cd /var/www/devrms.oneberrysystem.com/api-main-service
spawn git pull "http://administrator@42.61.118.105:7990/scm/rms/api-main-service.git" staging
expect "Password for"
send "$password\n"
interact
echo "================================================================";
echo "======================REMOVING API SERVICES=====================";
docker-compose -f api-main-service/docker-compose.yml down
echo "================================================================";
cd /var/www/red-ci-cd/
echo "================================================================";
echo "======================BUILDING API SERVICES=====================";
docker-compose -f api-main-service/docker-compose.yml up -d --build --remove-orphans
echo "================================================================";
from django.apps import AppConfig
class AccesslayerConfig(AppConfig):
name = 'accesslayer'
from app.middleware.current_user.data import set_current_user
from rest_framework.authentication import TokenAuthentication
from rest_framework import exceptions
from datetime import datetime, timedelta
from django.conf import settings
class AppTokenAuthentication(TokenAuthentication):
keyword = 'Bearer'
def authenticate_credentials(self, key):
user, token = super(AppTokenAuthentication,
self).authenticate_credentials(key)
if token.created < datetime.now() - timedelta(minutes=int(settings.SESSION_TIMEOUT)):
token.delete()
raise exceptions.AuthenticationFailed('Token has expired')
else:
token.created = datetime.now()
token.save()
set_current_user(user)
return (user, token)
from django.urls import path, re_path
from app.accesslayer import views
urlpatterns = (
path(r'login/', views.Login.as_view(), name="API Login"),
re_path(r'^logout/', views.Logout.as_view(), name="API Logout"),
re_path(r'^refresh-token/(?P<token>\w+)/$', views.RefreshToken.as_view(), name="Refresh Token"),
path(r'current-user/', views.CurrentUser.as_view(), name="Current User"),
# re_path(r'^forgot-password/(?P<username>\w+)/$', views.ForgotPassword.as_view(), name="Forgot Password"),
# re_path(r'^validate-forgot-password-reset-token/(?P<token>\w+)/$', views.ValidateForgotPasswordResetToken.as_view(), name="Validate Forgot Password Reset Token"),
# re_path(r'^forgot-password-reset/(?P<token>\w+)/$', views.ForgotPasswordReset.as_view(), name="Forgot Password Reset"),
path('forgot-password/', views.ForgotPassword.as_view()),
path('reset-password-link/', views.ValidateForgotPasswordResetToken.as_view()),
path('forgot-password-reset/', views.ForgotPasswordReset.as_view()),
)
import json
import threading
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import AllowAny
from django.db import transaction
from app.helper import decorators
from app.applicationlayer.management.account.serializer import (
UserManagementRetreiveSerializer
)
from app.entities.models import User, AuthToken
from datetime import datetime
from random import randrange
from django.conf import settings
from app.helper.email_service import sender
from app.applicationlayer.utils import main_threading
from rest_framework.exceptions import ParseError
from datetime import timedelta
class Login(ObtainAuthToken):
@decorators.error_safe
def post(self, request, *args, **kwargs):
try:
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
if not created:
token.created = datetime.now()
token.save()
return Response({
'token': token.key,
})
except Exception as e:
return Response(
{"message": "Unable to log in with provided credentials."}
)
class Logout(APIView):
@decorators.error_safe
def post(self, request, *args, **kwargs):
existingUser = self.request.user
if existingUser:
existingToken = Token.objects.filter(user=existingUser).first()
if existingToken:
existingToken.delete()
return Response(data={"detail": "User was logged out"},
status=status.HTTP_200_OK)
else:
return Response(data={"detail": "User session not found"},
status=status.HTTP_404_NOT_FOUND)
else:
return Response(data={"detail": "User not found"},
status=status.HTTP_404_NOT_FOUND)
class RefreshToken(APIView):
@decorators.error_safe
def post(self, request, token=None, *args, **kwargs):
existingToken = Token.objects.filter(key=token).first()
if existingToken:
if existingToken.user == request.user:
existingToken.created = datetime.now()
existingToken.save()
return Response(data={"detail": "Token refreshed"},
status=status.HTTP_200_OK)
else:
return Response(data={"detail": "Token user not match"},
status=status.HTTP_401_UNAUTHORIZED)
else:
return Response(data={"detail": "Token not found"},
status=status.HTTP_404_NOT_FOUND)
class CurrentUser(APIView):
def get(self, request, token=None, *args, **kwargs):
serializer = UserManagementRetreiveSerializer
context = {"request": request}
serializer = serializer(request.user, context=context)
serializer.data['key'] = 'value'
serialized = serializer.data
return Response(data=serialized,
status=status.HTTP_200_OK)
'''
***********************
** RESET CREDENTIALS **
***********************
'''
class ForgotPassword(APIView):
permission_classes = (AllowAny,)
@transaction.atomic
def post(self, request, *args, **kwargs):
email = request.data['email']
username = request.data['username']
try:
user = request.user.email
except Exception as e:
user = str(settings.CATCH_EMAIL)
existingUser = User.objects.filter(
email=email,
username=username).first()
date_now = datetime.now()
if existingUser:
# Check if there's existing request
exToken = AuthToken\
.objects\
.filter(
user=existingUser.code,
is_active=True,
timeout_at__lt=date_now)\
.first()
if exToken:
raise ParseError('There is an existing password reset for this user.')
REF = 'AUTH'
TOKEN = ''
PASSCODE = 0
# Generate Random token for TOKEN
TOKEN = Token().generate_key()
# Generate Random number for PASSCODE
rands = []
rands.append(randrange(10))
rands.append(randrange(10))
rands.append(randrange(10))
rands.append(randrange(10))
PASSCODE = f"{rands[0]}{rands[1]}{rands[2]}{rands[3]}"
date_now = datetime.now()
timeout_at = date_now + timedelta(seconds=3600)
AuthToken(
ref=REF,
token=TOKEN,
passcode=PASSCODE,
is_active=True,
user=existingUser,
created=date_now,
timeout_at=timeout_at
).save()
url = f"{settings.FRONT_END_URL}/forgot-password/reset"\
f"?token={TOKEN}"
args = [str(PASSCODE), str(url), user, str(existingUser.email),
str(existingUser.name)]
main_threading(args, sender.forgot_password)
return Response(data={"detail": "Forgot Password Sent"},
status=status.HTTP_200_OK)
else:
return Response(data={"error": "User not found"},
status=status.HTTP_404_NOT_FOUND)
class ValidateForgotPasswordResetToken(APIView):
permission_classes = (AllowAny,)
@decorators.error_safe
@transaction.atomic
def post(self, request, *args, **kwargs):
token = request.data['token']
date_now = datetime.now()
existingToken = AuthToken.objects.filter(token=token).first()
if existingToken:
if not existingToken.is_active:
raise Exception('Request is no longer active')
elif existingToken.timeout_at < date_now:
raise Exception('Token already expired')
return Response(data={
"username": existingToken.user.username,
"email": existingToken.user.email
},
status=status.HTTP_200_OK)
else:
return Response(data={"error": "Token not found"},
status=status.HTTP_404_NOT_FOUND)
class ForgotPasswordReset(APIView):
permission_classes = (AllowAny,)
@decorators.error_safe
@transaction.atomic
def post(self, request, *args, **kwargs):
body_unicode = request.body.decode('utf-8')
body_data = json.loads(body_unicode)
username = body_data['username']
password = body_data['password']
password_confirm = body_data['password_confirm']
passcode = body_data['passcode']
token = body_data['token']
if not username:
raise Exception('Username is required')
if not passcode:
raise Exception('Passcode is required')
if password != password_confirm:
raise Exception('Passwords must match')
existingToken = AuthToken.objects.filter(token=token).first()
if existingToken:
if existingToken.user.username != username:
raise Exception('Username does not match')
if not existingToken.is_active:
raise Exception('Request is no longer active')
if existingToken.passcode != passcode:
raise Exception('Invalid Passcode')
# TODO: Reset password here
exUser = User.objects.filter(id=existingToken.user.id).first()
exUser.set_password(password_confirm)
exUser.save()
existingToken.is_active = False
existingToken.save()
# sender.password_changed(
# str(existingToken.user.username),
# str(datetime.now()),
# str(existingToken.user.email))
# args = [str(PASSCODE), str(url), str(existingUser.email), user]
# t1 = threading.Thread(target=sender.forgot_password, args=(args,))
# t1.start()
# args = [str(PASSCODE), str(url), user, str(existingUser.email)]
# t2 = threading.Thread(target=sender.forgot_password, args=(args,))
# t2.start()
return Response(data={"detail": "Forgot Password Reset Success"},
status=status.HTTP_200_OK)
else:
return Response(data={"error": "Token not found"},
status=status.HTTP_404_NOT_FOUND)
from rest_framework import serializers
from app.entities.models import AMSPrivilege
from app.applicationlayer.utils import model_to_dict
class AMSPrivilegeSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
ret = super().to_representation(instance)
ret['id_number'] = model_to_dict(instance.id_number)
ret['department'] = model_to_dict(instance.department)
ret['company'] = model_to_dict(instance.department.company)
ret['ams_user_type'] = instance.ams_user_type
return ret
class Meta:
model = AMSPrivilege
fields = '__all__'
from django_filters import rest_framework as filters
from app.entities.models import AMSPrivilege
from django.db.models import Q
class AMSPrivilegeFilterSet(filters.FilterSet):
class Meta:
model = AMSPrivilege
fields = '__all__'
from rest_framework import status, viewsets
from rest_framework.response import Response
from app.entities.models import AMSPrivilege
from app.applicationlayer.utils import (CustomPagination,
status_message_response)
from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from app.applicationlayer.ams.ams_privilege.serializer import (
AMSPrivilegeSerializer
)
from app.applicationlayer.ams.ams_privilege.table_filter import (
AMSPrivilegeFilterSet
)
from django.db import transaction
from rest_framework.exceptions import ValidationError
from django.db import IntegrityError
class AMSPrivilegeViewSet(viewsets.ModelViewSet):
queryset = AMSPrivilege.objects.select_related(
'id_number', 'department'
).all()
serializer_class = AMSPrivilegeSerializer
pagination_class = CustomPagination
lookup_field = 'id_number'
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
filterset_class = AMSPrivilegeFilterSet
ordering_fields = '__all__'
search_fields = (
'id_number__code',
'department__code',
'ams_user_type'
)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'list of AMS Privilege found',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(queryset, many=True)
return Response(
serializer.data,
status=status.HTTP_200_OK
)
@transaction.atomic
def create(self, request, *args, **kwargs):
try:
form = request.data['form']
id_number = form[0]['id_number']
AMSPrivilege.objects.filter(id_number=id_number).delete()
serializer = AMSPrivilegeSerializer(
data=form, many=True
)
if serializer.is_valid(raise_exception=True):
serializer.save()
message = {
'code': 201,
'status': 'success',
'message': 'AMS User Privilege successfully saved!',
'results': serializer.data
}
return Response(message, status=status.HTTP_201_CREATED)
except ValidationError as e:
message = {
'code': 400,
'status': 'failed',
'message': str(e),
}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
except IntegrityError as e:
error = 'Adding the same Department under the same Company is not allowed'
message = {
'code': 400,
'status': 'failed',
'message': error,
}
return Response(message,
status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
message = {
'code': 500,
'status': 'failed',
'message': str(e),
}
return Response(message,
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
from rest_framework.views import APIView
from rest_framework.response import Response
from app.entities import enums
class AMSUserTypeViewSet(APIView):
def get(self, request, format=None):
manager_value = enums.AMSUserTypeEnum.MANAGER.value
user_client_value = enums.AMSUserTypeEnum.USER_CLIENT.value
support_value = enums.AMSUserTypeEnum.SUPPORT.value
viewer_value = enums.AMSUserTypeEnum.VIEWER.value
manager_key = str(enums.AMSUserTypeEnum.MANAGER.name).capitalize()
user_client_key = str(enums.AMSUserTypeEnum.USER_CLIENT.name).replace('_', '/').title()
support_key = str(enums.AMSUserTypeEnum.SUPPORT.name).capitalize()
viewer_key = str(enums.AMSUserTypeEnum.VIEWER.name).capitalize()
data = {
"code": "200",
"status": "success",
"message": "list of ams user types",
"results":
[
{
"name": manager_key,
"value": manager_value,
},
{
"name": user_client_key,
"value": user_client_value
},
{
"name": support_key,
"value": support_value
},
{
"name": viewer_key,
"value": viewer_value
}
]
}
return Response(data)
\ No newline at end of file
from app.entities.models import AMSAsset
from rest_framework import serializers
class AMSAssetSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
ret = super().to_representation(instance)
ret['asset_type'] = instance.asset_type.name
ret['in_qty'] = instance.ams_asset_to_assetdetail.filter(status='In-Store').count()
ret['out_qty'] = instance.ams_asset_to_assetdetail.filter(status='Deployed').count()
ret['unavailable'] = instance.ams_asset_to_assetdetail.filter(
status__in=['Faulty', 'Returned', 'Written Off']
).count()
ret['created_by'] = instance.created_by.name
return ret
class Meta:
model = AMSAsset
fields = '__all__'
from django_filters import rest_framework as filters
from app.entities.models import AMSAsset
class AMSAssetFilter(filters.FilterSet):
class Meta:
model = AMSAsset
fields = '__all__'
from app.entities.models import AMSAsset, AMSAssetType
from rest_framework import viewsets, status
from rest_framework.response import Response
from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from app.applicationlayer.ams.asset.table_filters import AMSAssetFilter
from app.applicationlayer.ams.asset_stock.serializers import AMSAssetStockSerializer
from app.applicationlayer.ams.asset import serializers
from django.db import transaction
from app.applicationlayer.utils import(
CustomPagination, status_message_response
)
from rest_framework.decorators import action
from app.applicationlayer.utils import log_save, enums
from app.applicationlayer.utils import model_to_dict
from django.db import IntegrityError
from django.db.models import Q
from app.helper.decorators import AssetValidation
class AMSAssetViewSet(viewsets.ModelViewSet):
queryset = AMSAsset.objects.all()
serializer_class = serializers.AMSAssetSerializer
pagination_class = CustomPagination
lookup_field = 'code'
filter_backends = (
DjangoFilterBackend, SearchFilter,
OrderingFilter
)
ordering_fields = '__all__'
search_filter = (
'code',
'name',
'asset_type__name'
)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset()).filter(is_active=True)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'List of Asset found!',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(self.queryset, many=True)
return Response(
serializer.data,
status=status.HTTP_200_OK
)
@AssetValidation
@transaction.atomic
def create(self, request, *args, **kwargs):
form = request.data
form['created_by'] = request.user.code
serializer = self.get_serializer(data=form)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
message = status_message_response(
201, 'success',
'New Asset created', serializer.data
)
return Response(
message
)
@AssetValidation
@transaction.atomic
def update(self, request, *args, **kwargs):
try:
partial = kwargs.pop('partial', False)
instance = self.get_object()
form = request.data
form['created_by'] = request.user.code
serializer = self.get_serializer(instance, data=form, partial=partial)
serializer.is_valid(raise_exception=True)
old_instance = model_to_dict(instance)
self.perform_update(serializer)
new_instance = serializer.data
log_save(
enums.LogEnum.UPDATE.value,
enums.LogEntitiesEnum.AMSAsset.value,
old_instance['id'],
old_instance,
new_instance
)
return Response(serializer.data)
except IntegrityError as e:
return Response(
{"message": "Cannot delete or update this reocrd it has foreign key constraint to other tables"},
status=status.HTTP_400_BAD_REQUEST
)
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
@transaction.atomic
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
if instance.ams_asset_to_assetdetail.count() > 0:
message = status_message_response(
400, 'Failed',
'This Asset had an Asset Stocks', model_to_dict(instance)
)
return Response(
message,
status=status.HTTP_400_BAD_REQUEST
)
instance.is_active = False
instance.save()
new_instance = model_to_dict(instance)
log_save(
enums.LogEnum.DELETED.value,
enums.LogEntitiesEnum.AMSAsset.value,
new_instance['id'],
new_instance,
''
)
return Response(status=status.HTTP_204_NO_CONTENT)
@action(
methods=['PATCH'], detail=True,
url_path='restore', url_name='restore'
)
def restore(self, request, code=None):
try:
instance = self.get_object()
instance.is_active = True
instance.save()
new_instance = model_to_dict(instance)
log_save(
enums.LogEnum.UPDATE.value,
enums.LogEntitiesEnum.AMSAsset.value,
new_instance['id'],
new_instance,
''
)
message = status_message_response(
200, 'success',
'Archived Asset restored',
''
)
return Response(message, status=status.HTTP_200_OK)
except Exception as e:
message = status_message_response(
500, 'failed',
'Request was not able to process' + str(e), []
)
return Response(message,
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
@action(methods=['GET'], detail=True,
url_path='stock-list', url_name='stock_list')
def stock_list(self, request, code):
self.serializer_class = AMSAssetStockSerializer
queryset = self.get_object().ams_asset_to_assetdetail.all()
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'List of Archived Assets found',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(self.queryset, many=True)
return Response(
serializer.data,
status=status.HTTP_200_OK
)
@action(
methods=['GET'], detail=False,
url_path='archived', url_name='archived'
)
def archived(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
queryset = queryset.filter(is_active=False)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'List of Archived Assets found',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(self.queryset, many=True)
return Response(
serializer.data,
status=status.HTTP_200_OK
)
\ No newline at end of file
from app.entities import models
from rest_framework import serializers
from app.applicationlayer.utils import model_to_dict
from django.db.models import Q
class AssetGroupSerializerList(serializers.ModelSerializer):
def to_representation(self, instance):
ret = super().to_representation(instance)
ret['asset_type_count'] = instance.ams_asset_to_assetgroup.values('asset__asset_type').distinct().count()
ret['asset_count'] = instance.ams_asset_to_assetgroup.values('asset').distinct().count()
ret['asset_stock_count'] = instance.ams_asset_to_assetgroup.count()
return ret
class Meta:
model = models.AMSAssetGroup
fields = '__all__'
read_only_fields = ['created', 'code']
class AssetGroupSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
ret = super().to_representation(instance)
ret['asset_type_count'] = instance.ams_asset_to_assetgroup.values('asset__asset_type').distinct().count()
ret['asset_count'] = instance.ams_asset_to_assetgroup.values('asset').distinct().count()
ret['asset_stock_count'] = instance.ams_asset_to_assetgroup.count()
ret['own'] = instance.ams_asset_to_assetgroup.filter(acquisition_type='Owned').count()
ret['loan'] = instance.ams_asset_to_assetgroup.filter(acquisition_type='Loan').count()
ret['in_qty'] = instance.ams_asset_to_assetgroup.filter(status='In-Store').count()
ret['out_qty'] = instance.ams_asset_to_assetgroup.filter(status='Deployed').count()
ret['unavailable'] = instance.ams_asset_to_assetgroup.filter(
status__in=['Faulty', 'Returned', 'Written Off']
).count()
ret['in_store'] = instance.ams_asset_to_assetgroup.filter(status='In-Store').count()
ret['deployed'] = instance.ams_asset_to_assetgroup.filter(status='Deployed').count()
ret['returned'] = instance.ams_asset_to_assetgroup.filter(status='Returned').count()
ret['written_off'] = instance.ams_asset_to_assetgroup.filter(status='Written Off').count()
ret['faulty'] = instance.ams_asset_to_assetgroup.filter(status='Faulty').count()
return ret
class Meta:
model = models.AMSAssetGroup
fields = '__all__'
read_only_fields = ['created', 'code']
\ No newline at end of file
from django_filters import rest_framework as filters
from app.entities.models import AMSAssetGroup
class AssetGroupFilter(filters.FilterSet):
class Meta:
model = AMSAssetGroup
fields = '__all__'
from app.entities import models
from rest_framework import viewsets, status
from rest_framework.response import Response
from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from app.applicationlayer.ams.asset_group.table_filters import AssetGroupFilter
from app.applicationlayer.ams.asset_group import serializers
from app.applicationlayer.utils import (
CustomPagination, status_message_response
)
from app.helper import decorators
from django.db import transaction
from rest_framework.decorators import action
from app.applicationlayer.utils import log_save, enums
from app.applicationlayer.utils import model_to_dict
from app.helper.decorators import AssetGroupValidation
from django.db import IntegrityError
class AssetGroupViewset(viewsets.ModelViewSet):
queryset = models.AMSAssetGroup.objects.all()
serializer_class = serializers.AssetGroupSerializer
pagination_class = CustomPagination
lookup_field = "code"
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
ordering_fields = '__all__'
search_fields = (
'code', 'name'
)
@AssetGroupValidation
@transaction.atomic
def create(self, request, *args, **kwargs):
form = request.data
form['created_by'] = request.user.code
serializer = self.get_serializer(data=form)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
message = status_message_response(
201, 'success',
'New Asset Group created', serializer.data
)
return Response(
message
)
# @AssetGroupValidation
@transaction.atomic
def update(self, request, *args, **kwargs):
try:
partial = kwargs.pop('partial', False)
instance = self.get_object()
form = request.data
form['created_by'] = request.user.code
serializer = self.get_serializer(instance, data=form, partial=partial)
serializer.is_valid(raise_exception=True)
old_instance = model_to_dict(instance)
self.perform_update(serializer)
new_instance = serializer.data
log_save(
enums.LogEnum.UPDATE.value,
enums.LogEntitiesEnum.AMSAssetGroup.value,
old_instance['id'],
old_instance,
new_instance
)
return Response(serializer.data)
except IntegrityError as e:
return Response(
{"message": "Cannot delete or update this record it has foreign key constraint to other tables"},
status=status.HTTP_400_BAD_REQUEST
)
def list(self, request, *args, **kwargs):
self.serializer_class = serializers.AssetGroupSerializerList
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'List of Asset Groups found!',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(self.queryset, many=True)
return Response(
serializer.data,
status=status.HTTP_200_OK
)
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
@transaction.atomic
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
if instance.ams_asset_to_assetgroup.count() > 0:
message = status_message_response(
400, 'Failed',
'This Asset Group had an Asset Stocks', model_to_dict(instance)
)
return Response(
message,
status=status.HTTP_400_BAD_REQUEST
)
instance.is_active = False
instance.save()
new_instance = model_to_dict(instance)
log_save(
enums.LogEnum.DELETED.value,
enums.LogEntitiesEnum.AMSAssetGroup.value,
new_instance['id'],
new_instance,
''
)
return Response(status=status.HTTP_204_NO_CONTENT)
@action(
methods=['GET'], detail=False,
url_path='archived', url_name='archived'
)
def archived(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
queryset = queryset.filter(is_active=False)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'List of Archived Asset Groups found',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(self.queryset, many=True)
return Response(
serializer.data,
status=status.HTTP_200_OK
)
@action(
methods=['PATCH'], detail=True,
url_path='restore', url_name='restore'
)
def restore(self, request, code=None):
try:
instance = self.get_object()
instance.is_active = True
instance.save()
new_instance = model_to_dict(instance)
log_save(
enums.LogEnum.UPDATE.value,
enums.LogEntitiesEnum.AMSAssetGroup.value,
new_instance['id'],
new_instance,
''
)
message = status_message_response(
200, 'success',
'Archived Asset Group restored',
''
)
return Response(message, status=status.HTTP_200_OK)
except Exception as e:
message = status_message_response(
500, 'failed',
'Request was not able to process' + str(e), []
)
return Response(message,
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
from app.entities.models import AMSAssetStock
from rest_framework import serializers
class AMSAssetStockSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
ret = super().to_representation(instance)
ret['asset_group'] = instance.asset_group.name
ret['asset'] = instance.asset.name
ret['asset_type'] = instance.asset.asset_type.name
ret['manager'] = instance.manager.name
ret['user_client'] = instance.user_client.name
# ret['created_by'] = {
# "name": instance.created_by.name,
# "code": instance.created_by.code
# }
return ret
class Meta:
model = AMSAssetStock
fields = '__all__'
\ No newline at end of file
from django_filters import rest_framework as filters
from app.entities.models import AMSAssetStock
class AMSAssetStockFilter(filters.FilterSet):
class Meta:
model = AMSAssetStock
fields = '__all__'
\ No newline at end of file
from app.entities.models import AMSAssetStock
from rest_framework import viewsets, status
from rest_framework.response import Response
from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from app.applicationlayer.ams.asset_stock.table_filters import AMSAssetStockFilter
from app.applicationlayer.ams.asset_stock import serializers
from django.db import transaction
from app.applicationlayer.utils import(
CustomPagination, status_message_response
)
from rest_framework.decorators import action
from app.applicationlayer.utils import log_save, enums
from app.applicationlayer.utils import model_to_dict
from django.db import IntegrityError
from django.db.models import Q
from app.helper.decorators import AssetStockValidation
from django.db.models import Count
class AMSAssetStockViewSet(viewsets.ModelViewSet):
queryset = AMSAssetStock.objects.all()
serializer_class = serializers.AMSAssetStockSerializer
pagination_class = CustomPagination
lookup_field = 'code'
filter_backends = (
DjangoFilterBackend, SearchFilter,
OrderingFilter
)
ordering_fields = '__all__'
search_filter = (
'code',
'name',
'asset_group__name',
'asset__name',
'asset_type__name',
)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset()).filter(is_active=True)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'List of Asset Stock list found!',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(self.queryset, many=True)
return Response(
serializer.data,
status=status.HTTP_200_OK
)
@action(detail=False,
methods=['get'],
url_path='dashboard',
name="Dashboard Summary")
def dashboard_asset(self, request):
in_store = self.queryset.filter(
status__iexact='In-Store'
).count()
deployed = self.queryset.filter(
status__iexact='Deployed'
).count()
returned = self.queryset.filter(
status__iexact='Returned'
).count()
written_off = self.queryset.filter(
status__iexact='Written Off'
).count()
faulty = self.queryset.filter(
status__iexact='Faulty'
).count()
in_qty = self.queryset.filter(
status__iexact='In-Store'
).count()
out_qty = self.queryset.filter(
status__iexact='Deployed'
).count()
unavailable = self.queryset.filter(
status__iexact=['Faulty', 'Returned', 'Written Off']
).count()
top_asset_groups = self.queryset.values('asset_group__name', 'asset_group', 'asset').annotate(
asset_type_count=Count('asset__asset_type'),
asset_count=Count('asset'),
asset_stock_count=Count('asset_group')).order_by('-asset_group')
recent_reports = 0
message = {
'in_store': in_store,
'deployed': deployed,
'returned': returned,
'written_off': written_off,
'faulty': faulty,
'in_qty': in_qty,
'out_qty': out_qty,
'unavailable': unavailable,
'top_asset_groups': top_asset_groups,
'recent_reports': recent_reports,
'code': 200,
'status': 'success',
'message': 'Dashboard Summary'
}
return Response(message, status=status.HTTP_200_OK)
@AssetStockValidation
@transaction.atomic
def update(self, request, *args, **kwargs):
try:
partial = kwargs.pop('partial', False)
instance = self.get_object()
form = request.data
form['created_by'] = request.user.code
serializer = self.get_serializer(instance, data=form, partial=partial)
serializer.is_valid(raise_exception=True)
old_instance = model_to_dict(instance)
self.perform_update(serializer)
new_instance = serializer.data
log_save(
enums.LogEnum.UPDATE.value,
enums.LogEntitiesEnum.AMSReport.value,
old_instance['id'],
old_instance,
new_instance
)
return Response(serializer.data)
except IntegrityError as e:
return Response(
{"message": "Cannot delete or update this reocrd it has foreign key constraint to other tables"},
status=status.HTTP_400_BAD_REQUEST
)
@AssetStockValidation
@transaction.atomic
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
message = status_message_response(
201, 'success',
'New Asset Stock created', serializer.data
)
return Response(
message
)
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
@transaction.atomic
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
instance.is_active = False
instance.save()
new_instance = model_to_dict(instance)
log_save(
enums.LogEnum.UPDATE.value,
enums.LogEntitiesEnum.AMSAssetStock.value,
new_instance['id'],
new_instance,
''
)
return Response(status=status.HTTP_204_NO_CONTENT)
@action(
methods=['GET'], detail=False,
url_path='archived', url_name='archived'
)
def archived(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
queryset = queryset.filter(is_active=False)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'List of Archived Asset Stocks found',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(self.queryset, many=True)
return Response(
serializer.data,
status=status.HTTP_200_OK
)
@action(
methods=['PATCH'], detail=True,
url_path='restore', url_name='restore'
)
def restore(self, request, code=None):
try:
instance = self.get_object()
instance.is_active = True
instance.save()
new_instance = model_to_dict(instance)
log_save(
enums.LogEnum.UPDATE.value,
enums.LogEntitiesEnum.AMSAssetStock.value,
new_instance['id'],
new_instance,
''
)
message = status_message_response(
200, 'success',
'Archived Asset Stock restored',
''
)
return Response(message, status=status.HTTP_200_OK)
except Exception as e:
message = status_message_response(
500, 'failed',
'Request was not able to process' + str(e), []
)
return Response(message,
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
from app.entities import models
from rest_framework import serializers
from app.applicationlayer.utils import model_to_dict
class AssetTypeSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
ret = super().to_representation(instance)
assets = instance.ams_asset_type.select_related()
# assets1 = instance.ams_asset_type.count()
# asset_stock1 = instance.ams_asset_type.values('')
# ams_asset_to_assetdetail
# print(assets1)
ams_stock_counter = 0
for asset in assets:
ams_stock_counter = ams_stock_counter + models.AMSAsset.objects.get(code=asset.code).ams_asset_to_assetdetail.count()
ret['asset_count'] = assets.count()
ret['asset_stock_count'] = ams_stock_counter
return ret
class Meta:
model = models.AMSAssetType
fields = '__all__'
read_only_fields = ['created', 'code']
\ No newline at end of file
from django_filters import rest_framework as filters
from app.entities.models import AMSAssetType
class AssetTypeFilter(filters.FilterSet):
class Meta:
model = AMSAssetType
fields = '__all__'
\ No newline at end of file
from app.entities import models
from rest_framework import viewsets, status
from rest_framework.response import Response
from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from app.applicationlayer.ams.asset_type.table_filters import AssetTypeFilter
from app.applicationlayer.ams.asset_type import serializers
from app.applicationlayer.utils import (
CustomPagination, status_message_response
)
from app.helper import decorators
from django.db import transaction
from rest_framework.decorators import action
from app.applicationlayer.utils import model_to_dict
from app.applicationlayer.utils import log_save, enums
from app.applicationlayer.utils import model_to_dict
from app.helper.decorators import AssetTypeValidation
from django.db import IntegrityError
class AssetTypeViewset(viewsets.ModelViewSet):
queryset = models.AMSAssetType.objects.all()
serializer_class = serializers.AssetTypeSerializer
pagination_class = CustomPagination
lookup_field = "code"
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
ordering_fields = '__all__'
search_fields = (
'code', 'name'
)
@AssetTypeValidation
@transaction.atomic
def create(self, request, *args, **kwargs):
form = request.data
form['created_by'] = request.user.code
serializer = self.get_serializer(data=form)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
message = status_message_response(
201, 'success',
'New Asset Type created', serializer.data
)
return Response(
message
)
# @AssetTypeValidation
@transaction.atomic
def update(self, request, *args, **kwargs):
try:
partial = kwargs.pop('partial', False)
instance = self.get_object()
form = request.data
form['created_by'] = request.user.code
serializer = self.get_serializer(instance, data=form, partial=partial)
serializer.is_valid(raise_exception=True)
old_instance = model_to_dict(instance)
self.perform_update(serializer)
new_instance = serializer.data
log_save(
enums.LogEnum.UPDATE.value,
enums.LogEntitiesEnum.AMSAsset.value,
old_instance['id'],
old_instance,
new_instance
)
return Response(serializer.data)
except IntegrityError as e:
return Response(
{"message": "Cannot delete or update this record it has foreign key constraint to other tables"},
status=status.HTTP_400_BAD_REQUEST
)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'List of Asset Types found!',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(self.queryset, many=True)
return Response(
serializer.data,
status=status.HTTP_200_OK
)
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
@transaction.atomic
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
if instance.ams_asset_type.count() > 0:
message = status_message_response(
400, 'Failed',
'This Asset Type had an Assets', model_to_dict(instance)
)
return Response(
message,
status=status.HTTP_400_BAD_REQUEST
)
instance.is_active = False
instance.save()
new_instance = model_to_dict(instance)
log_save(
enums.LogEnum.DELETED.value,
enums.LogEntitiesEnum.AMSAssetType.value,
new_instance['id'],
new_instance,
''
)
return Response(status=status.HTTP_204_NO_CONTENT)
@action(
methods=['GET'], detail=False,
url_path='archived', url_name='archived'
)
def archived(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
queryset = queryset.filter(is_active=False)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'List of Archived Asset Types found',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(self.queryset, many=True)
return Response(
serializer.data,
status=status.HTTP_200_OK
)
@action(
methods=['PATCH'], detail=True,
url_path='restore', url_name='restore'
)
def restore(self, request, code=None):
try:
instance = self.get_object()
instance.is_active = True
instance.save()
new_instance = model_to_dict(instance)
log_save(
enums.LogEnum.UPDATE.value,
enums.LogEntitiesEnum.AMSAssetType.value,
new_instance['id'],
new_instance,
''
)
message = status_message_response(
200, 'success',
'Archived Asset Type restored',
''
)
return Response(message, status=status.HTTP_200_OK)
except Exception as e:
message = status_message_response(
500, 'failed',
'Request was not able to process' + str(e), []
)
return Response(message,
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
\ No newline at end of file
from app.entities import models
from rest_framework import serializers
from app.applicationlayer.utils import model_to_dict
class AuditTrailSerializer(serializers.ModelSerializer):
class Meta:
model = models.AMSAuditTrail
fields = '__all__'
read_only_fields = ['created', 'code']
\ No newline at end of file
from django_filters import rest_framework as filters
from app.entities.models import AMSAuditTrail
class AuditTrailFilter(filters.FilterSet):
class Meta:
model = AMSAuditTrail
fields = '__all__'
\ No newline at end of file
from app.entities import models
from rest_framework import viewsets, status
from rest_framework.response import Response
from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from app.applicationlayer.ams.audit_trail.table_filters import AuditTrailFilter
from app.applicationlayer.ams.audit_trail import serializers
from app.applicationlayer.utils import (
CustomPagination, status_message_response
)
from app.helper import decorators
from django.db import transaction
from rest_framework.decorators import action
from app.applicationlayer.utils import model_to_dict
from app.applicationlayer.utils import log_save, enums
from app.applicationlayer.utils import model_to_dict
from django.db import IntegrityError
class AMSAuditTrailViewset(viewsets.ModelViewSet):
queryset = models.AMSAuditTrail.objects.all()
serializer_class = serializers.AuditTrailSerializer
pagination_class = CustomPagination
lookup_field = "code"
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
ordering_fields = '__all__'
search_fields = (
'code', 'name'
)
def create(self, request, *args, **kwargs):
form = request.data
form['created_by'] = request.user.code
serializer = self.get_serializer(data=form)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
message = status_message_response(
201, 'success',
'New Audit Trail created', serializer.data
)
return Response(
message
)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'List of Asset Types found!',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(self.queryset, many=True)
return Response(
serializer.data,
status=status.HTTP_200_OK
)
def audit_trail_create(name, desc, asset_stock, created_by):
try:
models.AMSAuditTrail.objects.create(
name=name,
desciption=desc,
asset_stock=asset_stock,
created_by=created_by
)
return True
except Exception as e:
message = {
'code': 500,
'status': 'failed',
'message': 'Request was not able to process' + str(e.__class__)
}
return Response(message,
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
\ No newline at end of file
from rest_framework import serializers
from app.entities.models import Company
class AMSCompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = '__all__'
from django_filters import rest_framework as filters
from app.entities.models import Company
class AMSCompanyFilterSet(filters.FilterSet):
class Meta:
model = Company
fields = '__all__'
from django_filters import rest_framework as filters
from rest_framework.filters import SearchFilter, OrderingFilter
from app.entities.models import (
Company, AMSPrivilege
)
from rest_framework import viewsets
from app.applicationlayer.utils import (CustomPagination,
status_message_response)
from django_filters.rest_framework import DjangoFilterBackend
from app.applicationlayer.ams.master.ams_company import serializer
from app.applicationlayer.ams.master.ams_company.table_filter import (
AMSCompanyFilterSet
)
from rest_framework.response import Response
from django.db.models import Q
from rest_framework.decorators import action
class AMSCompanyFormViewSet(viewsets.ModelViewSet):
queryset = Company.objects.all()
serializer_class = serializer.AMSCompanySerializer
pagination_class = CustomPagination
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
filterset_class = AMSCompanyFilterSet
ordering_fields = '__all__'
search_fields = ('name',)
http_method_names = ['get']
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
allowed = AMSPrivilege.objects.filter(
Q(id_number=self.request.user.code)
).values('department__company')
queryset = queryset.filter(code__in=allowed)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'list of Company found',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
from rest_framework import serializers
from app.entities.models import Department
class AMSDepartmentSerializer(serializers.ModelSerializer):
class Meta:
model = Department
fields = '__all__'
from django_filters import rest_framework as filters
from app.entities.models import Department
class AMSDepartmentFilterSet(filters.FilterSet):
class Meta:
model = Department
fields = '__all__'
from django_filters import rest_framework as filters
from rest_framework.filters import SearchFilter, OrderingFilter
from app.entities.models import (
Company, Department, AMSPrivilege
)
from rest_framework import viewsets
from app.applicationlayer.utils import (CustomPagination,
status_message_response)
from django_filters.rest_framework import DjangoFilterBackend
from app.applicationlayer.ams.master.ams_department import serializer
from app.applicationlayer.ams.master.ams_department.table_filter import (
AMSDepartmentFilterSet
)
from rest_framework.response import Response
from django.db.models import Q
from rest_framework.decorators import action
class AMSDepartmentFormViewSet(viewsets.ModelViewSet):
queryset = Department.objects.all()
serializer_class = serializer.AMSDepartmentSerializer
pagination_class = CustomPagination
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
filterset_class = AMSDepartmentFilterSet
ordering_fields = '__all__'
search_fields = ('name',)
http_method_names = ['get']
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
allowed = AMSPrivilege.objects.filter(
Q(id_number=self.request.user.code)
).values('department')
queryset = queryset.filter(code__in=allowed)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
message = status_message_response(
200,
'success',
'list of Department found',
serializer.data
)
return self.get_paginated_response(message)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment