def create_stock( offer: models.Offer, price: float, quantity: int = None, beginning: datetime.datetime = None, booking_limit_datetime: datetime.datetime = None, ) -> Stock: """Return the new stock or raise an exception if it's not possible.""" validation.check_required_dates_for_stock(offer, beginning, booking_limit_datetime) validation.check_offer_is_editable(offer) validation.check_stocks_are_editable_for_offer(offer) stock = models.Stock( offer=offer, price=price, quantity=quantity, beginningDatetime=beginning, bookingLimitDatetime=booking_limit_datetime, ) repository.save(stock) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_offer_id(client=app.redis_client, offer_id=offer.id) return stock
def create_mediation( user: User, offer: Offer, credit: str, image_as_bytes: bytes, crop_params=None, ) -> Mediation: validation.check_mediation_thumb_quality(image_as_bytes) mediation = Mediation( author=user, offer=offer, credit=credit, ) # `create_thumb()` requires the object to have an id, so we must save now. repository.save(mediation) create_thumb(mediation, image_as_bytes, image_index=0, crop_params=crop_params) mediation.thumbCount = 1 repository.save(mediation) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_offer_id(client=app.redis_client, offer_id=offer.id) return mediation
def synchronize_fnac_stocks(app) -> None: if not feature_queries.is_active(FeatureToggle.FNAC_SYNCHRONIZATION_V2): fnac_stocks_provider_id = get_provider_by_local_class("FnacStocks").id synchronize_venue_providers_for_provider(fnac_stocks_provider_id) return synchronize_fnac_venues_stocks()
def cancel_booking_by_beneficiary(user: User, booking: Booking) -> None: if not user.isBeneficiary: raise RuntimeError( "Unexpected call to cancel_booking_by_beneficiary with non-beneficiary user %s" % user) validation.check_beneficiary_can_cancel_booking(user, booking) booking.isCancelled = True booking.cancellationReason = BookingCancellationReasons.BENEFICIARY repository.save(booking) notifier = MailjetNotificationService() notifier.send_booking_cancellation_emails_to_user_and_offerer( booking=booking, reason=booking.cancellationReason, # FIXME: we should not have to pass this argument. # Notification-related code should be reorganized. send_email=send_raw_email, ) # FIXME: why do we do that when the booking is cancelled by the # *beneficiary*, but not when it's cancelled by the *offerer* (see # cancel_booking_by_offerer)? if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_offer_id(client=app.redis_client, offer_id=booking.stock.offerId)
def create_deposit(beneficiary: User, deposit_source: str, version: int = None) -> Deposit: """Create a new deposit for the user if there is no deposit yet. The ``version`` argument MUST NOT be used outside (very specific) tests. """ existing_deposits = bool( Deposit.query.filter_by(userId=beneficiary.id).count()) if existing_deposits: raise exceptions.AlreadyActivatedException( {"user": ["Cet utilisateur a déjà crédité son pass Culture"]}) if version is None: version = 2 if feature_queries.is_active( FeatureToggle.APPLY_BOOKING_LIMITS_V2) else 1 booking_configuration = bookings_conf.LIMIT_CONFIGURATIONS[version] deposit = Deposit( version=version, amount=booking_configuration.TOTAL_CAP, source=deposit_source, user=beneficiary, expirationDate=_get_expiration_date(), ) return deposit
def delete_stock(stock: Stock) -> None: validation.check_stock_is_deletable(stock) stock.isSoftDeleted = True cancelled_bookings = [] for booking in stock.bookings: if not booking.isCancelled and not booking.isUsed: booking.isCancelled = True cancelled_bookings.append(booking) repository.save(stock, *cancelled_bookings) if cancelled_bookings: try: user_emails.send_batch_cancellation_emails_to_users(cancelled_bookings, mailing.send_raw_email) except mailing.MailServiceException as exc: app.logger.exception("Could not notify beneficiaries about deletion of stock=%s: %s", stock.id, exc) try: user_emails.send_offerer_bookings_recap_email_after_offerer_cancellation( cancelled_bookings, mailing.send_raw_email ) except mailing.MailServiceException as exc: app.logger.exception("Could not notify offerer about deletion of stock=%s: %s", stock.id, exc) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_offer_id(client=app.redis_client, offer_id=stock.offerId)
def update_mediation(mediation: Mediation, is_active: bool) -> Mediation: mediation.isActive = is_active repository.save(mediation) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_offer_id(client=app.redis_client, offer_id=mediation.offerId) return mediation
def test_is_active_returns_false_when_feature_is_inactive(self): # Given feature = Feature.query.filter_by( name=FeatureToggle.WEBAPP_SIGNUP.name).first() feature.isActive = False repository.save(feature) # When / Then assert not is_active(FeatureToggle.WEBAPP_SIGNUP)
def update_venues_for_specific_provider(provider_id: int): if feature_queries.is_active(FeatureToggle.PARALLEL_SYNCHRONIZATION_OF_VENUE_PROVIDER): _update_venues_in_parallel(provider_id) return venue_providers_to_sync = get_active_venue_providers_for_specific_provider(provider_id) for venue_provider in venue_providers_to_sync: synchronize_venue_provider(venue_provider)
def create_mediation_v2( user: User, offer: Offer, credit: str, image_as_bytes: bytes, crop_params: tuple = None, ) -> Mediation: # checks image type, min dimensions validation.check_image(image_as_bytes) existing_mediations = mediation_queries.get_mediations_for_offers( [offer.id]) mediation = Mediation( author=user, offer=offer, credit=credit, ) # `create_thumb()` requires the object to have an id, so we must save now. repository.save(mediation) try: # TODO(fseguin): cleanup after image upload v2 launch create_thumb(mediation, image_as_bytes, image_index=0, crop_params=crop_params, use_v2=True) except Exception as exc: app.logger.exception( "An unexpected error was encountered during the thumbnail creation: %s", exc) # I could not use savepoints and rollbacks with SQLA repository.delete(mediation) raise ThumbnailStorageError else: mediation.thumbCount = 1 repository.save(mediation) # cleanup former thumbnails and mediations for previous_mediation in existing_mediations: try: for thumb_index in range(0, mediation.thumbCount): remove_thumb(previous_mediation, image_index=thumb_index) except Exception as exc: # pylint: disable=broad-except app.logger.exception( "An unexpected error was encountered during the thumbnails deletion for %s: %s", mediation, exc, ) else: repository.delete(previous_mediation) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_offer_id(client=app.redis_client, offer_id=offer.id) return mediation
def cancel_booking_by_offerer(booking: Booking) -> None: validation.check_offerer_can_cancel_booking(booking) booking.isCancelled = True booking.cancellationReason = BookingCancellationReasons.OFFERER repository.save(booking) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_offer_id(client=app.redis_client, offer_id=booking.stock.offerId)
def wrapper(*args, **kwargs): if feature_queries.is_active(feature_toggle): return f(*args, **kwargs) errors = ApiErrors() errors.add_error("Forbidden", "You don't have access to this page or resource") errors.status_code = 403 raise errors
def book_offer( beneficiary: User, stock: Stock, quantity: int, ) -> Booking: """Return a booking or raise an exception if it's not possible.""" validation.check_can_book_free_offer(beneficiary, stock) validation.check_offer_already_booked(beneficiary, stock.offer) validation.check_quantity(stock.offer, quantity) validation.check_stock_is_bookable(stock) total_amount = quantity * stock.price validation.check_expenses_limits(beneficiary, total_amount, stock.offer) # FIXME (dbaty, 2020-10-20): if we directly set relations (for # example with `booking.user = beneficiary`) instead of foreign keys, # the session tries to add the object when `get_user_expenses()` # is called because autoflush is enabled. As such, the PostgreSQL # exceptions (tooManyBookings and insufficientFunds) may raise at # this point and will bubble up. If we want them to be caught, we # have to set foreign keys, so that the session is NOT autoflushed # in `get_user_expenses` and is only committed in `repository.save()` # where exceptions are caught. Since we are using flask-sqlalchemy, # I don't think that we should use autoflush, nor should we use # the `pcapi.repository.repository` module. booking = Booking( userId=beneficiary.id, stockId=stock.id, amount=stock.price, quantity=quantity, token=generate_booking_token(), ) booking.dateCreated = datetime.datetime.utcnow() booking.confirmationDate = compute_confirmation_date( stock.beginningDatetime, booking.dateCreated) repository.save(booking) try: user_emails.send_booking_recap_emails(booking) except MailServiceException as error: logger.exception( "Could not send booking=%s confirmation email to offerer: %s", booking.id, error) try: user_emails.send_booking_confirmation_email_to_beneficiary(booking) except MailServiceException as error: logger.exception( "Could not send booking=%s confirmation email to beneficiary: %s", booking.id, error) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_offer_id(client=app.redis_client, offer_id=stock.offerId) return booking
def update_offer( # pylint: disable=redefined-builtin offer: Offer, bookingEmail: str = UNCHANGED, description: str = UNCHANGED, isNational: bool = UNCHANGED, name: str = UNCHANGED, extraData: dict = UNCHANGED, type: str = UNCHANGED, url: str = UNCHANGED, withdrawalDetails: str = UNCHANGED, isActive: bool = UNCHANGED, isDuo: bool = UNCHANGED, durationMinutes: int = UNCHANGED, mediaUrls: List[str] = UNCHANGED, ageMin: int = UNCHANGED, ageMax: int = UNCHANGED, conditions: str = UNCHANGED, venueId: str = UNCHANGED, productId: str = UNCHANGED, ) -> Offer: # fmt: off modifications = { field: new_value for field, new_value in locals().items() if field != 'offer' and new_value is not UNCHANGED # has the user provided a value for this field and getattr(offer, field) != new_value # is the value different from what we have on database? } # fmt: on if not modifications: return offer validation.check_offer_is_editable(offer) if offer.isFromAllocine: validation.check_update_only_allowed_offer_fields_for_allocine_offer(set(modifications)) offer.populate_from_dict(modifications) if offer.product.owningOfferer and offer.product.owningOfferer == offer.venue.managingOfferer: offer.product.populate_from_dict(modifications) product_has_been_updated = True else: product_has_been_updated = False if offer.isFromAllocine: offer.fieldsUpdated = list(set(offer.fieldsUpdated) | set(modifications)) repository.save(offer) if product_has_been_updated: repository.save(offer.product) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_offer_id(client=app.redis_client, offer_id=offer.id) return offer
def activate_venue_offers(venue_id): venue = load_or_404(Venue, venue_id) check_user_has_access_to_offerer(current_user, venue.managingOffererId) offers = venue.offers activated_offers = update_is_active_status(offers, True) repository.save(*activated_offers) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_venue_id(client=app.redis_client, venue_id=venue.id) return jsonify([ as_dict(offer, includes=OFFER_INCLUDES) for offer in activated_offers ]), 200
def deactivate_venue_offers(venue_id): venue = load_or_404(VenueSQLEntity, venue_id) ensure_current_user_has_rights(RightsType.editor, venue.managingOffererId) offers = venue.offers deactivated_offers = update_is_active_status(offers, False) repository.save(*deactivated_offers) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_venue_id(client=app.redis_client, venue_id=venue.id) return jsonify([ as_dict(offer, includes=OFFER_INCLUDES) for offer in deactivated_offers ]), 200
def _reindex_offers(created_or_updated_objects): if not feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): return offer_ids = set() for obj in created_or_updated_objects: if isinstance(obj, Stock): offer_ids.add(obj.offerId) elif isinstance(obj, Offer): offer_ids.add(obj.id) for offer_id in offer_ids: redis.add_offer_id(client=app.redis_client, offer_id=offer_id)
def update_offers_active_status(query, is_active): # We cannot just call `query.update()` because `distinct()` may # already have been called on `query`. query_to_update = Offer.query.filter(Offer.id.in_(query.with_entities(Offer.id))) query_to_update.update({"isActive": is_active}, synchronize_session=False) db.session.commit() if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): offer_ids = {offer_id for offer_id, in query.with_entities(Offer.id)} for offer_id in offer_ids: redis.add_offer_id(client=app.redis_client, offer_id=offer_id)
def upsert_stocks( offer_id: int, stock_data_list: List[Union[StockCreationBodyModel, StockEditionBodyModel]] ) -> List[Stock]: stocks = [] edited_stocks = [] edited_stocks_previous_beginnings = {} offer = offer_queries.get_offer_by_id(offer_id) for stock_data in stock_data_list: if isinstance(stock_data, StockEditionBodyModel): stock = Stock.queryNotSoftDeleted().filter_by( id=stock_data.id).first_or_404() if stock.offerId != offer_id: errors = ApiErrors() errors.add_error( "global", "Vous n'avez pas les droits d'accès suffisant pour accéder à cette information." ) errors.status_code = 403 raise errors edited_stocks_previous_beginnings[ stock.id] = stock.beginningDatetime edited_stock = _edit_stock( stock, price=stock_data.price, quantity=stock_data.quantity, beginning=stock_data.beginning_datetime, booking_limit_datetime=stock_data.booking_limit_datetime, ) edited_stocks.append(edited_stock) stocks.append(edited_stock) else: created_stock = _create_stock( offer=offer, price=stock_data.price, quantity=stock_data.quantity, beginning=stock_data.beginning_datetime, booking_limit_datetime=stock_data.booking_limit_datetime, ) stocks.append(created_stock) repository.save(*stocks) for stock in edited_stocks: previous_beginning = edited_stocks_previous_beginnings[stock.id] if stock.beginningDatetime != previous_beginning: _notify_beneficiaries_upon_stock_edit(stock) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_offer_id(client=app.redis_client, offer_id=offer.id) return stocks
def _create(cls, model_class, *args, **kwargs): if "amount" in kwargs: raise ValueError( "You cannot directly set deposit amount: set version instead") version = kwargs.get("version") if not version: version = 2 if feature_queries.is_active( FeatureToggle.APPLY_BOOKING_LIMITS_V2) else 1 amount = bookings_conf.LIMIT_CONFIGURATIONS[version].TOTAL_CAP kwargs["version"] = version kwargs["amount"] = amount return super()._create(model_class, *args, **kwargs)
def _run_first_synchronization(new_venue_provider: VenueProvider): if not feature_queries.is_active( FeatureToggle.SYNCHRONIZE_VENUE_PROVIDER_IN_WORKER): subprocess.Popen([ "python", "src/pcapi/scripts/pc.py", "update_providables", "--venue-provider-id", str(new_venue_provider.id), ]) return venue_provider_job.delay(new_venue_provider.id)
def request_password_reset(body: RequestPasswordResetRequest) -> None: if feature_queries.is_active(FeatureToggle.ENABLE_NATIVE_APP_RECAPTCHA): try: api_recaptcha.check_native_app_recaptcha_token(body.token) except api_recaptcha.ReCaptchaException: raise ApiErrors({"token": "The given token is not invalid"}) user = find_user_by_email(body.email) try: users_api.request_password_reset(user) except users_exceptions.EmailNotSent: raise ApiErrors( {"email": ["L'email n'a pas pu être envoyé"]}, status_code=400, )
def _is_postal_code_eligible(code: str) -> bool: # FIXME (dbaty, 2020-01-14): remove this block once we have opened # to (almost) all departments. # Legacy behaviour: only a few departments are eligible. if not feature_queries.is_active(FeatureToggle.WHOLE_FRANCE_OPENING): for department in ELIGIBLE_DEPARTMENTS: if code.startswith(department): return True return False # New behaviour: all departments are eligible, except a few. for department in EXCLUDED_DEPARTMENTS: if code.startswith(department): return False return True
def generate_and_send_payments(payment_message_id: str = None): logger.info( "[BATCH][PAYMENTS] STEP 0 : validate bookings associated to outdated stocks" ) if feature_queries.is_active(FeatureToggle.UPDATE_BOOKING_USED): update_booking_used_after_stock_occurrence() not_processable_payments, payments_to_send = generate_or_collect_payments( payment_message_id) try: logger.info("[BATCH][PAYMENTS] STEP 3 : send transactions") send_transactions( payments_to_send, settings.PASS_CULTURE_IBAN, settings.PASS_CULTURE_BIC, settings.PASS_CULTURE_REMITTANCE_CODE, settings.TRANSACTIONS_RECIPIENTS, ) except Exception as e: # pylint: disable=broad-except logger.exception("[BATCH][PAYMENTS] STEP 3: %s", e) try: logger.info("[BATCH][PAYMENTS] STEP 4 : send payments report") send_payments_report(payments_to_send + not_processable_payments, settings.PAYMENTS_REPORT_RECIPIENTS) except Exception as e: # pylint: disable=broad-except logger.exception("[BATCH][PAYMENTS] STEP 4: %s", e) try: logger.info("[BATCH][PAYMENTS] STEP 5 : send payments details") send_payments_details(payments_to_send, settings.PAYMENTS_DETAILS_RECIPIENTS) except Exception as e: # pylint: disable=broad-except logger.exception("[BATCH][PAYMENTS] STEP 5: %s", e) try: logger.info("[BATCH][PAYMENTS] STEP 6 : send wallet balances") send_wallet_balances(settings.WALLET_BALANCES_RECIPIENTS) except Exception as e: # pylint: disable=broad-except logger.exception("[BATCH][PAYMENTS] STEP 6: %s", e) logger.info("[BATCH][PAYMENTS] generate_and_send_payments is done")
def create_account(body: serializers.AccountRequest) -> None: if feature_queries.is_active(FeatureToggle.ENABLE_NATIVE_APP_RECAPTCHA): try: api_recaptcha.check_native_app_recaptcha_token(body.token) except api_recaptcha.ReCaptchaException: raise ApiErrors({"token": "The given token is not invalid"}) try: api.create_account( email=body.email, password=body.password, birthdate=body.birthdate, has_allowed_recommendations=body.has_allowed_recommendations, is_email_validated=False, ) except exceptions.UserAlreadyExistsException: user = find_user_by_email(body.email) api.request_password_reset(user) except exceptions.UnderAgeUserException: raise ApiErrors({"dateOfBirth": "The birthdate is invalid"})
def edit_venue(venue_id): venue = load_or_404(Venue, venue_id) previous_venue = copy.deepcopy(venue) check_valid_edition(request, venue) validate_coordinates(request.json.get("latitude", None), request.json.get("longitude", None)) check_user_has_access_to_offerer(current_user, venue.managingOffererId) venue.populate_from_dict(request.json) if not venue.isVirtual: delete_venue_from_iris_venues(venue.id) repository.save(venue) link_valid_venue_to_irises(venue) if is_algolia_indexing(previous_venue, request.json): if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_venue_id(client=app.redis_client, venue_id=dehumanize(venue_id)) return jsonify(as_dict(venue, includes=VENUE_INCLUDES)), 200
def generate_new_payments() -> Tuple[List[Payment], List[Payment]]: offerers = Offerer.query.all() all_payments = [] for offerer in offerers: if is_active(FeatureToggle.DEGRESSIVE_REIMBURSEMENT_RATE): booking_reimbursements = [] for venue in offerer.managedVenues: final_bookings = booking_repository.find_bookings_eligible_for_payment_for_venue( venue.id) booking_reimbursements += find_all_booking_reimbursements( final_bookings, NEW_RULES) else: final_bookings = booking_repository.find_bookings_eligible_for_payment_for_offerer( offerer.id) booking_reimbursements = find_all_booking_reimbursements( final_bookings, CURRENT_RULES) booking_reimbursements_to_pay = filter_out_already_paid_for_bookings( filter_out_bookings_without_cost(booking_reimbursements)) with db.session.no_autoflush: payments = list( map(create_payment_for_booking, booking_reimbursements_to_pay)) if payments: repository.save(*payments) all_payments.extend(payments) logger.info("[BATCH][PAYMENTS] Saved %i payments for offerer : %s", len(payments), offerer.name) logger.info( "[BATCH][PAYMENTS] Generated %i payments for %i offerers in total", len(all_payments), len(offerers)) pending_payments = keep_only_pending_payments(all_payments) not_processable_payments = keep_only_not_processable_payments(all_payments) logger.info("[BATCH][PAYMENTS] %s Payments in status PENDING to send", len(pending_payments)) return pending_payments, not_processable_payments
def validate_new_offerer(token): check_validation_request(token) offerer = Offerer.query.filter_by(validationToken=token).first() check_validation_token_has_been_already_used(offerer) offerer.validationToken = None managed_venues = offerer.managedVenues for venue in managed_venues: link_valid_venue_to_irises(venue) repository.save(offerer) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): venue_ids = map(lambda managed_venue: managed_venue.id, managed_venues) sorted_venue_ids = sorted(venue_ids, key=int) for venue_id in sorted_venue_ids: redis.add_venue_id(client=app.redis_client, venue_id=venue_id) try: send_validation_confirmation_email_to_pro(offerer, send_raw_email) except MailServiceException as mail_service_exception: app.logger.exception("Email service failure", mail_service_exception) return "Validation effectuée", 202
def cancel_booking_by_beneficiary(user: User, booking: Booking) -> None: if not user.isBeneficiary: raise RuntimeError( "Unexpected call to cancel_booking_by_beneficiary with non-beneficiary user %s" % user) validation.check_beneficiary_can_cancel_booking(user, booking) booking.isCancelled = True booking.cancellationReason = BookingCancellationReasons.BENEFICIARY repository.save(booking) try: user_emails.send_booking_cancellation_emails_to_user_and_offerer( booking, booking.cancellationReason) except MailServiceException as error: logger.exception( "Could not send booking=%s cancellation emails to user and offerer: %s", booking.id, error) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): redis.add_offer_id(client=app.redis_client, offer_id=booking.stock.offerId)
def updateObjects(self, limit=None): # pylint: disable=too-many-nested-blocks if self.venue_provider and not self.venue_provider.isActive: logger.info("Venue provider %s is inactive", self.venue_provider) return if not self.provider.isActive: provider_name = self.__class__.__name__ logger.info("Provider %s is inactive", provider_name) return self.log_provider_event(LocalProviderEventType.SyncStart) chunk_to_insert = {} chunk_to_update = {} for providable_infos in self: objects_limit_reached = limit and self.checkedObjects >= limit if objects_limit_reached: break has_no_providables_info = len(providable_infos) == 0 if has_no_providables_info: self.checkedObjects += 1 continue for providable_info in providable_infos: chunk_key = providable_info.id_at_providers + "|" + str(providable_info.type.__name__) pc_object = get_existing_pc_obj(providable_info, chunk_to_insert, chunk_to_update) if pc_object is None: if not self.can_create: continue try: pc_object = self._create_object(providable_info) chunk_to_insert[chunk_key] = pc_object except ApiErrors: continue else: last_update_for_current_provider = get_last_update_for_provider(self.provider.id, pc_object) object_need_update = ( last_update_for_current_provider is None or last_update_for_current_provider < providable_info.date_modified_at_provider ) if object_need_update: try: self._handle_update(pc_object, providable_info) if chunk_key in chunk_to_insert: chunk_to_insert[chunk_key] = pc_object else: chunk_to_update[chunk_key] = pc_object except ApiErrors: continue if isinstance(pc_object, HasThumbMixin): initial_thumb_count = pc_object.thumbCount try: self._handle_thumb(pc_object) except Exception as e: # pylint: disable=broad-except self.log_provider_event(LocalProviderEventType.SyncError, e.__class__.__name__) self.erroredThumbs += 1 logger.info("ERROR during handle thumb: %s", e, exc_info=True) pc_object_has_new_thumbs = pc_object.thumbCount != initial_thumb_count if pc_object_has_new_thumbs: errors = entity_validator.validate(pc_object) if errors and len(errors.errors) > 0: self.log_provider_event(LocalProviderEventType.SyncError, "ApiErrors") continue chunk_to_update[chunk_key] = pc_object self.checkedObjects += 1 if len(chunk_to_insert) + len(chunk_to_update) >= CHUNK_MAX_SIZE: save_chunks(chunk_to_insert, chunk_to_update) chunk_to_insert = {} chunk_to_update = {} if len(chunk_to_insert) + len(chunk_to_update) > 0: save_chunks(chunk_to_insert, chunk_to_update) self._print_objects_summary() self.log_provider_event(LocalProviderEventType.SyncEnd) if self.venue_provider is not None: self.venue_provider.lastSyncDate = datetime.utcnow() self.venue_provider.syncWorkerId = None repository.save(self.venue_provider) if feature_queries.is_active(FeatureToggle.SYNCHRONIZE_ALGOLIA): send_venue_provider_data_to_redis(self.venue_provider)