Skip to content

Commit

Permalink
feat: add utility api for forms metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
nit23uec committed Jan 30, 2025
1 parent ae1b0e6 commit f5bec3f
Show file tree
Hide file tree
Showing 5 changed files with 179 additions and 5 deletions.
98 changes: 98 additions & 0 deletions packages/spacecat-shared-utils/src/formcalc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright 2025 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

const DAILY_PAGEVIEW_THRESHOLD = 200;
const CR_THRESHOLD_RATIO = 0.2;
const MOBILE = 'mobile';
const DESKTOP = 'desktop';

/**
* Aggregates the form vitals by device type.
*
* @param {*} formVitalsCollection - form vitals collection
* @returns {object} - aggregated form vitals by device type
*/
function aggregateFormVitalsByDevice(formVitalsCollection) {
const resultMap = new Map();

formVitalsCollection.forEach((item) => {
const {
url, formview = {}, formengagement = {}, pageview = {}, formsubmit = {},
} = item;

const totals = {
formview: { total: 0, desktop: 0, mobile: 0 },
formengagement: { total: 0, desktop: 0, mobile: 0 },
pageview: { total: 0, desktop: 0, mobile: 0 },
formsubmit: { total: 0, desktop: 0, mobile: 0 },
};

const calculateSums = (metric, initialTarget) => {
const updatedTarget = { ...initialTarget }; // Create a new object to store the updated totals
Object.entries(metric).forEach(([key, value]) => {
updatedTarget.total += value;
if (key.startsWith(DESKTOP)) {
updatedTarget.desktop += value;
} else if (key.startsWith(MOBILE)) {
updatedTarget.mobile += value;
}
});
return updatedTarget; // Return the updated target
};

totals.formview = calculateSums(formview, totals.formview);
totals.formengagement = calculateSums(formengagement, totals.formengagement);
totals.pageview = calculateSums(pageview, totals.pageview);
totals.formsubmit = calculateSums(formsubmit, totals.formsubmit);

resultMap.set(url, totals);
});

return resultMap;
}

function hasHighPageViews(interval, pageViews) {
return pageViews > DAILY_PAGEVIEW_THRESHOLD * interval;
}

function hasLowerConversionRate(formSubmit, formViews) {
return formSubmit / formViews < CR_THRESHOLD_RATIO;
}

/**
* Returns the form urls with high form views and low conversion rate
*
* @param {*} formVitalsCollection - form vitals collection
* @returns {Array} - urls with high form views and low conversion rate
*/
export function getHighFormViewsLowConversionMetrics(formVitalsCollection, interval) {
const resultMap = aggregateFormVitalsByDevice(formVitalsCollection);
const urls = [];
resultMap.forEach((metrics, url) => {
const pageViews = metrics.pageview.total;
// Default to pageViews if formViews are not available
const formViews = metrics.formview.total || pageViews;
const formEngagement = metrics.formengagement.total;
const formSubmit = metrics.formsubmit.total || formEngagement;

if (hasHighPageViews(interval, pageViews) && hasLowerConversionRate(formSubmit, formViews)) {
urls.push({
url,
pageViews,
formViews,
formEngagement,
formSubmit,
});
}
});
return urls;
}
19 changes: 14 additions & 5 deletions packages/spacecat-shared-utils/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,16 @@ declare function replacePlaceholders(content: string, placeholders: object): str
* or null if an error occurs.
*/
declare function getPrompt(placeholders: object, filename: string, log: object):
Promise<string|null>;
Promise<string | null>;

/**
* Retrieves the high-form-view-low-form-conversion metrics from the provided array of form vitals.
* @param {Object[]} formVitals - An array of form vitals.
* @param {number} interval - The interval in days.
* @returns {Object[]} - An array of high-form-view-low-form-conversion metrics.
*/
declare function getHighFormViewsLowConversionMetrics(formVitals: object[], interval: number):
object[];

/**
* Retrieves stored metrics from S3.
Expand All @@ -179,7 +188,7 @@ declare function getPrompt(placeholders: object, filename: string, log: object):
* @returns {Promise<any|*[]>} - The stored metrics
*/
export function getStoredMetrics(config: object, context: object):
Promise<Array<object>>;
Promise<Array<object>>;

/**
* Stores metrics in S3.
Expand All @@ -198,8 +207,8 @@ export function getStoredMetrics(config: object, context: object):
export function storeMetrics(content: object, config: object, context: object): Promise<string>;

export function s3Wrapper(fn: (request: object, context: object) => Promise<Response>):
(request: object, context: object) => Promise<Response>;
(request: object, context: object) => Promise<Response>;

export function fetch(url: string|Request, options?: RequestOptions): Promise<Response>;
export function fetch(url: string | Request, options?: RequestOptions): Promise<Response>;

export function tracingFetch(url: string|Request, options?: RequestOptions): Promise<Response>;
export function tracingFetch(url: string | Request, options?: RequestOptions): Promise<Response>;
1 change: 1 addition & 0 deletions packages/spacecat-shared-utils/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@ export { s3Wrapper } from './s3.js';

export { fetch } from './adobe-fetch.js';
export { tracingFetch } from './tracing-fetch.js';
export { getHighFormViewsLowConversionMetrics } from './formcalc.js';
65 changes: 65 additions & 0 deletions packages/spacecat-shared-utils/test/formcalc.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2025 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

/* eslint-env mocha */

import { expect } from 'chai';
import {
getHighFormViewsLowConversionMetrics,
} from '../src/index.js';

describe('Form Calc functions', () => {
it('getHighFormViewsLowConversion', () => {
const formVitalsCollection = [
{
url: 'https://www.surest.com/contact-us',
formsubmit: {
'desktop:windows': 100,
},
formview: {},
formengagement: {
'desktop:windows': 700,
'mobile:ios': 300,
},
pageview: {
'desktop:windows': 5690,
'mobile:ios': 1000,
},
},
{
url: 'https://www.surest.com/info/win',
formsubmit: {
},
formview: {},
formengagement: {
'desktop:windows': 4000,
'mobile:ios': 300,
},
pageview: {
'desktop:windows': 4670,
'mobile:ios': 1000,
},
},
];

const result = getHighFormViewsLowConversionMetrics(formVitalsCollection, 7);
expect(result).to.eql([
{
url: 'https://www.surest.com/contact-us',
pageViews: 6690,
formViews: 6690,
formEngagement: 1000,
formSubmit: 100,
},
]);
});
});
1 change: 1 addition & 0 deletions packages/spacecat-shared-utils/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe('Index Exports', () => {
'stripWWW',
'toBoolean',
'tracingFetch',
'getHighFormViewsLowConversionMetrics',
];

it('exports all expected functions', () => {
Expand Down

0 comments on commit f5bec3f

Please sign in to comment.