Advertisement
kastielspb

utils.py

Dec 12th, 2018
202
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 21.58 KB | None | 0 0
  1. import os
  2. # import json, requests
  3. from typing import List
  4. import requests
  5. # from urllib import request
  6.  
  7. from django.core.files.base import ContentFile
  8. from django.conf import settings
  9. # from django.core.files import File
  10. # from django.core.files.temp import NamedTemporaryFile
  11.  
  12. from apps.realty.models import (
  13.     RealtyObject,
  14.  
  15.     ObjectBeside,
  16.     MetroStation,
  17.  
  18.     SingleRowAttribute,
  19.     MultipleRowAttribute,
  20.     Attribute,
  21.  
  22.  
  23.     AttrValue,    SliderImage,
  24. )
  25. from .megabaz_api import (
  26.     Client as MegabazClient,
  27.     ItemsCollection as MegabazItemsCollection,
  28.     ItemNormalizer as MegabazItemNormalizer,
  29.     TaskType as MegabazTaskType,
  30.     from_timestamp,
  31. )
  32. from .const import (
  33.     STATUS
  34. )
  35.  
  36. __all__ = (
  37.     'realty_import_controller',
  38. )
  39.  
  40.  
  41. class ServiceImporterInterface:
  42.     service_name = None
  43.  
  44.     class Meta:
  45.         abstract = True
  46.  
  47.     def get_service_name(self):
  48.         if not self.service_name:
  49.             raise NotImplementedError('Property "service_name" have to be established')
  50.         return self.service_name
  51.  
  52.     def parse(self):
  53.         raise NotImplementedError('There is no implementation for method "parse"')
  54.  
  55.     def get_object_contact(self):
  56.         raise NotImplementedError('There is no implementation for method "get_service_object_contact"')
  57.  
  58.  
  59. class MegabazServiceImporter(ServiceImporterInterface):
  60.     service_name = 'megabaz'
  61.     api_key = getattr(settings, 'MEGABAZ_PASSWORD', '')
  62.     parsed = False
  63.  
  64.     name_convert_list = {
  65.         'kids': 'children',
  66.         'pets': 'animals',
  67.  
  68.         'washer': 'washer',
  69.         'tv': 'tv',
  70.         'fridge': 'fridge',
  71.         'kitchen_furniture': 'kitchen_furniture',
  72.         'furniture': 'furniture',
  73.     }
  74.  
  75.     _rooms = None
  76.  
  77.     def get_object_contact(self, contact_id):
  78.         return None
  79.  
  80.     def parse(self):
  81.         print('Start to parse...\n')
  82.  
  83.         self.client = MegabazClient(self.api_key)
  84.         self.item_normalizer = MegabazItemNormalizer()
  85.  
  86.         while not self.parsed:
  87.             print('Parsing page...')
  88.             self.handle_data()
  89.         print(f'Ended parse {self.service_name}')
  90.         self.parsed = False
  91.  
  92.     def handle_data(self):
  93.         data = self.client.receive_items()
  94.  
  95.         print(f'data: {data}')
  96.  
  97.         items = data['items']
  98.         tasks = {
  99.             task['item_id']: task['type'] for task in data['tasks']
  100.         }
  101.  
  102.  
  103.         if not len(items):
  104.             self.parsed = True
  105.  
  106.         i=0
  107.         for item in items:
  108.             # if i > 10:
  109.             #     break
  110.  
  111.             print('***************************')
  112.             # print(items[item])
  113.             # print('---------------------------')
  114.             _data = self.item_normalizer.normalize(items[item])
  115.             # _data['item_id'] = task['item_id']
  116.             print('item:', _data)
  117.             # print('task: ', tasks)
  118.             _operation = tasks.get(_data['id'], MegabazTaskType.ADD)
  119.             print('_operation: ', _operation)
  120.             if _operation == MegabazTaskType.ADD:
  121.                 print('_operation: ADD')
  122.                 print('type: ', _data['type'])
  123.                 print('contract_type: ', _data['contract_type'])
  124.                 if (
  125.                     _data['type'] == 'rent'
  126.                     and _data['contract_type'] == 'long'
  127.                 ):
  128.                     self.save(_data)
  129.  
  130.             if _operation == MegabazTaskType.UPDATE:
  131.                 print('_operation: UPDATE')
  132.                 print(f"data['id']: {data['id']}")
  133.                 self.update(_data)
  134.  
  135.             if _operation == MegabazTaskType.DELETE:
  136.                 print('_operation: DELETE')
  137.                 print(f"data['id']: {data['id']}")
  138.                 self.delete(_data['id'])
  139.            
  140.             print('***************************\n')
  141.             i +=1
  142.    
  143.     def delete(self, obj_id):
  144.         print(f'Deleting object {obj_id}...\n')
  145.  
  146.         _log = RealtyObject.objects.filter(
  147.             alien_id=obj_id,
  148.             alien_name=self.service_name
  149.         ).delete()
  150.         print('Deleted: ', _log, '\n')
  151.  
  152.     @staticmethod
  153.     def str_to_int(data:str = None):
  154.         try:
  155.             return int(data)    
  156.         except ValueError:
  157.             return None    
  158.  
  159.     def update(self, data):
  160.         update_data = {
  161.             'status': self.get_status(data),
  162.             'price': self.get_price(data),
  163.         }
  164.  
  165.         def add_update(key, value):
  166.             if value:
  167.                 update_data[key] = value
  168.  
  169.         address = f"{data['street']}, {data['street_number'] if data['street_number'] else ''}" if data['street'] else ''
  170.         add_update('address', address)
  171.         add_update('number_of_storeys', data['floors'])
  172.         add_update('floor', int(data['floor']) if data['floor'] else None)
  173.         add_update('area', float(data['square']) if data['square'] else None)
  174.         add_update('location_lon', float(data['longitude']) if data['longitude'] else None)
  175.         add_update('location_lat', float(data['latitude']) if data['latitude'] else None)
  176.         add_update('description', data['description'])
  177.         add_update('alien_creator_name', data['contact_name'])
  178.         add_update('alien_creator_contact', data['phone'])
  179.  
  180.         RealtyObject.objects.filter(
  181.             alien_id=int(data['id'])
  182.         ).update(**update_data)
  183.  
  184.     def is_dublicate(self, data, title):
  185.         if not (data['street'] and data['street_number']):
  186.             return False
  187.         if title:
  188.             return RealtyObject.objects.filter(title=title).exists()
  189.         return False
  190.  
  191.     def get_status(self, data, title=None):
  192.         status = data['status']
  193.         if self.is_dublicate(data, title):
  194.             return STATUS.dublicate
  195.         if status == 'checked':
  196.             return STATUS.checked
  197.         if status in [
  198.             'not_checked',
  199.             'not_called',
  200.             'planned'
  201.         ]:
  202.             return STATUS.moderate
  203.         return STATUS.archive
  204.  
  205.     def get_price(self, data):
  206.         _price = data['price'] if data['price'] else None
  207.         if not _price and data['price_from'] and data['price_to']:
  208.             _price = (int(data['price_from']) + int(data['price_to'])) / 2
  209.         return int(_price) if _price else None
  210.  
  211.     def save(self, data):
  212.         print('Executing "save" method...\n')
  213.  
  214.         _title_parts = []
  215.  
  216.         print('object_type: ', data['object_type'])
  217.         print('image: ', data['image'])
  218.         if not (data['object_type'] and data['image'] and data['status'] in [
  219.             'not_checked',
  220.             'checked',
  221.             'not_called',
  222.             'planned'
  223.         ]):
  224.             return
  225.            
  226.         _obj_type = data['object_type'][0]
  227.         rooms = self.str_to_int(_obj_type)
  228.         print('rooms: ', _obj_type)
  229.         if rooms:
  230.             self._rooms = rooms
  231.             _title_parts.append(f'Сдам {self._rooms} комнатную квартиру')
  232.         else:
  233.             return
  234.  
  235.         _address=f"{data['street']}, {data['street_number'] if data['street_number'] else ''}" if data['street'] else ''
  236.         if _address:
  237.             _title_parts.append(_address)
  238.  
  239.         title = ', '.join(_title_parts)
  240.  
  241.         _obj = RealtyObject.objects.create(
  242.             status=self.get_status(data, title),
  243.             title=title,
  244.             description=data['description'],
  245.  
  246.             number_of_storeys=int(data['floors']) if data['floors'] else None,
  247.             floor=int(data['floor']) if data['floor'] else None,
  248.             area=float(data['square']) if data['square'] else None,
  249.             price=self.get_price(data),
  250.  
  251.             address=_address,
  252.             created=data['created_at'],
  253.  
  254.             location_lon=float(data['longitude']) if data['longitude'] else None,
  255.             location_lat=float(data['latitude']) if data['latitude'] else None,
  256.  
  257.             alien_name=self.service_name,
  258.             alien_id=int(data['id']),
  259.             alien_creator_id=0,
  260.             alien_creator_name=data['contact_name'],
  261.             alien_creator_contact=data['phone'],
  262.         )
  263.         print(f'Created object ({data["id"]}): ', _obj)
  264.         self.save_metro(data, _obj)
  265.         self.save_single_attrs(data, _obj)
  266.         self.save_multiple_attrs(data, _obj)
  267.         self.save_images(data, _obj)
  268.  
  269.     @staticmethod
  270.     def save_metro(data, obj):
  271.         print('Start to save metro stations...\n')
  272.         stations = data['station']
  273.         print('metro stations to save: ', stations)
  274.         for station in stations:
  275.             metro = MetroStation.objects.filter(name=station['name']).first()
  276.             if not metro:
  277.                 print(f'Metro with name "{station["name"]}" not found!')
  278.                 continue
  279.             metro_obj = ObjectBeside.objects.create(
  280.                 metro=metro,
  281.                 model=obj,
  282.                 time=station['time']['walk']
  283.             )
  284.             print('Added metro object: ', metro_obj)
  285.  
  286.     def save_single_attrs(self, data, obj):
  287.         print('Start to save single attrs...\n')
  288.  
  289.         if not self._rooms:
  290.             return
  291.  
  292.         attr = Attribute.objects.filter(slug='rooms').first()
  293.         print('try find rooms: ', self._rooms)
  294.         attr_value = AttrValue.objects.filter(
  295.             attribute=attr,
  296.             name__contains=self._rooms
  297.         ).first()
  298.         if attr and attr_value:
  299.             row_attr = SingleRowAttribute.objects.create(
  300.                 product=obj,
  301.                 attribute=attr,
  302.                 value=attr_value
  303.             )
  304.             print('Saved attr "rooms": ', row_attr)
  305.         else:
  306.             print('Attr "rooms" was NOT save: ', attr, attr_value)
  307.  
  308.     def save_multiple_attrs(self, data, obj):
  309.         print('Start to save multiple attrs...')
  310.         print('Options: ', data['options'])
  311.  
  312.         row_attr = None
  313.         for key in self.name_convert_list:
  314.             attr = Attribute.objects.filter(slug='features').first()
  315.             attr_values = attr.attr_values.all()
  316.  
  317.             if self.name_convert_list[key] not in data['options']:
  318.                 continue
  319.  
  320.             attr_value = attr_values.filter(
  321.                 slug=key
  322.             ).first()
  323.             print('attr_value: ', attr_value)
  324.  
  325.             if not row_attr and attr_value:
  326.                 row_attr = MultipleRowAttribute.objects.create(
  327.                     product=obj,
  328.                     attribute=attr,
  329.                 )
  330.                 print('Created multiple attr: ', row_attr)
  331.  
  332.             if attr_value:
  333.                 row_attr.values.add(attr_value)
  334.                 print(f'Added attr_value: ', attr_value)
  335.  
  336.     @staticmethod
  337.     def save_images(data, obj):
  338.         print('Start to save obj images...')
  339.         for img_url in data['image']:
  340.             response = requests.get(img_url)
  341.             if response.status_code == requests.codes.ok:
  342.                 _img = ContentFile(response.content)
  343.                 img_filename = os.path.basename(img_url)
  344.                 img_obj = SliderImage.objects.create(
  345.                     model=obj
  346.                 )
  347.                 img_obj.image.save(img_filename, _img, save=True)
  348.                 print('Created image: ', img_obj)
  349.  
  350.  
  351. class SmartAgentImporter(ServiceImporterInterface):
  352.     service_name = 'smart_agent'
  353.     login = getattr(settings, 'SMART_AGENT_LOGIN', '')
  354.     password = getattr(settings, 'SMART_AGENT_PASSWORD', '')
  355.  
  356.     name_convert_list = {
  357.         'for_whom': {
  358.             'duo': [
  359.                 'for_couple'
  360.             ],
  361.             'one': [
  362.                 'for_1_woman',
  363.                 'for_1_man'
  364.             ],
  365.             'family': [
  366.                 'for_many',
  367.                 'for_couple'
  368.             ],
  369.             'students': [
  370.                 'for_many',
  371.                 'for_1_woman',
  372.                 'for_1_man',
  373.                 'for_2_woman',
  374.                 'for_2_man'
  375.             ]
  376.         },
  377.         'features': {
  378.             'children': [
  379.                 'children'
  380.             ],
  381.             'pets': [
  382.                 'pets'
  383.             ],
  384.             'fridge': [
  385.                 'fridge'
  386.             ],
  387.             'washer': [
  388.                 'washer'
  389.             ],
  390.             'balcony': [
  391.                 'balcony'
  392.             ],
  393.             'tv': [
  394.                 'tv'
  395.             ],
  396.         },
  397.     }
  398.     _rooms = None
  399.     _rooms_attr = None
  400.  
  401.     def parse(self):
  402.         self.delete()
  403.         self.create()
  404.  
  405.     @staticmethod
  406.     def check_response(response, method_name='not_set'):
  407.         if not response["success"]:
  408.             print(f'Method "{method_name}"  refused!')
  409.             return False
  410.         if not response['payload']["items"]:
  411.             print(f'Item list empty for method "{method_name}"')
  412.             return False
  413.         return True
  414.  
  415.     @staticmethod
  416.     def check_object_response(response, method_name='get_object'):
  417.         if not response["success"]:
  418.             print(f'Method "{method_name}"  refused!')
  419.             return False
  420.         if not response['payload']:
  421.             print(f'Attrs list empty for method "{method_name}"')
  422.             return False
  423.         return True
  424.  
  425.     def get_object_contact(self, contact_id):
  426.         print('Getting contact...\n')
  427.  
  428.         url = f'http://smartagent.ru/api/phone' \
  429.               f'?id={contact_id}' \
  430.               f'&auth[login]={self.login}' \
  431.               f'&auth[password]={self.password}' \
  432.               f'&property=1'
  433.         response = requests.get(url).json()
  434.         print('V get_object_contact response:', response)
  435.         if not self.check_object_response(response, 'get_object_contact'):
  436.             return {'errors': [item['desc'] for item in response['msg']['errors']]}
  437.         # print('Deleted: ', _log, '\n')
  438.         return response
  439.  
  440.     def delete(self):
  441.         print('Deleting...\n')
  442.  
  443.         url = f'http://smartagent.ru/api/deleted' \
  444.               f'?auth[login]={self.login}' \
  445.               f'&auth[password]={self.password}' \
  446.               f'&property=1&region=1'
  447.         response = requests.get(url).json()
  448.         print('V delete response:', response)
  449.         if not self.check_response(response, 'delete'):
  450.             return
  451.         _ids = [item['id'] for item in response['payload']['items']]
  452.         print('V delete _ids:', _ids)
  453.         _log = RealtyObject.objects.filter(
  454.             alien_id__in=_ids,
  455.             alien_name=self.service_name
  456.         ).delete()
  457.         print('Deleted: ', _log, '\n')
  458.  
  459.     def create(self):
  460.         print('Creating...\n')
  461.  
  462.         self._rooms_attr = Attribute.objects.filter(slug='rooms').first()
  463.  
  464.         _new = self.get_new()
  465.         print('Actual: ', _new)
  466.         if not _new:
  467.             print('Nothing to create')
  468.             return
  469.  
  470.         for i, item in enumerate(_new):
  471.             print(f'======================== START {i} ========================')
  472.             # if i > 10:
  473.             #     break
  474.             self.save(self.get_object(item['id']))
  475.             print(f'======================== END {i} ========================\n')
  476.  
  477.     def get_new(self) -> List:
  478.         print('Getting actual...\n')
  479.  
  480.         url = f'http://smartagent.ru/api/new' \
  481.               f'?auth[login]={self.login}' \
  482.               f'&auth[password]={self.password}' \
  483.               f'&property=1&region=1'
  484.         response = requests.get(url).json()
  485.         print('V get_new response:', response)
  486.         print('--- get_new ["success"]:', response["success"])
  487.         if not self.check_response(response, 'get_new'):
  488.             return
  489.         items = response['payload']['items']
  490.         return items
  491.  
  492.     def get_object(self, _id: int):
  493.         print('Getting Object with id: ', _id, '\n')
  494.  
  495.         url = f'http://smartagent.ru/api/get' \
  496.               f'?id={_id}' \
  497.               f'&auth[login]={self.login}' \
  498.               f'&auth[password]={self.password}' \
  499.               f'&property=1&region=1'
  500.         response = requests.get(url).json()
  501.         print('V get_object response:', response)
  502.         return response
  503.  
  504.     def save(self, _data):
  505.         print('Executing "save" method...\n')
  506.  
  507.         if not self.check_object_response(_data, 'save'):
  508.             return
  509.  
  510.         data = _data['payload']
  511.  
  512.         print('try find rooms: ', data['rooms'])
  513.         self._rooms = AttrValue.objects.filter(
  514.             attribute=self._rooms_attr,
  515.             name__contains=data['rooms']
  516.         ).first()
  517.         if not self._rooms:
  518.             print('Obj don`t have attr "rooms"! Skiped...')
  519.             return
  520.  
  521.         if not data['images']:
  522.             print('Obj don`t have images! Skiped...')
  523.             return
  524.  
  525.         _obj = RealtyObject.objects.create(
  526.             status=STATUS.moderate,
  527.             title=data['note_generated'].split('.')[0],
  528.             description=data['note_generated'],
  529.             # owners_text=data['note_generated'],
  530.  
  531.             number_of_storeys=int(data['floors']),
  532.             floor=int(data['floor']),
  533.             area=float(data['total_area']),
  534.             price=int(data['price']),
  535.  
  536.             address=f"{data['street_cache']}, {data['building']}'"
  537.                     + f", корпус {data['corpus']}" if data.get('corpus') else '',
  538.             location_lon=float(data['lon']),
  539.             location_lat=float(data['lat']),
  540.  
  541.             alien_name=self.service_name,
  542.             alien_id=int(data['id']),
  543.             alien_creator_id=int(data['owner']),
  544.             alien_creator_name=data['name'],
  545.         )
  546.         print(f'Created object ({data["id"]}): ', _obj)
  547.         self.save_metro(data, _obj)
  548.         self.save_single_attrs(data, _obj)
  549.         self.save_multiple_attrs(data, _obj)
  550.         self.save_images(data, _obj)
  551.  
  552.     @staticmethod
  553.     def save_metro(data, obj):
  554.         print('Start to save metro stations...\n')
  555.         metro_names = data['metro_cache'].split(',')
  556.         for metro_name in metro_names:
  557.             metro = MetroStation.objects.filter(name=metro_name).first()
  558.             if not metro:
  559.                 print(f'Metro with name "{metro_name}" not found!')
  560.                 continue
  561.             metro_obj = ObjectBeside.objects.create(
  562.                 metro=metro,
  563.                 model=obj,
  564.                 time=data['distance']
  565.             )
  566.             print('Added metro object: ', metro_obj)
  567.  
  568.     def save_single_attrs(self, data, obj):
  569.         print('Start to save single attrs...')
  570.         row_attr = SingleRowAttribute.objects.create(
  571.             product=obj,
  572.             attribute=self._rooms_attr,
  573.             value=self._rooms
  574.         )
  575.         print('Saved attr "rooms": ', row_attr)
  576.  
  577.     def save_multiple_attrs(self, data, obj):
  578.         print('Start to save multiple attrs...')
  579.         for key in self.name_convert_list:
  580.             row_attr = None
  581.             attr = Attribute.objects.filter(slug=key).first()
  582.             attr_values = attr.attr_values.all()
  583.  
  584.             for attr_name in self.name_convert_list[key]:
  585.  
  586.                 for alien_attr_name in self.name_convert_list[key][attr_name]:
  587.                     # print(f'data[{alien_attr_name}] "{data[alien_attr_name"')
  588.                     if data[alien_attr_name] != '1':
  589.                         # print('continue...')
  590.                         continue
  591.  
  592.                     attr_value = attr_values.filter(
  593.                         slug=attr_name
  594.                     ).first()
  595.                     print('attr_value: ', attr_value)
  596.  
  597.                     if not row_attr and attr_value:
  598.                         row_attr = MultipleRowAttribute.objects.create(
  599.                             product=obj,
  600.                             attribute=attr,
  601.                         )
  602.                         print('Created multiple attr: ', row_attr)
  603.  
  604.                     if attr_value:
  605.                         row_attr.values.add(attr_value)
  606.                         # row_attr.save()
  607.                         print(f'Added attr_value: ', attr_value)
  608.  
  609.     @staticmethod
  610.     def save_images(data, obj):
  611.         print('Start to save obj images...')
  612.         img_urls = data['images']
  613.         for img_url in img_urls:
  614.             response = requests.get(img_url)
  615.             if response.status_code == requests.codes.ok:
  616.                 _img = ContentFile(response.content)
  617.                 img_filename = os.path.basename(img_url)
  618.                 img_obj = SliderImage.objects.create(
  619.                     model=obj
  620.                 )
  621.                 img_obj.image.save(img_filename, _img, save=True)
  622.                 print('Created image: ', img_obj)
  623.  
  624.  
  625. class RealtyImportController:
  626.     service_classes = [
  627.         MegabazServiceImporter,
  628.         SmartAgentImporter,
  629.     ]
  630.  
  631.     def __init__(self):
  632.         self.services = {
  633.             service_class.service_name: service_class()
  634.             for service_class in self.service_classes
  635.         }
  636.  
  637.     def _get_service(self, service_name: str):
  638.         if service_name in self.services.keys():
  639.             return self.services[service_name]
  640.         # raise KeyError(f'Service with name: "{service_name}" not found')
  641.         return None
  642.  
  643.     def parse_all(self):
  644.         for service_name in self.services:
  645.             print(f'Executing: {service_name} parser\n')
  646.             self.services[service_name].parse()
  647.  
  648.     def parse(self, service_name: str):
  649.         service = self._get_service(service_name)
  650.         if service:
  651.             service.parse()
  652.  
  653.     def get_service_object_contact(self, service_name: str, contact_id: int):
  654.         service = self._get_service(service_name)
  655.         if service:
  656.             return service.get_object_contact(contact_id)
  657.         return None
  658.  
  659.  
  660. realty_import_controller = RealtyImportController()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement