Skip to content

Commit

Permalink
(PC-34204)[API] feat: update criteria for is eligible for search
Browse files Browse the repository at this point in the history
  • Loading branch information
bpeyrou-pass committed Feb 4, 2025
1 parent 4d2a174 commit a5ee635
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 5 deletions.
2 changes: 1 addition & 1 deletion api/src/pcapi/core/offers/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,7 @@ def is_eligible_for_search(self) -> bool:

@is_eligible_for_search.expression # type: ignore[no-redef]
def is_eligible_for_search(cls) -> BooleanClauseList: # pylint: disable=no-self-argument
return sa.and_(cls._released, sa.or_(cls.is_released_and_bookable, FutureOffer.isWaitingForPublication))
return sa.and_(cls._released, sa.or_(Stock._bookable, FutureOffer.isWaitingForPublication))

@hybrid_property
def is_released_and_bookable(self) -> bool:
Expand Down
6 changes: 6 additions & 0 deletions api/src/pcapi/core/search/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,13 @@ def get_base_query_for_offer_indexation() -> BaseQuery:
offers_models.Stock,
(offers_models.Stock.offerId == offers_models.Offer.id) & offers_models.Stock._bookable,
)
.outerjoin(
offers_models.FutureOffer,
(offers_models.FutureOffer.offerId == offers_models.Offer.id)
& offers_models.FutureOffer.isWaitingForPublication,
)
.options(sa.orm.contains_eager(offers_models.Offer.stocks))
.options(sa.orm.contains_eager(offers_models.Offer.futureOffer))
.options(sa.orm.joinedload(offers_models.Offer.venue).joinedload(offerers_models.Venue.managingOfferer))
.options(sa.orm.joinedload(offers_models.Offer.venue).joinedload(offerers_models.Venue.googlePlacesInfo))
.options(sa.orm.joinedload(offers_models.Offer.criteria))
Expand Down
13 changes: 9 additions & 4 deletions api/src/pcapi/core/search/staging_indexation.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ def get_relevant_offers_to_index() -> set[int]:

def get_offers_for_each_subcategory(size_per_subcategory: int) -> set[int]:
result = set()

for subcategory in ALL_SUBCATEGORIES:
query = (
offers_models.Offer.query.join(offers_models.Stock)
offers_models.Offer.query.outerjoin(offers_models.Stock)
.outerjoin(offers_models.FutureOffer)
.join(offerer_models.Venue)
.join(offerer_models.Offerer)
.filter(
Expand All @@ -44,7 +46,8 @@ def get_offers_for_each_subcategory(size_per_subcategory: int) -> set[int]:

def get_offers_with_gtl(size: int) -> set[int]:
query = (
offers_models.Offer.query.join(offers_models.Stock)
offers_models.Offer.query.outerjoin(offers_models.Stock)
.outerjoin(offers_models.FutureOffer)
.filter(
offers_models.Offer.extraData["gtl_id"].is_not(None),
offers_models.Offer.is_eligible_for_search,
Expand All @@ -61,7 +64,8 @@ def get_offers_for_each_gtl_level_1(size_per_gtl: int) -> set[int]:
result = set()
for i in range(1, 14):
query = (
offers_models.Offer.query.join(offers_models.Stock)
offers_models.Offer.query.outerjoin(offers_models.Stock)
.outerjoin(offers_models.FutureOffer)
.filter(
offers_models.Offer.extraData["gtl_id"].astext.startswith(str(i).zfill(2)),
offers_models.Offer.is_eligible_for_search,
Expand All @@ -76,7 +80,8 @@ def get_offers_for_each_gtl_level_1(size_per_gtl: int) -> set[int]:

def get_random_offers(size: int, excluded_offer_ids: set[int]) -> set[int]:
query = (
offers_models.Offer.query.join(offers_models.Stock)
offers_models.Offer.query.outerjoin(offers_models.Stock)
.outerjoin(offers_models.FutureOffer)
.filter(
offers_models.Offer.is_eligible_for_search,
offers_models.Offer.id.not_in(excluded_offer_ids),
Expand Down
47 changes: 47 additions & 0 deletions api/tests/core/offers/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from pcapi.core.offers import factories
from pcapi.core.offers import models
import pcapi.core.providers.factories as providers_factories
from pcapi.models import db
from pcapi.models import offer_mixin
from pcapi.models.api_errors import ApiErrors
from pcapi.models.validation_status_mixin import ValidationStatus
Expand Down Expand Up @@ -742,3 +743,49 @@ def test_unicity_headline_offer_by_venue(self):
factories.HeadlineOfferFactory(offer=offer)
with pytest.raises(exc.IntegrityError):
factories.HeadlineOfferFactory(offer=another_offer_on_the_same_venue)


class OfferIsSearchableTest:
def test_offer_is_future(self):
offer_1 = factories.OfferFactory(isActive=True)
offer_2 = factories.OfferFactory(isActive=True)
offer_3 = factories.OfferFactory(isActive=False)
future_publication_date = datetime.datetime.utcnow() + datetime.timedelta(days=30)
past_publication_date = datetime.datetime.utcnow() - datetime.timedelta(days=30)
_ = factories.FutureOfferFactory(offerId=offer_1.id, publicationDate=future_publication_date)
_ = factories.FutureOfferFactory(offerId=offer_2.id, publicationDate=past_publication_date)
_ = factories.FutureOfferFactory(offerId=offer_3.id, publicationDate=future_publication_date)

assert offer_1.is_eligible_for_search
assert not offer_2.is_eligible_for_search
assert not offer_3.is_eligible_for_search

# hybrid property: also check SQL expression
results = (
db.session.query(models.Offer.id)
.outerjoin(models.Stock)
.outerjoin(models.FutureOffer)
.filter(models.Offer.is_eligible_for_search)
.all()
)
assert len(results) == 1
assert results[0].id == offer_1.id

def test_offer_is_bookable(self):
offer_1 = factories.OfferFactory(isActive=True)
offer_2 = factories.OfferFactory(isActive=False)
_ = factories.StockFactory(offer=offer_1)

assert offer_1.is_eligible_for_search
assert not offer_2.is_eligible_for_search

# hybrid property: also check SQL expression
results = (
db.session.query(models.Offer.id)
.outerjoin(models.Stock)
.outerjoin(models.FutureOffer)
.filter(models.Offer.is_eligible_for_search)
.all()
)
assert len(results) == 1
assert results[0].id == offer_1.id

0 comments on commit a5ee635

Please sign in to comment.