1 parent
f9a638e951
commit
2cf37b3763
8 files changed
+104
-5
No files matched your search
@@ -1,3 +1,4 @@
|
||||
* Add UTM parameters to shortened URL
|
||||
|
||||
Version 7.0.0 - 2023-10-30
|
||||
--------------------------
|
||||
|
||||
@@ -13,10 +13,11 @@ try:
|
||||
except ImportError:
|
||||
OpportunityAbstract = None
|
||||
|
||||
from . import marketing, sale, sale_opportunity_reporting, sale_reporting
|
||||
from .marketing import MarketingCampaignMixin, Parameter
|
||||
from . import marketing, sale, sale_opportunity_reporting, sale_reporting, web
|
||||
from .marketing import MarketingCampaignMixin, MarketingCampaignUTM, Parameter
|
||||
|
||||
__all__ = ['register', 'Parameter', 'MarketingCampaignMixin']
|
||||
__all__ = [
|
||||
'register', 'Parameter', 'MarketingCampaignMixin', 'MarketingCampaignUTM']
|
||||
|
||||
|
||||
def register():
|
||||
@@ -41,6 +42,9 @@ def register():
|
||||
Pool.register(
|
||||
sale.POSSale,
|
||||
module='marketing_campaign', type_='model', depends=['sale_point'])
|
||||
Pool.register(
|
||||
web.ShortenedURL,
|
||||
module='marketing_campaign', type_='model', depends=['web_shortener'])
|
||||
if SaleAbstract:
|
||||
Pool.register_mixin(
|
||||
sale_reporting.AbstractMarketingCampaign, SaleAbstract,
|
||||
|
||||
@@ -31,4 +31,10 @@ Campaign
|
||||
|
||||
Yield the field names of the :class:`Parameter` fields.
|
||||
|
||||
.. class:: MarketingCampaignUTM
|
||||
|
||||
The *MarketingCampaignUTM* is a mixin_ that helps to add UTM_ parameters to
|
||||
URL.
|
||||
|
||||
.. _mixin: https://en.wikipedia.org/wiki/Mixin
|
||||
.. _UTM: https://en.wikipedia.org/wiki/UTM_parameters
|
||||
@@ -1,6 +1,8 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse
|
||||
|
||||
from sql.functions import Lower
|
||||
from sql.operators import Equal
|
||||
|
||||
@@ -78,6 +80,38 @@ class Source(Parameter):
|
||||
__name__ = 'marketing.source'
|
||||
|
||||
|
||||
class MarketingCampaignUTM:
|
||||
__slots__ = ()
|
||||
|
||||
@property
|
||||
def utm_campaign(self):
|
||||
if campaign := getattr(self, 'marketing_campaign', None):
|
||||
return campaign.name
|
||||
|
||||
@property
|
||||
def utm_medium(self):
|
||||
if medium := getattr(self, 'marketing_medium', None):
|
||||
return medium.name
|
||||
|
||||
@property
|
||||
def utm_source(self):
|
||||
if source := getattr(self, 'marketing_source', None):
|
||||
return source.name
|
||||
|
||||
def add_utm_parameters(self, url):
|
||||
params = {}
|
||||
for name in ['utm_campaign', 'utm_medium', 'utm_source']:
|
||||
if value := getattr(self, name):
|
||||
params[name] = value
|
||||
if params:
|
||||
url_parts = list(urlparse(url))
|
||||
query = dict(parse_qsl(url_parts[4]))
|
||||
query.update(params)
|
||||
url_parts[4] = urlencode(query)
|
||||
url = urlunparse(url_parts)
|
||||
return url
|
||||
|
||||
|
||||
class MarketingCampaignMixin(Model):
|
||||
|
||||
marketing_campaign = fields.Many2One(
|
||||
|
||||
@@ -55,6 +55,7 @@ tests_require = [
|
||||
get_require_version('trytond_sale'),
|
||||
get_require_version('trytond_sale_opportunity'),
|
||||
get_require_version('trytond_sale_point'),
|
||||
get_require_version('trytond_web_shortener'),
|
||||
]
|
||||
|
||||
setup(name=name,
|
||||
|
||||
@@ -1,12 +1,47 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from trytond.tests.test_tryton import ModuleTestCase
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from trytond.model import ModelStorage
|
||||
from trytond.modules.marketing_campaign import MarketingCampaignUTM
|
||||
from trytond.pool import Pool
|
||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
|
||||
|
||||
class MarketingCampaignTestCase(ModuleTestCase):
|
||||
"Test Marketing Campaign module"
|
||||
module = 'marketing_campaign'
|
||||
extras = ['sale', 'sale_opportunity', 'sale_point']
|
||||
extras = ['sale', 'sale_opportunity', 'sale_point', 'web_shortener']
|
||||
|
||||
@with_transaction()
|
||||
def test_shortened_url_add_utm(self):
|
||||
"Test add UTM to shortened URL"
|
||||
pool = Pool()
|
||||
ShortenedURL = pool.get('web.shortened_url')
|
||||
URLAccess = pool.get('web.shortened_url.access')
|
||||
Campaign = pool.get('marketing.campaign')
|
||||
|
||||
with patch.object(URLAccess, 'save'):
|
||||
class Record(MarketingCampaignUTM, ModelStorage):
|
||||
__slots__ = ('marketing_campaign',)
|
||||
id = 1
|
||||
|
||||
Record.__setup__()
|
||||
Record.__post_setup__()
|
||||
|
||||
campaign = Campaign(name='campaign')
|
||||
campaign.save()
|
||||
record = Record()
|
||||
record.marketing_campaign = campaign
|
||||
shortened_url = ShortenedURL(
|
||||
redirect_url='http://example.com/',
|
||||
record=record,
|
||||
method=None)
|
||||
|
||||
self.assertEqual(
|
||||
shortened_url.access(),
|
||||
'http://example.com/?utm_campaign=campaign')
|
||||
|
||||
|
||||
del ModuleTestCase
|
||||
@@ -7,6 +7,7 @@ extras_depend:
|
||||
sale
|
||||
sale_opportunity
|
||||
sale_point
|
||||
web_shortener
|
||||
xml:
|
||||
marketing.xml
|
||||
sale.xml
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
|
||||
from trytond.pool import PoolMeta
|
||||
|
||||
from .marketing import MarketingCampaignUTM
|
||||
|
||||
|
||||
class ShortenedURL(metaclass=PoolMeta):
|
||||
__name__ = 'web.shortened_url'
|
||||
|
||||
def access(self, **values):
|
||||
url = super().access(**values)
|
||||
if isinstance(self.record, MarketingCampaignUTM):
|
||||
url = self.record.add_utm_parameters(url)
|
||||
return url
|
||||
Reference in new issue
Block a user