Skip to content

Commit 25716ee

Browse files
authored
Merge pull request #218 from w3c/attribution
Spec n-last-touch attribution
2 parents 2264e71 + 42d07f2 commit 25716ee

File tree

1 file changed

+83
-18
lines changed

1 file changed

+83
-18
lines changed

api.bs

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ The histogram created by the [=conversion report=] is constructed as follows:
358358
a histogram consisting entirely of zeros (0) is constructed.
359359

360360
* If one or more matching impressions is found, the browser runs the attribution
361-
logic (default last-touch) to select the most recent impression. The provided conversion
361+
logic (default last-n-touch) to select the most recent impression. The provided conversion
362362
value is added to a histogram at the bucket that was specified at the time of the
363363
attributed impression. All other buckets are set to zero.
364364

@@ -597,6 +597,7 @@ dictionary AttributionImpressionOptions {
597597
sequence<USVString> conversionSites = [];
598598
sequence<USVString> conversionCallers = [];
599599
unsigned long lifetimeDays = 30;
600+
long priority = 0;
600601
};
601602

602603
dictionary AttributionImpressionResult {
@@ -646,6 +647,10 @@ The arguments to <a method for=Attribution>saveImpression()</a> are as follows:
646647
The [=user agent=] should impose an upper limit on the lifetime,
647648
and silently reduce the value specified here if it exceeds that limit.
648649
</dd>
650+
<dt><dfn>priority</dfn></dt>
651+
<dd>
652+
An integer used to sort [=impressions=] during attribution.
653+
</dd>
649654
</dl>
650655

651656
## Requesting Attribution for a Conversion ## {#measure-conversion-api}
@@ -721,7 +726,10 @@ contribute to the histogram, i.e., will be uniformly zero.
721726

722727
<xmp highlight=js>
723728
const attributionDetails = {
724-
logic: "last-touch",
729+
logic: "last-n-touch",
730+
logicOptions: {
731+
credit: [.25, .25, .5]
732+
}
725733
value: 3,
726734
maxValue: 7,
727735
};
@@ -759,13 +767,18 @@ dictionary AttributionConversionOptions {
759767
sequence<USVString> impressionSites = [];
760768
sequence<USVString> impressionCallers = [];
761769

762-
AttributionLogic logic = "last-touch";
770+
AttributionLogic logic = "last-n-touch";
771+
AttributionLogicOptions logicOptions;
763772
unsigned long value = 1;
764773
unsigned long maxValue = 1;
765774
};
766775

767776
enum AttributionLogic {
768-
"last-touch",
777+
"last-n-touch",
778+
};
779+
780+
dictionary AttributionLogicOptions {
781+
sequence<double> credit;
769782
};
770783

771784
dictionary AttributionConversionResult {
@@ -960,6 +973,7 @@ The [=impression store=] is a [=set=] of [=impression|impressions=]:
960973
<dfn>Timestamp</dfn>: The time at which <a>saveImpression()</a> was called.
961974
<dfn>Lifetime</dfn>: The [=duration=] an [=/impression=] remains eligible for attribution, either from the call to <a>saveImpression()</a>, or a [=/user agent=]-defined limit.
962975
<dfn>Histogram Index</dfn>: The histogram index passed to <a>saveImpression()</a>.
976+
<dfn>Priority</dfn>: An integer used to sort [=/impressions=] during attribution.
963977
</pre>
964978
</div>
965979

@@ -1382,6 +1396,8 @@ The <dfn method for=Attribution>saveImpression(|options|)</dfn> method steps are
13821396
multiplied by a [=duration=] of one day
13831397
: [=impression/Histogram Index=]
13841398
:: |options|.{{AttributionImpressionOptions/histogramIndex}}
1399+
: [=impression/Priority=]
1400+
:: |options|.{{AttributionImpressionOptions/priority}}
13851401
1. If the Attribution API is [[#opt-out|enabled]],
13861402
save |impression| to the [=impression store=].
13871403
1. Let |result| be a new {{AttributionImpressionResult}}.
@@ -1464,6 +1480,7 @@ The <dfn method for=Attribution>measureConversion(|options|)</dfn> method steps
14641480
<dfn>Impression Sites</dfn>: A [=set=] of [=sites=].
14651481
<dfn>Impression Callers</dfn>: A [=set=] of [=sites=].
14661482
<dfn>Logic</dfn>: A {{AttributionLogic}}.
1483+
<dfn>Logic Options</dfn>: An {{AttributionLogicOptions}}.
14671484
<dfn>Value</dfn>: A [=32-bit unsigned integer=].
14681485
<dfn>Max Value</dfn>: A [=32-bit unsigned integer=].
14691486
</pre>
@@ -1486,16 +1503,25 @@ To <dfn>validate {{AttributionConversionOptions}}</dfn> |options|:
14861503
or is greater than the <dfn ignore>maximum aggregation-service histogram size</dfn>,
14871504
if any, for |options|.{{AttributionConversionOptions/aggregationService}},
14881505
throw a {{RangeError}}.
1506+
1. Let |credit| be «1».
14891507
1. Switch on the value of |options|.{{AttributionConversionOptions/logic}}:
14901508
<dl class="switch">
1491-
: <a enum-value for=AttributionLogic>"last-touch"</a>
1509+
: <a enum-value for=AttributionLogic>"last-n-touch"</a>
14921510
:: Perform the following steps:
14931511
1. If |options|.{{AttributionConversionOptions/value}} is 0,
14941512
throw a {{RangeError}}.
14951513
1. If |options|.{{AttributionConversionOptions/value}}
14961514
is greater than |options|.{{AttributionConversionOptions/maxValue}},
14971515
throw a {{RangeError}}.
1516+
1. If |options|.{{AttributionConversionOptions}}.{{AttributionLogicOptions/credit}} [=map/exists=]:
1517+
1. Set |credit| to |options|.{{AttributionConversionOptions}}.{{AttributionLogicOptions/credit}}.
1518+
1. If |credit| is not a [=list=] or [=list/is empty=], throw a {{RangeError}}.
1519+
1. If any of the [=list/items=] of |credit| are less than or equal to 0, throw a {{RangeError}}.
1520+
1. If the [=list/size=] of |credit| exceeds an implementation-defined maximum, throw a {{RangeError}}.
14981521
</dl>
1522+
1. Let |validatedLogicOptions| be a {{AttributionLogicOptions}} with the following fields:
1523+
: {{AttributionLogicOptions/credit}}
1524+
:: |credit|
14991525
1. Let |lookback| be |options|.{{AttributionConversionOptions/lookbackDays}} [=days=]
15001526
if it [=map/exists=], the [=implementation-defined=] maximum otherwise.
15011527
1. If |lookback| is 0 [=days=], throw a {{RangeError}}.
@@ -1535,6 +1561,8 @@ To <dfn>validate {{AttributionConversionOptions}}</dfn> |options|:
15351561
:: |impressionCallers|
15361562
: [=validated conversion options/Logic=]
15371563
:: |options|.{{AttributionConversionOptions/logic}}
1564+
: [=validated conversion options/Logic Options=]
1565+
:: |validatedLogicOptions|
15381566
: [=validated conversion options/Value=]
15391567
:: |options|.{{AttributionConversionOptions/value}}
15401568
: [=validated conversion options/Max Value=]
@@ -1591,10 +1619,11 @@ To <dfn>do attribution and fill a histogram</dfn>, given
15911619

15921620
1. Switch on |options|' [=validated conversion options/logic=]:
15931621
<dl class="switch">
1594-
: <a enum-value for=AttributionLogic>"last-touch"</a>
1595-
:: Return the result of [=fill a histogram with last-touch attribution=] with |matchedImpressions|,
1596-
|options|' [=validated conversion options/histogram size=], and
1597-
|options|' [=validated conversion options/value=].
1622+
: <a enum-value for=AttributionLogic>"last-n-touch"</a>
1623+
:: Return the result of [=fill a histogram with last-n-touch attribution=] with |matchedImpressions|,
1624+
|options|' [=validated conversion options/histogram size=],
1625+
|options|' [=validated conversion options/value=], and
1626+
|options|' [=validated conversion options/logic options=].{{AttributionLogicOptions/credit}}.
15981627

15991628
</dl>
16001629

@@ -1668,23 +1697,50 @@ To perform <dfn>common matching logic</dfn>, given
16681697

16691698
</div>
16701699

1671-
#### Last-Touch Attribution #### {#last-touch-attribution}
1700+
#### Last-N-Touch Attribution #### {#last-n-touch-attribution}
16721701

16731702
<div algorithm>
1674-
To <dfn>fill a histogram with last-touch attribution</dfn>, given a [=set=] of
1675-
[=impressions=] |matchedImpressions|, an integer |histogramSize|, and an integer |value|:
1703+
To <dfn>fairly allocate credit</dfn>, given a [=list=] of doubles |credit| and an integer |value|:
1704+
1. Multiply each entry in |credit| by |value|.
1705+
1. Let |sumCredit| be the sum of |credit|'s [=list/items=].
1706+
1. Let |rawNormalizedCredit| be the result of element-wise dividing each [=list/item=] in |credit| by |sumCredit|.
1707+
1. Let |normalizedCredit| be |rawNormalizedCredit|, with each [=list/item=] rounded towards positive Infinity and converted to an integer.
1708+
1. Let |shuffledFractionalIndices| be [=list/get the indices|the indices=] of |credit|, ordered randomly.
1709+
1. [=list/remove|Remove=] any index |i| from |shuffledFractionalIndices| where |rawNormalizedCredit|[|i|] has no fractional part.
1710+
1. [=list/iterate|For each=] |index| of |shuffledFractionalIndices|:
1711+
1. If the sum of |normalizedCredit|'s [=list/items=] is equal to |value|, [=iteration/break=].
1712+
1. Decrement |normalizedCredit|[|index|] by 1.
1713+
1. Return |normalizedCredit|.
1714+
<p class=note>The algorithm aims to 1) allocate exactly |value| total value across assignments,
1715+
2) never bias towards any particular credit assignment in rounding, and
1716+
3) never incur an error greater than 1 to any assignment.
1717+
<p class=issue>
1718+
While the algorithm is "unbiased" in the sense that the probability of being decremented is equal
1719+
across entries, ideally the final array of value is unbiased in the sense that its expectation
1720+
should be equal to |normalizedCredit|. This is not achieved by the current algorithm.
16761721

1677-
1. [=Assert=]: |matchedImpressions| is [=set/is empty|not empty=].
1722+
</div>
16781723

1679-
1. Let |impression| be the value in |matchedImpressions| with the most recent
1680-
[=impression/timestamp=].
1724+
<div algorithm>
1725+
To <dfn>fill a histogram with last-n-touch attribution</dfn>, given a [=set=] of
1726+
[=impressions=] |matchedImpressions|, an integer |histogramSize|, an integer |value|, and
1727+
a [=list=] of doubles |credit|:
16811728

1729+
1. [=Assert=]: |matchedImpressions| is [=set/is empty|not empty=].
1730+
1. Let |sortedImpressions| be |matchedImpressions|, [=list/sorted in ascending order=]
1731+
by [=impression/priority=], then by [=impression/timestamp=].
1732+
1. Let |N| be the minimum of the [=list/size=] of |credit|, and the [=list/size=] of |sortedImpressions|.
1733+
1. Let |lastNImpressions| be the last |N| entries of |sortedImpressions|.
1734+
1. [=list/remove|Remove=] all but the last |N| entries of |credit|.
1735+
1. Let |normalizedCredit| be the result of [=fairly allocate credit|fairly allocating credit=] with |credit| and |value|.
16821736
1. Let |histogram| be the result of invoking [=create an all-zero histogram=]
16831737
with |histogramSize|.
16841738

1685-
1. Let |index| be |impression|'s [=impression/histogram index=].
1686-
1687-
1. If |index| is less than |histogram|'s [=list/size=], set |histogram|[|index|] to |value|.
1739+
1. [=list/iterate|For each=] |i| of [=list/get the indices|the indices=] of |lastNImpressions|:
1740+
1. Let |impression| be |lastNImpressions|[|i|].
1741+
1. Let |value| be |normalizedCredit|[|i|].
1742+
1. Let |index| be |impression|'s [=impression/histogram index=].
1743+
1. If |index| is less than |histogram|'s [=list/size=], increment |histogram|[|index|] by |value|.
16881744

16891745
1. Return |histogram|.
16901746

@@ -1753,6 +1809,11 @@ the {{AttributionImpressionOptions}} dictionary passed to
17531809
Value of <a dict-member for=AttributionImpressionOptions>histogramIndex</a>,
17541810
a non-negative [=structured header/integer=]. This key is required.
17551811
</dd>
1812+
<dt><dfn noexport><code>priority</code></dfn></dt>
1813+
<dd>
1814+
Value of <a dict-member for=AttributionImpressionOptions>priority</a>,
1815+
an [=structured header/integer=]. This key is optional.
1816+
</dd>
17561817
<dt><dfn noexport><code>match-value</code></dfn></dt>
17571818
<dd>
17581819
Value of <a dict-member for=AttributionImpressionOptions>matchValue</a>,
@@ -1787,6 +1848,8 @@ To <dfn noexport>parse a `Save-Impression` header</dfn> given a [=header value=]
17871848
1. If |matchValue| is not an [=structured header/integer=] or is less than 0, return an error.
17881849
1. Let |lifetimeDays| be |dict|["<code>[=save-impression/lifetime-days=]</code>"] [=map/with default=] 30.
17891850
1. If |lifetimeDays| is not an [=structured header/integer=] or is less than or equal to 0, return an error.
1851+
1. Let |priority| be |dict|["<code>[=save-impression/priority=]</code>"] [=map/with default=] 0.
1852+
1. If |priority| is not an [=structured header/integer=], return an error.
17901853
1. Return a new {{AttributionImpressionOptions}} with the following items:
17911854
: {{AttributionImpressionOptions/histogramIndex}}
17921855
:: |histogramIndex|
@@ -1798,6 +1861,8 @@ To <dfn noexport>parse a `Save-Impression` header</dfn> given a [=header value=]
17981861
:: |conversionCallers|
17991862
: {{AttributionImpressionOptions/lifetimeDays}}
18001863
:: |lifetimeDays|
1864+
: {{AttributionImpressionOptions/priority}}
1865+
:: |priority|
18011866

18021867
</div>
18031868

0 commit comments

Comments
 (0)