sak1b

bulk-target-7-april

Apr 7th, 2021 (edited)
127
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 16.11 KB | None | 0 0
  1. import csv
  2. import io
  3. from collections import defaultdict, deque
  4. from csv import DictReader
  5.  
  6. import graphene
  7. from django.db import transaction
  8. from graphene.types import InputObjectType
  9. from django.core.exceptions import ValidationError
  10.  
  11. from ...core.mutations import ModelMutation, BaseMutation
  12. from ...core.types import Upload
  13. from ...core.types.common import TargetError
  14. from ....account.models import User
  15. from ....core.permissions import TargetPermissions
  16. from ....partner.models import Partner
  17. from ....target import models
  18. from ....target.error_codes import TargetErrorCode
  19. from dateutil import parser, relativedelta
  20. from datetime import datetime as dt
  21.  
  22. from ....target.models import Achievement, TargetUser, PartnerTargetSales
  23. from ...utils import check_if_attempted_from_valid_parent
  24. from ...target.types import PartnerTargetSales as PartnerTargetSalesType
  25.  
  26.  
  27. class CreateTargetInput(InputObjectType):
  28.     user = graphene.ID(description="ID of the Agent.", name="user", required=True)
  29.     month = graphene.Date(required=True, description="Target month(YYYY-MM)")
  30.     target_orders = graphene.Int(
  31.         description="Number of orders for the target.", required=True
  32.     )
  33.     target_sales = graphene.Int(
  34.         description="Amount of sales for the target.", required=True
  35.     )
  36.  
  37.  
  38. class CreateTarget(ModelMutation):
  39.     class Arguments:
  40.         input = CreateTargetInput(
  41.             required=True, description="Fields required to create a target."
  42.         )
  43.  
  44.     class Meta:
  45.         description = "Creates a new target of an agent."
  46.         model = models.Target
  47.         permissions = (TargetPermissions.MANAGE_TARGETS,)
  48.         error_type_class = TargetError
  49.         error_type_field = "target_errors"
  50.  
  51.     @classmethod
  52.     def clean_input(cls, info, instance, data):
  53.         user = cls.get_node_or_error(info, data["user"])
  54.         requestor = info.context.user
  55.         if requestor.groups.first().name in ["dco", "dcm", "cm"]:
  56.             check_if_attempted_from_valid_parent(requestor, user)
  57.  
  58.         cleaned_input = super().clean_input(info, instance, data)
  59.         if cleaned_input['target_orders'] < 1:
  60.             raise ValidationError(
  61.                 {
  62.                     "target_orders": ValidationError(
  63.                         "Target order should be positive number",
  64.                         code=TargetErrorCode.INVALID,
  65.                     )
  66.                 }
  67.             )
  68.  
  69.         if cleaned_input['target_sales'] < 1:
  70.             raise ValidationError(
  71.                 {
  72.                     "target_sales": ValidationError(
  73.                         "Target sales should be positive number",
  74.                         code=TargetErrorCode.INVALID,
  75.                     )
  76.                 }
  77.             )
  78.  
  79.         user = cleaned_input['user']
  80.         month = parser.parse(str(cleaned_input['month'])).replace(day=1)
  81.         cleaned_input['month'] = month
  82.         target = models.Target.objects.filter(user=user, month=month)
  83.  
  84.         if target:
  85.             raise ValidationError(
  86.                 {
  87.                     "month": ValidationError(
  88.                         "Target for specified month already exists.",
  89.                         code=TargetErrorCode.ALREADY_EXISTS,
  90.                     )
  91.                 }
  92.             )
  93.  
  94.         return cleaned_input
  95.  
  96.     @classmethod
  97.     def save(cls, info, instance, cleaned_input):
  98.         instance.save()
  99.         user = cleaned_input['user']
  100.         month = cleaned_input['month']
  101.         achievement = Achievement.objects.filter(user=user, month=month).first()
  102.         if achievement:
  103.             achievement.target = instance
  104.             achievement.save()
  105.  
  106.  
  107. class UpdateTargetInput(graphene.InputObjectType):
  108.     target_orders = graphene.Int(
  109.         description="Number of orders for the target.", required=False
  110.     )
  111.     target_sales = graphene.Int(
  112.         description="Amount of sales for the target.", required=False
  113.     )
  114.  
  115.  
  116. class UpdateTarget(ModelMutation):
  117.     class Arguments:
  118.         id = graphene.ID(required=True, description="ID of a target to update.")
  119.         input = UpdateTargetInput(
  120.             required=True, description="Fields required to update a target."
  121.         )
  122.  
  123.     class Meta:
  124.         description = "Updates a new target of an agent."
  125.         model = models.Target
  126.         permissions = (TargetPermissions.MANAGE_TARGETS,)
  127.         error_type_class = TargetError
  128.         error_type_field = "target_errors"
  129.  
  130.     @classmethod
  131.     def clean_input(cls, info, instance, data):
  132.         target_month = instance.month.strftime("%Y-%m")
  133.         current_month = dt.now().date().strftime("%Y-%m")
  134.         if target_month < current_month:
  135.             raise ValidationError(
  136.                 {
  137.                     "id": ValidationError(
  138.                         "This item can not be modified.",
  139.                         code=TargetErrorCode.INVALID
  140.                     )
  141.                 }
  142.             )
  143.         target_orders = data.get("target_orders")
  144.         target_sales = data.get("target_sales")
  145.  
  146.         if not target_sales and not target_orders:
  147.             raise ValidationError(
  148.                 {
  149.                     "target_sales": ValidationError(
  150.                         "Target sales can not be empty",
  151.                         code=TargetErrorCode.INVALID
  152.                     ),
  153.                     "target_orders": ValidationError(
  154.                         "Target orders can not be empty",
  155.                         code=TargetErrorCode.INVALID
  156.                     )
  157.                 }
  158.             )
  159.  
  160.         if target_orders < 1:
  161.             raise ValidationError(
  162.                 {
  163.                     "target_orders": ValidationError(
  164.                         "Target order should be positive number",
  165.                         code=TargetErrorCode.INVALID
  166.                     )
  167.                 }
  168.             )
  169.  
  170.         if target_sales < 1:
  171.             raise ValidationError(
  172.                 {
  173.                     "target_sales": ValidationError(
  174.                         "Target sales should be positive number",
  175.                         code=TargetErrorCode.INVALID,
  176.                     )
  177.                 }
  178.             )
  179.  
  180.         return super().clean_input(info, instance, data)
  181.  
  182.  
  183. class CreateTargetsInBulk(BaseMutation):
  184.     partner_target_sales = graphene.List(PartnerTargetSalesType, description="List of partner target sales.")
  185.     graph = defaultdict(list)
  186.  
  187.     class Arguments:
  188.         file = Upload(
  189.             required=True,
  190.             description="A csv file that contains agent email, partner id, target amount to set targets in bulk.",
  191.         )
  192.  
  193.     class Meta:
  194.         description = "Upload file(csv) to create targets in bulk."
  195.         error_type_class = TargetError
  196.         error_type_field = "target_errors"
  197.         permissions = (TargetPermissions.MANAGE_TARGETS,)
  198.  
  199.     @classmethod
  200.     def get_parents_list(self, source, include_self=False):
  201.         if len(self.graph) == 0:
  202.             all_users = User.objects.all()
  203.             for user in all_users:
  204.                 if user.parent_id:
  205.                     self.graph[user.id].append(user.parent_id)
  206.  
  207.         parents_list = []
  208.         visited = set()
  209.         queue = deque()
  210.  
  211.         visited.add(source)
  212.         queue.append(source)
  213.  
  214.         while queue:
  215.             s = queue.popleft()
  216.             for neighbour in self.graph[s]:
  217.                 if neighbour not in visited:
  218.                     parents_list.append(neighbour)
  219.                     visited.add(neighbour)
  220.                     queue.append(neighbour)
  221.  
  222.         if include_self:
  223.             parents_list.append(source)
  224.  
  225.         return parents_list
  226.  
  227.     @classmethod
  228.     def perform_mutation(cls, _root, info, file):
  229.         with transaction.atomic():
  230.             csv_file = info.context.FILES.get(file)
  231.  
  232.             if not csv_file.name.endswith(".csv"):
  233.                 raise ValidationError(
  234.                     {
  235.                         "target_sales": ValidationError(
  236.                             "The file provided is not in csv format.",
  237.                             code=TargetErrorCode.INVALID,
  238.                         )
  239.                     }
  240.                 )
  241.  
  242.             data_set = csv_file.read().decode("UTF-8")
  243.             io_string = io.StringIO(data_set)
  244.  
  245.             csv_reader = csv.reader(io_string, delimiter=',')
  246.             header = next(csv_reader)
  247.             no_of_columns = len(header)
  248.  
  249.             agents = {}
  250.             partners = {}
  251.             target_user_objects_cache = {}
  252.             partner_target_sales_cache = {}
  253.  
  254.             all_agents = User.objects.filter(groups__name='agent')
  255.             all_partners = Partner.objects.all()
  256.  
  257.             for agent in all_agents:
  258.                 agents[agent.email] = agent
  259.  
  260.             for partner in all_partners:
  261.                 partners[partner.partner_id] = partner
  262.  
  263.             target_user_list = []
  264.             partner_target_sales_list_new = []
  265.             partner_target_sales_list_existing = []
  266.  
  267.             partner_target_sales_hash_new = {}
  268.             partner_target_sales_hash_existing = {}
  269.             raw_data = []
  270.             current_month = dt.now().replace(day=1).date()
  271.             agent_ids = set()
  272.             agent_months = defaultdict(set)
  273.  
  274.             for row in csv_reader:
  275.                 raw_data.append(
  276.                     {
  277.                         'row': row,
  278.                         'target_user_objects_list_new': [],
  279.                         'target_user_objects_list_existing': [],
  280.  
  281.                     }
  282.                 )
  283.  
  284.             target_users_all = TargetUser.objects.filter(created__gte=current_month)
  285.  
  286.             for item in target_users_all:
  287.                 hash_val = str(item.user_id) + "-" + str(item.month)
  288.                 target_user_objects_cache[hash_val] = item
  289.  
  290.             for item in raw_data:
  291.                 email = item['row'][0]
  292.                 month = item['row'][1]
  293.                 date_object = parser.parse(str(month)).replace(day=1).date()
  294.                 month = str(date_object)
  295.                 agent = agents.get(email, None)
  296.  
  297.                 if date_object < current_month:
  298.                     raise ValidationError(
  299.                         {
  300.                             "month": ValidationError(
  301.                                 "%s is invalid, current or future month is allowed only." % item['row'][1],
  302.                                 code=TargetErrorCode.INVALID,
  303.                             )
  304.                         }
  305.                     )
  306.  
  307.                 if agent is None:
  308.                     raise ValidationError(
  309.                         {
  310.                             "agent": ValidationError(
  311.                                 "%s email is invalid." % email,
  312.                                 code=TargetErrorCode.INVALID,
  313.                             )
  314.                         }
  315.                     )
  316.  
  317.                 users_list = cls.get_parents_list(agent.id, True)
  318.                 agent_ids.add(agent.id)
  319.                 agent_months[agent.id].add(month)
  320.  
  321.                 for user_id in users_list:
  322.                     hash_val = str(user_id) + "-" + str(month)
  323.                     target_user_item = target_user_objects_cache.get(hash_val, None)
  324.                     if target_user_item is None:
  325.                         target_user_item = TargetUser(
  326.                             user_id=user_id,
  327.                             month=month
  328.                         )
  329.                         target_user_list.append(target_user_item)
  330.                         item['target_user_objects_list_new'].append(target_user_item)
  331.  
  332.                         target_user_objects_cache[hash_val] = target_user_item  # added in cache
  333.                     else:
  334.                         if target_user_item.id is None:
  335.                             item['target_user_objects_list_new'].append(target_user_item)
  336.                         else:
  337.                             item['target_user_objects_list_existing'].append(target_user_item)
  338.  
  339.             TargetUser.objects.bulk_create(target_user_list)
  340.  
  341.             partner_target_sales_all = PartnerTargetSales.objects\
  342.                 .filter(created__gte=current_month).prefetch_related('target_user')
  343.  
  344.             for item in partner_target_sales_all:
  345.                 _user_id = item.target_user.user_id
  346.                 _month = str(item.target_user.month)
  347.  
  348.                 if _user_id in agent_ids and _month in agent_months[_user_id]:
  349.                     raise ValidationError(
  350.                         {
  351.                             "agent": ValidationError(
  352.                                 "%s already has a target for %s." % (item.target_user.user.email, _month),
  353.                                 code=TargetErrorCode.INVALID,
  354.                             )
  355.                         }
  356.                     )
  357.                 hash_val = str(item.target_user_id) + "-" + str(item.partner_id)
  358.                 partner_target_sales_cache[hash_val] = item
  359.  
  360.             for item in raw_data:
  361.                 target_user_objects_list_new = item['target_user_objects_list_new']
  362.                 target_user_objects_list_existing = item['target_user_objects_list_existing']
  363.                 target_user_objects_list_new.extend(target_user_objects_list_existing)
  364.  
  365.                 if len(target_user_objects_list_new) > 0:
  366.                     for target_user_obj in target_user_objects_list_new:
  367.                         index = 2
  368.                         while index + 1 < no_of_columns:
  369.                             if len(item['row'][index]) == 0:
  370.                                 index += 2
  371.                                 continue
  372.  
  373.                             partner_id = item['row'][index]
  374.                             target_sales = int(item['row'][index + 1])
  375.  
  376.                             partner_obj = partners.get(partner_id, None)
  377.  
  378.                             if partner_obj is None:
  379.                                 raise ValidationError(
  380.                                     {
  381.                                         "partner": ValidationError(
  382.                                             "%s partner id is invalid." % partner_id,
  383.                                             code=TargetErrorCode.INVALID,
  384.                                         )
  385.                                     }
  386.                                 )
  387.  
  388.                             hash_val = str(target_user_obj.id) + "-" + str(partner_obj.id)
  389.                             partner_target_sales_item = partner_target_sales_cache.get(hash_val, None)
  390.  
  391.                             if partner_target_sales_item is None:
  392.  
  393.                                 partner_target_sales_item = PartnerTargetSales(
  394.                                     target_user=target_user_obj,
  395.                                     partner=partner_obj,
  396.                                     target_sales=target_sales
  397.                                 )
  398.                                 partner_target_sales_hash_new[hash_val] = partner_target_sales_item
  399.                                 partner_target_sales_cache[hash_val] = partner_target_sales_item
  400.  
  401.                             else:
  402.                                 partner_target_sales_item.target_sales += target_sales
  403.  
  404.                                 if partner_target_sales_item.id is None:
  405.                                     partner_target_sales_hash_new[hash_val] = partner_target_sales_item
  406.                                 else:
  407.                                     partner_target_sales_hash_existing[hash_val] = partner_target_sales_item
  408.  
  409.                             index += 2
  410.  
  411.             for key, val in partner_target_sales_hash_new.items():
  412.                 partner_target_sales_list_new.append(val)
  413.  
  414.             for key, val in partner_target_sales_hash_existing.items():
  415.                 partner_target_sales_list_existing.append(val)
  416.  
  417.             PartnerTargetSales.objects.bulk_create(partner_target_sales_list_new)
  418.             PartnerTargetSales.objects.bulk_update(partner_target_sales_list_existing, ['target_sales', 'updated'])
  419.  
  420.             partner_target_sales = PartnerTargetSales.objects.all()
  421.  
  422.             return CreateTargetsInBulk(partner_target_sales=partner_target_sales)
  423.  
Add Comment
Please, Sign In to add comment