183 lines
5.4 KiB
Python
183 lines
5.4 KiB
Python
from datetime import timedelta
|
|
|
|
from django.utils import timezone
|
|
|
|
from .models import ListingClick
|
|
|
|
|
|
def _clean_param(params, key: str, max_len: int = 255) -> str:
|
|
raw = params.get(key)
|
|
if raw is None:
|
|
return ""
|
|
s = str(raw).strip()
|
|
return s[:max_len]
|
|
|
|
|
|
def classify_traffic(
|
|
*,
|
|
utm_source: str,
|
|
utm_medium: str,
|
|
utm_campaign: str,
|
|
utm_term: str,
|
|
utm_content: str,
|
|
gclid: str,
|
|
fbclid: str,
|
|
) -> str:
|
|
if any(
|
|
(
|
|
utm_source,
|
|
utm_medium,
|
|
utm_campaign,
|
|
utm_term,
|
|
utm_content,
|
|
gclid,
|
|
fbclid,
|
|
)
|
|
):
|
|
return ListingClick.TrafficType.MARKETING
|
|
return ListingClick.TrafficType.ORGANIC
|
|
|
|
|
|
def extract_attribution_from_request(request) -> dict:
|
|
p = request.query_params if hasattr(request, "query_params") else request.GET
|
|
utm_source = _clean_param(p, "utm_source")
|
|
utm_medium = _clean_param(p, "utm_medium")
|
|
utm_campaign = _clean_param(p, "utm_campaign")
|
|
utm_term = _clean_param(p, "utm_term")
|
|
utm_content = _clean_param(p, "utm_content")
|
|
gclid = _clean_param(p, "gclid")
|
|
fbclid = _clean_param(p, "fbclid")
|
|
referrer = ""
|
|
if hasattr(request, "META"):
|
|
ref = request.META.get("HTTP_REFERER") or ""
|
|
referrer = ref[:2048]
|
|
traffic_type = classify_traffic(
|
|
utm_source=utm_source,
|
|
utm_medium=utm_medium,
|
|
utm_campaign=utm_campaign,
|
|
utm_term=utm_term,
|
|
utm_content=utm_content,
|
|
gclid=gclid,
|
|
fbclid=fbclid,
|
|
)
|
|
return {
|
|
"utm_source": utm_source,
|
|
"utm_medium": utm_medium,
|
|
"utm_campaign": utm_campaign,
|
|
"utm_term": utm_term,
|
|
"utm_content": utm_content,
|
|
"gclid": gclid,
|
|
"fbclid": fbclid,
|
|
"referrer": referrer,
|
|
"traffic_type": traffic_type,
|
|
}
|
|
|
|
|
|
def record_equipment_listing_click(request, equipment_item) -> ListingClick:
|
|
attrs = extract_attribution_from_request(request)
|
|
user = request.user if getattr(request, "user", None) and request.user.is_authenticated else None
|
|
return ListingClick.objects.create(
|
|
vendor=equipment_item.vendor,
|
|
listing_type=ListingClick.ListingType.EQUIPMENT,
|
|
equipment_item=equipment_item,
|
|
adventure_offering=None,
|
|
user=user,
|
|
**attrs,
|
|
)
|
|
|
|
|
|
def record_adventure_listing_click(request, adventure_offering) -> ListingClick:
|
|
attrs = extract_attribution_from_request(request)
|
|
user = request.user if getattr(request, "user", None) and request.user.is_authenticated else None
|
|
return ListingClick.objects.create(
|
|
vendor=adventure_offering.vendor,
|
|
listing_type=ListingClick.ListingType.ADVENTURE,
|
|
equipment_item=None,
|
|
adventure_offering=adventure_offering,
|
|
user=user,
|
|
**attrs,
|
|
)
|
|
|
|
|
|
def record_listing_click_from_payload(
|
|
*,
|
|
listing_type: str,
|
|
equipment_item=None,
|
|
adventure_offering=None,
|
|
utm_source: str = "",
|
|
utm_medium: str = "",
|
|
utm_campaign: str = "",
|
|
utm_term: str = "",
|
|
utm_content: str = "",
|
|
gclid: str = "",
|
|
fbclid: str = "",
|
|
referrer: str = "",
|
|
user=None,
|
|
) -> ListingClick:
|
|
traffic_type = classify_traffic(
|
|
utm_source=utm_source or "",
|
|
utm_medium=utm_medium or "",
|
|
utm_campaign=utm_campaign or "",
|
|
utm_term=utm_term or "",
|
|
utm_content=utm_content or "",
|
|
gclid=gclid or "",
|
|
fbclid=fbclid or "",
|
|
)
|
|
if listing_type == ListingClick.ListingType.EQUIPMENT:
|
|
return ListingClick.objects.create(
|
|
vendor=equipment_item.vendor,
|
|
listing_type=ListingClick.ListingType.EQUIPMENT,
|
|
equipment_item=equipment_item,
|
|
adventure_offering=None,
|
|
traffic_type=traffic_type,
|
|
utm_source=utm_source[:255],
|
|
utm_medium=utm_medium[:255],
|
|
utm_campaign=utm_campaign[:255],
|
|
utm_term=utm_term[:255],
|
|
utm_content=utm_content[:255],
|
|
gclid=gclid[:255],
|
|
fbclid=fbclid[:255],
|
|
referrer=referrer[:2048],
|
|
user=user,
|
|
)
|
|
return ListingClick.objects.create(
|
|
vendor=adventure_offering.vendor,
|
|
listing_type=ListingClick.ListingType.ADVENTURE,
|
|
equipment_item=None,
|
|
adventure_offering=adventure_offering,
|
|
traffic_type=traffic_type,
|
|
utm_source=utm_source[:255],
|
|
utm_medium=utm_medium[:255],
|
|
utm_campaign=utm_campaign[:255],
|
|
utm_term=utm_term[:255],
|
|
utm_content=utm_content[:255],
|
|
gclid=gclid[:255],
|
|
fbclid=fbclid[:255],
|
|
referrer=referrer[:2048],
|
|
user=user,
|
|
)
|
|
|
|
|
|
ATTRIBUTION_MAX_AGE_DAYS = 90
|
|
|
|
|
|
def listing_click_valid_for_booking(*, click, equipment_item=None, adventure_offering=None) -> bool:
|
|
if click is None:
|
|
return False
|
|
cutoff = timezone.now() - timedelta(days=ATTRIBUTION_MAX_AGE_DAYS)
|
|
if click.created_at < cutoff:
|
|
return False
|
|
if equipment_item is not None:
|
|
return (
|
|
click.listing_type == ListingClick.ListingType.EQUIPMENT
|
|
and click.equipment_item_id == equipment_item.id
|
|
and click.vendor_id == equipment_item.vendor_id
|
|
)
|
|
if adventure_offering is not None:
|
|
return (
|
|
click.listing_type == ListingClick.ListingType.ADVENTURE
|
|
and click.adventure_offering_id == adventure_offering.id
|
|
and click.vendor_id == adventure_offering.vendor_id
|
|
)
|
|
return False
|