| 1 |  |  | # -*- coding: utf-8 -*- | 
            
                                                                                                            
                            
            
                                    
            
            
                | 2 |  |  | import logging | 
            
                                                                                                            
                            
            
                                    
            
            
                | 3 |  |  |  | 
            
                                                                                                            
                            
            
                                    
            
            
                | 4 |  |  | import pyjobsweb.lib.geolocation as geolocation | 
            
                                                                                                            
                            
            
                                    
            
            
                | 5 |  |  | from pyjobsweb import model | 
            
                                                                                                            
                            
            
                                    
            
            
                | 6 |  |  | from pyjobsweb.commands import AppContextCommand | 
            
                                                                                                            
                            
            
                                    
            
            
                | 7 |  |  | from pyjobsweb.lib.lock import acquire_inter_process_lock | 
            
                                                                                                            
                            
            
                                    
            
            
                | 8 |  |  |  | 
            
                                                                                                            
                                                                
            
                                    
            
            
                | 9 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 10 |  |  | class GeocodeCommand(AppContextCommand): | 
            
                                                        
            
                                    
            
            
                | 11 |  |  |     """ | 
            
                                                        
            
                                    
            
            
                | 12 |  |  |     Start geotagging operations for job offers and/or companies | 
            
                                                        
            
                                    
            
            
                | 13 |  |  |     """ | 
            
                                                        
            
                                    
            
            
                | 14 |  |  |     def __init__(self, *args, **kwargs): | 
            
                                                        
            
                                    
            
            
                | 15 |  |  |         super(GeocodeCommand, self).__init__(args, kwargs) | 
            
                                                        
            
                                    
            
            
                | 16 |  |  |         self._geolocator = geolocation.Geolocator() | 
            
                                                        
            
                                    
            
            
                | 17 |  |  |         self._logger = logging.getLogger(__name__) | 
            
                                                        
            
                                    
            
            
                | 18 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 19 |  |  |     def get_parser(self, prog_name): | 
            
                                                        
            
                                    
            
            
                | 20 |  |  |         parser = super(GeocodeCommand, self).get_parser(prog_name) | 
            
                                                        
            
                                    
            
            
                | 21 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 22 |  |  |         jobs_help_msg = 'geocode job offers stored in the jobs table' | 
            
                                                        
            
                                    
            
            
                | 23 |  |  |         parser.add_argument('-j', '--jobs', | 
            
                                                        
            
                                    
            
            
                | 24 |  |  |                             help=jobs_help_msg, | 
            
                                                        
            
                                    
            
            
                | 25 |  |  |                             dest='geocode_jobs', | 
            
                                                        
            
                                    
            
            
                | 26 |  |  |                             action='store_const', const=True) | 
            
                                                        
            
                                    
            
            
                | 27 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 28 |  |  |         companies_help_msg = 'geocode the companies stored in the companies ' \ | 
            
                                                        
            
                                    
            
            
                | 29 |  |  |                              'table' | 
            
                                                        
            
                                    
            
            
                | 30 |  |  |         parser.add_argument('-co', '--companies', | 
            
                                                        
            
                                    
            
            
                | 31 |  |  |                             help=companies_help_msg, | 
            
                                                        
            
                                    
            
            
                | 32 |  |  |                             dest='geocode_companies', | 
            
                                                        
            
                                    
            
            
                | 33 |  |  |                             action='store_const', const=True) | 
            
                                                        
            
                                    
            
            
                | 34 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 35 |  |  |         return parser | 
            
                                                        
            
                                    
            
            
                | 36 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 37 |  |  |     def _logging(self, logging_level, message): | 
            
                                                        
            
                                    
            
            
                | 38 |  |  |         self._logger.log(logging_level, message) | 
            
                                                        
            
                                    
            
            
                | 39 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 40 |  |  |     def _job_id_logging(self, job_id, logging_level, message): | 
            
                                                        
            
                                    
            
            
                | 41 |  |  |         log_msg = u'[Job offer id: %s] %s' % (job_id, message) | 
            
                                                        
            
                                    
            
            
                | 42 |  |  |         self._logging(logging_level, log_msg) | 
            
                                                        
            
                                    
            
            
                | 43 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 44 |  |  |     def _company_id_logging(self, company_id, logging_level, message): | 
            
                                                        
            
                                    
            
            
                | 45 |  |  |         log_msg = u'[Company: %s] %s' % (company_id, message) | 
            
                                                        
            
                                    
            
            
                | 46 |  |  |         self._logging(logging_level, log_msg) | 
            
                                                        
            
                                    
            
            
                | 47 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 48 |  |  |     def _geocode(self, cls_to_geocode, id_logger): | 
            
                                                        
            
                                    
            
            
                | 49 |  |  |         to_geoloc = cls_to_geocode.get_pending_geolocations() | 
            
                                                        
            
                                    
            
            
                | 50 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 51 |  |  |         log_msg = 'Computing required geolocations...' | 
            
                                                        
            
                                    
            
            
                | 52 |  |  |         self._logging(logging.INFO, log_msg) | 
            
                                                        
            
                                    
            
            
                | 53 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 54 |  |  |         for obj in to_geoloc: | 
            
                                                        
            
                                    
            
            
                | 55 |  |  |             obj_id = obj.id | 
            
                                                        
            
                                    
            
            
                | 56 |  |  |             obj_address = obj.address | 
            
                                                        
            
                                    
            
            
                | 57 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 58 |  |  |             log_msg = 'Resolving address: %s.' % obj_address | 
            
                                                        
            
                                    
            
            
                | 59 |  |  |             id_logger(obj_id, logging.INFO, log_msg) | 
            
                                                        
            
                                    
            
            
                | 60 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 61 |  |  |             try: | 
            
                                                        
            
                                    
            
            
                | 62 |  |  |                 location = self._geolocator.geocode(obj_address) | 
            
                                                        
            
                                    
            
            
                | 63 |  |  |             except geolocation.GeolocationFailure as e: | 
            
                                                        
            
                                    
            
            
                | 64 |  |  |                 cls_to_geocode.set_address_is_valid(obj_id, False) | 
            
                                                        
            
                                    
            
            
                | 65 |  |  |                 cls_to_geocode.set_geolocation_is_valid(obj_id, False) | 
            
                                                        
            
                                    
            
            
                | 66 |  |  |                 id_logger(obj_id, logging.ERROR, e) | 
            
                                                        
            
                                    
            
            
                | 67 |  |  |             except geolocation.TemporaryError as e: | 
            
                                                        
            
                                    
            
            
                | 68 |  |  |                 cls_to_geocode.set_geolocation_is_valid(obj_id, False) | 
            
                                                        
            
                                    
            
            
                | 69 |  |  |                 id_logger(obj_id, logging.WARNING, e) | 
            
                                                        
            
                                    
            
            
                | 70 |  |  |             except geolocation.GeolocationError as e: | 
            
                                                        
            
                                    
            
            
                | 71 |  |  |                 cls_to_geocode.set_geolocation_is_valid(obj_id, False) | 
            
                                                        
            
                                    
            
            
                | 72 |  |  |                 id_logger(obj_id, logging.ERROR, e) | 
            
                                                        
            
                                    
            
            
                | 73 |  |  |             else: | 
            
                                                        
            
                                    
            
            
                | 74 |  |  |                 log_msg = 'Successful geocoding:  (lat: %s, lon: %s).' % ( | 
            
                                                        
            
                                    
            
            
                | 75 |  |  |                     location.latitude, location.longitude) | 
            
                                                        
            
                                    
            
            
                | 76 |  |  |                 id_logger(obj_id, logging.INFO, log_msg) | 
            
                                                        
            
                                    
            
            
                | 77 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 78 |  |  |                 cls_to_geocode.set_geolocation( | 
            
                                                        
            
                                    
            
            
                | 79 |  |  |                     obj_id, location.latitude, location.longitude) | 
            
                                                        
            
                                    
            
            
                | 80 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 81 |  |  |         log_msg = 'Geolocations computed.' | 
            
                                                        
            
                                    
            
            
                | 82 |  |  |         self._logging(logging.INFO, log_msg) | 
            
                                                        
            
                                    
            
            
                | 83 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 84 |  |  |     def _geocode_job_offers(self): | 
            
                                                        
            
                                    
            
            
                | 85 |  |  |         log_msg = 'Starting job offers geocoding operations...' | 
            
                                                        
            
                                    
            
            
                | 86 |  |  |         self._logging(logging.INFO, log_msg) | 
            
                                                        
            
                                    
            
            
                | 87 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 88 |  |  |         with acquire_inter_process_lock('geocode_job_offers') as acquired: | 
            
                                                        
            
                                    
            
            
                | 89 |  |  |             if not acquired: | 
            
                                                        
            
                                    
            
            
                | 90 |  |  |                 err_msg = 'Another process is already performing geocoding' \ | 
            
                                                        
            
                                    
            
            
                | 91 |  |  |                           'operations on the job offers, aborting now.' | 
            
                                                        
            
                                    
            
            
                | 92 |  |  |                 self._logging(logging.WARNING, err_msg) | 
            
                                                        
            
                                    
            
            
                | 93 |  |  |             else: | 
            
                                                        
            
                                    
            
            
                | 94 |  |  |                 self._geocode(model.JobAlchemy, self._job_id_logging) | 
            
                                                        
            
                                    
            
            
                | 95 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 96 |  |  |     def _geocode_companies(self): | 
            
                                                        
            
                                    
            
            
                | 97 |  |  |         log_msg = 'Starting companies geocoding operations...' | 
            
                                                        
            
                                    
            
            
                | 98 |  |  |         self._logging(logging.INFO, log_msg) | 
            
                                                        
            
                                    
            
            
                | 99 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 100 |  |  |         with acquire_inter_process_lock('geocode_companies') as acquired: | 
            
                                                        
            
                                    
            
            
                | 101 |  |  |             if not acquired: | 
            
                                                        
            
                                    
            
            
                | 102 |  |  |                 err_msg = 'Another process is already performing geocoding ' \ | 
            
                                                        
            
                                    
            
            
                | 103 |  |  |                           'operations on the companies, aborting now.' | 
            
                                                        
            
                                    
            
            
                | 104 |  |  |                 self._logging(logging.WARNING, err_msg) | 
            
                                                        
            
                                    
            
            
                | 105 |  |  |             else: | 
            
                                                        
            
                                    
            
            
                | 106 |  |  |                 self._geocode(model.CompanyAlchemy, self._company_id_logging) | 
            
                                                        
            
                                    
            
            
                | 107 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 108 |  |  |     def take_action(self, parsed_args): | 
            
                                                        
            
                                    
            
            
                | 109 |  |  |         super(GeocodeCommand, self).take_action(parsed_args) | 
            
                                                        
            
                                    
            
            
                | 110 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 111 |  |  |         log_msg = 'Starting geocoding operations.' | 
            
                                                        
            
                                    
            
            
                | 112 |  |  |         self._logging(logging.INFO, log_msg) | 
            
                                                        
            
                                    
            
            
                | 113 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 114 |  |  |         # We do not run geocoding operation for the job offers and companies in | 
            
                                                        
            
                                    
            
            
                | 115 |  |  |         # different sub-processes to prevent overloading the geocoding API. This | 
            
                                                        
            
                                    
            
            
                | 116 |  |  |         # is why geocoding operations are run sequentially. | 
            
                                                        
            
                                    
            
            
                | 117 |  |  |         if parsed_args.geocode_jobs: | 
            
                                                        
            
                                    
            
            
                | 118 |  |  |             self._geocode_job_offers() | 
            
                                                        
            
                                    
            
            
                | 119 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 120 |  |  |         if parsed_args.geocode_companies: | 
            
                                                        
            
                                    
            
            
                | 121 |  |  |             self._geocode_companies() | 
            
                                                        
            
                                    
            
            
                | 122 |  |  |  | 
            
                                                        
            
                                    
            
            
                | 123 |  |  |         log_msg = 'Geocoding operations done.' | 
            
                                                        
            
                                    
            
            
                | 124 |  |  |         self._logging(logging.INFO, log_msg) | 
            
                                                        
            
                                    
            
            
                | 125 |  |  |  |