Hello, ${record.name}!
Here is a nice link .
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1671451417.0 trytond_marketing_automation-6.0.4/tests/reminder.html 0000644 0001750 0001750 00000000332 14350051431 021464 0 ustar 00ced cedHello, ${record.name}!
We miss you!
././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1671451426.0 trytond_marketing_automation-6.0.4/tests/scenario_marketing_automation.rst 0000644 0001750 0001750 00000014361 14350051442 025640 0 ustar 00ced ced Marketing Automation Scenario ============================= Imports:: >>> import datetime >>> import re >>> from proteus import Model, Wizard >>> from proteus.config import get_config >>> from trytond.pyson import Eval, PYSONEncoder >>> from trytond.tests.tools import activate_modules >>> from trytond.tools import file_open Patch sendmail_transactional:: >>> from unittest.mock import patch >>> from trytond.modules.marketing_automation import marketing_automation >>> smtp_calls = patch.object( ... marketing_automation, 'sendmail_transactional').start() Activate modules:: >>> config = activate_modules(['marketing_automation', 'party']) Create a party:: >>> Party = Model.get('party.party') >>> party = Party() >>> party.name = "Michael Scott" >>> contact = party.contact_mechanisms.new() >>> contact.type = 'email' >>> contact.value = 'michael@example.com' >>> party.save() Create the running scenario:: >>> Scenario = Model.get('marketing.automation.scenario') >>> Activity = Model.get('marketing.automation.activity') >>> scenario = Scenario() >>> scenario.name = "Party Scenario" >>> scenario.model = 'party.party' >>> scenario.domain = '[["contact_mechanisms", "!=", null]]' >>> scenario.save() >>> root_activity = Activity() >>> root_activity.name = "First E-Mail" >>> root_activity.parent = scenario >>> root_activity.action = 'send_email' >>> root_activity.email_title = "Hello" >>> root_activity.condition = PYSONEncoder().encode( ... Eval('self', {}).get('active')) >>> with file_open('marketing_automation/tests/email.html', mode='r') as fp: ... root_activity.email_template = fp.read() >>> root_activity.save() >>> email_opened = Activity() >>> email_opened.name = "E-Mail Opened" >>> email_opened.parent = root_activity >>> email_opened.on = 'email_opened' >>> email_opened.save() >>> email_clicked = Activity() >>> email_clicked.name = "E-Mail Clicked" >>> email_clicked.parent = root_activity >>> email_clicked.on = 'email_clicked' >>> email_clicked.save() >>> email_not_clicked = Activity() >>> email_not_clicked.name = "E-Mail no clicked" >>> email_not_clicked.parent = root_activity >>> email_not_clicked.on = 'email_clicked_not' >>> email_not_clicked.delay = datetime.timedelta(days=2) >>> email_not_clicked.save() >>> email_reminder = Activity() >>> email_reminder.name = "E-Mail Reminder" >>> email_reminder.parent = root_activity >>> email_reminder.action = 'send_email' >>> email_reminder.email_title = "Reminder" >>> email_reminder.delay = datetime.timedelta() >>> with file_open('marketing_automation/tests/reminder.html', mode='r') as fp: ... email_reminder.email_template = fp.read() >>> email_reminder.save() >>> scenario.record_count 1 >>> scenario.click('run') Trigger scenario:: >>> Cron = Model.get('ir.cron') >>> cron_trigger, = Cron.find([ ... ('method', '=', 'marketing.automation.scenario|trigger'), ... ]) >>> cron_process, = Cron.find([ ... ('method', '=', 'marketing.automation.record.activity|process'), ... ]) >>> cron_trigger.click('run_once') >>> cron_process.click('run_once') >>> Record = Model.get('marketing.automation.record') >>> record, = Record.find([]) >>> record.record == party True >>> scenario.record_count 1 >>> scenario.record_count_blocked 0 Check email sent:: >>> ShortenedURL = Model.get('web.shortened_url') >>> open_url, = ShortenedURL.find([ ... ('redirect_url', 'like', '%/m/empty.gif'), ... ]) >>> click_url, = ShortenedURL.find([ ... ('redirect_url', '=', 'http://example.com/action'), ... ]) >>> RecordActivity = Model.get('marketing.automation.record.activity') >>> record_activity, = RecordActivity.find([ ... ('record', '=', record.id), ... ('activity', '=', root_activity.id), ... ]) >>> record_activity.state 'done' >>> root_activity.reload() >>> root_activity.record_count 1 >>> smtp_calls.call_count 1 >>> from_, to, msg = smtp_calls.call_args[0] >>> smtp_calls.reset_mock() >>> msg = msg.get_payload(0).get_payload(decode=True).decode('utf-8') >>> to == [contact.value] True >>> re.search(r'Hello, (.*)!', msg).group(1) == party.name True >>> open_url.shortened_url in msg True >>> open_url.record == record_activity True >>> open_url.method 'marketing.automation.record.activity|on_email_opened' >>> click_url.shortened_url in msg True >>> click_url.record == record_activity True >>> click_url.method 'marketing.automation.record.activity|on_email_clicked' >>> record.uuid in msg True Trigger open email and reminder after delay:: >>> record_activity.click('on_email_opened') >>> open_activity, = RecordActivity.find([ ... ('record', '=', record.id), ... ('activity', '=', email_opened.id), ... ]) >>> bool(open_activity.at) True >>> open_activity.state 'waiting' >>> cron_process.click('run_once') >>> open_activity.reload() >>> open_activity.state 'done' >>> root_activity.reload() >>> root_activity.email_opened 1 >>> email_reminder, = RecordActivity.find([ ... ('record', '=', record.id), ... ('activity', '=', email_reminder.id), ... ]) >>> email_reminder.state 'done' >>> smtp_calls.call_count 1 >>> smtp_calls.reset_mock() Trigger click email:: >>> record_activity.click('on_email_clicked') >>> cron_process.click('run_once') >>> clicked_activity, = RecordActivity.find([ ... ('record', '=', record.id), ... ('activity', '=', email_clicked.id), ... ]) >>> clicked_activity.state 'done' >>> root_activity.reload() >>> root_activity.email_clicked 1 >>> not_clicked_activity, = RecordActivity.find([ ... ('record', '=', record.id), ... ('activity', '=', email_not_clicked.id), ... ]) >>> not_clicked_activity.state 'cancelled' ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1671451426.0 trytond_marketing_automation-6.0.4/tests/test_marketing_automation.py 0000644 0001750 0001750 00000001644 14350051442 024634 0 ustar 00ced ced # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import unittest import doctest from trytond.tests.test_tryton import ModuleTestCase from trytond.tests.test_tryton import suite as test_suite from trytond.tests.test_tryton import doctest_teardown from trytond.tests.test_tryton import doctest_checker class MarketingAutomationTestCase(ModuleTestCase): 'Test Marketing Automation module' module = 'marketing_automation' def suite(): suite = test_suite() suite.addTests(unittest.TestLoader().loadTestsFromTestCase( MarketingAutomationTestCase)) suite.addTests(doctest.DocFileSuite( 'scenario_marketing_automation.rst', tearDown=doctest_teardown, encoding='utf-8', checker=doctest_checker, optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)) return suite ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1671451426.0 trytond_marketing_automation-6.0.4/tox.ini 0000644 0001750 0001750 00000000765 14350051442 017156 0 ustar 00ced ced [tox] envlist = {py36,py37,py38,py39}-{sqlite,postgresql},pypy3-{sqlite,postgresql} [testenv] commands = {envpython} setup.py test deps = {py36,py37,py38,py39}-postgresql: psycopg2 >= 2.5 pypy3-postgresql: psycopg2cffi >= 2.5 py36-sqlite: sqlitebck passenv = * setenv = sqlite: TRYTOND_DATABASE_URI={env:SQLITE_URI:sqlite://} postgresql: TRYTOND_DATABASE_URI={env:POSTGRESQL_URI:postgresql://} sqlite: DB_NAME={env:DB_NAME::memory:} postgresql: DB_NAME={env:DB_NAME:test} ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1671451426.0 trytond_marketing_automation-6.0.4/tryton.cfg 0000644 0001750 0001750 00000000243 14350051442 017652 0 ustar 00ced ced [tryton] version=6.0.4 depends: ir res marketing web_shortener extras_depend: sale party xml: marketing_automation.xml message.xml ././@PaxHeader 0000000 0000000 0000000 00000000034 00000000000 010212 x ustar 00 28 mtime=1680387958.7810683 trytond_marketing_automation-6.0.4/trytond_marketing_automation.egg-info/ 0000755 0001750 0001750 00000000000 14412127567 025344 5 ustar 00ced ced ././@PaxHeader 0000000 0000000 0000000 00000000026 00000000000 010213 x ustar 00 22 mtime=1680387957.0 trytond_marketing_automation-6.0.4/trytond_marketing_automation.egg-info/PKG-INFO 0000644 0001750 0001750 00000011161 14412127565 026437 0 ustar 00ced ced Metadata-Version: 2.1 Name: trytond-marketing-automation Version: 6.0.4 Summary: Tryton module to plan, coordinate and manage marketing campaigns Home-page: http://www.tryton.org/ Download-URL: http://downloads.tryton.org/6.0/ Author: Tryton Author-email: bugs@tryton.org License: GPL-3 Project-URL: Bug Tracker, https://bugs.tryton.org/ Project-URL: Documentation, https://docs.tryton.org/ Project-URL: Forum, https://www.tryton.org/forum Project-URL: Source Code, https://hg.tryton.org/modules/marketing_automation Keywords: tryton marketing automation Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Plugins Classifier: Framework :: Tryton Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Financial and Insurance Industry Classifier: Intended Audience :: Legal Industry Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+) Classifier: Natural Language :: Bulgarian Classifier: Natural Language :: Catalan Classifier: Natural Language :: Chinese (Simplified) Classifier: Natural Language :: Czech Classifier: Natural Language :: Dutch Classifier: Natural Language :: English Classifier: Natural Language :: Finnish Classifier: Natural Language :: French Classifier: Natural Language :: German Classifier: Natural Language :: Hungarian Classifier: Natural Language :: Indonesian Classifier: Natural Language :: Italian Classifier: Natural Language :: Persian Classifier: Natural Language :: Polish Classifier: Natural Language :: Portuguese (Brazilian) Classifier: Natural Language :: Romanian Classifier: Natural Language :: Russian Classifier: Natural Language :: Slovenian Classifier: Natural Language :: Spanish Classifier: Natural Language :: Turkish Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Office/Business Requires-Python: >=3.6 License-File: LICENSE Marketing Automation Module ########################### The marketing_automation module allows marketing actions to be automated. It is based on scenarios and activities that are executed on selected records. Scenario ******** A scenario provides the entry point for records into an automated marketing campaign. Each record can only enter a scenario once. A scenario is defined by: * Name * Model: the type of record for the scenario (by default Party and Sale) * Domain: used to filter records * State: * Draft * Running * Stopped A cron task runs periodically to find new records to apply the scenario to. Activity ******** The activities form a scenario. They define which action should be triggered and when it should happen. The activities are organized as a tree and defined by: * Name * Parent * Children * On: event from the parent that triggers the activity: * E-Mail Opened * E-Mail Not Opened * E-Mail Clicked * E-Mail Not Clicked * Condition: that the record must match to execute the activity * Delay: before the action is executed * Action: list of available actions Actions ------- Send E-Mail ........... The activity send an e-mail to the party defined on the record. The E-mail is composed using an HTML `GenshiYou have been successfully unsubscribed from this kind of email.
We are sorry to see you go, and apologize if we have overwhelmed your inbox.