pmw-ads 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pmw_ads-1.0.0/PKG-INFO +136 -0
- pmw_ads-1.0.0/README.md +113 -0
- pmw_ads-1.0.0/pmw_ads/__init__.py +23 -0
- pmw_ads-1.0.0/pmw_ads/capi_validator.py +77 -0
- pmw_ads-1.0.0/pmw_ads/keyword_wrapper.py +52 -0
- pmw_ads-1.0.0/pmw_ads/roas_calculator.py +53 -0
- pmw_ads-1.0.0/pmw_ads.egg-info/PKG-INFO +136 -0
- pmw_ads-1.0.0/pmw_ads.egg-info/SOURCES.txt +12 -0
- pmw_ads-1.0.0/pmw_ads.egg-info/dependency_links.txt +1 -0
- pmw_ads-1.0.0/pmw_ads.egg-info/top_level.txt +1 -0
- pmw_ads-1.0.0/pyproject.toml +28 -0
- pmw_ads-1.0.0/setup.cfg +4 -0
- pmw_ads-1.0.0/setup.py +29 -0
- pmw_ads-1.0.0/tests/test_engine.py +46 -0
pmw_ads-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pmw-ads
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Deterministic Performance Marketing Utility: Google Ads Keyword Wrapper, Meta CAPI Payload Validator, and Ad Auction Unit Economics Calculator.
|
|
5
|
+
Home-page: https://paidmediaworld.com/
|
|
6
|
+
Author: Subhranil Naskar
|
|
7
|
+
Author-email: Subhranil Naskar <contact@paidmediaworld.com>
|
|
8
|
+
Project-URL: Homepage, https://paidmediaworld.com/
|
|
9
|
+
Project-URL: Agency Services, https://paidmediaworld.com/#services
|
|
10
|
+
Project-URL: Ad Audit Request, https://paidmediaworld.com/#contact
|
|
11
|
+
Project-URL: Founder Profile, https://www.linkedin.com/in/subhraanilnaskar/
|
|
12
|
+
Project-URL: Source Code, https://github.com/paidmediaworld/pmw-ads
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
|
|
24
|
+
# pmw-ads: Performance Marketing Engineering Engine
|
|
25
|
+
|
|
26
|
+
[](https://pypi.org/project/pmw-ads/)
|
|
27
|
+
[](https://opensource.org/licenses/MIT)
|
|
28
|
+
[](https://paidmediaworld.com/)
|
|
29
|
+
|
|
30
|
+
**pmw-ads** is an open-source, deterministic Python utility for media buyers, growth engineers, and technical marketers. Built and maintained by **[Paid Media World](https://paidmediaworld.com/)**, an engineered digital advertising agency founded by **[Subhranil Naskar](https://www.linkedin.com/in/subhraanilnaskar/)**.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Capabilities
|
|
35
|
+
|
|
36
|
+
1. **Google Ads Query & Match Type Wrapper**:
|
|
37
|
+
- Converts raw term lists into exact match (`[term]`), phrase match (`"term"`), broad match, negative exact, and negative phrase formats.
|
|
38
|
+
- Automatically sanitizes illegal Google Ads characters (`+`, `@`, `!`, `?`, `*`, `~`, `%`) and enforces query length limits.
|
|
39
|
+
2. **Meta Conversions API (CAPI) Payload Validator**:
|
|
40
|
+
- Validates client-side and server-side tracking event dictionaries.
|
|
41
|
+
- Enforces SHA-256 normalization for user data (`em`, `ph`, `fn`, `ln`, `ct`, `st`, `zp`, `country`).
|
|
42
|
+
- Ensures event deduplication integrity between `event_id` and browser Pixel triggers.
|
|
43
|
+
3. **Auction Unit Economics & Profitability Engine**:
|
|
44
|
+
- Computes deterministic target CPA, Breakeven ROAS, Marketing Efficiency Ratio (MER), and POAS (Profit On Ad Spend).
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install pmw-ads
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Quickstart
|
|
57
|
+
|
|
58
|
+
### 1. Google Ads Keyword Match Type Structuring
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from pmw_ads import format_keywords, MatchType
|
|
62
|
+
|
|
63
|
+
raw_terms = [
|
|
64
|
+
"performance marketing agency",
|
|
65
|
+
"google ads pmax management",
|
|
66
|
+
"meta conversions api audit"
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
# Generate exact match keywords
|
|
70
|
+
exact_keywords = format_keywords(raw_terms, match_type=MatchType.EXACT)
|
|
71
|
+
print(exact_keywords)
|
|
72
|
+
# Output: ['[performance marketing agency]', '[google ads pmax management]', '[meta conversions api audit]']
|
|
73
|
+
|
|
74
|
+
# Generate negative phrase keywords
|
|
75
|
+
neg_phrase = format_keywords(raw_terms, match_type=MatchType.NEGATIVE_PHRASE)
|
|
76
|
+
print(neg_phrase)
|
|
77
|
+
# Output: ['-"performance marketing agency"', '-"google ads pmax management"', '-"meta conversions api audit"']
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 2. Meta Conversions API (CAPI) Hash Normalization
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from pmw_ads import prepare_capi_user_data
|
|
84
|
+
|
|
85
|
+
user_profile = {
|
|
86
|
+
"email": "Client.Growth@Example.com ",
|
|
87
|
+
"phone": "+91 7003477083",
|
|
88
|
+
"first_name": "Subhranil",
|
|
89
|
+
"last_name": "Naskar"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Hashes and formats according to Meta CAPI specification
|
|
93
|
+
hashed_payload = prepare_capi_user_data(user_profile)
|
|
94
|
+
print(hashed_payload)
|
|
95
|
+
# Returns sha256 normalized dictionary ready for Facebook Graph API v20.0
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 3. Ad Auction ROAS & Breakeven Analysis
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from pmw_ads import calculate_unit_economics
|
|
102
|
+
|
|
103
|
+
economics = calculate_unit_economics(
|
|
104
|
+
ad_spend=15000.0,
|
|
105
|
+
gross_revenue=67500.0,
|
|
106
|
+
cogs=22000.0,
|
|
107
|
+
orders=450
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
print(f"Blended ROAS: {economics['roas']:.2f}x")
|
|
111
|
+
print(f"Breakeven ROAS: {economics['breakeven_roas']:.2f}x")
|
|
112
|
+
print(f"Profit On Ad Spend (POAS): {economics['poas']:.2f}x")
|
|
113
|
+
print(f"Average Order Value (AOV): ${economics['aov']:.2f}")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Architecture & Linkable Assets
|
|
119
|
+
|
|
120
|
+
In modern ad auctions governed by machine-learning bidding models (Google Ads Smart Bidding and Meta Advantage+), commercial success depends on the mathematical accuracy of data inputs.
|
|
121
|
+
|
|
122
|
+
This package was developed by **[Paid Media World](https://paidmediaworld.com/)** to eliminate browser-side pixel bleed, standardize keyword governance across enterprise accounts, and automate deterministic ad financial reporting.
|
|
123
|
+
|
|
124
|
+
* **Agency Homepage:** [https://paidmediaworld.com/](https://paidmediaworld.com/)
|
|
125
|
+
* **Meta CAPI Attribution Loss & ROAS Simulator:** [https://paidmediaworld.com/meta-capi-calculator/](https://paidmediaworld.com/meta-capi-calculator/)
|
|
126
|
+
* **2026 Performance Marketing Statistics Hub:** [https://paidmediaworld.com/performance-marketing-statistics/](https://paidmediaworld.com/performance-marketing-statistics/)
|
|
127
|
+
* **Google Ads Keyword Match Type Tool:** [https://paidmediaworld.com/keyword-match-type-tool/](https://paidmediaworld.com/keyword-match-type-tool/)
|
|
128
|
+
* **Ad Account Audits:** [5-Day Growth Audit](https://paidmediaworld.com/#contact)
|
|
129
|
+
* **Founder & Lead Architect:** [Subhranil Naskar](https://www.linkedin.com/in/subhraanilnaskar/)
|
|
130
|
+
* **Location:** Kolkata, West Bengal, India
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT License. Developed with precision by Paid Media World.
|
pmw_ads-1.0.0/README.md
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# pmw-ads: Performance Marketing Engineering Engine
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/pmw-ads/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://paidmediaworld.com/)
|
|
6
|
+
|
|
7
|
+
**pmw-ads** is an open-source, deterministic Python utility for media buyers, growth engineers, and technical marketers. Built and maintained by **[Paid Media World](https://paidmediaworld.com/)**, an engineered digital advertising agency founded by **[Subhranil Naskar](https://www.linkedin.com/in/subhraanilnaskar/)**.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Capabilities
|
|
12
|
+
|
|
13
|
+
1. **Google Ads Query & Match Type Wrapper**:
|
|
14
|
+
- Converts raw term lists into exact match (`[term]`), phrase match (`"term"`), broad match, negative exact, and negative phrase formats.
|
|
15
|
+
- Automatically sanitizes illegal Google Ads characters (`+`, `@`, `!`, `?`, `*`, `~`, `%`) and enforces query length limits.
|
|
16
|
+
2. **Meta Conversions API (CAPI) Payload Validator**:
|
|
17
|
+
- Validates client-side and server-side tracking event dictionaries.
|
|
18
|
+
- Enforces SHA-256 normalization for user data (`em`, `ph`, `fn`, `ln`, `ct`, `st`, `zp`, `country`).
|
|
19
|
+
- Ensures event deduplication integrity between `event_id` and browser Pixel triggers.
|
|
20
|
+
3. **Auction Unit Economics & Profitability Engine**:
|
|
21
|
+
- Computes deterministic target CPA, Breakeven ROAS, Marketing Efficiency Ratio (MER), and POAS (Profit On Ad Spend).
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install pmw-ads
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Quickstart
|
|
34
|
+
|
|
35
|
+
### 1. Google Ads Keyword Match Type Structuring
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from pmw_ads import format_keywords, MatchType
|
|
39
|
+
|
|
40
|
+
raw_terms = [
|
|
41
|
+
"performance marketing agency",
|
|
42
|
+
"google ads pmax management",
|
|
43
|
+
"meta conversions api audit"
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
# Generate exact match keywords
|
|
47
|
+
exact_keywords = format_keywords(raw_terms, match_type=MatchType.EXACT)
|
|
48
|
+
print(exact_keywords)
|
|
49
|
+
# Output: ['[performance marketing agency]', '[google ads pmax management]', '[meta conversions api audit]']
|
|
50
|
+
|
|
51
|
+
# Generate negative phrase keywords
|
|
52
|
+
neg_phrase = format_keywords(raw_terms, match_type=MatchType.NEGATIVE_PHRASE)
|
|
53
|
+
print(neg_phrase)
|
|
54
|
+
# Output: ['-"performance marketing agency"', '-"google ads pmax management"', '-"meta conversions api audit"']
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 2. Meta Conversions API (CAPI) Hash Normalization
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from pmw_ads import prepare_capi_user_data
|
|
61
|
+
|
|
62
|
+
user_profile = {
|
|
63
|
+
"email": "Client.Growth@Example.com ",
|
|
64
|
+
"phone": "+91 7003477083",
|
|
65
|
+
"first_name": "Subhranil",
|
|
66
|
+
"last_name": "Naskar"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Hashes and formats according to Meta CAPI specification
|
|
70
|
+
hashed_payload = prepare_capi_user_data(user_profile)
|
|
71
|
+
print(hashed_payload)
|
|
72
|
+
# Returns sha256 normalized dictionary ready for Facebook Graph API v20.0
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3. Ad Auction ROAS & Breakeven Analysis
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from pmw_ads import calculate_unit_economics
|
|
79
|
+
|
|
80
|
+
economics = calculate_unit_economics(
|
|
81
|
+
ad_spend=15000.0,
|
|
82
|
+
gross_revenue=67500.0,
|
|
83
|
+
cogs=22000.0,
|
|
84
|
+
orders=450
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
print(f"Blended ROAS: {economics['roas']:.2f}x")
|
|
88
|
+
print(f"Breakeven ROAS: {economics['breakeven_roas']:.2f}x")
|
|
89
|
+
print(f"Profit On Ad Spend (POAS): {economics['poas']:.2f}x")
|
|
90
|
+
print(f"Average Order Value (AOV): ${economics['aov']:.2f}")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Architecture & Linkable Assets
|
|
96
|
+
|
|
97
|
+
In modern ad auctions governed by machine-learning bidding models (Google Ads Smart Bidding and Meta Advantage+), commercial success depends on the mathematical accuracy of data inputs.
|
|
98
|
+
|
|
99
|
+
This package was developed by **[Paid Media World](https://paidmediaworld.com/)** to eliminate browser-side pixel bleed, standardize keyword governance across enterprise accounts, and automate deterministic ad financial reporting.
|
|
100
|
+
|
|
101
|
+
* **Agency Homepage:** [https://paidmediaworld.com/](https://paidmediaworld.com/)
|
|
102
|
+
* **Meta CAPI Attribution Loss & ROAS Simulator:** [https://paidmediaworld.com/meta-capi-calculator/](https://paidmediaworld.com/meta-capi-calculator/)
|
|
103
|
+
* **2026 Performance Marketing Statistics Hub:** [https://paidmediaworld.com/performance-marketing-statistics/](https://paidmediaworld.com/performance-marketing-statistics/)
|
|
104
|
+
* **Google Ads Keyword Match Type Tool:** [https://paidmediaworld.com/keyword-match-type-tool/](https://paidmediaworld.com/keyword-match-type-tool/)
|
|
105
|
+
* **Ad Account Audits:** [5-Day Growth Audit](https://paidmediaworld.com/#contact)
|
|
106
|
+
* **Founder & Lead Architect:** [Subhranil Naskar](https://www.linkedin.com/in/subhraanilnaskar/)
|
|
107
|
+
* **Location:** Kolkata, West Bengal, India
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## License
|
|
112
|
+
|
|
113
|
+
MIT License. Developed with precision by Paid Media World.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pmw-ads: Performance Marketing Engineering Engine
|
|
3
|
+
Built & Maintained by Paid Media World (https://paidmediaworld.com/)
|
|
4
|
+
Lead Architect: Subhranil Naskar
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .keyword_wrapper import format_keyword, format_keywords, MatchType, sanitize_keyword
|
|
8
|
+
from .capi_validator import prepare_capi_user_data, validate_capi_event, hash_field
|
|
9
|
+
from .roas_calculator import calculate_unit_economics, calculate_breakeven_cpa
|
|
10
|
+
|
|
11
|
+
__version__ = "1.0.0"
|
|
12
|
+
__author__ = "Subhranil Naskar (Paid Media World)"
|
|
13
|
+
__all__ = [
|
|
14
|
+
"format_keyword",
|
|
15
|
+
"format_keywords",
|
|
16
|
+
"MatchType",
|
|
17
|
+
"sanitize_keyword",
|
|
18
|
+
"prepare_capi_user_data",
|
|
19
|
+
"validate_capi_event",
|
|
20
|
+
"hash_field",
|
|
21
|
+
"calculate_unit_economics",
|
|
22
|
+
"calculate_breakeven_cpa"
|
|
23
|
+
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Meta Conversions API (CAPI) Payload Normalizer & Validator
|
|
3
|
+
Paid Media World (https://paidmediaworld.com/)
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
import hashlib
|
|
8
|
+
from typing import Dict, Any, List
|
|
9
|
+
|
|
10
|
+
def hash_field(value: str) -> str:
|
|
11
|
+
"""Normalize and SHA-256 hash a customer data parameter according to Meta specifications."""
|
|
12
|
+
if not value:
|
|
13
|
+
return ""
|
|
14
|
+
normalized = value.strip().lower()
|
|
15
|
+
return hashlib.sha256(normalized.encode("utf-8")).hexdigest()
|
|
16
|
+
|
|
17
|
+
def normalize_phone(phone: str) -> str:
|
|
18
|
+
"""Normalize phone number to digits only (E.164 without plus sign)."""
|
|
19
|
+
digits = re.sub(r"\D", "", phone)
|
|
20
|
+
return digits
|
|
21
|
+
|
|
22
|
+
def prepare_capi_user_data(raw_data: Dict[str, str]) -> Dict[str, str]:
|
|
23
|
+
"""
|
|
24
|
+
Format raw customer parameters into Meta CAPI compliant hashed fields.
|
|
25
|
+
Supported keys: email, phone, first_name, last_name, city, state, zip_code, country.
|
|
26
|
+
"""
|
|
27
|
+
hashed = {}
|
|
28
|
+
if "email" in raw_data and raw_data["email"]:
|
|
29
|
+
hashed["em"] = hash_field(raw_data["email"])
|
|
30
|
+
|
|
31
|
+
if "phone" in raw_data and raw_data["phone"]:
|
|
32
|
+
cleaned_phone = normalize_phone(raw_data["phone"])
|
|
33
|
+
hashed["ph"] = hash_field(cleaned_phone)
|
|
34
|
+
|
|
35
|
+
if "first_name" in raw_data and raw_data["first_name"]:
|
|
36
|
+
hashed["fn"] = hash_field(raw_data["first_name"])
|
|
37
|
+
|
|
38
|
+
if "last_name" in raw_data and raw_data["last_name"]:
|
|
39
|
+
hashed["ln"] = hash_field(raw_data["last_name"])
|
|
40
|
+
|
|
41
|
+
if "city" in raw_data and raw_data["city"]:
|
|
42
|
+
hashed["ct"] = hash_field(raw_data["city"])
|
|
43
|
+
|
|
44
|
+
if "state" in raw_data and raw_data["state"]:
|
|
45
|
+
hashed["st"] = hash_field(raw_data["state"])
|
|
46
|
+
|
|
47
|
+
if "zip_code" in raw_data and raw_data["zip_code"]:
|
|
48
|
+
hashed["zp"] = hash_field(raw_data["zip_code"])
|
|
49
|
+
|
|
50
|
+
if "country" in raw_data and raw_data["country"]:
|
|
51
|
+
hashed["country"] = hash_field(raw_data["country"])
|
|
52
|
+
|
|
53
|
+
return hashed
|
|
54
|
+
|
|
55
|
+
def validate_capi_event(event: Dict[str, Any]) -> Dict[str, Any]:
|
|
56
|
+
"""Validate that an event payload contains all required Meta CAPI attributes for deduplication."""
|
|
57
|
+
errors = []
|
|
58
|
+
warnings = []
|
|
59
|
+
|
|
60
|
+
required_top = ["event_name", "event_time", "user_data", "action_source"]
|
|
61
|
+
for f in required_top:
|
|
62
|
+
if f not in event or not event[f]:
|
|
63
|
+
errors.append(f"Missing required top-level attribute: '{f}'")
|
|
64
|
+
|
|
65
|
+
if "event_id" not in event or not event["event_id"]:
|
|
66
|
+
warnings.append("Missing 'event_id'. Without an event_id, deduplication against browser Pixel events is impossible.")
|
|
67
|
+
|
|
68
|
+
user_data = event.get("user_data", {})
|
|
69
|
+
if not isinstance(user_data, dict) or (not user_data.get("em") and not user_data.get("ph")):
|
|
70
|
+
warnings.append("Neither hashed email ('em') nor hashed phone ('ph') found in user_data. Event Match Quality (EMQ) will be degraded.")
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
"valid": len(errors) == 0,
|
|
74
|
+
"errors": errors,
|
|
75
|
+
"warnings": warnings,
|
|
76
|
+
"emq_grade": "High" if len(user_data) >= 3 else ("Medium" if len(user_data) >= 1 else "Poor")
|
|
77
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Google Ads Keyword Match Type Wrapper
|
|
3
|
+
Paid Media World (https://paidmediaworld.com/)
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from typing import List, Union
|
|
9
|
+
|
|
10
|
+
class MatchType(Enum):
|
|
11
|
+
EXACT = "exact"
|
|
12
|
+
PHRASE = "phrase"
|
|
13
|
+
BROAD = "broad"
|
|
14
|
+
NEGATIVE_EXACT = "negative_exact"
|
|
15
|
+
NEGATIVE_PHRASE = "negative_phrase"
|
|
16
|
+
NEGATIVE_BROAD = "negative_broad"
|
|
17
|
+
|
|
18
|
+
ILLEGAL_CHARS_PATTERN = re.compile(r"[\+\@\!\?\*\~\%\^\=\:\;\"\[\]]")
|
|
19
|
+
|
|
20
|
+
def sanitize_keyword(keyword: str) -> str:
|
|
21
|
+
"""Sanitize raw keyword text by stripping illegal Google Ads punctuation and excess whitespace."""
|
|
22
|
+
cleaned = ILLEGAL_CHARS_PATTERN.sub("", keyword)
|
|
23
|
+
return " ".join(cleaned.split()).lower()
|
|
24
|
+
|
|
25
|
+
def format_keyword(keyword: str, match_type: MatchType = MatchType.EXACT) -> str:
|
|
26
|
+
"""Format a single keyword into the requested Google Ads match type."""
|
|
27
|
+
clean = sanitize_keyword(keyword)
|
|
28
|
+
if not clean:
|
|
29
|
+
return ""
|
|
30
|
+
|
|
31
|
+
if match_type == MatchType.EXACT:
|
|
32
|
+
return f"[{clean}]"
|
|
33
|
+
elif match_type == MatchType.PHRASE:
|
|
34
|
+
return f'"{clean}"'
|
|
35
|
+
elif match_type == MatchType.BROAD:
|
|
36
|
+
return clean
|
|
37
|
+
elif match_type == MatchType.NEGATIVE_EXACT:
|
|
38
|
+
return f"-[{clean}]"
|
|
39
|
+
elif match_type == MatchType.NEGATIVE_PHRASE:
|
|
40
|
+
return f'-"{clean}"'
|
|
41
|
+
elif match_type == MatchType.NEGATIVE_BROAD:
|
|
42
|
+
return f"-{clean}"
|
|
43
|
+
return clean
|
|
44
|
+
|
|
45
|
+
def format_keywords(keywords: List[str], match_type: MatchType = MatchType.EXACT) -> List[str]:
|
|
46
|
+
"""Batch format a list of keywords into the requested Google Ads match type."""
|
|
47
|
+
formatted = []
|
|
48
|
+
for kw in keywords:
|
|
49
|
+
res = format_keyword(kw, match_type)
|
|
50
|
+
if res:
|
|
51
|
+
formatted.append(res)
|
|
52
|
+
return formatted
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ad Auction Unit Economics & Profitability Calculator
|
|
3
|
+
Paid Media World (https://paidmediaworld.com/)
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import Dict, Any
|
|
7
|
+
|
|
8
|
+
def calculate_breakeven_cpa(gross_revenue_per_order: float, cogs_per_order: float, merchant_fee_pct: float = 0.029) -> float:
|
|
9
|
+
"""Calculate the maximum allowable acquisition cost before losing money on a single sale."""
|
|
10
|
+
merchant_fee = gross_revenue_per_order * merchant_fee_pct
|
|
11
|
+
breakeven_margin = gross_revenue_per_order - cogs_per_order - merchant_fee
|
|
12
|
+
return max(0.0, breakeven_margin)
|
|
13
|
+
|
|
14
|
+
def calculate_unit_economics(
|
|
15
|
+
ad_spend: float,
|
|
16
|
+
gross_revenue: float,
|
|
17
|
+
cogs: float = 0.0,
|
|
18
|
+
orders: int = 1,
|
|
19
|
+
operating_expenses: float = 0.0
|
|
20
|
+
) -> Dict[str, Any]:
|
|
21
|
+
"""
|
|
22
|
+
Calculate full-funnel media buying unit economics:
|
|
23
|
+
ROAS, Breakeven ROAS, POAS (Profit On Ad Spend), MER, CPA, and Net Contribution Margin.
|
|
24
|
+
"""
|
|
25
|
+
if ad_spend <= 0:
|
|
26
|
+
raise ValueError("ad_spend must be greater than zero.")
|
|
27
|
+
|
|
28
|
+
roas = gross_revenue / ad_spend
|
|
29
|
+
gross_profit = gross_revenue - cogs
|
|
30
|
+
net_contribution = gross_profit - ad_spend - operating_expenses
|
|
31
|
+
poas = gross_profit / ad_spend if ad_spend > 0 else 0.0
|
|
32
|
+
|
|
33
|
+
aov = gross_revenue / orders if orders > 0 else 0.0
|
|
34
|
+
cpa = ad_spend / orders if orders > 0 else 0.0
|
|
35
|
+
|
|
36
|
+
# Breakeven ROAS = Revenue / Gross Profit
|
|
37
|
+
breakeven_roas = (gross_revenue / gross_profit) if gross_profit > 0 else 0.0
|
|
38
|
+
profit_margin_pct = (net_contribution / gross_revenue * 100) if gross_revenue > 0 else 0.0
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
"ad_spend": round(ad_spend, 2),
|
|
42
|
+
"gross_revenue": round(gross_revenue, 2),
|
|
43
|
+
"gross_profit": round(gross_profit, 2),
|
|
44
|
+
"net_contribution": round(net_contribution, 2),
|
|
45
|
+
"orders": orders,
|
|
46
|
+
"aov": round(aov, 2),
|
|
47
|
+
"cpa": round(cpa, 2),
|
|
48
|
+
"roas": round(roas, 2),
|
|
49
|
+
"breakeven_roas": round(breakeven_roas, 2),
|
|
50
|
+
"poas": round(poas, 2),
|
|
51
|
+
"profit_margin_pct": round(profit_margin_pct, 2),
|
|
52
|
+
"is_profitable": net_contribution > 0
|
|
53
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pmw-ads
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Deterministic Performance Marketing Utility: Google Ads Keyword Wrapper, Meta CAPI Payload Validator, and Ad Auction Unit Economics Calculator.
|
|
5
|
+
Home-page: https://paidmediaworld.com/
|
|
6
|
+
Author: Subhranil Naskar
|
|
7
|
+
Author-email: Subhranil Naskar <contact@paidmediaworld.com>
|
|
8
|
+
Project-URL: Homepage, https://paidmediaworld.com/
|
|
9
|
+
Project-URL: Agency Services, https://paidmediaworld.com/#services
|
|
10
|
+
Project-URL: Ad Audit Request, https://paidmediaworld.com/#contact
|
|
11
|
+
Project-URL: Founder Profile, https://www.linkedin.com/in/subhraanilnaskar/
|
|
12
|
+
Project-URL: Source Code, https://github.com/paidmediaworld/pmw-ads
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
17
|
+
Classifier: Intended Audience :: Developers
|
|
18
|
+
Requires-Python: >=3.8
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Dynamic: author
|
|
21
|
+
Dynamic: home-page
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
|
|
24
|
+
# pmw-ads: Performance Marketing Engineering Engine
|
|
25
|
+
|
|
26
|
+
[](https://pypi.org/project/pmw-ads/)
|
|
27
|
+
[](https://opensource.org/licenses/MIT)
|
|
28
|
+
[](https://paidmediaworld.com/)
|
|
29
|
+
|
|
30
|
+
**pmw-ads** is an open-source, deterministic Python utility for media buyers, growth engineers, and technical marketers. Built and maintained by **[Paid Media World](https://paidmediaworld.com/)**, an engineered digital advertising agency founded by **[Subhranil Naskar](https://www.linkedin.com/in/subhraanilnaskar/)**.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Capabilities
|
|
35
|
+
|
|
36
|
+
1. **Google Ads Query & Match Type Wrapper**:
|
|
37
|
+
- Converts raw term lists into exact match (`[term]`), phrase match (`"term"`), broad match, negative exact, and negative phrase formats.
|
|
38
|
+
- Automatically sanitizes illegal Google Ads characters (`+`, `@`, `!`, `?`, `*`, `~`, `%`) and enforces query length limits.
|
|
39
|
+
2. **Meta Conversions API (CAPI) Payload Validator**:
|
|
40
|
+
- Validates client-side and server-side tracking event dictionaries.
|
|
41
|
+
- Enforces SHA-256 normalization for user data (`em`, `ph`, `fn`, `ln`, `ct`, `st`, `zp`, `country`).
|
|
42
|
+
- Ensures event deduplication integrity between `event_id` and browser Pixel triggers.
|
|
43
|
+
3. **Auction Unit Economics & Profitability Engine**:
|
|
44
|
+
- Computes deterministic target CPA, Breakeven ROAS, Marketing Efficiency Ratio (MER), and POAS (Profit On Ad Spend).
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pip install pmw-ads
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Quickstart
|
|
57
|
+
|
|
58
|
+
### 1. Google Ads Keyword Match Type Structuring
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from pmw_ads import format_keywords, MatchType
|
|
62
|
+
|
|
63
|
+
raw_terms = [
|
|
64
|
+
"performance marketing agency",
|
|
65
|
+
"google ads pmax management",
|
|
66
|
+
"meta conversions api audit"
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
# Generate exact match keywords
|
|
70
|
+
exact_keywords = format_keywords(raw_terms, match_type=MatchType.EXACT)
|
|
71
|
+
print(exact_keywords)
|
|
72
|
+
# Output: ['[performance marketing agency]', '[google ads pmax management]', '[meta conversions api audit]']
|
|
73
|
+
|
|
74
|
+
# Generate negative phrase keywords
|
|
75
|
+
neg_phrase = format_keywords(raw_terms, match_type=MatchType.NEGATIVE_PHRASE)
|
|
76
|
+
print(neg_phrase)
|
|
77
|
+
# Output: ['-"performance marketing agency"', '-"google ads pmax management"', '-"meta conversions api audit"']
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 2. Meta Conversions API (CAPI) Hash Normalization
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from pmw_ads import prepare_capi_user_data
|
|
84
|
+
|
|
85
|
+
user_profile = {
|
|
86
|
+
"email": "Client.Growth@Example.com ",
|
|
87
|
+
"phone": "+91 7003477083",
|
|
88
|
+
"first_name": "Subhranil",
|
|
89
|
+
"last_name": "Naskar"
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Hashes and formats according to Meta CAPI specification
|
|
93
|
+
hashed_payload = prepare_capi_user_data(user_profile)
|
|
94
|
+
print(hashed_payload)
|
|
95
|
+
# Returns sha256 normalized dictionary ready for Facebook Graph API v20.0
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 3. Ad Auction ROAS & Breakeven Analysis
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from pmw_ads import calculate_unit_economics
|
|
102
|
+
|
|
103
|
+
economics = calculate_unit_economics(
|
|
104
|
+
ad_spend=15000.0,
|
|
105
|
+
gross_revenue=67500.0,
|
|
106
|
+
cogs=22000.0,
|
|
107
|
+
orders=450
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
print(f"Blended ROAS: {economics['roas']:.2f}x")
|
|
111
|
+
print(f"Breakeven ROAS: {economics['breakeven_roas']:.2f}x")
|
|
112
|
+
print(f"Profit On Ad Spend (POAS): {economics['poas']:.2f}x")
|
|
113
|
+
print(f"Average Order Value (AOV): ${economics['aov']:.2f}")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Architecture & Linkable Assets
|
|
119
|
+
|
|
120
|
+
In modern ad auctions governed by machine-learning bidding models (Google Ads Smart Bidding and Meta Advantage+), commercial success depends on the mathematical accuracy of data inputs.
|
|
121
|
+
|
|
122
|
+
This package was developed by **[Paid Media World](https://paidmediaworld.com/)** to eliminate browser-side pixel bleed, standardize keyword governance across enterprise accounts, and automate deterministic ad financial reporting.
|
|
123
|
+
|
|
124
|
+
* **Agency Homepage:** [https://paidmediaworld.com/](https://paidmediaworld.com/)
|
|
125
|
+
* **Meta CAPI Attribution Loss & ROAS Simulator:** [https://paidmediaworld.com/meta-capi-calculator/](https://paidmediaworld.com/meta-capi-calculator/)
|
|
126
|
+
* **2026 Performance Marketing Statistics Hub:** [https://paidmediaworld.com/performance-marketing-statistics/](https://paidmediaworld.com/performance-marketing-statistics/)
|
|
127
|
+
* **Google Ads Keyword Match Type Tool:** [https://paidmediaworld.com/keyword-match-type-tool/](https://paidmediaworld.com/keyword-match-type-tool/)
|
|
128
|
+
* **Ad Account Audits:** [5-Day Growth Audit](https://paidmediaworld.com/#contact)
|
|
129
|
+
* **Founder & Lead Architect:** [Subhranil Naskar](https://www.linkedin.com/in/subhraanilnaskar/)
|
|
130
|
+
* **Location:** Kolkata, West Bengal, India
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT License. Developed with precision by Paid Media World.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
setup.py
|
|
4
|
+
pmw_ads/__init__.py
|
|
5
|
+
pmw_ads/capi_validator.py
|
|
6
|
+
pmw_ads/keyword_wrapper.py
|
|
7
|
+
pmw_ads/roas_calculator.py
|
|
8
|
+
pmw_ads.egg-info/PKG-INFO
|
|
9
|
+
pmw_ads.egg-info/SOURCES.txt
|
|
10
|
+
pmw_ads.egg-info/dependency_links.txt
|
|
11
|
+
pmw_ads.egg-info/top_level.txt
|
|
12
|
+
tests/test_engine.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pmw_ads
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pmw-ads"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Subhranil Naskar", email="contact@paidmediaworld.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "Deterministic Performance Marketing Utility: Google Ads Keyword Wrapper, Meta CAPI Payload Validator, and Ad Auction Unit Economics Calculator."
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Topic :: Scientific/Engineering :: Information Analysis",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
]
|
|
21
|
+
dependencies = []
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
"Homepage" = "https://paidmediaworld.com/"
|
|
25
|
+
"Agency Services" = "https://paidmediaworld.com/#services"
|
|
26
|
+
"Ad Audit Request" = "https://paidmediaworld.com/#contact"
|
|
27
|
+
"Founder Profile" = "https://www.linkedin.com/in/subhraanilnaskar/"
|
|
28
|
+
"Source Code" = "https://github.com/paidmediaworld/pmw-ads"
|
pmw_ads-1.0.0/setup.cfg
ADDED
pmw_ads-1.0.0/setup.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
with open("README.md", "r", encoding="utf-8") as fh:
|
|
4
|
+
long_description = fh.read()
|
|
5
|
+
|
|
6
|
+
setup(
|
|
7
|
+
name="pmw-ads",
|
|
8
|
+
version="1.0.0",
|
|
9
|
+
author="Subhranil Naskar",
|
|
10
|
+
author_email="contact@paidmediaworld.com",
|
|
11
|
+
description="Deterministic Performance Marketing Utility: Google Ads Keyword Wrapper, Meta CAPI Payload Validator, and Ad Auction Unit Economics Calculator.",
|
|
12
|
+
long_description=long_description,
|
|
13
|
+
long_description_content_type="text/markdown",
|
|
14
|
+
url="https://paidmediaworld.com/",
|
|
15
|
+
project_urls={
|
|
16
|
+
"Homepage": "https://paidmediaworld.com/",
|
|
17
|
+
"Agency Services": "https://paidmediaworld.com/#services",
|
|
18
|
+
"Ad Audit Request": "https://paidmediaworld.com/#contact",
|
|
19
|
+
"Founder Profile": "https://www.linkedin.com/in/subhraanilnaskar/",
|
|
20
|
+
"Source Code": "https://github.com/paidmediaworld/pmw-ads",
|
|
21
|
+
},
|
|
22
|
+
packages=find_packages(),
|
|
23
|
+
classifiers=[
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
],
|
|
28
|
+
python_requires=">=3.8",
|
|
29
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for pmw-ads package
|
|
3
|
+
Paid Media World (https://paidmediaworld.com/)
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import unittest
|
|
7
|
+
from pmw_ads import (
|
|
8
|
+
format_keyword,
|
|
9
|
+
format_keywords,
|
|
10
|
+
MatchType,
|
|
11
|
+
sanitize_keyword,
|
|
12
|
+
prepare_capi_user_data,
|
|
13
|
+
validate_capi_event,
|
|
14
|
+
calculate_unit_economics,
|
|
15
|
+
calculate_breakeven_cpa
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
class TestPMWAds(unittest.TestCase):
|
|
19
|
+
|
|
20
|
+
def test_keyword_formatting(self):
|
|
21
|
+
term = " Google Ads Agency! "
|
|
22
|
+
self.assertEqual(format_keyword(term, MatchType.EXACT), "[google ads agency]")
|
|
23
|
+
self.assertEqual(format_keyword(term, MatchType.PHRASE), '"google ads agency"')
|
|
24
|
+
self.assertEqual(format_keyword(term, MatchType.NEGATIVE_EXACT), "-[google ads agency]")
|
|
25
|
+
|
|
26
|
+
def test_capi_normalization(self):
|
|
27
|
+
user_info = {
|
|
28
|
+
"email": " Test.User@Gmail.Com ",
|
|
29
|
+
"phone": "+91 (700) 347-7083",
|
|
30
|
+
"first_name": "Subhranil"
|
|
31
|
+
}
|
|
32
|
+
hashed = prepare_capi_user_data(user_info)
|
|
33
|
+
self.assertIn("em", hashed)
|
|
34
|
+
self.assertIn("ph", hashed)
|
|
35
|
+
self.assertIn("fn", hashed)
|
|
36
|
+
self.assertEqual(len(hashed["em"]), 64) # Valid SHA-256 length
|
|
37
|
+
|
|
38
|
+
def test_unit_economics(self):
|
|
39
|
+
econ = calculate_unit_economics(ad_spend=1000, gross_revenue=4000, cogs=1000, orders=40)
|
|
40
|
+
self.assertEqual(econ["roas"], 4.0)
|
|
41
|
+
self.assertEqual(econ["aov"], 100.0)
|
|
42
|
+
self.assertEqual(econ["cpa"], 25.0)
|
|
43
|
+
self.assertTrue(econ["is_profitable"])
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
unittest.main()
|