def test_should_register_new_venue(app): # given user = ProFactory() auth_request = TestClient(app.test_client()).with_session_auth(email=user.email) venue_data = create_valid_venue_data(user) # when response = auth_request.post("/venues", json=venue_data) # then assert response.status_code == 201 idx = response.json["id"] venue = Venue.query.filter_by(id=dehumanize(idx)).one() assert venue.name == venue_data["name"] assert venue.publicName == venue_data["publicName"] assert venue.siret == venue_data["siret"] assert venue.venueTypeId == dehumanize(venue_data["venueTypeId"]) assert venue.venueLabelId == dehumanize(venue_data["venueLabelId"]) assert venue.description == venue_data["description"] assert venue.audioDisabilityCompliant == venue_data["audioDisabilityCompliant"] assert venue.mentalDisabilityCompliant == venue_data["mentalDisabilityCompliant"] assert venue.motorDisabilityCompliant == venue_data["motorDisabilityCompliant"] assert venue.visualDisabilityCompliant == venue_data["visualDisabilityCompliant"] assert venue.contact.email == venue_data["contact"]["email"] assert venue.isValidated assert not venue.isPermanent assert not venue.contact.phone_number assert not venue.contact.social_medias
def get_stocks(offer_id: str) -> StocksResponseModel: offerer = offerer_queries.get_by_offer_id(dehumanize(offer_id)) check_user_has_access_to_offerer(current_user, offerer.id) stocks = get_stocks_for_offer(dehumanize(offer_id)) return StocksResponseModel( stocks=[StockResponseModel.from_orm(stock) for stock in stocks], )
def get_stocks(offer_id: str) -> StocksResponseModel: try: offerer = pcapi.core.offerers.repository.get_by_offer_id(dehumanize(offer_id)) except offerers_exceptions.CannotFindOffererForOfferId: raise ApiErrors({"offerer": ["Aucune structure trouvée à partir de cette offre"]}, status_code=404) check_user_has_access_to_offerer(current_user, offerer.id) stocks = get_stocks_for_offer(dehumanize(offer_id)) return StocksResponseModel( stocks=[StockResponseModel.from_orm(stock) for stock in stocks], )
def when_creating_new_thing_offer(self, app): # Given venue = offers_factories.VirtualVenueFactory() offerer = venue.managingOfferer offers_factories.UserOffererFactory(offerer=offerer, user__email="*****@*****.**") # When client = TestClient(app.test_client()).with_auth("*****@*****.**") data = { "venueId": humanize(venue.id), "bookingEmail": "*****@*****.**", "mediaUrls": ["http://example.com/media"], "name": "Les lièvres pas malins", "type": "ThingType.JEUX_VIDEO", "url": "http://example.com/offer", } response = client.post("/offers", json=data) # Then assert response.status_code == 201 offer_id = dehumanize(response.json["id"]) offer = Offer.query.get(offer_id) assert offer.bookingEmail == "*****@*****.**" assert offer.type == str(ThingType.JEUX_VIDEO) assert offer.venue == venue assert offer.product.name == "Les lièvres pas malins" assert offer.product.url == "http://example.com/offer" assert offer.url == "http://example.com/offer" assert offer.isDigital assert offer.isNational assert offer.product.isNational assert offer.product.owningOfferer == offerer
def test_create_event_offer(self, app): # Given venue = offers_factories.VenueFactory() offerer = venue.managingOfferer offers_factories.UserOffererFactory(offerer=offerer, user__email="*****@*****.**") # When data = { "venueId": humanize(venue.id), "bookingEmail": "*****@*****.**", "durationMinutes": 60, "name": "La pièce de théâtre", "type": str(EventType.SPECTACLE_VIVANT), "extraData": {"toto": "text"}, } client = TestClient(app.test_client()).with_auth("*****@*****.**") response = client.post("/offers", json=data) # Then assert response.status_code == 201 offer_id = dehumanize(response.json["id"]) offer = Offer.query.get(offer_id) assert offer.bookingEmail == "*****@*****.**" assert offer.type == str(EventType.SPECTACLE_VIVANT) assert offer.extraData == {"toto": "text"} assert offer.venue == venue assert offer.product.durationMinutes == 60 assert offer.product.owningOfferer == offerer
def when_venue_provider_is_successfully_created( self, mock_siret_can_be_synchronized, mock_synchronize_venue_provider, app): # Given user = user_factories.AdminFactory() venue = offer_factories.VenueFactory(siret="12345678912345") provider = offerers_factories.APIProviderFactory() venue_provider_data = { "providerId": humanize(provider.id), "venueId": humanize(venue.id), } mock_siret_can_be_synchronized.return_value = True auth_request = TestClient( app.test_client()).with_session_auth(email=user.email) # When response = auth_request.post("/venueProviders", json=venue_provider_data) # Then assert response.status_code == 201 venue_provider = VenueProvider.query.one() assert venue_provider.venueId == venue.id assert venue_provider.providerId == provider.id assert venue_provider.venueIdAtOfferProvider == "12345678912345" assert "id" in response.json venue_provider_id = response.json["id"] mock_synchronize_venue_provider.assert_called_once_with( dehumanize(venue_provider_id))
def test_create_valid_educational_offer(self, app): # Given venue = offers_factories.VenueFactory() offerer = venue.managingOfferer offers_factories.UserOffererFactory(offerer=offerer, user__email="*****@*****.**") # When data = { "venueId": humanize(venue.id), "bookingEmail": "*****@*****.**", "durationMinutes": 60, "name": "La pièce de théâtre", "subcategoryId": subcategories.SPECTACLE_REPRESENTATION.id, "extraData": {"toto": "text"}, "externalTicketOfficeUrl": "http://example.net", "audioDisabilityCompliant": False, "mentalDisabilityCompliant": True, "motorDisabilityCompliant": False, "visualDisabilityCompliant": False, "isEducational": True, } client = TestClient(app.test_client()).with_session_auth("*****@*****.**") response = client.post("/offers", json=data) # Then assert response.status_code == 201 offer_id = dehumanize(response.json["id"]) offer = Offer.query.get(offer_id) assert offer.isEducational
def search(self, description, criteria): filters = self._build_filters(criteria) start = time.perf_counter() results = self.index.search(criteria.get("text", ""), filters) elapsed = time.perf_counter() - start # XXX: apparently, the Algolia index still contains offers # whose 'objectID' are a humanized id. This should not happen # anymore and those offers should be reindexed under their # numeric id instead. Example: CUUEE. for result in results["hits"]: if not result["objectID"].isdigit(): print( f'[Algolia] Found offer indexed with its humanized id: {result["objectID"]}' ) result["objectID"] = human_ids.dehumanize(result["objectID"]) return ResultSet( elapsed=elapsed, query= filters, # searched text is missing but we don't need it for Algolia results=[ Result( id=int(result["objectID"]), score=None, # not useful for Algolia results name=result["offer"]["name"], full=result, ) for result in results["hits"] ], )
def when_venue_provider_is_successfully_created( self, stubbed_find_by_id, stubbed_check, mock_synchronize_venue_provider, app ): # Given user = create_user(is_admin=True, is_beneficiary=False) offerer = create_offerer() venue = create_venue(offerer, siret="12345678912345") repository.save(venue, user) stubbed_find_by_id.return_value = venue provider = activate_provider("LibrairesStocks") venue_provider_data = { "providerId": humanize(provider.id), "venueId": humanize(venue.id), } auth_request = TestClient(app.test_client()).with_auth(email=user.email) stubbed_check.return_value = True # When response = auth_request.post("/venueProviders", json=venue_provider_data) # Then assert response.status_code == 201 venue_provider = VenueProvider.query.one() assert venue_provider.venueId == venue.id assert venue_provider.providerId == provider.id assert venue_provider.venueIdAtOfferProvider == "12345678912345" assert "id" in response.json venue_provider_id = response.json["id"] mock_synchronize_venue_provider.assert_called_once_with(dehumanize(venue_provider_id))
def get_booking(booking_id: int) -> Any: booking = ( Booking.query.filter_by(id=dehumanize(booking_id)).options(joinedload(Booking.individualBooking)).first_or_404() ) booking.userId = booking.individualBooking.userId return jsonify(as_dict(booking, includes=WEBAPP_GET_BOOKING_INCLUDES)), 200
def get_user_offerer(offerer_id): user_offerers = UserOfferer.query.filter_by( user=current_user, offererId=dehumanize(offerer_id)).all() return jsonify([ as_dict(user_offerer, includes=USER_OFFERER_INCLUDES) for user_offerer in user_offerers ]), 200
def test_edit_one_stock(self, app): # Given offer = offers_factories.ThingOfferFactory() existing_stock = offers_factories.StockFactory(offer=offer, price=10) offers_factories.UserOffererFactory( user__email="*****@*****.**", offerer=offer.venue.managingOfferer, ) # When stock_data = { "offerId": humanize(offer.id), "stocks": [{ "id": humanize(existing_stock.id), "price": 20 }], } response = TestClient( app.test_client()).with_auth("*****@*****.**").post( "/stocks/bulk/", json=stock_data) # Then assert response.status_code == 201 response_dict = response.json assert len(response_dict["stockIds"]) == len(stock_data["stocks"]) edited_stock = Stock.query.get( dehumanize(response_dict["stockIds"][0]["id"])) assert edited_stock.price == 20
def _connect_stock_providers_to_venue(venue: VenueSQLEntity, venue_provider_payload: Dict) -> VenueProvider: venue_provider = VenueProvider() venue_provider.venue = venue venue_provider.providerId = dehumanize(venue_provider_payload["providerId"]) venue_provider.venueIdAtOfferProvider = venue.siret repository.save(venue_provider) return venue_provider
def delete_favorite(offer_id): dehumanized_offer_id = dehumanize(offer_id) favorite = find_favorite_for_offer_and_user(dehumanized_offer_id, current_user.id).first_or_404() repository.delete(favorite) return jsonify(as_dict(favorite)), 200
def update_from_csv_file(csv_file): # XXX: We don't look at CustomReimbursementRule.timespan here, # since we currently don't have multiple rules for the same offer, # and this script does not support that for simplicity's sake. existing_rules = { rule.offerId: rule for rule in CustomReimbursementRule.query.all() } results = {"errors": [], "warnings": [], "created": []} reader = csv.DictReader(csv_file, fieldnames=FIELD_NAMES) for line_nb, row in enumerate(reader, 1): if line_nb == 1: continue # ignore header offer_id = dehumanize(row["humanized_offer_id"]) reimbursed_amount = _get_amount(row["reimbursed_amount"]) file_offer_amount = _get_amount(row["offer_amount"]) offer = Offer.query.options(joinedload( Offer.stocks)).filter_by(id=offer_id).one_or_none() if not offer: results["errors"].append( f"line {line_nb}: File references unknown offer {row['humanized_offer_id']} ({offer_id})" ) continue rule = existing_rules.get(offer_id) if rule: if rule.amount != reimbursed_amount: results["errors"].append( f"line {line_nb}: Existing rule on offer {row['humanized_offer_id']} ({offer_id}) reimburses {rule.amount}, file says {reimbursed_amount}" ) continue prices = {stock.price for stock in offer.stocks} if len(prices) > 1: results["warnings"].append( f"line {line_nb}: Found multiple prices for offer {row['humanized_offer_id']} ({offer_id}): {prices}" ) if not prices: results["warnings"].append( f"line {line_nb}: No stock found in database for offer {row['humanized_offer_id']} ({offer_id}), but reimbursement rule has been added nevertheless" ) else: db_offer_amount = prices.pop() if file_offer_amount != db_offer_amount: results["warnings"].append( f"line {line_nb}: Price in database for offer {row['humanized_offer_id']} ({offer_id}) is {db_offer_amount}, file says {file_offer_amount}" ) rule = CustomReimbursementRule(offerId=offer_id, amount=reimbursed_amount, timespan=DEFAULT_TIMESPAN) repository.save(rule) existing_rules[offer_id] = rule results["created"].append(rule) return results
def _ensure_current_user_has_rights(user_id): if current_user.id != dehumanize(user_id): errors = ApiErrors() errors.add_error( "global", "Vous n'avez pas les droits d'accès suffisant pour effectuer cette modificaiton." ) errors.status_code = 403 raise errors
def dehumanize_id(id_to_dehumanize: Optional[Union[int, str]]) -> Optional[int]: if id_to_dehumanize is None: return None # This is because dehumanize_id will be called on a str the first time # and then on ids already dehumanized. dehumanize can't work with int if isinstance(id_to_dehumanize, str): return dehumanize(id_to_dehumanize) return int(id_to_dehumanize)
def create_booking(body: PostBookingBodyModel) -> PostBookingResponseModel: stock = Stock.query.filter_by(id=dehumanize(body.stock_id)).first_or_404() if body.stock_id else None booking = bookings_api.book_offer( beneficiary=current_user, stock=stock, quantity=body.quantity, ) return PostBookingResponseModel(**serialize_booking_minimal(booking))
def _create_allocine_venue_provider( allocine_theater_id: str, venue_provider_payload: Dict, venue: Venue ) -> AllocineVenueProvider: allocine_venue_provider = AllocineVenueProvider() allocine_venue_provider.venue = venue allocine_venue_provider.providerId = dehumanize(venue_provider_payload["providerId"]) allocine_venue_provider.venueIdAtOfferProvider = allocine_theater_id allocine_venue_provider.isDuo = venue_provider_payload.get("isDuo") allocine_venue_provider.quantity = venue_provider_payload.get("quantity") return allocine_venue_provider
def generate_api_key_route(offerer_id: str) -> GenerateOffererApiKeyResponse: check_user_has_access_to_offerer(current_user, dehumanize(offerer_id)) offerer = load_or_404(Offerer, offerer_id) try: clear_key = api.generate_and_save_api_key(offerer.id) except ApiKeyCountMaxReached: raise ApiErrors({"api_key_count_max": "Le nombre de clés maximal a été atteint"}) except ApiKeyPrefixGenerationError: raise ApiErrors({"api_key": "Could not generate api key"}) return GenerateOffererApiKeyResponse(apiKey=clear_key)
def test_on_pc_object_for_valid_sql_humanize_id_value_with_key_finishing_by_Id(self): # Given test_pc_object = TestPcObject() humanized_entity_id = "AE" data = {"entityId": humanized_entity_id} # When test_pc_object.populate_from_dict(data) # Then assert test_pc_object.entityId == dehumanize(humanized_entity_id)
def load_or_raise_error(obj_class, human_id): data = obj_class.query.filter_by(id=dehumanize(human_id)).first() if data is None: errors = ApiErrors() errors.add_error( "global", "Aucun objet ne correspond à cet identifiant dans notre base de données" ) errors.status_code = 400 raise errors return data
def delete_stock(stock_id: str) -> StockResponseIdModel: # fmt: off stock = (Stock.queryNotSoftDeleted().filter_by( id=dehumanize(stock_id)).join(Offer, VenueSQLEntity).first_or_404()) # fmt: on offerer_id = stock.offer.venue.managingOffererId ensure_current_user_has_rights(RightsType.editor, offerer_id) offers_api.delete_stock(stock) return StockResponseIdModel.from_orm(stock)
def list_venue_providers(): venue_id = request.args.get("venueId") if venue_id is None: e = ApiErrors() e.add_error("venueId", "Vous devez obligatoirement fournir le paramètre venueId") return jsonify(e.errors), 400 vp_query = VenueProvider.query.filter_by(venueId=dehumanize(venue_id)) return jsonify([ as_dict(venue_provider, includes=VENUE_PROVIDER_INCLUDES) for venue_provider in vp_query.all() ])
def load_or_raise_error(obj_class: Model, human_id: str) -> Model: try: data = obj_class.query.filter_by(id=dehumanize(human_id)).one() except NoResultFound: raise ApiErrors( errors={ "global": [ "Aucun objet ne correspond à cet identifiant dans notre base de données" ], }, status_code=404, ) return data
def patch_booking_by_token(token: str) -> tuple[str, int]: email: Optional[str] = request.args.get("email", None) humanized_offer_id: Optional[str] = request.args.get("offer_id") offer_id: Optional[int] = dehumanize(humanized_offer_id) booking = booking_repository.find_by(token, email, offer_id) if current_user.is_authenticated: check_user_has_access_to_offerer(current_user, booking.offererId) else: check_email_and_offer_id_for_anonymous_user(email, offer_id) bookings_api.mark_as_used(booking) return "", 204
def get_booking_by_token(token: str) -> tuple[str, int]: email: Optional[str] = request.args.get("email", None) offer_id = dehumanize(request.args.get("offer_id", None)) check_user_is_logged_in_or_email_is_provided(current_user, email) booking = booking_repository.find_by(token, email, offer_id) bookings_validation.check_is_usable(booking) if check_user_can_validate_bookings(current_user, booking.offererId): response = _create_response_to_get_booking_by_token(booking) return jsonify(response), 200 return "", 204
def create_booking(body: PostBookingBodyModel) -> PostBookingResponseModel: if not body.stock_id: abort(404) try: booking = bookings_api.book_offer( beneficiary=current_user, stock_id=dehumanize(body.stock_id), quantity=body.quantity, ) except StockDoesNotExist: abort(404) return PostBookingResponseModel(**serialize_booking_minimal(booking))
def update_mediation( mediation_id: str, body: UpdateMediationBodyModel) -> UpdateMediationResponseModel: mediation = Mediation.query.filter_by( id=dehumanize(mediation_id)).first_or_404() ensure_current_user_has_rights(RightsType.editor, mediation.offer.venue.managingOffererId) mediation = offers_api.update_mediation(mediation=mediation, is_active=body.isActive) return UpdateMediationResponseModel.from_orm(mediation)
def delete_venue_and_offers_for_venue_id(humanized_venue_id: str): dehumanized_venue_id = dehumanize(humanized_venue_id) offers = get_offers_by_venue_id(dehumanized_venue_id) venue = find_by_id(dehumanized_venue_id) if any(offer.stocks for offer in offers): raise AttributeError( "Offres non supprimables car au moins une contient des stocks") for offer in offers: repository.delete(offer) if venue: repository.delete(venue)