diff --git a/api/src/pcapi/core/external_bookings/serialize.py b/api/src/pcapi/core/external_bookings/serialize.py index e2196685782..6ef3ac4b0c4 100644 --- a/api/src/pcapi/core/external_bookings/serialize.py +++ b/api/src/pcapi/core/external_bookings/serialize.py @@ -6,8 +6,8 @@ import pcapi.core.bookings.models as bookings_models import pcapi.core.finance.utils as finance_utils import pcapi.core.offers.models as offers_models +from pcapi.core.offers.utils import get_offer_address import pcapi.core.users.models as users_models -from pcapi.models.feature import FeatureToggle logger = logging.getLogger(__name__) @@ -59,16 +59,7 @@ def build_external_booking( else {"offer_price": finance_utils.to_eurocents(stock.price)} ) - offer_address = stock.offer.offererAddress - if FeatureToggle.WIP_USE_OFFERER_ADDRESS_AS_DATA_SOURCE.is_active() and offer_address: - # The label is the name of the venue, if it exists, otherwise the street is used - address = offer_address.address.street - venue_name = offer_address.label if offer_address.label else stock.offer.venue.name - department_code = offer_address.address.departmentCode - else: - address = stock.offer.venue.street - venue_name = stock.offer.venue.name - department_code = stock.offer.venue.departementCode + offer_address = get_offer_address(stock.offer) return cls( booking_confirmation_date=booking.cancellationLimitDate, @@ -85,10 +76,10 @@ def build_external_booking( user_first_name=user.firstName, user_last_name=user.lastName, user_phone=user.phoneNumber, - venue_address=address, - venue_department_code=department_code, + venue_address=offer_address.street, + venue_department_code=offer_address.departmentCode, venue_id=stock.offer.venue.id, - venue_name=venue_name, + venue_name=offer_address.label, **price_data, # type: ignore[arg-type] ) diff --git a/api/src/pcapi/core/offers/offer_metadata.py b/api/src/pcapi/core/offers/offer_metadata.py index bb4f7bc5bd2..ca4327822b6 100644 --- a/api/src/pcapi/core/offers/offer_metadata.py +++ b/api/src/pcapi/core/offers/offer_metadata.py @@ -2,8 +2,8 @@ import typing from pcapi.core.categories import subcategories_v2 -from pcapi.core.offerers.models import Venue import pcapi.core.offers.models as offers_models +from pcapi.core.offers.utils import get_offer_address from pcapi.core.offers.utils import offer_app_link from pcapi.core.providers import constants as providers_constants @@ -17,20 +17,21 @@ } -def _get_metadata_from_venue(venue: Venue) -> Metadata: +def _get_location_metadata(offer: offers_models.Offer) -> Metadata: + offer_address = get_offer_address(offer) return { "@type": "Place", - "name": venue.name, + "name": offer_address.label, "address": { "@type": "PostalAddress", - "streetAddress": venue.street, - "postalCode": venue.postalCode, - "addressLocality": venue.city, + "streetAddress": offer_address.street, + "postalCode": offer_address.postalCode, + "addressLocality": offer_address.city, }, "geo": { "@type": "GeoCoordinates", - "latitude": str(venue.latitude), - "longitude": str(venue.longitude), + "latitude": str(offer_address.latitude), + "longitude": str(offer_address.longitude), }, } @@ -74,7 +75,7 @@ def _get_event_metadata_from_offer(offer: offers_models.Offer) -> Metadata: event_metadata["eventAttendanceMode"] = "OnlineEventAttendanceMode" else: event_metadata["eventAttendanceMode"] = "OfflineEventAttendanceMode" - event_metadata["location"] = _get_metadata_from_venue(offer.venue) + event_metadata["location"] = _get_location_metadata(offer) if offer.metadataFirstBeginningDatetime: firstBeginningDatetime: datetime.datetime = offer.metadataFirstBeginningDatetime diff --git a/api/src/pcapi/core/offers/utils.py b/api/src/pcapi/core/offers/utils.py index 34c72104803..785a2b07e72 100644 --- a/api/src/pcapi/core/offers/utils.py +++ b/api/src/pcapi/core/offers/utils.py @@ -1,3 +1,6 @@ +import dataclasses +from decimal import Decimal + from pcapi import settings from pcapi.core.educational.models import CollectiveOffer from pcapi.core.offers.models import Offer @@ -13,3 +16,39 @@ def offer_app_redirect_link(offer: Offer) -> str: if FeatureToggle.ENABLE_IOS_OFFERS_LINK_WITH_REDIRECTION.is_active(): return f"{settings.WEBAPP_V2_REDIRECT_URL}/offre/{offer.id}" return offer_app_link(offer) + + +@dataclasses.dataclass +class CalculatedOfferAddress: + city: str | None + departmentCode: str | None + label: str + latitude: Decimal | None + longitude: Decimal | None + postalCode: str | None + street: str | None + + +def get_offer_address(offer: Offer) -> CalculatedOfferAddress: + if FeatureToggle.WIP_USE_OFFERER_ADDRESS_AS_DATA_SOURCE.is_active(): + offerer_address = offer.offererAddress + if offerer_address: + return CalculatedOfferAddress( + city=offerer_address.address.city, + departmentCode=offerer_address.address.departmentCode, + label=offerer_address.label or offer.venue.name, + latitude=offerer_address.address.latitude, + longitude=offerer_address.address.longitude, + postalCode=offerer_address.address.postalCode, + street=offerer_address.address.street, + ) + + return CalculatedOfferAddress( + city=offer.venue.city, + departmentCode=offer.venue.departementCode, + label=offer.venue.name, + latitude=offer.venue.latitude, + longitude=offer.venue.longitude, + postalCode=offer.venue.postalCode, + street=offer.venue.street, + ) diff --git a/api/src/pcapi/core/search/backends/algolia.py b/api/src/pcapi/core/search/backends/algolia.py index 61f29dcaa10..e4ef96c8d74 100644 --- a/api/src/pcapi/core/search/backends/algolia.py +++ b/api/src/pcapi/core/search/backends/algolia.py @@ -23,6 +23,7 @@ import pcapi.core.offerers.api as offerers_api import pcapi.core.offerers.models as offerers_models import pcapi.core.offers.models as offers_models +from pcapi.core.offers.utils import get_offer_address from pcapi.core.providers import titelive_gtl from pcapi.core.search.backends import base from pcapi.domain.music_types import MUSIC_TYPES_LABEL_BY_CODE @@ -509,21 +510,12 @@ def serialize_offer(cls, offer: offers_models.Offer, last_30_days_bookings: int) gtl_code_3 = gtl_id[:6] + "00" * 1 gtl_code_4 = gtl_id - address = None - city = None - department_code = None - postal_code = None - if FeatureToggle.WIP_USE_OFFERER_ADDRESS_AS_DATA_SOURCE.is_active(): - if venue.offererAddress is not None: - address = venue.offererAddress.address.street - city = venue.offererAddress.address.city - department_code = venue.offererAddress.address.departmentCode - postal_code = venue.offererAddress.address.postalCode - else: - address = venue.street - city = venue.city - department_code = venue.departementCode - postal_code = venue.postalCode + offer_address = get_offer_address(offer) + label = offer_address.label + address = offer_address.street + city = offer_address.city + department_code = offer_address.departmentCode + postal_code = offer_address.postalCode search_groups = ( offer.subcategory.native_category.parents @@ -592,7 +584,7 @@ def serialize_offer(cls, offer: offers_models.Offer, last_30_days_bookings: int) "isVisualDisabilityCompliant": venue.visualDisabilityCompliant, "name": venue.name, "postalCode": postal_code, - "publicName": venue.publicName, + "publicName": label, "venue_type": venue.venueTypeCode.name, }, "_geoloc": position(venue), diff --git a/api/tests/core/offers/test_offer_metadata.py b/api/tests/core/offers/test_offer_metadata.py index 02cc6502351..a4827556f39 100644 --- a/api/tests/core/offers/test_offer_metadata.py +++ b/api/tests/core/offers/test_offer_metadata.py @@ -3,9 +3,12 @@ import pytest from pcapi.core.categories import subcategories_v2 +from pcapi.core.geography import factories as geography_factories +from pcapi.core.offerers import factories as offerers_factories import pcapi.core.offers.factories as offers_factories from pcapi.core.offers.offer_metadata import get_metadata_from_offer from pcapi.core.providers.constants import BookFormat +from pcapi.core.testing import override_features pytestmark = pytest.mark.usefixtures("db_session") @@ -219,6 +222,46 @@ def should_be_sold_out_when_offer_has_no_active_stocks(self): assert metadata["offers"]["availability"] == "https://schema.org/SoldOut" + @override_features(WIP_USE_OFFERER_ADDRESS_AS_DATA_SOURCE=True) + def should_have_offer_location_when_available(self): + venue = offerers_factories.VenueFactory( + name="Le Poney qui tousse", + street="Rue du Poney qui tousse", + postalCode="75001", + city="Boulgourville", + latitude="47.097456", + longitude="-1.270040", + ) + address = geography_factories.AddressFactory( + street="Rue du poney qui respire", + postalCode="75002", + city="Paris", + latitude="47", + longitude="-1", + ) + offer_address = offerers_factories.OffererAddressFactory( + address=address, label="Le grand poney qui respire" + ) + offer = offers_factories.EventOfferFactory(venue=venue, offererAddress=offer_address) + + metadata = get_metadata_from_offer(offer) + + assert metadata["location"] == { + "@type": "Place", + "name": "Le grand poney qui respire", + "address": { + "@type": "PostalAddress", + "streetAddress": "Rue du poney qui respire", + "postalCode": "75002", + "addressLocality": "Paris", + }, + "geo": { + "@type": "GeoCoordinates", + "latitude": "47.00000", + "longitude": "-1.00000", + }, + } + class GivenABookTest: @pytest.mark.parametrize( "subcategoryId",